]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/sasl.c
remove dbenv->lock_put() call from transaction-protected operations
[openldap] / servers / slapd / sasl.c
index 3d6113b9170c164c11108652d279df2690428d29..ba1cdb0c2fae812807adca27485fa1e622506513 100644 (file)
@@ -176,33 +176,43 @@ int slap_sasl_getdn( Connection *conn, char *id, int len,
 
        ctx = conn->c_sasl_context;
 
-       /* An authcID needs to be converted to authzID form */
+       /* An authcID needs to be converted to authzID form. Set the
+        * values directly into *dn; they will be normalized later. (and
+        * normalizing always makes a new copy.) An ID from a TLS certificate
+        * is already normalized, so copy it and skip normalization.
+        */
        if( flags & FLAG_GETDN_AUTHCID ) {
+#ifdef HAVE_TLS
                if( conn->c_is_tls && conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len
                        && ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) ) {
                        /* X.509 DN is already normalized */
                        do_norm = 0;
                        is_dn = SET_DN;
+                       ber_str2bv( id, len, 1, dn );
 
-               } else {
+               } else
+#endif
+               {
                        /* convert to u:<username> form */
                        is_dn = SET_U;
+                       dn->bv_val = id;
+                       dn->bv_len = len;
                }
-               ber_str2bv( id, len, 1, dn );
        }
        if( !is_dn ) {
                if( !strncasecmp( id, "u:", sizeof("u:")-1 )) {
                        is_dn = SET_U;
-                       ber_str2bv( id+2, len-2, 1, dn );
+                       dn->bv_val = id+2;
+                       dn->bv_len = len-2;
                } else if ( !strncasecmp( id, "dn:", sizeof("dn:")-1) ) {
                        is_dn = SET_DN;
-                       ber_str2bv( id+3, len-3, 1, dn );
+                       dn->bv_val = id+3;
+                       dn->bv_len = len-3;
                }
        }
 
-       /* An authzID must be properly prefixed */
-       if( (flags & FLAG_GETDN_AUTHZID) && !is_dn ) {
-               free( dn->bv_val );
+       /* No other possibilities from here */
+       if( !is_dn ) {
                dn->bv_val = NULL;
                dn->bv_len = 0;
                return( LDAP_INAPPROPRIATE_AUTH );
@@ -210,10 +220,14 @@ int slap_sasl_getdn( Connection *conn, char *id, int len,
 
        /* Username strings */
        if( is_dn == SET_U ) {
-               char *p;
+               char *p, *realm;
                len = dn->bv_len + sizeof("uid=")-1 + sizeof(",cn=auth")-1;
 
-               if( user_realm && *user_realm ) {
+               /* username may have embedded realm name */
+               if( realm = strchr( dn->bv_val, '@') ) {
+                       *realm++ = '\0';
+                       len += sizeof(",cn=")-2;
+               } else if( user_realm && *user_realm ) {
                        len += strlen( user_realm ) + sizeof(",cn=")-1;
                }
 
@@ -225,20 +239,24 @@ int slap_sasl_getdn( Connection *conn, char *id, int len,
                c1 = dn->bv_val;
                dn->bv_val = ch_malloc( len+1 );
                p = slap_strcopy( dn->bv_val, "uid=" );
-               p = slap_strcopy( p, c1 );
-               ch_free( c1 );
+               p = slap_strncopy( p, c1, dn->bv_len );
 
-               if( user_realm && *user_realm ) {
+               if( realm ) {
+                       int rlen = dn->bv_len - ( realm - c1 );
+                       p = slap_strcopy( p, ",cn=" );
+                       p = slap_strncopy( p, realm, rlen );
+                       realm[-1] = '@';
+               } else if( user_realm && *user_realm ) {
                        p = slap_strcopy( p, ",cn=" );
                        p = slap_strcopy( p, user_realm );
                }
+
                if( conn->c_sasl_bind_mech.bv_len ) {
                        p = slap_strcopy( p, ",cn=" );
                        p = slap_strcopy( p, conn->c_sasl_bind_mech.bv_val );
                }
                p = slap_strcopy( p, ",cn=auth" );
                dn->bv_len = p - dn->bv_val;
-               is_dn = SET_DN;
 
 #ifdef NEW_LOGGING
                LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
@@ -248,27 +266,14 @@ int slap_sasl_getdn( Connection *conn, char *id, int len,
 #endif
        }
 
-       /* DN strings that are a cn=auth identity to run through regexp */
-       if( is_dn == SET_DN )
-       {
-               slap_sasl2dn( conn, dn, &dn2 );
-               if( dn2.bv_val ) {
-                       ch_free( dn->bv_val );
-                       *dn = dn2;
-                       do_norm = 0;    /* slap_sasl2dn normalizes */
-#ifdef NEW_LOGGING
-                       LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
-                               "slap_sasl_getdn: dn:id converted to %s.\n", dn->bv_val ));
-#else
-                       Debug( LDAP_DEBUG_TRACE, "getdn: dn:id converted to %s\n",
-                               dn->bv_val, 0, 0 );
-#endif
-               }
-       }
-
+       /* All strings are in DN form now. Normalize if needed. */
        if ( do_norm ) {
                rc = dnNormalize2( NULL, dn, &dn2 );
-               free(dn->bv_val);
+
+               /* User DNs were constructed above and must be freed now */
+               if ( is_dn == SET_U )
+                       ch_free( dn->bv_val );
+
                if ( rc != LDAP_SUCCESS ) {
                        dn->bv_val = NULL;
                        dn->bv_len = 0;
@@ -277,6 +282,20 @@ int slap_sasl_getdn( Connection *conn, char *id, int len,
                *dn = dn2;
        }
 
+       /* Run thru regexp */
+       slap_sasl2dn( conn, dn, &dn2 );
+       if( dn2.bv_val ) {
+               ch_free( dn->bv_val );
+               *dn = dn2;
+#ifdef NEW_LOGGING
+               LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
+                       "slap_sasl_getdn: dn:id converted to %s.\n", dn->bv_val ));
+#else
+               Debug( LDAP_DEBUG_TRACE, "getdn: dn:id converted to %s\n",
+                       dn->bv_val, 0, 0 );
+#endif
+       }
+
        return( LDAP_SUCCESS );
 }
 
@@ -291,9 +310,9 @@ slap_auxprop_lookup(
        const char *user,
        unsigned ulen)
 {
-       int rc;
+       int rc, i, last;
        struct berval dn;
-       const struct propval *list, *cur;
+       const struct propval *list;
        BerVarray vals, bv;
        AttributeDescription *ad;
        const char *text;
@@ -301,25 +320,29 @@ slap_auxprop_lookup(
        list = sparams->utils->prop_get( sparams->propctx );
 
        /* Find our DN first */
-       for( cur = list; cur->name; cur++ ) {
-               if ( cur->name[0] == '*' ) {
+       for( i = 0, last = 0; list[i].name; i++ ) {
+               if ( list[i].name[0] == '*' ) {
                        if ( (flags & SASL_AUXPROP_AUTHZID) &&
-                               !strcmp( cur->name, slap_propnames[1] ) ) {
-                               if ( cur->values && cur->values[0] )
-                                       AC_MEMCPY( &dn, cur->values[0], sizeof( dn ) );
+                               !strcmp( list[i].name, slap_propnames[1] ) ) {
+                               if ( list[i].values && list[i].values[0] )
+                                       AC_MEMCPY( &dn, list[i].values[0], sizeof( dn ) );
+                               if ( !last ) last = i;
                                break;
                        }
-                       if ( !strcmp( cur->name, slap_propnames[0] ) ) {
-                               AC_MEMCPY( &dn, cur->values[0], sizeof( dn ) );
-                               if ( !(flags & SASL_AUXPROP_AUTHZID) )
-                                       break;
+                       if ( !strcmp( list[i].name, slap_propnames[0] ) ) {
+                               if ( !last ) last = i;
+                               if ( list[i].values && list[i].values[0] ) {
+                                       AC_MEMCPY( &dn, list[i].values[0], sizeof( dn ) );
+                                       if ( !(flags & SASL_AUXPROP_AUTHZID) )
+                                               break;
+                               }
                        }
                }
        }
 
        /* Now fetch the rest */
-       for( cur = list; cur->name; cur++ ) {
-               const char *name = cur->name;
+       for( i = 0; i < last; i++ ) {
+               const char *name = list[i].name;
 
                if ( name[0] == '*' ) {
                        if ( flags & SASL_AUXPROP_AUTHZID ) continue;
@@ -327,9 +350,9 @@ slap_auxprop_lookup(
                } else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
                        continue;
 
-               if ( cur->values ) {
+               if ( list[i].values ) {
                        if ( !(flags & SASL_AUXPROP_OVERRIDE) ) continue;
-                       sparams->utils->prop_erase( sparams->propctx, cur->name );
+                       sparams->utils->prop_erase( sparams->propctx, list[i].name );
                }
                ad = NULL;
                rc = slap_str2ad( name, &ad, &text );
@@ -347,7 +370,7 @@ slap_auxprop_lookup(
                rc = backend_attribute( NULL,NULL,NULL,NULL, &dn, ad, &vals );
                if ( rc != LDAP_SUCCESS ) continue;
                for ( bv = vals; bv->bv_val; bv++ ) {
-                       sparams->utils->prop_set( sparams->propctx, cur->name,
+                       sparams->utils->prop_set( sparams->propctx, list[i].name,
                                bv->bv_val, bv->bv_len );
                }
                ber_bvarray_free( vals );
@@ -468,9 +491,9 @@ slap_sasl_canonicalize(
 {
        Connection *conn = (Connection *)context;
        struct propctx *props = sasl_auxprop_getctx( sconn );
+       struct propval auxvals[3];
        struct berval dn;
-       int rc, ext = 0;
-       char *realm;
+       int rc, which;
        const char *names[2];
 
        *out_len = 0;
@@ -489,68 +512,66 @@ slap_sasl_canonicalize(
                        in ? in : "<empty>" );
 #endif
 
+       /* If name is too big, just truncate. We don't care, we're
+        * using DNs, not the usernames.
+        */
        if ( inlen > out_max )
-               return SASL_BUFOVER;
+               inlen = out_max-1;
+
+       /* See if we need to add request, can only do it once */
+       prop_getnames( props, slap_propnames, auxvals );
+       if ( !auxvals[0].name )
+               prop_request( props, slap_propnames );
+
+       if ( flags & SASL_CU_AUTHID )
+               which = 0;
+       else
+               which = 1;
+
+       /* Already been here? */
+       if ( auxvals[which].values )
+               goto done;
 
        if ( flags == SASL_CU_AUTHZID ) {
        /* If we got unqualified authzid's, they probably came from SASL
-        * itself just passing the authcid to us. Ignore it.
+        * itself just passing the authcid to us. Look inside the oparams
+        * structure to see if that's true. (HACK: the out_len pointer is
+        * the address of a member of a sasl_out_params_t structure...)
         */
-               if (strncasecmp(in, "u:", 2) && strncasecmp(in, "dn:", 3)) {
-                       AC_MEMCPY( out, in, inlen );
-                       out[inlen] = '\0';
-                       *out_len = inlen;
-
-                       return SASL_OK;
-               }
+               sasl_out_params_t dummy;
+               int offset = (void *)&dummy.ulen - (void *)&dummy.authid;
+               char **authid = (void *)out_len - offset;
+               if ( *authid && !strcmp( in, *authid ) )
+                       goto done;
        }
 
-       /* If using SASL-EXTERNAL, don't modify the ID in any way */
-       if ( conn->c_is_tls && conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len
-               && ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) ) {
-               ext = 1;
-               realm = NULL;
-       } else {
-       /* Else look for an embedded realm in the name */
-               realm = strchr( in, '@' );
-               if ( realm ) *realm++ = '\0';
-       }
-       rc = slap_sasl_getdn( conn, (char *)in, inlen, realm ? realm : (char *)user_realm, &dn,
+       rc = slap_sasl_getdn( conn, (char *)in, inlen, (char *)user_realm, &dn,
                (flags & SASL_CU_AUTHID) ? FLAG_GETDN_AUTHCID : FLAG_GETDN_AUTHZID );
-       if ( realm )
-               realm[-1] = '@';
        if ( rc != LDAP_SUCCESS ) {
                sasl_seterror( sconn, 0, ldap_err2string( rc ) );
                return SASL_NOAUTHZ;
        }               
 
-       AC_MEMCPY( out, in, inlen );
-       out[inlen] = '\0';
-
-       *out_len = inlen;
-
-       if ( flags & SASL_CU_AUTHID )
-               names[0] = slap_propnames[0];
-       else
-               names[0] = slap_propnames[1];
+       names[0] = slap_propnames[which];
        names[1] = NULL;
 
-       sasl_auxprop_request( sconn, names );
        prop_set( props, names[0], (char *)&dn, sizeof( dn ) );
                
 #ifdef NEW_LOGGING
        LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
                "slap_sasl_canonicalize: conn %d %s=\"%s\"\n",
                        conn ? conn->c_connid : -1,
-                       (flags & SASL_CU_AUTHID) ? "authcDN" : "authzDN",
-                       dn.bv_val ));
+                       names[0]+1, dn.bv_val ));
 #else
        Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: "
                "%s=\"%s\"\n",
                        conn ? conn->c_connid : -1,
-                       (flags & SASL_CU_AUTHID) ? "authcDN" : "authzDN",
-                       dn.bv_val );
+                       names[0]+1, dn.bv_val );
 #endif
+done:  AC_MEMCPY( out, in, inlen );
+       out[inlen] = '\0';
+
+       *out_len = inlen;
 
        return SASL_OK;
 }
@@ -585,7 +606,7 @@ slap_sasl_authorize(
        prop_getnames( props, slap_propnames, auxvals );
        
        /* Nothing to do if no authzID was given */
-       if ( !auxvals[1].name )
+       if ( !auxvals[1].name || !auxvals[1].values )
                return SASL_OK;
        
        AC_MEMCPY( &authcDN, auxvals[0].values[0], sizeof(authcDN) );
@@ -628,9 +649,9 @@ slap_sasl_authorize(
        const char **errstr)
 {
        struct berval authcDN, authzDN;
-       int rc, ext = 0;
+       int rc;
        Connection *conn = context;
-       char *realm, *xrealm;
+       char *realm;
 
        *user = NULL;
 
@@ -664,16 +685,7 @@ slap_sasl_authorize(
 
        /* Convert the identities to DN's. If no authzid was given, client will
           be bound as the DN matching their username */
-       if ( conn->c_is_tls && conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len
-               && ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) ) {
-               ext = 1;
-               xrealm = NULL;
-       } else {
-               xrealm = strchr( authcid, '@' );
-               if ( xrealm ) *xrealm++ = '\0';
-       }
-       rc = slap_sasl_getdn( conn, (char *)authcid, 0, xrealm ? xrealm : realm, &authcDN, FLAG_GETDN_AUTHCID );
-       if ( xrealm ) xrealm[-1] = '@';
+       rc = slap_sasl_getdn( conn, (char *)authcid, 0, realm, &authcDN, FLAG_GETDN_AUTHCID );
        if( rc != LDAP_SUCCESS ) {
                *errstr = ldap_err2string( rc );
                return SASL_NOAUTHZ;
@@ -692,14 +704,7 @@ slap_sasl_authorize(
                *errstr = NULL;
                return SASL_OK;
        }
-       if ( ext ) {
-               xrealm = NULL;
-       } else {
-               xrealm = strchr( authzid, '@' );
-               if ( xrealm ) *xrealm++ = '\0';
-       }
-       rc = slap_sasl_getdn( conn, (char *)authzid, 0, xrealm ? xrealm : realm, &authzDN, FLAG_GETDN_AUTHZID );
-       if ( xrealm ) xrealm[-1] = '@';
+       rc = slap_sasl_getdn( conn, (char *)authzid, 0, realm, &authzDN, FLAG_GETDN_AUTHZID );
        if( rc != LDAP_SUCCESS ) {
                ch_free( authcDN.bv_val );
                *errstr = ldap_err2string( rc );
@@ -1172,7 +1177,7 @@ int slap_sasl_bind(
                prop_getnames( props, slap_propnames, vals );
 
                AC_MEMCPY( edn, vals[0].values[0], sizeof(*edn) );
-               if ( vals[1].name ) {
+               if ( vals[1].name && vals[1].values ) {
                        ch_free( edn->bv_val );
                        AC_MEMCPY( edn, vals[1].values[0], sizeof(*edn) );
                }