From: Kurt Zeilenga Date: Thu, 18 Apr 2002 17:39:20 +0000 (+0000) Subject: More resyncing with HEAD X-Git-Tag: OPENLDAP_REL_ENG_2_1_BETA1~4 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=26a48389a1d3e5ac65480c1bf1f0fc626443cc26;p=openldap More resyncing with HEAD --- diff --git a/include/ldap.h b/include/ldap.h index 302802bbc3..fd9d231e13 100644 --- a/include/ldap.h +++ b/include/ldap.h @@ -1259,6 +1259,12 @@ ldap_explode_rdn LDAP_P(( /* deprecated */ LDAP_CONST char *rdn, int notypes )); +typedef int LDAPDN_rewrite_func LDAP_P(( LDAPDN *dn, unsigned flags )); + +LDAP_F( int ) +ldap_X509dn2bv LDAP_P(( void *x509_name, struct berval *dn, + LDAPDN_rewrite_func *func, unsigned flags )); + LDAP_F( char * ) ldap_dn2dcedn LDAP_P(( LDAP_CONST char *dn )); /* deprecated */ diff --git a/include/ldap_pvt.h b/include/ldap_pvt.h index 8a794e6f05..d1d73eba21 100644 --- a/include/ldap_pvt.h +++ b/include/ldap_pvt.h @@ -179,8 +179,12 @@ LDAP_F (void *) ldap_pvt_tls_sb_ctx LDAP_P(( Sockbuf *sb )); LDAP_F (int) ldap_pvt_tls_init_default_ctx LDAP_P(( void )); -LDAP_F (char *) ldap_pvt_tls_get_peer LDAP_P(( void *ctx )); -LDAP_F (char *) ldap_pvt_tls_get_peer_dn LDAP_P(( void *ctx )); +typedef int LDAPDN_rewrite_dummy LDAP_P (( void *dn, unsigned flags )); + +LDAP_F (char *) ldap_pvt_tls_get_my_dn LDAP_P(( void *ctx, + LDAPDN_rewrite_dummy *func, unsigned flags )); +LDAP_F (char *) ldap_pvt_tls_get_peer_dn LDAP_P(( void *ctx, + LDAPDN_rewrite_dummy *func, unsigned flags )); LDAP_F (int) ldap_pvt_tls_get_strength LDAP_P(( void *ctx )); LDAP_END_DECL diff --git a/libraries/libldap/cyrus.c b/libraries/libldap/cyrus.c index d14e893c05..6aa10edfab 100644 --- a/libraries/libldap/cyrus.c +++ b/libraries/libldap/cyrus.c @@ -619,6 +619,9 @@ ldap_int_sasl_bind( if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { ld->ld_errno = sasl_err2ldap( saslrc ); +#if SASL_VERSION_MAJOR >= 2 + ld->ld_error = (char *)sasl_errdetail( ctx ); +#endif return ld->ld_errno; } @@ -712,6 +715,9 @@ ldap_int_sasl_bind( if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { ld->ld_errno = sasl_err2ldap( saslrc ); +#if SASL_VERSION_MAJOR >= 2 + ld->ld_error = (char *)sasl_errdetail( ctx ); +#endif return ld->ld_errno; } } while ( rc == LDAP_SASL_BIND_IN_PROGRESS ); @@ -721,6 +727,9 @@ ldap_int_sasl_bind( } if ( saslrc != SASL_OK ) { +#if SASL_VERSION_MAJOR >= 2 + ld->ld_error = (char *)sasl_errdetail( ctx ); +#endif return ld->ld_errno = sasl_err2ldap( saslrc ); } @@ -779,6 +788,8 @@ ldap_int_sasl_external( #if SASL_VERSION_MAJOR >= 2 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf ); + if ( sc == SASL_OK ) + sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid ); #else memset( &extprops, '\0', sizeof(extprops) ); extprops.ssf = ssf; diff --git a/libraries/libldap/getdn.c b/libraries/libldap/getdn.c index db9fdb9083..df8ecf7cad 100644 --- a/libraries/libldap/getdn.c +++ b/libraries/libldap/getdn.c @@ -3291,3 +3291,108 @@ return_results:; return( rc ); } +#ifdef HAVE_TLS +#include +#include + +/* Convert a structured DN from an X.509 certificate into an LDAPV3 DN. + * x509_name must be an (X509_NAME *). If func is non-NULL, the + * constructed DN will use numeric OIDs to identify attributeTypes, + * and the func() will be invoked to rewrite the DN with the given + * flags. + * + * Otherwise the DN will use shortNames as defined in the OpenSSL + * library. + * + * It's preferable to let slapd do the OID to attributeType mapping, + * because the OpenSSL tables are known to have many typos in versions + * up to (at least) 0.9.6c. However, the LDAP client has no schema tables, + * so we're forced to use OpenSSL's mapping there. + * -- Howard Chu 2002-04-18 + */ + +int +ldap_X509dn2bv( void *x509_name, struct berval *bv, LDAPDN_rewrite_func *func, + unsigned flags ) +{ + LDAPDN *newDN = NULL; + LDAPRDN *newRDN = NULL; + X509_NAME_ENTRY *ne; + ASN1_OBJECT *obj; + ASN1_STRING *str; + char oidbuf[2048]; + int i, j, nrdns, rc = LDAP_NO_MEMORY; + + struct berval Type; + struct berval Val; + + assert( bv ); + bv->bv_len = 0; + bv->bv_val = NULL; + + nrdns = X509_NAME_entry_count( x509_name ); + newDN = (LDAPDN *)LDAP_MALLOC( sizeof(LDAPDN) + sizeof(LDAPRDN *) + * (nrdns+1) ); + if ( newDN == NULL ) + return LDAP_NO_MEMORY; + + newDN[0] = (LDAPRDN**)(newDN+1); + + /* Retrieve RDNs in reverse order; LDAP is backwards from X.500. + * The OpenSSL library appears to allow only 1 AVA per RDN. + */ + for ( i = nrdns - 1, j = 0; i >= 0; i--, j++ ) { + newDN[0][j] = NULL; + ne = X509_NAME_get_entry( x509_name, i ); + obj = X509_NAME_ENTRY_get_object( ne ); + str = X509_NAME_ENTRY_get_data( ne ); + + if ( !func ) { + int n = OBJ_obj2nid( obj ); + + if (n == NID_undef) + goto get_oid; + Type.bv_val = (char *)OBJ_nid2sn( n ); + Type.bv_len = strlen( Type.bv_val ); + } else { +get_oid: Type.bv_val = oidbuf; + Type.bv_len = OBJ_obj2txt( oidbuf, sizeof( oidbuf ), obj, 1 ); + } + Val.bv_len = str->length; + Val.bv_val = str->data; + + newRDN = (LDAPRDN *)LDAP_MALLOC( sizeof(LDAPRDN) + sizeof(LDAPAVA *) * 2); + if ( newRDN == NULL ) + goto nomem; + + newRDN[0] = (LDAPAVA**)(newRDN+1); + newRDN[0][0] = ldapava_new( &Type, &Val, LDAP_AVA_STRING ); + if ( newRDN[0][0] == NULL ) + goto nomem; + + newRDN[0][1] = NULL; + newDN[0][j] = newRDN; + newRDN = NULL; + } + newDN[0][j] = NULL; + + if ( func ) { + rc = func( newDN, flags ); + if ( rc != LDAP_SUCCESS ) + goto nomem; + } + + rc = ldap_dn2bv( newDN, bv, LDAP_DN_FORMAT_LDAPV3 ); + ldap_dnfree( newDN ); + + return rc; + +nomem: + if ( newRDN ) + LDAP_FREE( newRDN ); + if ( newDN ) + ldap_dnfree( newDN ); + return rc; +} +#endif /* HAVE_TLS */ + diff --git a/libraries/libldap/tls.c b/libraries/libldap/tls.c index 7fcb778c11..609730a10f 100644 --- a/libraries/libldap/tls.c +++ b/libraries/libldap/tls.c @@ -788,6 +788,23 @@ ldap_pvt_tls_get_strength( void *s ) } +char * +ldap_pvt_tls_get_my_dn( void *s, LDAPDN_rewrite_dummy *func, unsigned flags ) +{ + X509 *x; + X509_NAME *xn; + struct berval dn; + + x = SSL_get_certificate((SSL *)s); + + if (!x) return NULL; + + xn = X509_get_subject_name(x); + ldap_X509dn2bv(xn, &dn, (LDAPDN_rewrite_func *)func, flags ); + X509_free(x); + return dn.bv_val; +} + static X509 * tls_get_cert( SSL *s ) { @@ -803,42 +820,20 @@ tls_get_cert( SSL *s ) } char * -ldap_pvt_tls_get_peer( void *s ) -{ - X509 *x; - X509_NAME *xn; - char buf[2048], *p; - - - x = tls_get_cert((SSL *)s); - - if (!x) - return NULL; - - xn = X509_get_subject_name(x); - p = LDAP_STRDUP(X509_NAME_oneline(xn, buf, sizeof(buf))); - X509_free(x); - return p; -} - -char * -ldap_pvt_tls_get_peer_dn( void *s ) +ldap_pvt_tls_get_peer_dn( void *s, LDAPDN_rewrite_dummy *func, unsigned flags ) { X509 *x; X509_NAME *xn; - char buf[2048], *p, *dn; + struct berval dn; x = tls_get_cert((SSL *)s); if (!x) return NULL; xn = X509_get_subject_name(x); - p = X509_NAME_oneline(xn, buf, sizeof(buf)); - - dn = ldap_dcedn2dn( p ); - + ldap_X509dn2bv(xn, &dn, (LDAPDN_rewrite_func *)func, flags); X509_free(x); - return dn; + return dn.bv_val; } char * @@ -983,7 +978,7 @@ ldap_pvt_tls_check_hostname( void *s, const char *name_in ) const char * ldap_pvt_tls_get_peer_issuer( void *s ) { -#if 0 /* currently unused; see ldap_pvt_tls_get_peer() if needed */ +#if 0 /* currently unused; see ldap_pvt_tls_get_peer_dn() if needed */ X509 *x; X509_NAME *xn; char buf[2048], *p; @@ -1256,9 +1251,10 @@ ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv ) /* we need to let SASL know */ ssf = ldap_pvt_tls_get_strength( ssl ); - authid = ldap_pvt_tls_get_peer( ssl ); + authid = ldap_pvt_tls_get_my_dn( ssl, NULL, 0 ); (void) ldap_int_sasl_external( ld, conn, authid, ssf ); + LDAP_FREE( authid ); } return LDAP_SUCCESS; diff --git a/servers/slapd/ad.c b/servers/slapd/ad.c new file mode 100644 index 0000000000..3d1335b435 --- /dev/null +++ b/servers/slapd/ad.c @@ -0,0 +1,633 @@ +/* $OpenLDAP$ */ +/* + * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved. + * COPYING RESTRICTIONS APPLY, see COPYRIGHT file + */ +/* ad.c - routines for dealing with attribute descriptions */ + +#include "portable.h" + +#include + +#include +#include +#include +#include +#include + +#include "ldap_pvt.h" +#include "slap.h" + +static int ad_keystring( + struct berval *bv ) +{ + ber_len_t i; + + if( !AD_CHAR( bv->bv_val[0] ) ) { + return 1; + } + + for( i=1; ibv_len; i++ ) { + if( !AD_CHAR( bv->bv_val[i] ) ) { + return 1; + } + } + return 0; +} + +void ad_destroy( AttributeDescription *ad ) +{ + AttributeDescription *n; + + for (; ad != NULL; ad = n) { + n = ad->ad_next; + ldap_memfree( ad ); + } +} + +/* Is there an AttributeDescription for this type that uses this language? */ +AttributeDescription * ad_find_lang( + AttributeType *type, + struct berval *lang ) +{ + AttributeDescription *ad; + + ldap_pvt_thread_mutex_lock( &type->sat_ad_mutex ); + for (ad = type->sat_ad; ad; ad=ad->ad_next) + { + if (ad->ad_lang.bv_len == lang->bv_len && + !strcasecmp(ad->ad_lang.bv_val, lang->bv_val)) + break; + } + ldap_pvt_thread_mutex_unlock( &type->sat_ad_mutex ); + return ad; +} + +int slap_str2ad( + const char *str, + AttributeDescription **ad, + const char **text ) +{ + struct berval bv; + bv.bv_val = (char *) str; + bv.bv_len = strlen( str ); + + return slap_bv2ad( &bv, ad, text ); +} + +static char *strchrlen( + const char *p, + const char ch, + int *len ) +{ + int i; + + for( i=0; p[i]; i++ ) { + if( p[i] == ch ) { + *len = i; + return (char *) &p[i]; + } + } + + *len = i; + return NULL; +} + +int slap_bv2ad( + struct berval *bv, + AttributeDescription **ad, + const char **text ) +{ + int rtn = LDAP_UNDEFINED_TYPE; + AttributeDescription desc, *d2; + char *name, *options; + char *opt, *next; + int nlang; + int langlen; + + /* hardcoded limits for speed */ +#define MAX_LANG_OPTIONS 128 + struct berval langs[MAX_LANG_OPTIONS+1]; +#define MAX_LANG_LEN 1024 + char langbuf[MAX_LANG_LEN]; + + assert( ad != NULL ); + assert( *ad == NULL ); /* temporary */ + + if( bv == NULL || bv->bv_len == 0 ) { + *text = "empty attribute description"; + return rtn; + } + + /* make sure description is IA5 */ + if( ad_keystring( bv ) ) { + *text = "attribute description contains inappropriate characters"; + return rtn; + } + + /* find valid base attribute type; parse in place */ + memset( &desc, 0, sizeof( desc )); + desc.ad_cname = *bv; + name = bv->bv_val; + options = strchr(name, ';'); + if( options != NULL ) { + desc.ad_cname.bv_len = options - name; + } + desc.ad_type = at_bvfind( &desc.ad_cname ); + if( desc.ad_type == NULL ) { + *text = "attribute type undefined"; + return rtn; + } + + if( is_at_operational( desc.ad_type ) && options != NULL ) { + *text = "operational attribute with options undefined"; + return rtn; + } + + /* + * parse options in place + */ + nlang = 0; + memset( langs, 0, sizeof( langs )); + langlen = 0; + + for( opt=options; opt != NULL; opt=next ) { + int optlen; + opt++; + next = strchrlen( opt, ';', &optlen ); + + if( optlen == 0 ) { + *text = "zero length option is invalid"; + return rtn; + + } else if ( optlen == sizeof("binary")-1 && + strncasecmp( opt, "binary", sizeof("binary")-1 ) == 0 ) + { + /* binary option */ + if( slap_ad_is_binary( &desc ) ) { + *text = "option \"binary\" specified multiple times"; + return rtn; + } + + if( !slap_syntax_is_binary( desc.ad_type->sat_syntax )) { + /* not stored in binary, disallow option */ + *text = "option \"binary\" not supported with type"; + return rtn; + } + + desc.ad_flags |= SLAP_DESC_BINARY; + continue; + + } else if ( optlen >= sizeof("lang-")-1 && + strncasecmp( opt, "lang-", sizeof("lang-")-1 ) == 0 ) + { + int i; + + if( opt[optlen-1] == '-' ) { + desc.ad_flags |= SLAP_DESC_LANG_RANGE; + } + + if( nlang >= MAX_LANG_OPTIONS ) { + *text = "too many language options"; + return rtn; + } + + /* + * tags should be presented in sorted order, + * so run the array in reverse. + */ + for( i=nlang-1; i>=0; i-- ) { + int rc; + + rc = strncasecmp( opt, langs[i].bv_val, + (unsigned) optlen < langs[i].bv_len + ? optlen : langs[i].bv_len ); + + if( rc == 0 && (unsigned)optlen == langs[i].bv_len ) { + /* duplicate (ignore) */ + goto done; + + } else if ( rc > 0 || + ( rc == 0 && (unsigned)optlen > langs[i].bv_len )) + { + AC_MEMCPY( &langs[i+1], &langs[i], + (nlang-i)*sizeof(struct berval) ); + langs[i].bv_val = opt; + langs[i].bv_len = optlen; + goto done; + } + } + + if( nlang ) { + AC_MEMCPY( &langs[1], &langs[0], + nlang*sizeof(struct berval) ); + } + langs[0].bv_val = opt; + langs[0].bv_len = optlen; + +done:; + langlen += optlen + 1; + nlang++; + + } else { + *text = "unrecognized option"; + return rtn; + } + } + + if( nlang > 0 ) { + int i; + + if( langlen > MAX_LANG_LEN ) { + *text = "language options too long"; + return rtn; + } + + desc.ad_lang.bv_val = langbuf; + langlen = 0; + + for( i=0; isat_ad; d2; d2=d2->ad_next) { + if( d2->ad_flags != desc.ad_flags ) { + continue; + } + if( d2->ad_lang.bv_len != desc.ad_lang.bv_len ) { + continue; + } + if( d2->ad_lang.bv_len == 0 ) { + break; + } + if( strncasecmp( d2->ad_lang.bv_val, desc.ad_lang.bv_val, + desc.ad_lang.bv_len ) == 0 ) + { + break; + } + } + + /* Not found, add new one */ + while (d2 == NULL) { + size_t dlen = 0; + ldap_pvt_thread_mutex_lock( &desc.ad_type->sat_ad_mutex ); + /* check again now that we've locked */ + for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) { + if (d2->ad_flags != desc.ad_flags) + continue; + if (d2->ad_lang.bv_len != desc.ad_lang.bv_len) + continue; + if (d2->ad_lang.bv_len == 0) + break; + if (strncasecmp(d2->ad_lang.bv_val, desc.ad_lang.bv_val, + desc.ad_lang.bv_len) == 0) + break; + } + if (d2) { + ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex ); + break; + } + + /* Allocate a single contiguous block. If there are no + * options, we just need space for the AttrDesc structure. + * Otherwise, we need to tack on the full name length + + * options length. + */ + if (desc.ad_lang.bv_len || desc.ad_flags != SLAP_DESC_NONE) { + dlen = desc.ad_type->sat_cname.bv_len; + if (desc.ad_lang.bv_len) { + dlen += 1+desc.ad_lang.bv_len; + } + if( slap_ad_is_binary( &desc ) ) { + dlen += sizeof(";binary")-1; + } + } + + d2 = ch_malloc(sizeof(AttributeDescription) + dlen + 1); + d2->ad_type = desc.ad_type; + d2->ad_flags = desc.ad_flags; + d2->ad_cname.bv_len = desc.ad_type->sat_cname.bv_len; + d2->ad_lang.bv_len = desc.ad_lang.bv_len; + + if (dlen == 0) { + d2->ad_cname.bv_val = d2->ad_type->sat_cname.bv_val; + d2->ad_lang.bv_val = NULL; + } else { + d2->ad_cname.bv_val = (char *)(d2+1); + strcpy(d2->ad_cname.bv_val, d2->ad_type->sat_cname.bv_val); + if( slap_ad_is_binary( &desc ) ) { + strcpy(d2->ad_cname.bv_val+d2->ad_cname.bv_len, + ";binary"); + d2->ad_cname.bv_len += sizeof(";binary")-1; + } + if( d2->ad_lang.bv_len ) { + d2->ad_cname.bv_val[d2->ad_cname.bv_len++]=';'; + d2->ad_lang.bv_val = d2->ad_cname.bv_val+ + d2->ad_cname.bv_len; + strncpy(d2->ad_lang.bv_val,desc.ad_lang.bv_val, + d2->ad_lang.bv_len); + d2->ad_lang.bv_val[d2->ad_lang.bv_len] = '\0'; + ldap_pvt_str2lower(d2->ad_lang.bv_val); + d2->ad_cname.bv_len += d2->ad_lang.bv_len; + } + } + /* Add new desc to list. We always want the bare Desc with + * no options to stay at the head of the list, assuming + * that one will be used most frequently. + */ + if (desc.ad_type->sat_ad == NULL || dlen == 0) { + d2->ad_next = desc.ad_type->sat_ad; + desc.ad_type->sat_ad = d2; + } else { + d2->ad_next = desc.ad_type->sat_ad->ad_next; + desc.ad_type->sat_ad->ad_next = d2; + } + ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex ); + } + + if( *ad == NULL ) { + *ad = d2; + } else { + **ad = *d2; + } + + return LDAP_SUCCESS; +} + +static int is_ad_sublang( + const char *sublang, + const char *suplang ) +{ + const char *supp, *supdelimp; + const char *subp, *subdelimp; + int suplen, sublen; + + if( suplang == NULL ) return 1; + if( sublang == NULL ) return 0; + + for( supp=suplang ; supp; supp=supdelimp ) { + supdelimp = strchrlen( supp, ';', &suplen ); + if( supdelimp ) supdelimp++; + + for( subp=sublang ; subp; subp=subdelimp ) { + subdelimp = strchrlen( subp, ';', &sublen ); + if( subdelimp ) subdelimp++; + + if ((( suplen < sublen && supp[suplen-1] == '-' ) || + suplen == sublen ) && strncmp( supp, subp, suplen ) == 0 ) + { + goto match; + } + } + + return 0; +match:; + } + return 1; +} + +int is_ad_subtype( + AttributeDescription *sub, + AttributeDescription *super +) +{ + int lr; + + if( !is_at_subtype( sub->ad_type, super->ad_type ) ) { + return 0; + } + + /* ensure sub does support all flags of super */ + lr = sub->ad_lang.bv_len ? SLAP_DESC_LANG_RANGE : 0; + if(( super->ad_flags & ( sub->ad_flags | lr )) != super->ad_flags ) { + return 0; + } + + /* check for language tags */ + if ( !is_ad_sublang( sub->ad_lang.bv_val, super->ad_lang.bv_val )) { + return 0; + } + + return 1; +} + +int ad_inlist( + AttributeDescription *desc, + AttributeName *attrs ) +{ + if (! attrs ) return 0; + + for( ; attrs->an_name.bv_val; attrs++ ) { + ObjectClass *oc; + int rc; + + if ( attrs->an_desc ) { + if ( is_ad_subtype( desc, attrs->an_desc )) + return 1; + continue; + } + + /* + * EXTENSION: see if requested description is an object class + * if so, return attributes which the class requires/allows + */ + oc = attrs->an_oc; + if( oc == NULL ) { + oc = oc_bvfind( &attrs->an_name ); + attrs->an_oc = oc; + } + if( oc != NULL ) { + if ( oc == slap_schema.si_oc_extensibleObject ) { + /* extensibleObject allows the return of anything */ + return 1; + } + + if( oc->soc_required ) { + /* allow return of required attributes */ + int i; + for ( i = 0; oc->soc_required[i] != NULL; i++ ) { + rc = is_at_subtype( desc->ad_type, + oc->soc_required[i] ); + if( rc ) return 1; + } + } + + if( oc->soc_allowed ) { + /* allow return of allowed attributes */ + int i; + for ( i = 0; oc->soc_allowed[i] != NULL; i++ ) { + rc = is_at_subtype( desc->ad_type, + oc->soc_allowed[i] ); + if( rc ) return 1; + } + } + + } else { + /* short-circuit this search next time around */ + if (!slap_schema.si_at_undefined->sat_ad) { + const char *text; + slap_bv2undef_ad(&attrs->an_name, + &attrs->an_desc, &text); + } else { + attrs->an_desc = + slap_schema.si_at_undefined->sat_ad; + } + } + } + + return 0; +} + + +int slap_str2undef_ad( + const char *str, + AttributeDescription **ad, + const char **text ) +{ + struct berval bv; + bv.bv_val = (char *) str; + bv.bv_len = strlen( str ); + + return slap_bv2undef_ad( &bv, ad, text ); +} + +int slap_bv2undef_ad( + struct berval *bv, + AttributeDescription **ad, + const char **text ) +{ + AttributeDescription *desc; + + assert( ad != NULL ); + + if( bv == NULL || bv->bv_len == 0 ) { + *text = "empty attribute description"; + return LDAP_UNDEFINED_TYPE; + } + + /* make sure description is IA5 */ + if( ad_keystring( bv ) ) { + *text = "attribute description contains inappropriate characters"; + return LDAP_UNDEFINED_TYPE; + } + + for( desc = slap_schema.si_at_undefined->sat_ad; desc; + desc=desc->ad_next ) + { + if( desc->ad_cname.bv_len == bv->bv_len && + !strcasecmp( desc->ad_cname.bv_val, bv->bv_val )) + { + break; + } + } + + if( !desc ) { + desc = ch_malloc(sizeof(AttributeDescription) + 1 + + bv->bv_len); + + desc->ad_flags = SLAP_DESC_NONE; + desc->ad_lang.bv_val = NULL; + desc->ad_lang.bv_len = 0; + + desc->ad_cname.bv_len = bv->bv_len; + desc->ad_cname.bv_val = (char *)(desc+1); + strcpy(desc->ad_cname.bv_val, bv->bv_val); + + /* canonical to upper case */ + ldap_pvt_str2upper( desc->ad_cname.bv_val ); + + desc->ad_type = slap_schema.si_at_undefined; + desc->ad_next = desc->ad_type->sat_ad; + desc->ad_type->sat_ad = desc; + } + + if( !*ad ) { + *ad = desc; + } else { + **ad = *desc; + } + + return LDAP_SUCCESS; +} + +int +an_find( + AttributeName *a, + struct berval *s +) +{ + if( a == NULL ) return 0; + + for ( ; a->an_name.bv_val; a++ ) { + if ( a->an_name.bv_len != s->bv_len) continue; + if ( strcasecmp( s->bv_val, a->an_name.bv_val ) == 0 ) { + return( 1 ); + } + } + + return( 0 ); +} + +/* Convert a delimited string into a list of AttributeNames; Add on + * to an existing list if it was given. + */ +AttributeName * +str2anlist( AttributeName *an, char *in, const char *brkstr ) +{ + char *str; + char *s; + char *lasts; + int i, j; + const char *text; + AttributeName *anew; + + /* find last element in list */ + for (i = 0; an && an[i].an_name.bv_val; i++); + + /* protect the input string from strtok */ + str = ch_strdup( in ); + + /* Count words in string */ + j=1; + for ( s = str; *s; s++ ) { + if ( strchr( brkstr, *s ) != NULL ) { + j++; + } + } + + an = ch_realloc( an, ( i + j + 1 ) * sizeof( AttributeName ) ); + anew = an + i; + for ( s = ldap_pvt_strtok( str, brkstr, &lasts ); + s != NULL; + s = ldap_pvt_strtok( NULL, brkstr, &lasts ) ) + { + anew->an_desc = NULL; + anew->an_oc = NULL; + ber_str2bv(s, 0, 1, &anew->an_name); + slap_bv2ad(&anew->an_name, &anew->an_desc, &text); + if ( !anew->an_desc ) { + anew->an_oc = oc_bvfind( &anew->an_name ); + if ( !anew->an_oc ) { + free( an ); + /* overwrites input string on error! */ + strcpy( in, s ); + return NULL; + } + } + anew++; + } + anew->an_name.bv_val = NULL; + + free( str ); + return( an ); +} + diff --git a/servers/slapd/config.c b/servers/slapd/config.c index 2865316814..94f0b91c1f 100644 --- a/servers/slapd/config.c +++ b/servers/slapd/config.c @@ -57,7 +57,6 @@ char *slapd_args_file = NULL; int nSaslRegexp = 0; SaslRegexp_t *SaslRegexp = NULL; -int sasl_external_x509dn_convert; #ifdef SLAPD_RLOOKUPS int use_reverse_lookup = 1; @@ -677,9 +676,6 @@ read_config( const char *fname ) return 1; } - } else if ( strcasecmp( cargv[0], "sasl-external-x509dn-convert" ) == 0 ) { - sasl_external_x509dn_convert++; - /* set UCDATA path */ } else if ( strcasecmp( cargv[0], "ucdata-path" ) == 0 ) { int err; diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c index 41828b6aca..50fba1a220 100644 --- a/servers/slapd/connection.c +++ b/servers/slapd/connection.c @@ -1201,8 +1201,9 @@ int connection_read(ber_socket_t s) c->c_ssf = c->c_tls_ssf; } - authid = (char *)ldap_pvt_tls_get_peer( ssl ); + authid = dnX509peerNormalize( ssl ); slap_sasl_external( c, c->c_tls_ssf, authid ); + if ( authid ) free( authid ); } connection_return( c ); ldap_pvt_thread_mutex_unlock( &connections_mutex ); diff --git a/servers/slapd/dn.c b/servers/slapd/dn.c index 40064a47fa..3c68a0b98a 100644 --- a/servers/slapd/dn.c +++ b/servers/slapd/dn.c @@ -831,38 +831,20 @@ dnIsSuffix( } /* - * Convert a DN from X.500 format into a normalized DN + * Convert an X.509 DN into a normalized LDAP DN */ int -dnDCEnormalize( char *dce, struct berval *out ) +dnX509normalize( void *x509_name, struct berval *out ) { - int rc; - LDAPDN *dn = NULL; - - out->bv_val = NULL; - out->bv_len = 0; - - rc = ldap_str2dn( dce, &dn, LDAP_DN_FORMAT_DCE ); - if ( rc != LDAP_SUCCESS ) - return rc; - - /* - * Schema-aware rewrite - */ - if ( LDAPDN_rewrite( dn, 0 ) != LDAP_SUCCESS ) { - ldap_dnfree( dn ); - return LDAP_INVALID_SYNTAX; - } - - /* - * Back to string representation - */ - rc = ldap_dn2bv( dn, out, LDAP_DN_FORMAT_LDAPV3 ); - - ldap_dnfree( dn ); + /* Invoke the LDAP library's converter with our schema-rewriter */ + return ldap_X509dn2bv( x509_name, out, LDAPDN_rewrite, 0 ); +} - if ( rc != LDAP_SUCCESS ) { - rc = LDAP_INVALID_SYNTAX; - } - return rc; +/* + * Get the TLS session's peer's DN into a normalized LDAP DN + */ +char * +dnX509peerNormalize( void *ssl ) +{ + return ldap_pvt_tls_get_peer_dn( ssl, (LDAPDN_rewrite_dummy *)LDAPDN_rewrite, 0 ); } diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index 0e0939eac9..3559936721 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -404,7 +404,9 @@ LDAP_SLAPD_F (void) build_new_dn LDAP_P(( LDAP_SLAPD_F (void) dnParent LDAP_P(( struct berval *dn, struct berval *pdn )); -LDAP_SLAPD_F (int) dnDCEnormalize LDAP_P(( char *dce, struct berval *out )); +LDAP_SLAPD_F (int) dnX509normalize LDAP_P(( void *x509_name, struct berval *out )); + +LDAP_SLAPD_F (char *) dnX509peerNormalize LDAP_P(( void *ssl )); /* * entry.c @@ -999,7 +1001,6 @@ LDAP_SLAPD_V (int) global_idletimeout; LDAP_SLAPD_V (int) global_schemacheck; LDAP_SLAPD_V (char *) global_host; LDAP_SLAPD_V (char *) global_realm; -LDAP_SLAPD_V (int) sasl_external_x509dn_convert; LDAP_SLAPD_V (char *) default_passwd_hash; LDAP_SLAPD_V (int) lber_debug; LDAP_SLAPD_V (int) ldap_syslog; diff --git a/servers/slapd/sasl.c b/servers/slapd/sasl.c index 87f29735aa..a6bcb45099 100644 --- a/servers/slapd/sasl.c +++ b/servers/slapd/sasl.c @@ -173,22 +173,17 @@ int slap_sasl_getdn( Connection *conn, char *id, int len, /* An authcID needs to be converted to authzID form */ if( flags & FLAG_GETDN_AUTHCID ) { - if( sasl_external_x509dn_convert - && conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len - && ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) - && id[0] == '/' ) - { - /* check SASL external for X.509 style DN and */ - /* convert to dn: form, result is normalized */ - dnDCEnormalize( id, dn ); + 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; } else { /* convert to u: form */ - ber_str2bv( id, len, 1, dn ); is_dn = SET_U; } + ber_str2bv( id, len, 1, dn ); } if( !is_dn ) { if( !strncasecmp( id, "u:", sizeof("u:")-1 )) { @@ -449,9 +444,8 @@ slap_sasl_authorize( equal = !strcmp( auth_identity, requested_user ); /* If using SASL-EXTERNAL, don't modify the ID in any way */ - if ( conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len - && ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) - && auth_identity[0] == '/' ) { + 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 { @@ -582,9 +576,8 @@ 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_sasl_bind_mech.bv_len == ext_bv.bv_len - && ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) - && authcid[0] == '/' ) { + 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 { @@ -1083,13 +1076,7 @@ int slap_sasl_bind( if ( sc == SASL_OK ) { char *username = NULL; - char *realm = NULL; -#if SASL_VERSION_MAJOR >= 2 - sc = sasl_getprop( ctx, SASL_DEFUSERREALM, (const void **)&realm ); -#else - sc = sasl_getprop( ctx, SASL_REALM, (void **)&realm ); -#endif sc = sasl_getprop( ctx, SASL_USERNAME, (SASL_CONST void **)&username ); diff --git a/servers/slapd/schema_init.c b/servers/slapd/schema_init.c index 85e9f2526f..ff204a1903 100644 --- a/servers/slapd/schema_init.c +++ b/servers/slapd/schema_init.c @@ -3480,16 +3480,6 @@ asn1_integer2str(ASN1_INTEGER *a, struct berval *bv) return ber_str2bv( p, 0, 1, bv ); } -/* Get a DN in RFC2253 format from a X509_NAME internal struct */ -int -dn_openssl2ldap(X509_NAME *name, struct berval *out) -{ - char buf[2048], *p; - - p = X509_NAME_oneline( name, buf, sizeof( buf ) ); - return dnDCEnormalize( p, out ); -} - /* * Given a certificate in DER format, extract the corresponding * assertion value for certificateExactMatch @@ -3522,7 +3512,7 @@ certificateExactConvert( X509_free(xcert); return LDAP_INVALID_SYNTAX; } - if ( dn_openssl2ldap(X509_get_issuer_name(xcert), &issuer_dn ) != LDAP_SUCCESS ) { + if ( dnX509normalize(X509_get_issuer_name(xcert), &issuer_dn ) != LDAP_SUCCESS ) { X509_free(xcert); ber_memfree(serial.bv_val); return LDAP_INVALID_SYNTAX; @@ -3636,7 +3626,7 @@ certificateExactMatch( } asn1_integer2str(xcert->cert_info->serialNumber, &serial); - dn_openssl2ldap(X509_get_issuer_name(xcert), &issuer_dn); + dnX509normalize(X509_get_issuer_name(xcert), &issuer_dn); X509_free(xcert);