diff --git a/CHANGELOG b/CHANGELOG index 08afa7c..c208b31 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,7 @@ - fix couple of edge case parse fails of timeout option. - check for "*" when looking up wildcard in LDAP. - fix LDAP schema discovery. +- add SEARCH_BASE configuration option. 18/06/2007 autofs-5.0.2 ----------------------- diff --git a/include/defaults.h b/include/defaults.h index 9aec11a..0984b1c 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -37,6 +37,9 @@ #define DEFAULT_APPEND_OPTIONS 1 #define DEFAULT_AUTH_CONF_FILE AUTOFS_MAP_DIR "/autofs_ldap_auth.conf" +struct ldap_schema; +struct ldap_searchdn; + unsigned int defaults_read_config(void); const char *defaults_get_master_map(void); unsigned int defaults_get_timeout(void); @@ -45,6 +48,8 @@ unsigned int defaults_get_logging(void); const char *defaults_get_ldap_server(void); struct ldap_schema *defaults_get_default_schema(void); struct ldap_schema *defaults_get_schema(void); +struct ldap_searchdn *defaults_get_searchdns(void); +void defaults_free_searchdns(struct ldap_searchdn *); unsigned int defaults_get_append_options(void); const char *defaults_get_auth_conf_file(void); diff --git a/include/lookup_ldap.h b/include/lookup_ldap.h index 1378b9e..1a924be 100644 --- a/include/lookup_ldap.h +++ b/include/lookup_ldap.h @@ -18,6 +18,11 @@ struct ldap_schema { char *value_attr; }; +struct ldap_searchdn { + char *basedn; + struct ldap_searchdn *next; +}; + struct lookup_context { char *mapname; @@ -32,6 +37,10 @@ struct lookup_context { /* LDAP lookup configuration */ struct ldap_schema *schema; + /* List of base dns for searching */ + char *cur_host; + struct ldap_searchdn *sdns; + /* TLS and SASL authentication information */ char *auth_conf; unsigned use_tls; diff --git a/lib/defaults.c b/lib/defaults.c index b146f13..c2f86c0 100644 --- a/lib/defaults.c +++ b/lib/defaults.c @@ -32,6 +32,8 @@ #define ENV_LDAP_SERVER "LDAP_SERVER" +#define SEARCH_BASE "SEARCH_BASE" + #define ENV_NAME_MAP_OBJ_CLASS "MAP_OBJECT_CLASS" #define ENV_NAME_ENTRY_OBJ_CLASS "ENTRY_OBJECT_CLASS" #define ENV_NAME_MAP_ATTR "MAP_ATTRIBUTE" @@ -130,6 +132,52 @@ static int check_set_config_value(const char *res, const char *name, const char return 0; } +static int parse_line(char *line, char **res, char **value) +{ + volatile char *key, *val, *trailer; + int len; + + key = line; + + if (*key == '#' || !isalpha(*key)) + return 0; + + while (*key && *key == ' ') + key++; + + if (!key) + return 0; + + if (!(val = strchr(key, '='))) + return 0; + + *val++ = '\0'; + + while (*val && (*val == '"' || isblank(*val))) + val++; + + len = strlen(val); + + if (val[len - 1] == '\n') { + val[len - 1] = '\0'; + len--; + } + + trailer = strchr(val, '#'); + if (!trailer) + trailer = val + len - 1; + else + trailer--; + + while (*trailer && (*trailer == '"' || isblank(*trailer))) + *(trailer--) = '\0';; + + *res = key; + *value = val; + + return 1; +} + /* * Read config env variables and check they have been set. * @@ -141,61 +189,30 @@ unsigned int defaults_read_config(void) { FILE *f; char buf[MAX_LINE_LEN]; - char *res, *value; + char *res; f = fopen(DEFAULTS_CONFIG_FILE, "r"); if (!f) return 0; while ((res = fgets(buf, MAX_LINE_LEN, f))) { - char *trailer; - int len; - - if (*res == '#' || !isalpha(*res)) - continue; - - while (*res && *res == ' ') - res++; - - if (!res) - continue; + char *key, *value; - if (!(value = strchr(res, '='))) + if (!parse_line(res, &key, &value)) continue; - *value++ = '\0'; - - while (*value && (*value == '"' || isblank(*value))) - value++; - - len = strlen(value); - - if (value[len - 1] == '\n') { - value[len - 1] = '\0'; - len--; - } - - trailer = strchr(value, '#'); - if (!trailer) - trailer = value + len - 1; - else - trailer--; - - while (*trailer && (*trailer == '"' || isblank(*trailer))) - *(trailer--) = '\0';; - - if (check_set_config_value(res, ENV_NAME_MASTER_MAP, value) || - check_set_config_value(res, ENV_NAME_TIMEOUT, value) || - check_set_config_value(res, ENV_NAME_BROWSE_MODE, value) || - check_set_config_value(res, ENV_NAME_LOGGING, value) || - check_set_config_value(res, ENV_LDAP_SERVER, value) || - check_set_config_value(res, ENV_NAME_MAP_OBJ_CLASS, value) || - check_set_config_value(res, ENV_NAME_ENTRY_OBJ_CLASS, value) || - check_set_config_value(res, ENV_NAME_MAP_ATTR, value) || - check_set_config_value(res, ENV_NAME_ENTRY_ATTR, value) || - check_set_config_value(res, ENV_NAME_VALUE_ATTR, value) || - check_set_config_value(res, ENV_APPEND_OPTIONS, value) || - check_set_config_value(res, ENV_AUTH_CONF_FILE, value)) + if (check_set_config_value(key, ENV_NAME_MASTER_MAP, value) || + check_set_config_value(key, ENV_NAME_TIMEOUT, value) || + check_set_config_value(key, ENV_NAME_BROWSE_MODE, value) || + check_set_config_value(key, ENV_NAME_LOGGING, value) || + check_set_config_value(key, ENV_LDAP_SERVER, value) || + check_set_config_value(key, ENV_NAME_MAP_OBJ_CLASS, value) || + check_set_config_value(key, ENV_NAME_ENTRY_OBJ_CLASS, value) || + check_set_config_value(key, ENV_NAME_MAP_ATTR, value) || + check_set_config_value(key, ENV_NAME_ENTRY_ATTR, value) || + check_set_config_value(key, ENV_NAME_VALUE_ATTR, value) || + check_set_config_value(key, ENV_APPEND_OPTIONS, value) || + check_set_config_value(key, ENV_AUTH_CONF_FILE, value)) ; } @@ -336,6 +353,86 @@ struct ldap_schema *defaults_get_default_schema(void) return schema; } +static struct ldap_searchdn *alloc_searchdn(const char *value) +{ + struct ldap_searchdn *sdn; + char *val; + + sdn = malloc(sizeof(struct ldap_searchdn)); + if (!sdn) + return NULL; + + val = strdup(value); + if (!val) { + free(sdn); + return NULL; + } + + sdn->basedn = val; + sdn->next = NULL; + + return sdn; +} + +void defaults_free_searchdns(struct ldap_searchdn *sdn) +{ + struct ldap_searchdn *this = sdn; + struct ldap_searchdn *next; + + next = this; + while (this) { + next = this->next; + free(this->basedn); + free(this); + this = next; + } + + return; +} + +struct ldap_searchdn *defaults_get_searchdns(void) +{ + FILE *f; + char buf[MAX_LINE_LEN]; + char *res; + struct ldap_searchdn *sdn, *last; + + f = fopen(DEFAULTS_CONFIG_FILE, "r"); + if (!f) + return NULL; + + sdn = last = NULL; + + while ((res = fgets(buf, MAX_LINE_LEN, f))) { + char *key, *value; + + if (!parse_line(res, &key, &value)) + continue; + + if (!strcasecmp(key, SEARCH_BASE)) { + struct ldap_searchdn *new = alloc_searchdn(value); + + if (!new) { + defaults_free_searchdns(sdn); + return NULL; + } + + if (!last) + last = new; + else { + last->next = new; + last = new; + } + + if (!sdn) + sdn = new; + } + } + + fclose(f); + return sdn; +} + struct ldap_schema *defaults_get_schema(void) { struct ldap_schema *schema; diff --git a/man/auto.master.5.in b/man/auto.master.5.in index ab5ab1e..0cb2f07 100644 --- a/man/auto.master.5.in +++ b/man/auto.master.5.in @@ -230,6 +230,11 @@ values must be set, any partial schema specification will be ignored. .P The configuration settings available are: .TP +.B SEARCH_BASE +The base dn to use when searching for amap base dn. This entry may be +given multiple times and each will be checked for a map base dn in +the order they occur in the configuration. +.TP .B MAP_OBJECT_CLASS The map object class. In the \fBnisMap\fP schema this corresponds to the class \fBnisMap\fP and in the \fBautomountMap\fP schema it corresponds to the class diff --git a/modules/lookup_ldap.c b/modules/lookup_ldap.c index 9c18ca1..da52e71 100644 --- a/modules/lookup_ldap.c +++ b/modules/lookup_ldap.c @@ -171,10 +171,207 @@ LDAP *init_ldap_connection(struct lookup_context *ctxt) return ldap; } +static int get_query_dn(LDAP *ldap, struct lookup_context *ctxt, const char *class, const char *key) +{ + char buf[PARSE_MAX_BUF]; + char *query, *dn; + LDAPMessage *result = NULL, *e; + struct ldap_searchdn *sdns = NULL; + char *attrs[2]; + int scope; + int rv, l; + + attrs[0] = LDAP_NO_ATTRS; + attrs[1] = NULL; + + if (!ctxt->mapname && !ctxt->base) { + error(LOGOPT_ANY, MODPREFIX "no master map to lookup"); + return 0; + } + + /* Build a query string. */ + l = strlen("(objectclass=)") + strlen(class) + 1; + if (ctxt->mapname) + l += strlen(key) + strlen(ctxt->mapname) + strlen("(&(=))"); + + query = alloca(l); + if (query == NULL) { + char *estr = strerror_r(errno, buf, MAX_ERR_BUF); + crit(LOGOPT_ANY, MODPREFIX "alloca: %s", estr); + return NSS_STATUS_UNAVAIL; + } + + /* + * If we have a master mapname construct a query using it + * otherwise assume the base dn will catch it. + */ + if (ctxt->mapname) { + if (sprintf(query, "(&(objectclass=%s)(%s=%.*s))", class, + key, (int) strlen(ctxt->mapname), ctxt->mapname) >= l) { + debug(LOGOPT_NONE, + MODPREFIX "error forming query string"); + return 0; + } + scope = LDAP_SCOPE_SUBTREE; + } else { + if (sprintf(query, "(objectclass=%s)", class) >= l) { + debug(LOGOPT_NONE, + MODPREFIX "error forming query string"); + return 0; + } + scope = LDAP_SCOPE_SUBTREE; + } + query[l] = '\0'; + + if (!ctxt->base) { + sdns = defaults_get_searchdns(); + if (sdns) + ctxt->sdns = sdns; + } + + if (!sdns) + rv = ldap_search_s(ldap, ctxt->base, + scope, query, attrs, 0, &result); + else { + struct ldap_searchdn *this = sdns; + + debug(LOGOPT_NONE, MODPREFIX + "check search base list"); + + while (this) { + rv = ldap_search_s(ldap, this->basedn, + scope, query, attrs, 0, &result); + + if ((rv == LDAP_SUCCESS) && result) { + debug(LOGOPT_NONE, MODPREFIX + "found search base under %s", + this->basedn); + break; + } + + this = this->next; + + if (result) { + ldap_msgfree(result); + result = NULL; + } + } + } + + if ((rv != LDAP_SUCCESS) || !result) { + error(LOGOPT_NONE, + MODPREFIX "query failed for %s: %s", + query, ldap_err2string(rv)); + return 0; + } + + e = ldap_first_entry(ldap, result); + if (e) { + dn = ldap_get_dn(ldap, e); + debug(LOGOPT_NONE, MODPREFIX "query dn %s", dn); + ldap_msgfree(result); + } else { + debug(LOGOPT_NONE, + MODPREFIX "query succeeded, no matches for %s", + query); + ldap_msgfree(result); + return 0; + } + + ctxt->qdn = dn; + + return 1; +} + +static struct ldap_schema *alloc_common_schema(struct ldap_schema *s) +{ + struct ldap_schema *schema; + char *mc, *ma, *ec, *ea, *va; + + mc = strdup(s->map_class); + if (!mc) + return NULL; + + ma = strdup(s->map_attr); + if (!ma) { + free(mc); + return NULL; + } + + ec = strdup(s->entry_class); + if (!ec) { + free(mc); + free(ma); + return NULL; + } + + ea = strdup(s->entry_attr); + if (!ea) { + free(mc); + free(ma); + free(ec); + return NULL; + } + + va = strdup(s->value_attr); + if (!va) { + free(mc); + free(ma); + free(ec); + free(ea); + return NULL; + } + + schema = malloc(sizeof(struct ldap_schema)); + if (!schema) { + free(mc); + free(ma); + free(ec); + free(ea); + free(va); + return NULL; + } + + schema->map_class = mc; + schema->map_attr = ma; + schema->entry_class = ec; + schema->entry_attr = ea; + schema->value_attr = va; + + return schema; +} + +static int find_query_dn(LDAP *ldap, struct lookup_context *ctxt) +{ + struct ldap_schema *schema; + unsigned int i; + + if (ctxt->schema) + return 0; + + for (i = 0; i < common_schema_count; i++) { + const char *class = common_schema[i].map_class; + const char *key = common_schema[i].map_attr; + if (get_query_dn(ldap, ctxt, class, key)) { + schema = alloc_common_schema(&common_schema[i]); + if (!schema) { + error(LOGOPT_ANY, + MODPREFIX "failed to allocate schema"); + return 0; + } + ctxt->schema = schema; + return 1; + } + } + + return 0; +} + static LDAP *do_connect(struct lookup_context *ctxt) { LDAP *ldap; - int rv; + char *host = NULL, *nhost; + int rv, need_base = 1; ldap = init_ldap_connection(ctxt); if (!ldap) @@ -204,6 +401,61 @@ static LDAP *do_connect(struct lookup_context *ctxt) return NULL; } + rv = ldap_get_option(ldap, LDAP_OPT_HOST_NAME, &host); + if (rv != LDAP_SUCCESS || !host) { + unbind_ldap_connection(ldap, ctxt); + debug(LOGOPT_ANY, "failed to get hostname for connection"); + return NULL; + } + + nhost = strdup(host); + if (!nhost) { + unbind_ldap_connection(ldap, ctxt); + debug(LOGOPT_ANY, "failed to alloc context for hostname"); + return NULL; + } + ldap_memfree(host); + + if (!ctxt->cur_host) { + ctxt->cur_host = nhost; + /* Check if schema defined in conf first time only */ + ctxt->schema = defaults_get_schema(); + } else { + /* If connection host has changed update */ + if (strcmp(ctxt->cur_host, nhost)) { + free(ctxt->cur_host); + ctxt->cur_host = nhost; + } else { + free(nhost); + need_base = 0; + } + } + + if (!need_base) + return ldap; + + /* + * If the schema isn't defined in the configuration then check for + * presence of a map dn with a the common schema. Then calculate the + * base dn for searches. + */ + if (!ctxt->schema) { + if (!find_query_dn(ldap, ctxt)) { + unbind_ldap_connection(ldap, ctxt); + error(LOGOPT_ANY, + MODPREFIX "failed to find valid query dn"); + return NULL; + } + } else { + const char *class = ctxt->schema->map_class; + const char *key = ctxt->schema->map_attr; + if (!get_query_dn(ldap, ctxt, class, key)) { + unbind_ldap_connection(ldap, ctxt); + error(LOGOPT_ANY, MODPREFIX "failed to get query dn"); + return NULL; + } + } + return ldap; } @@ -769,175 +1021,17 @@ static void free_context(struct lookup_context *ctxt) ldap_memfree(ctxt->qdn); if (ctxt->server) free(ctxt->server); + if (ctxt->cur_host) + free(ctxt->cur_host); if (ctxt->base) free(ctxt->base); + if (ctxt->sdns) + defaults_free_searchdns(ctxt->sdns); free(ctxt); return; } -static int get_query_dn(LDAP *ldap, struct lookup_context *ctxt, const char *class, const char *key) -{ - char buf[PARSE_MAX_BUF]; - char *query, *dn; - LDAPMessage *result, *e; - char *attrs[2]; - int scope; - int rv, l; - - attrs[0] = LDAP_NO_ATTRS; - attrs[1] = NULL; - - if (!ctxt->mapname && !ctxt->base) { - error(LOGOPT_ANY, MODPREFIX "no master map to lookup"); - return 0; - } - - /* Build a query string. */ - l = strlen("(objectclass=)") + strlen(class) + 1; - if (ctxt->mapname) - l += strlen(key) + strlen(ctxt->mapname) + strlen("(&(=))"); - - query = alloca(l); - if (query == NULL) { - char *estr = strerror_r(errno, buf, MAX_ERR_BUF); - crit(LOGOPT_ANY, MODPREFIX "alloca: %s", estr); - return NSS_STATUS_UNAVAIL; - } - - /* - * If we have a master mapname construct a query using it - * otherwise assume the base dn will catch it. - */ - if (ctxt->mapname) { - if (sprintf(query, "(&(objectclass=%s)(%s=%.*s))", class, - key, (int) strlen(ctxt->mapname), ctxt->mapname) >= l) { - debug(LOGOPT_NONE, - MODPREFIX "error forming query string"); - return 0; - } - scope = LDAP_SCOPE_SUBTREE; - } else { - if (sprintf(query, "(objectclass=%s)", class) >= l) { - debug(LOGOPT_NONE, - MODPREFIX "error forming query string"); - return 0; - } - scope = LDAP_SCOPE_SUBTREE; - } - query[l] = '\0'; - - rv = ldap_search_s(ldap, ctxt->base, scope, query, attrs, 0, &result); - - if ((rv != LDAP_SUCCESS) || !result) { - error(LOGOPT_NONE, - MODPREFIX "query failed for %s: %s", - query, ldap_err2string(rv)); - return 0; - } - - e = ldap_first_entry(ldap, result); - if (e) { - dn = ldap_get_dn(ldap, e); - debug(LOGOPT_NONE, MODPREFIX "query dn %s", dn); - ldap_msgfree(result); - } else { - debug(LOGOPT_NONE, - MODPREFIX "query succeeded, no matches for %s", - query); - ldap_msgfree(result); - return 0; - } - - ctxt->qdn = dn; - - return 1; -} - -static struct ldap_schema *alloc_common_schema(struct ldap_schema *s) -{ - struct ldap_schema *schema; - char *mc, *ma, *ec, *ea, *va; - - mc = strdup(s->map_class); - if (!mc) - return NULL; - - ma = strdup(s->map_attr); - if (!ma) { - free(mc); - return NULL; - } - - ec = strdup(s->entry_class); - if (!ec) { - free(mc); - free(ma); - return NULL; - } - - ea = strdup(s->entry_attr); - if (!ea) { - free(mc); - free(ma); - free(ec); - return NULL; - } - - va = strdup(s->value_attr); - if (!va) { - free(mc); - free(ma); - free(ec); - free(ea); - return NULL; - } - - schema = malloc(sizeof(struct ldap_schema)); - if (!schema) { - free(mc); - free(ma); - free(ec); - free(ea); - free(va); - return NULL; - } - - schema->map_class = mc; - schema->map_attr = ma; - schema->entry_class = ec; - schema->entry_attr = ea; - schema->value_attr = va; - - return schema; -} - -static int find_query_dn(LDAP *ldap, struct lookup_context *ctxt) -{ - struct ldap_schema *schema; - unsigned int i; - - if (ctxt->schema) - return 0; - - for (i = 0; i < common_schema_count; i++) { - const char *class = common_schema[i].map_class; - const char *key = common_schema[i].map_attr; - if (get_query_dn(ldap, ctxt, class, key)) { - schema = alloc_common_schema(&common_schema[i]); - if (!schema) { - error(LOGOPT_ANY, - MODPREFIX "failed to allocate schema"); - return 0; - } - ctxt->schema = schema; - return 1; - } - } - - return 0; -} - /* * This initializes a context (persistent non-global data) for queries to * this module. Return zero if we succeed. @@ -994,31 +1088,6 @@ int lookup_init(const char *mapfmt, int argc, const char *const *argv, void **co free_context(ctxt); return 1; } - - /* - * Get default schema for queries. - * If the schema isn't defined in the configuration then check for - * presence of a map dn in the common schemas. - */ - ctxt->schema = defaults_get_schema(); - if (!ctxt->schema) { - if (!find_query_dn(ldap, ctxt)) { - unbind_ldap_connection(ldap, ctxt); - error(LOGOPT_ANY, - MODPREFIX "failed to find valid query dn"); - free_context(ctxt); - return 1; - } - } else { - const char *class = ctxt->schema->map_class; - const char *key = ctxt->schema->map_attr; - if (!get_query_dn(ldap, ctxt, class, key)) { - unbind_ldap_connection(ldap, ctxt); - error(LOGOPT_ANY, MODPREFIX "failed to get query dn"); - free_context(ctxt); - return 1; - } - } unbind_ldap_connection(ldap, ctxt); /* Open the parser, if we can. */ diff --git a/redhat/autofs.sysconfig.in b/redhat/autofs.sysconfig.in index 85f4e34..2b1e20a 100644 --- a/redhat/autofs.sysconfig.in +++ b/redhat/autofs.sysconfig.in @@ -21,6 +21,14 @@ BROWSE_MODE="no" # #LOGGING="none" # +# Define base dn for map dn lookup. +# +# SEARCH_BASE - base dn to use for searching for map search dn. +# Multiple entries can be given and they are checked +# in the order they occur here. +# +#SEARCH_BASE="" +# # Define the LDAP schema to used for lookups # # If no schema is set autofs will check each of the schemas diff --git a/samples/autofs.conf.default.in b/samples/autofs.conf.default.in index 85f4e34..2b1e20a 100644 --- a/samples/autofs.conf.default.in +++ b/samples/autofs.conf.default.in @@ -21,6 +21,14 @@ BROWSE_MODE="no" # #LOGGING="none" # +# Define base dn for map dn lookup. +# +# SEARCH_BASE - base dn to use for searching for map search dn. +# Multiple entries can be given and they are checked +# in the order they occur here. +# +#SEARCH_BASE="" +# # Define the LDAP schema to used for lookups # # If no schema is set autofs will check each of the schemas