diff -Nru autofs-4.1.4/Makefile.conf.in autofs-4.1.4-ldap-20050930/Makefile.conf.in --- autofs-4.1.4/Makefile.conf.in 2004-04-03 09:14:33.000000000 +0200 +++ autofs-4.1.4-ldap-20050930/Makefile.conf.in 2005-09-30 10:46:03.000000000 +0200 @@ -25,6 +25,11 @@ LIBLDAP= @LIBLDAP@ LDAP_FLAGS = @LDAP_FLAGS@ +# SASL support (as an add-on for LDAP): yes (1) no (0) +SASL = @HAVE_SASL@ +LIBSASL= @LIBSASL@ +SASL_FLAGS = @SASL_FLAGS@ + # NIS+ support: yes (1) no (0) NISPLUS = @HAVE_NISPLUS@ diff -Nru autofs-4.1.4/configure autofs-4.1.4-ldap-20050930/configure --- autofs-4.1.4/configure 2005-04-06 17:24:37.000000000 +0200 +++ autofs-4.1.4-ldap-20050930/configure 2005-09-30 10:46:03.000000000 +0200 @@ -1,4 +1,11 @@ #! /bin/sh + +echo +echo "configure is out of date (after patching), please run" +echo " autoconf" +echo "first!" +exit 1 + # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59. # diff -Nru autofs-4.1.4/configure.in autofs-4.1.4-ldap-20050930/configure.in --- autofs-4.1.4/configure.in 2005-04-06 17:24:37.000000000 +0200 +++ autofs-4.1.4-ldap-20050930/configure.in 2005-09-30 10:46:03.000000000 +0200 @@ -128,6 +128,39 @@ LDFLAGS="${AF_tmp_ldflags}" # +# SASL support: +# +AF_tmp_ldflags="$LDFLAGS" +LIBSASL='' +HAVE_SASL='' +AC_ARG_WITH(sasl, +--with-sasl=DIR enable SASL support for LDAP maps (libs and includes in DIR), + if test "$withval" = 'no'; then + HAVE_SASL=0 # Disable + elif test -z "$withval" -o "$withval" = 'yes' + then + : Search for SASL in normal directory path + else + : Search for SASL in specific directory + LDFLAGS="$LDFLAGS -L${withval}/lib" + LIBSASL="-L${withval}/lib" + SASL_FLAGS="-I${withval}/include" + fi +) +if test -z "$HAVE_SASL"; then + HAVE_SASL=0 + AC_CHECK_LIB(sasl2, sasl_client_start, HAVE_SASL=1 LIBSASL="$LIBSASL -lsasl2", , -lsasl2 $LIBS) +fi +SASL_FLAGS="$SASL_FLAGS -DHAVE_SASL=$HAVE_SASL" + +AC_SUBST(SASL_FLAGS) +AC_SUBST(HAVE_SASL) +AC_SUBST(LIBSASL) +LDFLAGS="${AF_tmp_ldflags}" + + + +# # Does gcc support building position independent executables? # AC_PROG_CC diff -Nru autofs-4.1.4/modules/Makefile autofs-4.1.4-ldap-20050930/modules/Makefile --- autofs-4.1.4/modules/Makefile 2004-08-29 14:46:23.000000000 +0200 +++ autofs-4.1.4-ldap-20050930/modules/Makefile 2005-09-30 10:46:03.000000000 +0200 @@ -83,6 +83,6 @@ $(STRIP) lookup_hesiod.so lookup_ldap.so: lookup_ldap.c - $(CC) $(SOLDFLAGS) $(CFLAGS) $(LDAP_FLAGS) -o lookup_ldap.so \ - lookup_ldap.c $(AUTOFS_LIB) $(LIBLDAP) + $(CC) $(SOLDFLAGS) $(CFLAGS) $(LDAP_FLAGS) $(SASL_FLAGS) -o lookup_ldap.so \ + lookup_ldap.c $(AUTOFS_LIB) $(LIBLDAP) $(LIBSASL) $(STRIP) lookup_ldap.so diff -Nru autofs-4.1.4/modules/lookup_ldap.c autofs-4.1.4-ldap-20050930/modules/lookup_ldap.c --- autofs-4.1.4/modules/lookup_ldap.c 2005-02-27 06:37:14.000000000 +0100 +++ autofs-4.1.4-ldap-20050930/modules/lookup_ldap.c 2005-09-30 13:30:08.000000000 +0200 @@ -16,6 +16,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * + * patched 2005 by Timo Felbinger to + * - support LDAP URLs (rfc2255) + * - support TLS and SASL */ #include @@ -31,6 +34,8 @@ #include #include #include +#include +#include #define MODULE_LOOKUP #include "automount.h" @@ -40,63 +45,439 @@ #define MODPREFIX "lookup(ldap): " struct lookup_context { - char *server, *base; - int port; + void *freeme; /* a chunk of malloc()ated memory */ + const char *mapname; + LDAP *ldap; /* ldap handle (NULL iff not bound) */ + int ldap_version; /* first try 3; on error, fallback to 2 */ struct parse_mod *parse; + /* the pieces of an LDAP URL (see rfc2255): */ + char *schemehostport; + char *base; + char *scope_s; /* e.g. "sub" */ + int scope; /* dito as int: e.g. LDAP_SCOPE_SUBTREE */ + char *key; /* name of key attribute (e.g. "cn")*/ + char *ai; /* name of automount information attribute */ + char *ai2; /* compatibility kludge: alternative ai attribute */ + char *filter; /* filter expression */ + char *ext; /* extensions */ + /* extension parameters (defaults are taken if no value is specified): */ + int timeout; /* timeout in seconds */ +# define LDAPEXTENSION_TIMEOUT_DEFAULT 8 + char *sasl_mech; /* SASL authentication mechanism (e.g. "EXTERNAL") */ + char *binddn; /* dn to bind as */ + char *bindpw; /* password to use for simple binds */ + /* return value of most recent LDAP library call: */ + int errno_ldap; }; int lookup_version = AUTOFS_LOOKUP_VERSION; /* Required by protocol */ -static LDAP *do_connect(struct lookup_context *ctxt, int *result_ldap) + +#if HAVE_SASL + +/* + * stuff required for SASL authentication + * (shamelessly stolen from OpenLDAP: this is not yet documented and + * highly experimental, but seems to work, for some mechanisms, somehow...) + * + */ + +#include + +typedef struct lutil_sasl_defaults_s { + char *mech; + char *realm; + char *authcid; + char *passwd; + char *authzid; + char **resps; + int nresps; +} lutilSASLdefaults; + +static void +lutil_sasl_freedefs( + void *defaults ) +{ + lutilSASLdefaults *defs = defaults; + + if (defs->mech) ber_memfree(defs->mech); + if (defs->realm) ber_memfree(defs->realm); + if (defs->authcid) ber_memfree(defs->authcid); + if (defs->passwd) ber_memfree(defs->passwd); + if (defs->authzid) ber_memfree(defs->authzid); + if (defs->resps) ldap_charray_free(defs->resps); + + ber_memfree(defs); +} + +static void * +lutil_sasl_defaults( + LDAP *ld, + char *mech, + char *realm, + char *authcid, + char *passwd, + char *authzid ) +{ + lutilSASLdefaults *defaults; + + defaults = ber_memalloc( sizeof( lutilSASLdefaults ) ); + + if( defaults == NULL ) return NULL; + + defaults->mech = mech ? ber_strdup(mech) : NULL; + defaults->realm = realm ? ber_strdup(realm) : NULL; + defaults->authcid = authcid ? ber_strdup(authcid) : NULL; + defaults->passwd = passwd ? ber_strdup(passwd) : NULL; + defaults->authzid = authzid ? ber_strdup(authzid) : NULL; + + if( defaults->mech == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_MECH, &defaults->mech ); + } + if( defaults->realm == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_REALM, &defaults->realm ); + } + if( defaults->authcid == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHCID, &defaults->authcid ); + } + if( defaults->authzid == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHZID, &defaults->authzid ); + } + defaults->resps = NULL; + defaults->nresps = 0; + + return defaults; +} + +static int interaction( + unsigned flags, + sasl_interact_t *interact, + lutilSASLdefaults *defaults ) +{ + const char *dflt = interact->defresult; + char input[1024]; + + int noecho=0; + int challenge=0; + + switch( interact->id ) { + case SASL_CB_GETREALM: + if( defaults ) dflt = defaults->realm; + break; + case SASL_CB_AUTHNAME: + if( defaults ) dflt = defaults->authcid; + break; + case SASL_CB_PASS: + if( defaults ) dflt = defaults->passwd; + noecho = 1; + break; + case SASL_CB_USER: + if( defaults ) dflt = defaults->authzid; + break; + case SASL_CB_NOECHOPROMPT: + noecho = 1; + challenge = 1; + break; + case SASL_CB_ECHOPROMPT: + challenge = 1; + break; + } + + if( dflt && !*dflt ) dflt = NULL; + + if( flags != LDAP_SASL_INTERACTIVE && + ( dflt || interact->id == SASL_CB_USER ) ) + { + goto use_default; + } + + /* TF: for an _auto_-mounter, the caller should set LDAP_SASL_QUIET: */ + if( flags == LDAP_SASL_QUIET ) { + /* don't prompt */ + return LDAP_OTHER; + } + /* TF: we can't interact and bail out: */ + crit( MODPREFIX "SASL interaction(): user interaction requested!" ); + return LDAP_OTHER; + + /* *SNIP* (interactive code deleted) */ + +use_default: + /* input must be empty */ + interact->result = (dflt && *dflt) ? dflt : ""; + interact->len = strlen( interact->result ); + + return LDAP_SUCCESS; +} + +static int lutil_sasl_interact( + LDAP *ld, + unsigned flags, + void *defaults, + void *in ) +{ + sasl_interact_t *interact = in; + + /* TF: we override any flags and try to avoid "interactive" interaction: */ + flags = LDAP_SASL_QUIET; + + if( ld == NULL ) return LDAP_PARAM_ERROR; + +/* + * if( flags == LDAP_SASL_INTERACTIVE ) { + * fputs( _("SASL Interaction\n"), stderr ); + * } + * + */ + + while( interact->id != SASL_CB_LIST_END ) { + int rc; + debug( MODPREFIX "attempting SASL interaction: %ld", interact->id ); + rc = interaction( flags, interact, defaults ); + + if( rc ) return rc; + interact++; + } + + return LDAP_SUCCESS; +} + +/* end of stolen SASL stuff... */ + +#endif /* HAVE_SASL */ + + + +/* do_connect: connect and bind to LDAP server, return 0 on success: */ +static int do_connect( struct lookup_context *ctxt ) { LDAP *ldap; - int version = 3; - int timeout = 8; - int rv; - /* Initialize the LDAP context. */ - ldap = ldap_init(ctxt->server, ctxt->port); - if (!ldap) { - crit(MODPREFIX "couldn't initialize LDAP connection" - " to %s", ctxt->server ? ctxt->server : "default server"); - return NULL; - } - - /* Use LDAPv3 */ - rv = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version); - if (rv != LDAP_SUCCESS) { - /* fall back to LDAPv2 */ - ldap_unbind(ldap); - ldap = ldap_init(ctxt->server, ctxt->port); - if (!ldap) { - crit(MODPREFIX "couldn't initialize LDAP"); - return NULL; + debug( MODPREFIX "do_connect: start" ); + + if( ctxt->ldap ) { + /* not an error, but should currently not happen: */ + crit( MODPREFIX "do_connect: already connected?" ); + ctxt->errno_ldap = LDAP_SUCCESS; + return 0; + } + +retry: + + debug( MODPREFIX "initialzing ldap session handle" ); + ctxt->errno_ldap = ldap_initialize( &ldap, ctxt->schemehostport ); + if( ctxt->errno_ldap != LDAP_SUCCESS || !ldap ) { + crit( MODPREFIX "ldap_initialize failed: %s", ldap_err2string(ctxt->errno_ldap) ); + if( ldap ) + ldap_unbind( ldap ); + return 1; + } + + debug( MODPREFIX "setting LDAP protocol version %d", ctxt->ldap_version ); + ctxt->errno_ldap = ldap_set_option( ldap, LDAP_OPT_PROTOCOL_VERSION, &(ctxt->ldap_version) ); + if( ctxt->errno_ldap != LDAP_SUCCESS ) { + /* the local library should definitely support version 3: */ + crit( MODPREFIX "ldap_set_option failed: %s", ldap_err2string(ctxt->errno_ldap) ); + ldap_unbind( ldap ); + return 1; + } + + if( ctxt->timeout > 0 ) { + debug(MODPREFIX "setting LDAP timeout to %d", ctxt->timeout ); + ctxt->errno_ldap = ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, &(ctxt->timeout) ); + if( ctxt->errno_ldap != LDAP_SUCCESS ) { + warn(MODPREFIX "failed to set connection timeout to %d", ctxt->timeout); + } + } + + debug( MODPREFIX "binding to server" ); + +#if HAVE_SASL + if( ctxt->sasl_mech ) { + void *defaults; + debug( MODPREFIX "attempting SASL mechanism %s", ctxt->sasl_mech ); + + /* The following funtion calls seem nowhere documented; passing mostly + * NULL arguments for defaults seems to work (and is what the sample + * clients in OpenLDAP do!): + */ + defaults = lutil_sasl_defaults( ldap, + ctxt->sasl_mech + , 0 + , 0 + , 0 + , 0 + ); + ctxt->errno_ldap = ldap_sasl_interactive_bind_s( ldap, NULL, + ctxt->sasl_mech, NULL, NULL, 0, lutil_sasl_interact, defaults ); + lutil_sasl_freedefs( defaults ); + + if( ctxt->errno_ldap != LDAP_SUCCESS ) { + crit(MODPREFIX "ldap_sasl_interactive_bind_s() failed: %s", ldap_err2string(ctxt->errno_ldap) ); + ldap_unbind( ldap ); + return 1; + } +#else + if(0) { +#endif + } else { /* no SASL: do simple bind */ + + if( ctxt->binddn ) { + debug( MODPREFIX "attempting simple bind as %s", ctxt->binddn ); + /* servers usually won't accept non-anonymous bind with no credentials: */ + if( ! ctxt->bindpw ) + debug( MODPREFIX "WARNING: have binddn but no password" ); } else { - version = 2; + debug( MODPREFIX "attempting simple anonymous bind" ); } + + ctxt->errno_ldap = ldap_simple_bind_s( ldap, ctxt->binddn, ctxt->bindpw ); + if (ctxt->errno_ldap != LDAP_SUCCESS) { + if( ctxt->ldap_version != LDAP_VERSION2 ) { + /* for the server, we provide a fallback: */ + debug(MODPREFIX "ldap_simple_bind_s() failed: %s", ldap_err2string(ctxt->errno_ldap) ); + debug(MODPREFIX "fallback to LDAPv2" ); + ctxt->ldap_version = LDAP_VERSION2; + ldap_unbind( ldap ); + goto retry; + } + crit(MODPREFIX "ldap_simple_bind_s() failed: %s", ldap_err2string(ctxt->errno_ldap) ); + ldap_unbind( ldap ); + return 1; + } + } + + ctxt->ldap = ldap; + return 0; +} - /* Sane network connection timeout */ - rv = ldap_set_option(ldap, LDAP_OPT_NETWORK_TIMEOUT, &timeout); - if (rv != LDAP_SUCCESS) { - warn(MODPREFIX - "failed to set connection timeout to %d", timeout); +static int do_disconnect( struct lookup_context *ctxt ) +{ + debug( MODPREFIX "do_disconnect: start" ); + if( ctxt->ldap ) { + ctxt->errno_ldap = ldap_unbind( ctxt->ldap ); + ctxt->ldap = NULL; + if (ctxt->errno_ldap != LDAP_SUCCESS) { + crit(MODPREFIX "ldap_unbind() failed: %s", ldap_err2string(ctxt->errno_ldap) ); + return 1; + } } + return 0; +} + +/* replace %-escape sequences: */ +static char *unpercent( char *s ) +{ + unsigned char *p1, *p2; + p1 = p2 = s; + while( 1 ) { + if( *p1 == '%' ) { + if( *(++p1) >= '0' && *p1 <= '9' ) + *p2 = ( *p1 - '0' ) << 4; + else if( *p1 >= 'a' && *p1 <= 'f' ) + *p2 = ( *p1 - ('a' - 10) ) << 4; + else if( *p1 >= 'A' && *p1 <= 'F' ) + *p2 = ( *p1 - ('A' - 10) ) << 4; + else { + crit( MODPREFIX "parse error in percent escape" ); + return 0; + } + if( *(++p1) >= '0' && *p1 <= '9' ) + *p2++ += ( *p1++ - '0' ); + else if( *p1 >= 'a' && *p1 <= 'f' ) + *p2++ += ( *p1++ - ('a' - 10) ); + else if( *p1 >= 'A' && *p1 <= 'F' ) + *p2++ += ( *p1++ - ('A' - 10) ); + else { + crit( MODPREFIX "parse error in percent escape" ); + return 0; + } + } else { + if( ! ( *p2++ = *p1++ ) ) + return s; + } + } +} + +/* parse_extension: parse one element of the extension part in the URL. + * return 0 on success. + */ +static int parse_extension( struct lookup_context *ctxt, char *token ) +{ + char *value; + int critical; - /* Connect to the server as an anonymous user. */ - if (version == 2) - rv = ldap_simple_bind_s(ldap, ctxt->base, NULL); + value = strchr( token, '=' ); + if( value ) + *value++ = 0; else - rv = ldap_simple_bind_s(ldap, NULL, NULL); + value = ""; + + debug( MODPREFIX "extension: %s%s%s", token, (*value ? "=" : ""), value ); + + if( *token == '!' ) + critical = 1, ++token; + else + critical = 0; - if (rv != LDAP_SUCCESS) { - crit(MODPREFIX "couldn't bind to %s", - ctxt->server ? ctxt->server : "default server"); - *result_ldap = rv; - return NULL; + if( ! strcasecmp( token, "timeout" ) ) { + ctxt->timeout = ( *value ? atoi( value ) : LDAPEXTENSION_TIMEOUT_DEFAULT ); +#if HAVE_SASL + } else if ( !strcasecmp( token, "sasl_mech" ) ) { + ctxt->sasl_mech = ( *value ? value : "default" /* not sure whether this works */ ); +#endif + } else if ( !strcasecmp( token, "tls_key" ) ) { + /* the only documented way (afaik) to pass tls_key and tls_cert to the ldap + * library is via the environment, so we just stuff it there: + */ + if( setenv( "LDAPTLS_KEY", value, 1 ) == -1 ) { + crit( MODPREFIX "setenv() failed for LDAPTLS_KEY" ); + return 1; + } + } else if ( !strcasecmp( token, "tls_cert" ) ) { + if( setenv( "LDAPTLS_CERT", value, 1 ) == -1 ) { + crit( MODPREFIX "setenv() failed for LDAPTLS_CERT" ); + return 1; + } + } else if ( !strcasecmp( token, "binddn" ) ) { + ctxt->binddn = value; + } else if ( !strcasecmp( token, "bindpwfile" ) ) { + int pwfile; + struct stat st; + pwfile = open( value, O_RDONLY ); + if( pwfile == -1 ) { + crit( MODPREFIX "failed to open password file %s", value ); + return 1; + } + if( fstat( pwfile, &st ) == -1 ) { + crit( MODPREFIX "fstat() failed for password file %s", value ); + close(pwfile); + return 1; + } + ctxt->bindpw = malloc( st.st_size + 1 ); + if( ! ctxt->bindpw ) { + crit( MODPREFIX "malloc() failed for bindpw" ); + close(pwfile); + return 1; + } + /* pw read _should_ be atomic */ + if( read( pwfile, ctxt->bindpw, st.st_size ) != st.st_size ) { + crit( MODPREFIX "failed to read bindpw" ); + close(pwfile); + return 1; + } + ctxt->bindpw[ st.st_size ] = 0; + close(pwfile); + } else { + if( critical ) { + crit( MODPREFIX "unsupported critical extension %s", token ); + return 1; + } else { + debug( MODPREFIX "ignoring unsupported extension %s", token ); + } } - - return ldap; + return 0; } /* @@ -106,223 +487,367 @@ int lookup_init(const char *mapfmt, int argc, const char *const *argv, void **context) { struct lookup_context *ctxt = NULL; - int l, rv; - LDAP *ldap; - char *ptr = NULL; + int rv; + char *p1, *p2; - /* If we can't build a context, bail. */ - ctxt = (struct lookup_context *) malloc(sizeof(struct lookup_context)); - *context = ctxt; - if (ctxt == NULL) { - crit(MODPREFIX "malloc: %m"); - return 1; - } - memset(ctxt, 0, sizeof(struct lookup_context)); + debug( MODPREFIX "lookup_init: parsing map name: %s", argv[0] ); - /* If a map type isn't explicitly given, parse it like sun entries. */ - if (mapfmt == NULL) { - mapfmt = MAPFMT_DEFAULT; + ctxt = (struct lookup_context *)malloc( sizeof( struct lookup_context ) ); + if( !ctxt ) { + crit( MODPREFIX "malloc failed." ); + goto bailout; } - - /* Now we sanity-check by binding to the server temporarily. We have - * to be a little strange in here, because we want to provide for - * use of the "default" server, which is set in an ldap.conf file - * somewhere. */ - - ctxt->server = NULL; - ctxt->port = LDAP_PORT; + ctxt->freeme = NULL; + ctxt->schemehostport = NULL; ctxt->base = NULL; - - ptr = (char *) argv[0]; - - if (!strncmp(ptr, "//", 2)) { - char *s = ptr + 2; - char *p = NULL, *q = NULL; - - /* Isolate the server's name and possibly port. But the : breaks - the SUN parser for submounts so we can't actually use it. - */ - if ((q = strchr(s, '/'))) { - if ((p = strchr(s, ':'))) { - l = p - s; - p++; - ctxt->port = atoi(p); - } else { - l = q - s; + ctxt->key = NULL; + ctxt->ai = NULL; + ctxt->ai2 = NULL; + ctxt->filter = NULL; + ctxt->scope_s = NULL; + ctxt->ext = NULL; + ctxt->ldap = NULL; + ctxt->parse = NULL; + ctxt->ldap_version = LDAP_VERSION3; + + ctxt->timeout = 0; + ctxt->sasl_mech = ctxt->binddn = ctxt->bindpw = NULL; + + /* we hope nobody is going to fiddle with argv[0] in place: */ + ctxt->mapname = argv[0]; + + ctxt->freeme = malloc( 5 + strlen( ctxt->mapname ) + 1 ); + if( ! ctxt->freeme ) { + crit( MODPREFIX "malloc() failed." ); + goto bailout; + } + p1 = (char *)ctxt->freeme + 5; + /* in rfc2255 and in (some versions of) libldap, scheme is not optional: */ + strcpy( p1 - 5, "ldap:" ); + strcpy( p1, ctxt->mapname ); + + /* parse an URL of the form (see README.ldap for more info) + * [[scheme:]//host[:port]][[/]basedn][?attr[?scope[?filter[?extension]]]] + * for compatibility, we also allow + * server:basedn + */ + + p2 = strchr( p1, '/' ); + if( p2 ) { + if( p2[1] == '/' ) { /* found '//server' part */ + ctxt->schemehostport = p1; + p1 = p2 + 2; + p2 = strchr( p1, '/' ); + if( p2 ) { /* there is /basedn too */ + *p2 = 0; + p1 = p2+1; + ctxt->base = p1; } + } else { /* no //server, but /basedn */ + *p2 = 0; + p1 = p2+1; + ctxt->base = p1; + } + } else { + /* server and basedn might be separated by : (why?) */ + p2 = strchr( p1, ':' ); + if( p2 ) { + ctxt->schemehostport = p1; + *p2 = 0; + p1 = p2+1; + } + /* allow basedn with no leading / : */ + ctxt->base = p1; + } + p2 = strchr( p1, '?' ); + if( p2 ) { + *p2 = 0; + p1 = p2+1; + ctxt->key = p1; + } + p2 = strchr( p1, '?' ); + if( p2 ) { + *p2 = 0; + p1 = p2+1; + ctxt->scope_s = p1; + } + p2 = strchr( p1, '?' ); + if( p2 ) { + *p2 = 0; + p1 = p2+1; + ctxt->filter = p1; + } + p2 = strchr( p1, '?' ); + if( p2 ) { + *p2 = 0; + p1 = p2+1; + ctxt->ext = p1; + } + + debug( MODPREFIX "parse result: " + "%s BASE: %s ATTR: %s SCOPE: %s FILTER: %s EXT: %s", + ctxt->schemehostport, ctxt->base, ctxt->key, ctxt->scope_s, + ctxt->filter, ctxt->ext + ); + + /* sanitize the parsing result and provide defaults: */ + + if( ctxt->schemehostport ) { + if( !unpercent( ctxt->schemehostport ) ) + goto bailout; + switch( *ctxt->schemehostport ) { + case 0: + ctxt->schemehostport = NULL; + break; + case '/': + /* no scheme given: we _must_ prepend one: */ + ctxt->schemehostport -= 5; + break; + } + } - ctxt->server = malloc(l + 1); - memset(ctxt->server, 0, l + 1); - memcpy(ctxt->server, s, l); - - ptr = q + 1; - } - } else if (strchr(ptr, ':') != NULL) { - l = strchr(ptr, ':') - ptr; - /* Isolate the server's name. */ - ctxt->server = malloc(l + 1); - memset(ctxt->server, 0, l + 1); - memcpy(ctxt->server, argv[0], l); - ptr += l+1; - } - - /* Isolate the base DN. */ - l = strlen(ptr); - ctxt->base = malloc(l + 1); - memset(ctxt->base, 0, l + 1); - memcpy(ctxt->base, ptr, l); - - debug(MODPREFIX "server = \"%s\", port = %d, base dn = \"%s\"", - ctxt->server ? ctxt->server : "(default)", - ctxt->port, ctxt->base); + if( ctxt->key && *ctxt->key ) { + p2 = strchr( ctxt->key, ',' ); + if( p2 ) { + *p2 = 0; + ctxt->ai = p2+1; + if( !unpercent( ctxt->ai ) ) + goto bailout; + } else { + ctxt->ai = "automountInformation"; + } + } else { + ctxt->key = "cn"; + ctxt->ai = "automountInformation"; + /* compatibility kludge: if nothing specified, try nis.schema too: */ + ctxt->ai2 = "nisMapEntry"; + } - /* Initialize the LDAP context. */ - ldap = do_connect(ctxt, &rv); - if (!ldap) - return 1; + if( ctxt->scope_s && *ctxt->scope_s ) { + if( !unpercent( ctxt->scope_s ) ) + goto bailout; + } else { + ctxt->scope_s = "sub"; + } + if( ! strcmp( ctxt->scope_s, "base" ) ) { + ctxt->scope = LDAP_SCOPE_BASE; + } else if ( ! strcmp( ctxt->scope_s, "one" ) ) { + ctxt->scope = LDAP_SCOPE_ONELEVEL; + } else if ( ! strcmp( ctxt->scope_s, "sub" ) ) { + ctxt->scope = LDAP_SCOPE_SUBTREE; + } else { + crit( MODPREFIX "in LDAP URL: scope %s unknown", ctxt->scope_s ); + goto bailout; + } - /* Okay, we're done here. */ - ldap_unbind(ldap); + if( ctxt->filter && *ctxt->filter ) { + if( !unpercent( ctxt->filter ) ) + goto bailout; + } else { + ctxt->filter = "(|(objectclass=automount)(objectclass=nisObject))"; + } + if( strchr( ctxt->filter , '&' ) ) { + debug( MODPREFIX "warning: & occurs in LDAP filter" ); + } + + for( p1 = ctxt->ext; p1 && *p1; p1 = p2 ) { + p2 = strchr( p1, ',' ); + if( p2 ) + *p2++ = 0; + if( !unpercent(p1) ) + goto bailout; + if( parse_extension( ctxt, p1 ) ) + goto bailout; + } + + debug( MODPREFIX "lookup_init: test-binding to LDAP server" ); + rv = do_connect( ctxt ); + if( rv ) { + crit( MODPREFIX "test-binding to LDAP server failed." ) + goto bailout; + } + + rv = do_disconnect( ctxt ); + if( rv ) { + crit( MODPREFIX "test-binding to LDAP server ok, but unbind failed." ); + goto bailout; + } + + mapfmt = mapfmt ? mapfmt : MAPFMT_DEFAULT; + debug( MODPREFIX "lookup_init: initializing parser: mapfmt: %s", mapfmt ); + ctxt->parse = open_parse( mapfmt, MODPREFIX, argc - 1, argv + 1 ); + if( !ctxt->parse ) { + crit( MODPREFIX "open_parse() failed." ); + goto bailout; + } + + *context = ctxt; + return 0; - /* Open the parser, if we can. */ - return !(ctxt->parse = open_parse(mapfmt, MODPREFIX, argc - 1, argv + 1)); +bailout: + if( ctxt ) { + if( ctxt->freeme ) + free( ctxt->freeme ); + free( ctxt ); + } + return 1; +} + +/* sanitize_filter: avoid potentially unsafe characters in the ldap + * filter expression. I _think_ that the only critical characters + * are the parentheses, and the wild card. + * The proper solution would be to replace them + * by backslash-hex-sequences but that would increase string length. + */ +static int sanitize_filter( const char *s, int len ) { + /* const char positive_list[] = "._-+/"; */ + const char negative_list[] = "()*"; + + debug( MODPREFIX "sanitize_filter: %s", s ); + while(len > 0) { +/* if( isalnum(*s) ) { + ++s, --len; + } else if( strchr( positive_list, *s ) ) { +*/ + if( ! strchr( negative_list, *s ) ) { + ++s, --len; + } else { + crit( MODPREFIX "sanitize_filter: unsafe character (%d) in search string", *s ); + return 1; + } + } + return 0; } -static int read_one_map(const char *root, - const char *class, char *key, - const char *keyval, int keyvallen, char *type, - struct lookup_context *ctxt, - time_t age, int *result_ldap) +static int read_one_map(const char *root, struct lookup_context *ctxt, + const char *keyval, int keyvallen, time_t age ) { - int rv, i, j, l, count, keycount; + int rv, i, j, l; + /* time_t age = time(NULL); */ char *query; - LDAPMessage *result, *e; - char **keyValue = NULL; - char **values = NULL; - char *attrs[] = { key, type, NULL }; - LDAP *ldap; + LDAPMessage *result = NULL; + LDAPMessage *e = NULL; + char **keyvalues = NULL; + char **aivalues = NULL; + char *attrs[4]; + int ret = 0; if (ctxt == NULL) { crit(MODPREFIX "context was NULL"); return 0; } - /* Build a query string. */ - l = strlen("(objectclass=)") + strlen(class) + 1; - if (keyvallen > 0) { - l += strlen(key) +keyvallen + strlen("(&(=))"); - } - - query = alloca(l); - if (query == NULL) { - crit(MODPREFIX "malloc: %m"); + if( sanitize_filter( keyval, keyvallen ) ) { + crit(MODPREFIX "read_one_map: illegal character in search key, bailing out" ); return 0; } - memset(query, '\0', l); - if (keyvallen > 0) { - if (sprintf(query, "(&(objectclass=%s)(%s=%.*s))", class, - key, keyvallen, keyval) >= l) { - debug(MODPREFIX "error forming query string"); + /* Build a query string. */ + if( keyvallen > 0 ) { + l = strlen( "(&(=))" ) + + strlen( ctxt->filter ) + + strlen( ctxt->key ) + + keyvallen + + 1; + query = alloca(l); + if( ! query ) { + crit( MODPREFIX "malloc %m" ); + return 0; } + snprintf( query, l, "(&%s(%s=%.*s))", ctxt->filter, ctxt->key, keyvallen, keyval ); + query[ l - 1 ] = 0; } else { - if (sprintf(query, "(objectclass=%s)", class) >= l) { - debug(MODPREFIX "error forming query string"); - } + query = ctxt->filter; } - query[l - 1] = '\0'; - /* Initialize the LDAP context. */ - ldap = do_connect(ctxt, result_ldap); - if (!ldap) - return 0; + if( ! ctxt->ldap ) { + rv = do_connect( ctxt ); + if( rv || ! ctxt->ldap ) { + crit( MODPREFIX "read_one_map: failed to setup LDAP connection." ); + goto exit; + } + } /* Look around. */ debug(MODPREFIX "searching for \"%s\" under \"%s\"", query, ctxt->base); - rv = ldap_search_s(ldap, ctxt->base, LDAP_SCOPE_SUBTREE, - query, attrs, 0, &result); - - if ((rv != LDAP_SUCCESS) || !result) { - crit(MODPREFIX "query failed for %s: %s", query, ldap_err2string(rv)); - ldap_unbind(ldap); - *result_ldap = rv; - return 0; + attrs[0] = ctxt->key; + attrs[1] = ctxt->ai; + attrs[2] = ctxt->ai2; + attrs[3] = NULL; + result = NULL; + ctxt->errno_ldap = ldap_search_s( ctxt->ldap, ctxt->base, + ctxt->scope, query, attrs, 0, &result ); + if ((ctxt->errno_ldap != LDAP_SUCCESS) || !result) { + crit(MODPREFIX "LDAP query failed: %s", ldap_err2string(ctxt->errno_ldap) ); + goto exit; } - e = ldap_first_entry(ldap, result); + e = ldap_first_entry(ctxt->ldap, result); if (!e) { debug(MODPREFIX "query succeeded, no matches for %s", query); - ldap_msgfree(result); - ldap_unbind(ldap); - return 0; - } else - debug(MODPREFIX "examining entries"); + goto exit; + } + + debug(MODPREFIX "query succeeded, examining entries..."); - while (e) { - keyValue = ldap_get_values(ldap, e, key); + for( + ; e + ; keyvalues ? ( ldap_value_free( keyvalues ), keyvalues = NULL ) : 0, + aivalues ? ( ldap_value_free( aivalues ), aivalues = NULL ) : 0, + e = ldap_next_entry( ctxt->ldap, e ) + ) { - if (!keyValue || !*keyValue) { - e = ldap_next_entry(ldap, e); + keyvalues = ldap_get_values( ctxt->ldap, e, ctxt->key ); + if ( !keyvalues || !*keyvalues ) continue; - } - values = ldap_get_values(ldap, e, type); - if (!values) { - info(MODPREFIX "no %s defined for %s", type, query); - ldap_value_free(keyValue); - e = ldap_next_entry(ldap, e); + aivalues = ldap_get_values( ctxt->ldap, e, ctxt->ai ); + if( ( !aivalues || !*aivalues ) ) { + if( ctxt->ai2 ) { + debug( MODPREFIX "no automount information found, trying alternative schema" ); + if( aivalues ) + ldap_value_free( aivalues ); + aivalues = ldap_get_values( ctxt->ldap, e, ctxt->ai2 ); + } + } + if( !aivalues || !*aivalues ) { + crit( MODPREFIX "no automount information found, skipping entry" ); continue; } - count = ldap_count_values(values); - keycount = ldap_count_values(keyValue); - for (i = 0; i < count; i++) { - for (j = 0; j < keycount; j++) { - if (*(keyValue[j]) == '/' && - strlen(keyValue[j]) == 1) - *(keyValue[j]) = '*'; - cache_add(root, keyValue[j], values[i], age); + for( j = 0; keyvalues[j]; ++j ) { + if (keyvalues[j][0] == '/' && keyvalues[j][1] == 0) + keyvalues[j][0] = '*'; + + for (i = 0; aivalues[i]; i++) { + debug( MODPREFIX "adding to cache: %s, %s", keyvalues[j], aivalues[i] ); + cache_add( root, keyvalues[j], aivalues[i], age); + ret = 1; } } - ldap_value_free(values); - - ldap_value_free(keyValue); - e = ldap_next_entry(ldap, e); } debug(MODPREFIX "done updating map"); +exit: + /* Clean up. */ - ldap_msgfree(result); - ldap_unbind(ldap); + if( result ) + ldap_msgfree(result); + if( ctxt->ldap ) + do_disconnect( ctxt ); - return 1; + return ret; } static int read_map(const char *root, struct lookup_context *ctxt, - const char *key, int keyvallen, time_t age, int *result_ldap) + const char *keyval, int keyvallen, time_t age ) { - int rv1 = LDAP_SUCCESS, rv2 = LDAP_SUCCESS; - int ret; - /* all else fails read entire map */ - ret = read_one_map(root, "nisObject", "cn", - key, keyvallen, "nisMapEntry", ctxt, age, &rv1); - if (ret) - goto ret_ok; - - ret = read_one_map(root, "automount", "cn", key, keyvallen, - "automountInformation", ctxt, age, &rv2); - if (ret) - goto ret_ok; - - if (result_ldap) - *result_ldap = (rv1 == LDAP_SUCCESS ? rv2 : rv1); - - return 0; + if (!read_one_map(root, ctxt, keyval, keyvallen, age )) + return 0; -ret_ok: /* Clean stale entries from the cache */ cache_clean(root, age); @@ -333,32 +858,22 @@ { struct lookup_context *ctxt = (struct lookup_context *) context; struct mapent_cache *me; - int status = 1, rv = LDAP_SUCCESS; - char *mapname; + int status = 1; time_t age = now ? now : time(NULL); chdir("/"); - if (!read_map(root, ctxt, NULL, 0, age, &rv)) - switch (rv) { + if (!read_map(root, ctxt, NULL, 0, age )) { + switch( ctxt->errno_ldap ) { case LDAP_SIZELIMIT_EXCEEDED: case LDAP_UNWILLING_TO_PERFORM: return LKP_NOTSUP; default: return LKP_FAIL; } - - if (ctxt->server) { - int len = strlen(ctxt->server) + strlen(ctxt->base) + 4; - - mapname = alloca(len); - sprintf(mapname, "//%s/%s", ctxt->server, ctxt->base); - } else { - mapname = alloca(strlen(ctxt->base) + 1); - sprintf(mapname, "%s", ctxt->base); } - status = cache_ghost(root, ghost, mapname, "ldap", ctxt->parse); + status = cache_ghost(root, ghost, ctxt->mapname, "ldap", ctxt->parse); me = cache_lookup_first(); /* me NULL => empty map */ @@ -378,17 +893,16 @@ return status; } -static int lookup_one(const char *root, const char *qKey, - const char *class, char *key, char *type, - struct lookup_context *ctxt) +static int lookup_one( const char *root, struct lookup_context *ctxt, + const char *qKey ) { int rv, i, l, ql; time_t age = time(NULL); char *query; - LDAPMessage *result, *e; + LDAPMessage *result = NULL; + LDAPMessage *e = NULL; char **values = NULL; - char *attrs[] = { key, type, NULL }; - LDAP *ldap; + char *attrs[3]; struct mapent_cache *me = NULL; int ret = CHE_OK; @@ -396,11 +910,18 @@ crit(MODPREFIX "context was NULL"); return 0; } + + if( sanitize_filter( qKey, strlen(qKey) ) ) { + crit(MODPREFIX "lookup_one: illegal character in search key, bailing out" ); + return 0; + } /* Build a query string. */ - l = strlen("(&(objectclass=") + strlen(class) + strlen(")"); - l += strlen("(") + strlen(key) + strlen("=") - + strlen(qKey) + strlen("))") + 1; + l = strlen( "(&(=))" ) + + strlen( ctxt->filter ) + + strlen( ctxt->key ) + + strlen( qKey ) + + 1; query = alloca(l); if (query == NULL) { @@ -410,47 +931,58 @@ /* Look around. */ memset(query, '\0', l); - ql = sprintf(query, "(&(objectclass=%s)(%s=%s))", class, key, qKey); + ql = sprintf( query, "(&%s(%s=%s))", ctxt->filter, ctxt->key, qKey); if (ql >= l) { - debug(MODPREFIX "error forming query string"); + crit(MODPREFIX "error forming query string"); return 0; } query[l - 1] = '\0'; debug(MODPREFIX "searching for \"%s\" under \"%s\"", query, ctxt->base); - /* Initialize the LDAP context. */ - ldap = do_connect(ctxt, &rv); - if (!ldap) - return 0; - - rv = ldap_search_s(ldap, ctxt->base, LDAP_SCOPE_SUBTREE, - query, attrs, 0, &result); + if( ! ctxt->ldap ) { + rv = do_connect( ctxt ); + if( rv || ! ctxt->ldap ) { + crit( MODPREFIX "lookup_one: failed to setup LDAP connection." ); + return 0; + } + } - if ((rv != LDAP_SUCCESS) || !result) { - crit(MODPREFIX "query failed for %s", query); - ldap_unbind(ldap); - return 0; + attrs[0] = ctxt->ai; + attrs[1] = ctxt->ai2; + attrs[2] = NULL; + result = NULL; + ctxt->errno_ldap = ldap_search_s( ctxt->ldap, ctxt->base, ctxt->scope, query, attrs, 0, &result ); + if ((ctxt->errno_ldap != LDAP_SUCCESS) || !result) { + crit(MODPREFIX "query failed for %s: %s", query, ldap_err2string(ctxt->errno_ldap) ); + ret = 0; + goto exit; } - debug(MODPREFIX "getting first entry for %s=\"%s\"", key, qKey); + debug(MODPREFIX "getting first entry for %s=\"%s\"", ctxt->key, qKey); - e = ldap_first_entry(ldap, result); + e = ldap_first_entry( ctxt->ldap, result ); if (!e) { crit(MODPREFIX "got answer, but no first entry for %s", query); - ldap_msgfree(result); - ldap_unbind(ldap); - return CHE_MISSING; + ret = CHE_MISSING; + goto exit; } debug(MODPREFIX "examining first entry"); - values = ldap_get_values(ldap, e, type); - if (!values) { - debug(MODPREFIX "no %s defined for %s", type, query); - ldap_msgfree(result); - ldap_unbind(ldap); - return CHE_MISSING; + values = ldap_get_values( ctxt->ldap, e, ctxt->ai ); + if( ( !values || !*values ) ) { + /* for backward compatibility: try second schema, too: */ + if( ctxt->ai2 ) { + debug( MODPREFIX "no automount information found, trying alternative schema" ); + if( values ) ldap_value_free( values ); + values = ldap_get_values( ctxt->ldap, e, ctxt->ai2 ); + } + } + if( !values || !*values ) { + debug(MODPREFIX "no %s defined for %s", ctxt->ai2 ? ctxt->ai2 : ctxt->ai, query); + ret = CHE_MISSING; + goto exit; } /* Compare cache entry against LDAP */ @@ -467,32 +999,36 @@ for (i = 0; values[i]; i++) { rv = cache_add(root, qKey, values[i], age); - if (!rv) - return 0; + if (!rv) { + ret = 0; + goto exit; + } } - ret = CHE_UPDATED; } +exit: + /* Clean up. */ - ldap_value_free(values); - ldap_msgfree(result); - ldap_unbind(ldap); + if(values) + ldap_value_free(values); + if(result) + ldap_msgfree(result); + if(ctxt->ldap) + do_disconnect(ctxt); return ret; } -static int lookup_wild(const char *root, - const char *class, char *key, char *type, - struct lookup_context *ctxt) +static int lookup_wild( const char *root, struct lookup_context *ctxt ) { int rv, i, l, ql; time_t age = time(NULL); char *query; - LDAPMessage *result, *e; + LDAPMessage *result = NULL; + LDAPMessage *e = NULL; char **values = NULL; - char *attrs[] = { key, type, NULL }; - LDAP *ldap; + char *attrs[3]; struct mapent_cache *me = NULL; int ret = CHE_OK; char qKey[KEY_MAX_LEN + 1]; @@ -507,8 +1043,11 @@ qKey_len = 1; /* Build a query string. */ - l = strlen("(&(objectclass=") + strlen(class) + strlen(")"); - l += strlen("(") + strlen(key) + strlen("=") + qKey_len + strlen("))") + 1; + l = strlen( "(&(=)" ) + + strlen( ctxt->filter ) + + strlen( ctxt->key ) + + qKey_len + + 1; query = alloca(l); if (query == NULL) { @@ -518,7 +1057,7 @@ /* Look around. */ memset(query, '\0', l); - ql = sprintf(query, "(&(objectclass=%s)(%s=%s))", class, key, qKey); + ql = sprintf(query, "(&%s(%s=%s))", ctxt->filter, ctxt->key, qKey); if (ql >= l) { debug(MODPREFIX "error forming query string"); return 0; @@ -528,37 +1067,49 @@ debug(MODPREFIX "searching for \"%s\" under \"%s\"", query, ctxt->base); /* Initialize the LDAP context. */ - ldap = do_connect(ctxt, &rv); - if (!ldap) - return 0; - - rv = ldap_search_s(ldap, ctxt->base, LDAP_SCOPE_SUBTREE, - query, attrs, 0, &result); + if( ! ctxt->ldap ) { + rv = do_connect( ctxt ); + if( rv || ! ctxt->ldap ) { + crit( MODPREFIX "read_one_map: failed to setup LDAP connection." ); + return 0; + } + } - if ((rv != LDAP_SUCCESS) || !result) { - crit(MODPREFIX "query failed for %s", query); - ldap_unbind(ldap); - return 0; + attrs[0] = ctxt->ai; + attrs[1] = ctxt->ai2; + attrs[2] = NULL; + result = NULL; + ctxt->errno_ldap = ldap_search_s( ctxt->ldap, ctxt->base, ctxt->scope, query, attrs, 0, &result ); + if ((ctxt->errno_ldap != LDAP_SUCCESS) || ! result) { + crit(MODPREFIX "query failed for %s: %s", query, ldap_err2string(ctxt->errno_ldap) ); + ret = 0; + goto exit; } - debug(MODPREFIX "getting first entry for %s=\"%s\"", key, qKey); + debug(MODPREFIX "getting first entry for %s=\"%s\"", ctxt->key, qKey); - e = ldap_first_entry(ldap, result); + e = ldap_first_entry(ctxt->ldap, result); if (!e) { crit(MODPREFIX "got answer, but no first entry for %s", query); - ldap_msgfree(result); - ldap_unbind(ldap); - return CHE_MISSING; + ret = CHE_MISSING; + goto exit; } debug(MODPREFIX "examining first entry"); - values = ldap_get_values(ldap, e, type); - if (!values) { - debug(MODPREFIX "no %s defined for %s", type, query); - ldap_msgfree(result); - ldap_unbind(ldap); - return CHE_MISSING; + values = ldap_get_values( ctxt->ldap, e, ctxt->ai ); + if( ( !values || !*values ) ) { + /* for backward compatibility: try second schema, too: */ + if( ctxt->ai2 ) { + debug( MODPREFIX "no automount information found, trying alternative schema" ); + if( values ) ldap_value_free( values ); + values = ldap_get_values( ctxt->ldap, e, ctxt->ai2 ); + } + } + if( !values || !*values ) { + debug(MODPREFIX "no %s defined for %s", ctxt->ai2 ? ctxt->ai2 : ctxt->ai, query); + ret = CHE_MISSING; + goto exit; } /* Compare cache entry against LDAP */ @@ -575,17 +1126,24 @@ for (i = 0; values[i]; i++) { rv = cache_add(root, "*", values[i], age); - if (!rv) - return 0; + if (!rv) { + ret = 0; + goto exit; + } } ret = CHE_UPDATED; } +exit: + /* Clean up. */ - ldap_value_free(values); - ldap_msgfree(result); - ldap_unbind(ldap); + if(values) + ldap_value_free(values); + if(result) + ldap_msgfree(result); + if(ctxt->ldap) + do_disconnect(ctxt); return ret; } @@ -593,11 +1151,11 @@ int lookup_mount(const char *root, const char *name, int name_len, void *context) { struct lookup_context *ctxt = (struct lookup_context *) context; - int ret, ret2; + int ret; char key[KEY_MAX_LEN + 1]; int key_len; char mapent[MAPENT_MAX_LEN + 1]; - char *mapname; + /* char *mapname; */ struct mapent_cache *me; time_t now = time(NULL); time_t t_last_read; @@ -611,36 +1169,28 @@ if (key_len > KEY_MAX_LEN) return 1; - ret = lookup_one(root, key, "nisObject", "cn", "nisMapEntry", ctxt); - ret2 = lookup_one(root, key, - "automount", "cn", "automountInformation", ctxt); - - debug("ret = %d, ret2 = %d", ret, ret2); + ret = lookup_one(root, ctxt, key); + debug("ret = %d", ret ); - if (!ret && !ret2) + if (!ret) return 1; me = cache_lookup_first(); t_last_read = me ? now - me->age : ap.exp_runfreq + 1; if (t_last_read > ap.exp_runfreq) - if ((ret & (CHE_MISSING | CHE_UPDATED)) && - (ret2 & (CHE_MISSING | CHE_UPDATED))) + if (ret & (CHE_MISSING | CHE_UPDATED)) need_hup = 1; - - if (ret == CHE_MISSING && ret2 == CHE_MISSING) { + + if (ret == CHE_MISSING ) { int wild = CHE_MISSING; - + /* Maybe update wild card map entry */ if (ap.type == LKP_INDIRECT) { - ret = lookup_wild(root, "nisObject", - "cn", "nisMapEntry", ctxt); - ret2 = lookup_wild(root, "automount", - "cn", "automountInformation", ctxt); - wild = (ret & (CHE_MISSING | CHE_FAIL)) && - (ret2 & (CHE_MISSING | CHE_FAIL)); + ret = lookup_wild(root, ctxt ); + wild = (ret & (CHE_MISSING | CHE_FAIL)); - if (ret & CHE_MISSING && ret2 & CHE_MISSING) + if (ret & CHE_MISSING ) cache_delete(root, "*", 0); } @@ -652,7 +1202,8 @@ if (me) { /* Try each of the LDAP entries in sucession. */ while (me) { - sprintf(mapent, me->mapent); + strncpy(mapent, me->mapent, MAPENT_MAX_LEN ); + mapent[MAPENT_MAX_LEN] = 0; debug(MODPREFIX "%s -> %s", key, mapent); ret = ctxt->parse->parse_mount(root, name, name_len, @@ -663,16 +1214,12 @@ /* path component, do submount */ me = cache_partial_match(key); if (me) { - if (ctxt->server) { - int len = strlen(ctxt->server) + - strlen(ctxt->base) + 2 + 1 + 1; - mapname = alloca(len); - sprintf(mapname, "//%s/%s", ctxt->server, ctxt->base); - } else { - mapname = alloca(strlen(ctxt->base) + 1); - sprintf(mapname, "%s", ctxt->base); - } - sprintf(mapent, "-fstype=autofs ldap:%s", mapname); + snprintf(mapent, MAPENT_MAX_LEN, "-fstype=autofs %s%s", + /* prepend scheme, if not given: (do we need this???) */ + ( ( *(ctxt->mapname) == '/' ) ? "ldap:" : "" ), + ctxt->mapname + ); + mapent[ MAPENT_MAX_LEN ] = 0; debug(MODPREFIX "%s -> %s", key, mapent); ret = ctxt->parse->parse_mount(root, name, name_len, @@ -694,9 +1241,15 @@ int lookup_done(void *context) { struct lookup_context *ctxt = (struct lookup_context *) context; - int rv = close_parse(ctxt->parse); - free(ctxt->server); - free(ctxt->base); - free(ctxt); + int rv = 0; + if( ctxt ) { + rv = close_parse(ctxt->parse); + if( ctxt->ldap ) + do_disconnect( ctxt ); + if( ctxt->freeme ) + free(ctxt->freeme); + free(ctxt); + } return rv; } +