From 9b8a5036dd0d52242e05e3348088ea3365e10149 Mon Sep 17 00:00:00 2001 From: Kurt Zeilenga Date: Thu, 29 Apr 1999 06:50:00 +0000 Subject: [PATCH] Update back-passwd based on patches from howard.chu@platinum.com. Modified by me to dn_*() and attr_*() functions and to use uidObject and to handle search scopes better. Could use some more work. A fun project for anyone wanting to learn how backends work. Modified dn_parent and dn_rdn to accept be=NULL (to bypass be_issuffix check). These functions should be generalized and moved to -lldap. A project for another day. --- servers/slapd/back-passwd/search.c | 226 ++++++++++++++++++++++------- servers/slapd/dn.c | 4 +- tests/Makefile.in | 7 + tests/data/slapd-passwd.conf | 18 +++ tests/scripts/defines.sh | 2 + tests/scripts/passwd-search | 107 ++++++++++++++ 6 files changed, 312 insertions(+), 52 deletions(-) create mode 100644 tests/data/slapd-passwd.conf create mode 100755 tests/scripts/passwd-search diff --git a/servers/slapd/back-passwd/search.c b/servers/slapd/back-passwd/search.c index 6c12f949af..859648e9ed 100644 --- a/servers/slapd/back-passwd/search.c +++ b/servers/slapd/back-passwd/search.c @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -13,7 +14,10 @@ #include "slap.h" #include "external.h" -static Entry *pw2entry(Backend *be, struct passwd *pw); +static Entry *pw2entry( + Backend *be, + struct passwd *pw, + char *rdn); int passwd_back_search( @@ -31,10 +35,17 @@ passwd_back_search( int attrsonly ) { + int sent = 0; struct passwd *pw; Entry *e; char *s; time_t stoptime; + int err = LDAP_NO_SUCH_OBJECT; + + char *rdn = NULL; + char *parent = NULL; + char *matched = NULL; + char *user = NULL; tlimit = (tlimit > be->be_timelimit || tlimit < 1) ? be->be_timelimit : tlimit; @@ -42,77 +53,154 @@ passwd_back_search( slimit = (slimit > be->be_sizelimit || slimit < 1) ? be->be_sizelimit : slimit; + endpwent(); + #ifdef HAVE_SETPWFILE if ( be->be_private != NULL ) { - endpwent(); (void) setpwfile( (char *) be->be_private ); } #endif /* HAVE_SETPWFILE */ - if ( scope == LDAP_SCOPE_BASE ) { - if ( (s = strchr( base, '@' )) != NULL ) { - *s = '\0'; - } + /* Handle a query for the base of this backend */ + if ( be_issuffix( be, base ) ) { + struct berval val, *vals[2]; + + vals[0] = &val; + vals[1] = NULL; - if ( (pw = getpwnam( base )) == NULL ) { - send_ldap_result( conn, op, LDAP_NO_SUCH_OBJECT, - s != NULL ? s + 1 : NULL, NULL ); - return( -1 ); + /* Create an entry corresponding to the base DN */ + e = (Entry *) ch_calloc(1, sizeof(Entry)); + e->e_attrs = NULL; + e->e_dn = strdup(base); + + /* Use the first attribute of the DN + * as an attribute within the entry itself. + */ + rdn = dn_rdn(NULL, base); + + if( rdn == NULL || (s = strchr(rdn, '=')) == NULL ) { + err = LDAP_INVALID_DN_SYNTAX; + goto done; } - e = pw2entry( be, pw ); + val.bv_val = rdn_attr_value(rdn); + val.bv_len = strlen( val.bv_val ); + attr_merge( e, rdn_attr_type(rdn), vals ); + + free(rdn); + rdn = NULL; + + /* Every entry needs an objectclass. We don't really + * know if our hardcoded choice here agrees with the + * DN that was configured for this backend, but it's + * better than nothing. + * + * should be a configuratable item + */ + val.bv_val = "organizationalUnit"; + val.bv_len = strlen( val.bv_val ); + attr_merge( e, "objectClass", vals ); + if ( test_filter( be, conn, op, e, filter ) == 0 ) { send_search_entry( be, conn, op, e, attrs, attrsonly ); + matched = strdup( be->be_suffix[0] ); + sent++; } - entry_free( e ); - send_ldap_result( conn, op, LDAP_SUCCESS, "", "" ); + if ( scope != LDAP_SCOPE_BASE ) { + /* check all our "children" */ - return( 0 ); - } + for ( pw = getpwent(); pw != NULL; pw = getpwent() ) { + /* check for abandon */ + ldap_pvt_thread_mutex_lock( &op->o_abandonmutex ); + if ( op->o_abandon ) { + ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex ); + endpwent(); + return( -1 ); + } + ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex ); + + /* check time limit */ + if ( slap_get_time() > stoptime ) { + send_ldap_result( conn, op, LDAP_TIMELIMIT_EXCEEDED, + NULL, NULL ); + endpwent(); + return( 0 ); + } + + e = pw2entry( be, pw, NULL ); - for ( pw = getpwent(); pw != NULL; pw = getpwent() ) { - /* check for abandon */ - ldap_pvt_thread_mutex_lock( &op->o_abandonmutex ); - if ( op->o_abandon ) { - ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex ); + if ( test_filter( be, conn, op, e, filter ) == 0 ) { + /* check size limit */ + if ( --slimit == -1 ) { + send_ldap_result( conn, op, LDAP_SIZELIMIT_EXCEEDED, + NULL, NULL ); + endpwent(); + return( 0 ); + } + + send_search_entry( be, conn, op, e, attrs, attrsonly ); + sent++; + } + + entry_free( e ); + } endpwent(); - return( -1 ); } - ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex ); - /* check time limit */ - if ( slap_get_time() > stoptime ) { - send_ldap_result( conn, op, LDAP_TIMELIMIT_EXCEEDED, - NULL, NULL ); - endpwent(); - return( 0 ); + } else { + parent = dn_parent( be, base ); + + /* This backend is only one layer deep. Don't answer requests for + * anything deeper than that. + */ + if( !be_issuffix( be, parent ) ) { + goto done; } - e = pw2entry( be, pw ); + rdn = dn_rdn( NULL, base ); - if ( test_filter( be, conn, op, e, filter ) == 0 ) { - /* check size limit */ - if ( --slimit == -1 ) { - send_ldap_result( conn, op, LDAP_SIZELIMIT_EXCEEDED, - NULL, NULL ); - endpwent(); - return( 0 ); - } + if ( (user = rdn_attr_value(rdn)) == NULL) { + err = LDAP_INVALID_DN_SYNTAX; + goto done; + } + for( s = user; *s ; s++ ) { + *s = TOLOWER( *s ); + } + + if ( (pw = getpwnam( user )) == NULL ) { + goto done; + } + + e = pw2entry( be, pw, rdn ); + + if ( test_filter( be, conn, op, e, filter ) == 0 ) { send_search_entry( be, conn, op, e, attrs, attrsonly ); + sent++; } entry_free( e ); } - endpwent(); - send_ldap_result( conn, op, LDAP_SUCCESS, "", "" ); + +done: + if( sent ) { + send_ldap_result( conn, op, LDAP_SUCCESS, "", "" ); + + } else { + send_ldap_result( conn, op, err, matched, NULL ); + } + + if( matched != NULL ) free( matched ); + if( parent != NULL ) free( parent ); + if( rdn != NULL ) free( rdn ); + if( user != NULL ) free( user ); return( 0 ); } static Entry * -pw2entry( Backend *be, struct passwd *pw ) +pw2entry( Backend *be, struct passwd *pw, char *rdn ) { Entry *e; char buf[256]; @@ -123,31 +211,69 @@ pw2entry( Backend *be, struct passwd *pw ) vals[1] = NULL; /* - * from pw we get pw_name and make it uid and cn and sn and - * we get pw_gecos and make it cn and we give it an objectclass - * of person. + * from pw we get pw_name and make it cn + * give it an objectclass of person. */ e = (Entry *) ch_calloc( 1, sizeof(Entry) ); e->e_attrs = NULL; - sprintf( buf, "%s@%s", pw->pw_name, be->be_suffix[0] ); + /* rdn attribute type should be a configuratable item */ + sprintf( buf, "uid=%s,%s", pw->pw_name, be->be_suffix[0] ); e->e_dn = ch_strdup( buf ); e->e_ndn = dn_normalize_case( ch_strdup( buf ) ); val.bv_val = pw->pw_name; val.bv_len = strlen( pw->pw_name ); - attr_merge( e, "cn", vals ); - attr_merge( e, "sn", vals ); - attr_merge( e, "uid", vals ); + attr_merge( e, "uid", vals ); /* required by uidObject */ + attr_merge( e, "cn", vals ); /* required by person */ + attr_merge( e, "sn", vals ); /* required by person */ + #ifdef HAVE_PW_GECOS - val.bv_val = pw->pw_gecos; - val.bv_len = strlen( pw->pw_gecos ); - attr_merge( e, "cn", vals ); + /* + * if gecos is present, add it as a cn. first process it + * according to standard BSD usage. If the processed cn has + * a space, use the tail as the surname. + */ + if (pw->pw_gecos[0]) { + char *s; + + val.bv_val = pw->pw_gecos; + val.bv_len = strlen(val.bv_val); + attr_merge(e, "description", vals); + + s = strchr(val.bv_val, ','); + if (s) + *s = '\0'; + s = strchr(val.bv_val, '&'); + if (s) { + int i = s - val.bv_val; + strncpy(buf, val.bv_val, i); + s = buf+i; + strcpy(s, pw->pw_name); + if (islower(*s)) + *s = toupper(*s); + strcat(s, val.bv_val+i+1); + val.bv_val = buf; + } + val.bv_len = strlen(val.bv_val); + if ( strcmp( val.bv_val, pw->pw_name )) + attr_merge( e, "cn", vals ); + if ( (s=strrchr(val.bv_val, ' '))) { + val.bv_val = s + 1; + val.bv_len = strlen(val.bv_val); + attr_merge(e, "sn", vals); + } + } #endif + + /* objectclasses should be configuratable items */ val.bv_val = "person"; val.bv_len = strlen( val.bv_val ); attr_merge( e, "objectclass", vals ); + val.bv_val = "uidObject"; + val.bv_len = strlen( val.bv_val ); + attr_merge( e, "objectclass", vals ); return( e ); } diff --git a/servers/slapd/dn.c b/servers/slapd/dn.c index e7bb61d584..4f28d164b8 100644 --- a/servers/slapd/dn.c +++ b/servers/slapd/dn.c @@ -169,7 +169,7 @@ dn_parent( return( NULL ); } - if ( be_issuffix( be, dn ) ) { + if ( be != NULL && be_issuffix( be, dn ) ) { return( NULL ); } @@ -239,7 +239,7 @@ char * dn_rdn( return( NULL ); } - if ( be_issuffix( be, dn ) ) { + if ( be != NULL && be_issuffix( be, dn ) ) { return( NULL ); } diff --git a/tests/Makefile.in b/tests/Makefile.in index 1c2018492f..21c1efa436 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -27,6 +27,13 @@ test-ldbm: FORCE @-$(MKDIR) test-db test-repl || true @$(srcdir)/scripts/all $(srcdir) ldbm +passwd: test-passwd +test-passwd: FORCE + @-$(LN_S) $(srcdir)/data . + @echo "Initiating LDAP tests..." + @-$(MKDIR) test-db test-repl || true + @$(srcdir)/scripts/passwd-search $(srcdir) passwd + clean-local: FORCE -$(RM) test-db/[!C]* test-repl/[!C]* *core diff --git a/tests/data/slapd-passwd.conf b/tests/data/slapd-passwd.conf new file mode 100644 index 0000000000..9acc2b1aad --- /dev/null +++ b/tests/data/slapd-passwd.conf @@ -0,0 +1,18 @@ +# +# master slapd config -- for testing +# +include ./data/slapd.at.conf +include ./data/slapd.oc.conf +schemacheck on +pidfile ./test-db/slapd.pid +argsfile ./test-db/slapd.args + +####################################################################### +# ldbm database definitions +####################################################################### + +database passwd +suffix "o=University of Michigan, c=US" +rootdn "cn=Manager, o=University of Michigan, c=US" +rootpw secret +#file ./data/passwd diff --git a/tests/scripts/defines.sh b/tests/scripts/defines.sh index 952571df02..021369d307 100755 --- a/tests/scripts/defines.sh +++ b/tests/scripts/defines.sh @@ -17,6 +17,8 @@ else SLAVECONF=$DATADIR/slapd-repl-slave.conf fi +PASSWDCONF=$DATADIR/slapd-passwd.conf + SLAPD=../servers/slapd/slapd SLURPD=../servers/slurpd/slurpd LDAPSEARCH=../clients/tools/ldapsearch diff --git a/tests/scripts/passwd-search b/tests/scripts/passwd-search new file mode 100755 index 0000000000..5bf321a1c5 --- /dev/null +++ b/tests/scripts/passwd-search @@ -0,0 +1,107 @@ +#! /bin/sh + +if test $# -eq 0 ; then + SRCDIR="." +else + SRCDIR=$1; shift +fi +if test $# -eq 1 ; then + BACKEND=$1; shift +fi + +echo "running defines.sh $SRCDIR $BACKEND" +. $SRCDIR/scripts/defines.sh + +echo "Cleaning up in $DBDIR..." + +rm -f $DBDIR/[!C]* + +echo "Starting slapd on TCP/IP port $PORT..." +$SLAPD -f $PASSWDCONF -p $PORT -d $LVL $TIMING > $MASTERLOG 2>&1 & +PID=$! + +echo "Testing slapd searching..." +for i in 0 1 2 3 4 5; do + $LDAPSEARCH -L -b "$BASEDN" -h localhost -p $PORT \ + 'objectclass=*' > /dev/null 2>&1 + RC=$? + if test $RC = 1 ; then + echo "Waiting 5 seconds for slapd to start..." + sleep 5 + fi +done + +if test $RC != 0 ; then + echo "ldapsearch failed!" + kill -HUP $PID + exit $RC +fi + +cat /dev/null > $TESTOUT + +echo "Testing base suffix searching..." +$LDAPSEARCH -L -S "" -b "$BASEDN" -s base -h localhost -p $PORT \ + '(objectclass=*)' >> $TESTOUT 2>&1 +if test $RC != 0 ; then + echo "ldapsearch failed!" + kill -HUP $PID + exit $RC +fi + +echo " ------------ " >> $TESTOUT + +echo "Testing user searching..." +$LDAPSEARCH -L -S "" -b "uid=root,$BASEDN" -s base -h localhost -p $PORT \ + '(objectclass=*)' >> $TESTOUT 2>&1 +if test $RC != 0 ; then + echo "ldapsearch failed!" + kill -HUP $PID + exit $RC +fi + +echo " ------------ " >> $TESTOUT + +echo "Testing exact searching..." +$LDAPSEARCH -L -S "" -b "$BASEDN" -h localhost -p $PORT \ + '(uid=root)' >> $TESTOUT 2>&1 +if test $RC != 0 ; then + echo "ldapsearch failed!" + kill -HUP $PID + exit $RC +fi + +echo " ------------ " >> $TESTOUT + +echo "Testing OR searching..." +$LDAPSEARCH -L -S "" -b "$BASEDN" -h localhost -p $PORT \ + '(|(objectclass=person)(cn=root))' >> $TESTOUT 2>&1 +if test $RC != 0 ; then + echo "ldapsearch failed!" + kill -HUP $PID + exit $RC +fi + +echo " ------------ " >> $TESTOUT + +echo "Testing AND searching..." +$LDAPSEARCH -L -S "" -b "$BASEDN" -h localhost -p $PORT \ + '(&(objectclass=person)(cn=root))' >> $TESTOUT 2>&1 +if test $RC != 0 ; then + echo "ldapsearch failed!" + kill -HUP $PID + exit $RC +fi + +kill -HUP $PID + +echo "Comparing results" +cmp $TESTOUT $SEARCHOUTMASTER +if test $? != 0 ; then + echo "Comparison failed" + exit 1 +fi + +echo ">>>>> Test succeeded" + + +exit 0 -- 2.39.5