xorl %eax, %eax

CVE-2010-0211 & CVE-2010-0212: OpenLDAP Invalid Pointer Access

leave a comment »

Everyone knows OpenLDAP project and it’s widely used. As we can read in the official bug report that was sent to openldap-its mailing list, the vulnerabilities were found by Ilkka Mattila and Tuomas Salom.ki and are both of them pre-authentication bugs. So, the first one (CVE-2010-0211) was reported like this:

OpenLDAP crashes with segfault during the processing of a modrdn call with
maliciously formed destination rdn string. No authentication is required to
trigger this vulnerability.

This vulnerability can be found at servers/slapd/modrdn.c and specifically in the following code…

int
slap_modrdn2mods(
        Operation       *op,
        SlapReply       *rs )
{
        int             a_cnt, d_cnt;
        LDAPRDN         old_rdn = NULL;
        LDAPRDN         new_rdn = NULL;
      ...
                if( desc->ad_type->sat_equality->smr_normalize) {
                        mod_tmp->sml_nvalues = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) );
                        (void) (*desc->ad_type->sat_equality->smr_normalize)(
                                SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
                                desc->ad_type->sat_syntax,
                                desc->ad_type->sat_equality,
                                &mod_tmp->sml_values[0],
                                &mod_tmp->sml_nvalues[0], NULL );
                        mod_tmp->sml_nvalues[1].bv_val = NULL;
                } else {
                        mod_tmp->sml_nvalues = NULL;
                }
      ...
        return rs->sr_err;
}

The above code is from 2.4.22 release of OpenLDAP and it’s responsible for modifying RDNs (Relative Distinguished Name). The vulnerable code path will be executed in case of the OpenLDAP receiving a new RDN that contains a UTF-8 value. However, as Ilkka Mattila and Tuomas Salom.ki noted, inside the ‘if’ clause of the provided code snippet of slap_modrdn2mods(), after allocating some space using ch_malloc() there is a call to a function pointer named ‘desc->ad_type->sat_equality->smr_normalize()’ which is a wrapper and in this scenario it is used to normalize the UTF-8 RDN through UTF8bvnormalize(). The problem is that this callback function when used for UTF-8 RDNs can fail if it attempts to handle a truncated RDN entry. If this is the case, it leaves ‘mod_tmp->sml_nvalues’ uninitialized and continues with the execution since there is no check for ‘desc->ad_type->sat_equality->smr_normalize()”s return value.
When OpenLDAP will attempt to free this pointer using slap_mods_free() inside do_modrdn() which is available at servers/slapd/modrdn.c it’ll execute the following:

int
do_modrdn(
    Operation   *op,
    SlapReply   *rs
)
{
      ...
cleanup:
      ...
        if ( op->orr_modlist != NULL )
                slap_mods_free( op->orr_modlist, 1 );
      ...
        return rs->sr_err;
}

This function can be read at servers/slapd/mods.c and it’s the following…

void
slap_mods_free(
    Modifications       *ml,
    int                 freevals )
{
        Modifications *next;

        for ( ; ml != NULL; ml = next ) {
                next = ml->sml_next;

                if ( freevals )
                        slap_mod_free( &ml->sml_mod, 0 );
                free( ml );
        }
}

It’s a simple wrapper around free(3) that it will free every pointer as long it isn’t NULL. In our case, ‘mod_tmp->sml_nvalues’ isn’t NULL, it’s just uninitialized. This leads in attempting to free an invalid pointer. In case you wonder what’s the ‘Modifications’ data type, here is its definition as seen at servers/slapd/slap.h:

typedef struct Modifications Modifications;
      ...
struct Modifications {
        Modification    sml_mod;
#define sml_op          sml_mod.sm_op
#define sml_flags       sml_mod.sm_flags
#define sml_desc        sml_mod.sm_desc
#define sml_type        sml_mod.sm_type
#define sml_values      sml_mod.sm_values
#define sml_nvalues     sml_mod.sm_nvalues
#define sml_numvals     sml_mod.sm_numvals
        Modifications   *sml_next;
};

To trigger this, Ilkka Mattila and Tuomas Salom.ki used ldapmodrdn(1) utility like this:

ldapmodrdn -x cn=something,dc=anything cn=#80

Which instructs the application to force “simple authentication” (not SASL) and rename the CN (Common Name) entry “something” to “#80” which is an incorrect UTF-8 value for the DN (Distinguished Name) “anything”. Clearly, since this attempts to free an uninitialized pointer it might not always work depending on the pointer it accesses each time.
To fix this they updated slap_modrdn2mods() to include the missing return value check like this:

 			mod_tmp->sml_nvalues = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) );
-			(void) (*desc->ad_type->sat_equality->smr_normalize)(
+			rs->sr_err = desc->ad_type->sat_equality->smr_normalize(
 				SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
 				desc->ad_type->sat_syntax,
 				desc->ad_type->sat_equality,
 				&mod_tmp->sml_values[0],
 				&mod_tmp->sml_nvalues[0], NULL );
+			if (rs->sr_err != LDAP_SUCCESS) {
+				ch_free(mod_tmp->sml_nvalues);
+				ch_free(mod_tmp->sml_values[0].bv_val);
+				ch_free(mod_tmp->sml_values);
+				ch_free(mod_tmp);
+				goto done;
+			}
 			mod_tmp->sml_nvalues[1].bv_val = NULL;
 		} else {

Now, if the return value isn’t ‘LDAP_SUCCESS’ it will use the ch_free() wrapper routine to free only the initialized pointers.
So, this was the first vulnerability.

The second one (CVE-2010-0212) is a NULL pointer dereference which was also found by Ilkka Mattila and Tuomas Salom.ki. This starts of from the same code path but smr_normalize() callback function will point to IA5StringNormalize() in case of an empty string. This leads to the following code at servers/slapd/schema_init.c:

static int
IA5StringNormalize(
        slap_mask_t use,
        Syntax *syntax,
        MatchingRule *mr,
        struct berval *val,
        struct berval *normalized,
        void *ctx )
{
      ...
        p = val->bv_val;
      ...
         return LDAP_SUCCESS;
}

Here, ‘val’ is NULL since the string was empty and the assignment to ‘p’ results in triggering a NULL pointer dereference. The fix of this bug was placed in the early beginning of the packet processing at LDAPRDN_rewrite() function like this:

 		ava->la_attr = ad->ad_cname;
 
 		if( ava->la_flags & LDAP_AVA_BINARY ) {
-			if( ava->la_value.bv_len == 0 ) {
-				/* BER encoding is empty */
-				return LDAP_INVALID_SYNTAX;
-			}
+			/* AVA is binary encoded, not supported */
+			return LDAP_INVALID_SYNTAX;
 
 			/* Do not allow X-ORDERED 'VALUES' naming attributes */
 		} else if( ad->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
 			return LDAP_INVALID_SYNTAX;
 
-			/* AVA is binary encoded, don't muck with it */
 		} else if( flags & SLAP_LDAPDN_PRETTY ) {
 			transf = ad->ad_type->sat_syntax->ssyn_pretty;
 			if( !transf ) {

Previously the zero length values were marked with AVA flag as you can see in the above code that was removed. This meant that this was an empty BER encoded value and it returned with ‘LDAP_INVALID_SYNTAX’ that lead us to the NULL pointer dereference shown above inside IA5StringNormalize(). To avoid this, the check was removed and the return value is immediately returned if the ‘LDA_AVA_BINARY’ flag is present. Also, the following code was committed to this routine:

 		}
+		/* reject empty values */
+		if (!ava->la_value.bv_len) {
+			return LDAP_INVALID_SYNTAX;
+		}
 	}
 	rc = LDAP_SUCCESS;

Which will recognize zero length values as invalid syntax messages and reject them.
Of course, to trigger it you only have to rewrite an existing value with an empty one like this:

ldapmodrdn -x dc=something,dc=anything dc= 

Where the DC (Domain Component) “something” of “anything” is changed to an empty string.

Written by xorl

September 21, 2010 at 20:21

Posted in bugs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s