Mercurial > dovecot > original-hg > dovecot-1.2
changeset 4741:deccf9e1aebc HEAD
LDAP code changes: If auth binds are used, bind back to the default dn
before doing a search. Otherwise it could fail if user gave an invalid
password. Initial binding is now also done asynchronously. Reconnecting to
LDAP server wasn't working with auth binds. Use pass_attrs even with
auth_bind=yes since it may contain other non-password fields also. Updated
dovecot-ldap.conf to contain sasl_bind settings and reflect these changes.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 04 Nov 2006 17:00:32 +0200 |
parents | 0a1fa29ac87f |
children | 62a5d2c10ecd |
files | doc/dovecot-ldap.conf src/auth/db-ldap.c src/auth/db-ldap.h src/auth/passdb-ldap.c src/auth/userdb-ldap.c |
diffstat | 5 files changed, 333 insertions(+), 145 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/dovecot-ldap.conf Sat Nov 04 14:05:13 2006 +0200 +++ b/doc/dovecot-ldap.conf Sat Nov 04 17:00:32 2006 +0200 @@ -22,15 +22,37 @@ # Password for LDAP server #dnpass = +# Use SASL binding instead of the simple binding. Note that this changes +# ldap_version automatically to be 3 if it's lower. Also note that SASL binds +# and auth_bind=yes don't work together. +#sasl_bind = no +# SASL mechanism name to use. +#sasl_mech = +# SASL realm to use. +#sasl_realm = +# SASL authorization ID, ie. the dnpass is for this "master user", but the +# dn is still the logged in user. Normally you want to keep this empty. +#sasl_authz_id = + # Use authentication binding for verifying password's validity. This works by # logging into LDAP server using the username and password given by client. -# NOTE: pass_attrs option will (naturally) be ignored if you enable this. +# The pass_filter is used to find the DN for the user. Note that the pass_attrs +# is still used, only the password field is ignored in it. Before doing any +# search, the binding is switched back to the default DN. #auth_bind = no # If authentication binding is used, you can save one LDAP request per login # if users' DN can be specified with a common template. The template can use -# the standard %variables (see user_filter). For example: +# the standard %variables (see user_filter). Note that you can't +# use any pass_attrs if you use this setting. # +# If you use this setting, it's a good idea to use a different +# dovecot-ldap.conf for userdb (it can even be a symlink, just as long as the +# filename is different in userdb's args). That way one connection is used only +# for LDAP binds and another connection is used for user lookups. Otherwise +# the binding is changed to the default DN before each user lookup. +# +# For example: # auth_bind_userdn = cn=%u,ou=people,o=org # #auth_bind_userdn =
--- a/src/auth/db-ldap.c Sat Nov 04 14:05:13 2006 +0200 +++ b/src/auth/db-ldap.c Sat Nov 04 17:00:32 2006 +0200 @@ -52,7 +52,6 @@ DEF(SET_STR, sasl_mech), DEF(SET_STR, sasl_realm), DEF(SET_STR, sasl_authz_id), - DEF(SET_STR, sasl_props), DEF(SET_STR, deref), DEF(SET_STR, scope), DEF(SET_STR, base), @@ -80,7 +79,6 @@ MEMBER(sasl_mech) NULL, MEMBER(sasl_realm) NULL, MEMBER(sasl_authz_id) NULL, - MEMBER(sasl_props) NULL, MEMBER(deref) "never", MEMBER(scope) "subtree", MEMBER(base) NULL, @@ -96,6 +94,7 @@ static struct ldap_connection *ldap_connections = NULL; +static int db_ldap_bind(struct ldap_connection *conn); static void ldap_conn_close(struct ldap_connection *conn, bool flush_requests); static int deref2str(const char *str) @@ -143,8 +142,17 @@ { int msgid; - if (!conn->connected) { - if (!db_ldap_connect(conn)) { + if (!conn->connected && !conn->connecting) { + if (db_ldap_connect(conn) < 0) { + request->callback(conn, request, NULL); + return; + } + } + + if (conn->last_auth_bind) { + /* switch back to the default dn before doing the search + request. */ + if (db_ldap_bind(conn) < 0) { request->callback(conn, request, NULL); return; } @@ -167,6 +175,7 @@ struct hash_table *old_requests; struct hash_iterate_context *iter; void *key, *value; + bool have_binds = FALSE; i_assert(conn->connected); @@ -176,22 +185,45 @@ old_requests = conn->requests; conn->requests = hash_create(default_pool, conn->pool, 0, NULL, NULL); + conn->retrying = TRUE; + /* first retry all the search requests */ iter = hash_iterate_init(old_requests); while (hash_iterate(iter, &key, &value)) { struct ldap_request *request = value; - i_assert(conn->connected); - db_ldap_search(conn, request, conn->set.ldap_scope); + if (request->filter == NULL) { + /* bind request */ + have_binds = TRUE; + } else { + i_assert(conn->connected); + db_ldap_search(conn, request, conn->set.ldap_scope); + } } hash_iterate_deinit(iter); + + if (have_binds && conn->set.auth_bind) { + /* next retry all the bind requests. without auth binds the + only bind request can be the initial connection binding, + which we don't care to retry. */ + iter = hash_iterate_init(old_requests); + while (hash_iterate(iter, &key, &value)) { + struct ldap_request *request = value; + + if (request->filter == NULL) + request->callback(conn, request, NULL); + } + hash_iterate_deinit(iter); + } hash_destroy(old_requests); + + conn->retrying = FALSE; } static void ldap_conn_reconnect(struct ldap_connection *conn) { ldap_conn_close(conn, FALSE); - if (!db_ldap_connect(conn)) { + if (db_ldap_connect(conn) < 0) { /* failed to reconnect. fail all requests. */ ldap_conn_close(conn, TRUE); } @@ -275,12 +307,87 @@ } #endif -bool db_ldap_connect(struct ldap_connection *conn) +static int db_ldap_connect_finish(struct ldap_connection *conn, int ret) +{ + if (ret == LDAP_SERVER_DOWN) { + i_error("LDAP: Can't connect to server: %s", + conn->set.uris != NULL ? + conn->set.uris : conn->set.hosts); + return -1; + } + if (ret != LDAP_SUCCESS) { + i_error("LDAP: binding failed (dn %s): %s", + conn->set.dn == NULL ? "(none)" : conn->set.dn, + ldap_get_error(conn)); + return -1; + } + + conn->connected = TRUE; + + /* in case there are requests waiting, retry them */ + ldap_conn_retry_requests(conn); + return 0; +} + +static void db_ldap_bind_callback(struct ldap_connection *conn, + struct ldap_request *ldap_request, + LDAPMessage *res) { - int ret, fd; + int ret; + + conn->connecting = FALSE; + i_free(ldap_request); + + if (res == NULL) { + /* aborted */ + return; + } + + ret = ldap_parse_sasl_bind_result(conn->ld, res, NULL, FALSE); + if (ret != LDAP_SUCCESS) { + i_error("LDAP: ldap_parse_sasl_bind_result() failed: %s", + ldap_err2string(ret)); + return; + } + + ret = ldap_result2error(conn->ld, res, FALSE); + (void)db_ldap_connect_finish(conn, ret); +} + +static int db_ldap_bind(struct ldap_connection *conn) +{ + struct ldap_request *ldap_request; + int msgid; + + conn->connecting = TRUE; + ldap_request = i_new(struct ldap_request, 1); + ldap_request->callback = db_ldap_bind_callback; + ldap_request->context = conn; + + msgid = ldap_bind(conn->ld, conn->set.dn, conn->set. + dnpass, LDAP_AUTH_SIMPLE); + if (msgid == -1) { + i_error("ldap_bind(%s) failed: %s", + conn->set.dn, ldap_get_error(conn)); + return -1; + } + hash_insert(conn->requests, POINTER_CAST(msgid), ldap_request); + + /* we're binding back to the original DN, not doing an + authentication bind */ + conn->last_auth_bind = FALSE; + return 0; +} + +int db_ldap_connect(struct ldap_connection *conn) +{ + unsigned int ldap_version; + int ret; + + i_assert(!conn->connecting); if (conn->connected) - return TRUE; + return 0; if (conn->ld == NULL) { if (conn->set.uris != NULL) { @@ -299,19 +406,34 @@ conn->set.hosts); ret = ldap_set_option(conn->ld, LDAP_OPT_DEREF, - (void *) &conn->set.ldap_deref); + (void *)&conn->set.ldap_deref); if (ret != LDAP_SUCCESS) { i_fatal("LDAP: Can't set deref option: %s", ldap_err2string(ret)); } + /* If SASL binds are used, the protocol version needs to be + at least 3 */ + ldap_version = conn->set.sasl_bind && + conn->set.ldap_version < 3 ? 3 : + conn->set.ldap_version; ret = ldap_set_option(conn->ld, LDAP_OPT_PROTOCOL_VERSION, - (void *) &conn->set.ldap_version); + (void *)&ldap_version); if (ret != LDAP_OPT_SUCCESS) { i_fatal("LDAP: Can't set protocol version %u: %s", - conn->set.ldap_version, ldap_err2string(ret)); + ldap_version, ldap_err2string(ret)); } + + /* get the connection's fd */ + ret = ldap_get_option(conn->ld, LDAP_OPT_DESC, + (void *)&conn->fd); + if (ret != LDAP_SUCCESS) { + i_fatal("LDAP: Can't get connection fd: %s", + ldap_err2string(ret)); + } + net_set_nonblock(conn->fd, TRUE); } + i_assert(conn->fd != -1); if (conn->set.tls) { #ifdef LDAP_HAVE_START_TLS_S @@ -319,11 +441,11 @@ if (ret != LDAP_SUCCESS) { i_error("LDAP: ldap_start_tls_s() failed: %s", ldap_err2string(ret)); - return FALSE; + return -1; } #else i_error("LDAP: Your LDAP library doesn't support TLS"); - return FALSE; + return -1; #endif } @@ -345,38 +467,15 @@ #else i_fatal("LDAP: sasl_bind=yes but no SASL support compiled in"); #endif + if (db_ldap_connect_finish(conn, ret) < 0) + return -1; } else { - ret = ldap_simple_bind_s(conn->ld, conn->set.dn, - conn->set.dnpass); - } - if (ret == LDAP_SERVER_DOWN) { - i_error("LDAP: Can't connect to server: %s", - conn->set.uris != NULL ? - conn->set.uris : conn->set.hosts); - return FALSE; - } - if (ret != LDAP_SUCCESS) { - i_error("LDAP: binding failed (dn %s): %s", - conn->set.dn == NULL ? "(none)" : conn->set.dn, - ldap_get_error(conn)); - return FALSE; + if (db_ldap_bind(conn) < 0) + return -1; } - conn->connected = TRUE; - - /* register LDAP input to ioloop */ - ret = ldap_get_option(conn->ld, LDAP_OPT_DESC, (void *) &fd); - if (ret != LDAP_SUCCESS) { - i_fatal("LDAP: Can't get connection fd: %s", - ldap_err2string(ret)); - } - - net_set_nonblock(fd, TRUE); - conn->io = io_add(fd, IO_READ, ldap_input, conn); - - /* in case there are requests waiting, retry them */ - ldap_conn_retry_requests(conn); - return TRUE; + conn->io = io_add(conn->fd, IO_READ, ldap_input, conn); + return 0; } static void ldap_conn_close(struct ldap_connection *conn, bool flush_requests) @@ -404,15 +503,17 @@ ldap_unbind(conn->ld); conn->ld = NULL; } + conn->fd = -1; } void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist, char ***attr_names_r, struct hash_table *attr_map, - const char *const default_attr_map[]) + const char *const default_attr_map[], + const char *skip_attr) { const char *const *attr; char *name, *value, *p; - unsigned int i, size; + unsigned int i, j, size; if (*attrlist == '\0') return; @@ -424,7 +525,7 @@ for (size = 0; attr[size] != NULL; size++) ; *attr_names_r = p_new(conn->pool, char *, size + 1); - for (i = 0; i < size; i++) { + for (i = j = 0; i < size; i++) { p = strchr(attr[i], '='); if (p == NULL) { name = p_strdup(conn->pool, attr[i]); @@ -435,9 +536,13 @@ value = p_strdup(conn->pool, p + 1); } - (*attr_names_r)[i] = name; - if (*name != '\0') + if (skip_attr != NULL && strcmp(skip_attr, value) == 0) + name = ""; + + if (*name != '\0') { hash_insert(attr_map, name, value); + (*attr_names_r)[j++] = name; + } if (*default_attr_map != NULL) default_attr_map++; @@ -516,6 +621,7 @@ conn->refcount = 1; conn->requests = hash_create(default_pool, pool, 0, NULL, NULL); + conn->fd = -1; conn->config_path = p_strdup(pool, config_path); conn->set = default_ldap_settings; if (!settings_read(config_path, NULL, parse_setting, NULL, conn))
--- a/src/auth/db-ldap.h Sat Nov 04 14:05:13 2006 +0200 +++ b/src/auth/db-ldap.h Sat Nov 04 17:00:32 2006 +0200 @@ -56,6 +56,7 @@ struct ldap_settings set; LDAP *ld; + int fd; /* only set when ld is not NULL */ struct io *io; struct hash_table *requests; @@ -63,12 +64,16 @@ struct hash_table *pass_attr_map, *user_attr_map; unsigned int connected:1; + unsigned int connecting:1; + unsigned int retrying:1; /* just reconnected, resending requests */ + unsigned int last_auth_bind:1; }; struct ldap_request { db_search_callback_t *callback; void *context; + /* for bind requests, base contains the DN and filter=NULL */ const char *base; const char *filter; char **attributes; /* points to pass_attr_names / user_attr_names */ @@ -86,12 +91,13 @@ void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist, char ***attr_names_r, struct hash_table *attr_map, - const char *const default_attr_map[]); + const char *const default_attr_map[], + const char *skip_attr); struct ldap_connection *db_ldap_init(const char *config_path); void db_ldap_unref(struct ldap_connection **conn); -bool db_ldap_connect(struct ldap_connection *conn); +int db_ldap_connect(struct ldap_connection *conn); const char *ldap_escape(const char *str, const struct auth_request *auth_request);
--- a/src/auth/passdb-ldap.c Sat Nov 04 14:05:13 2006 +0200 +++ b/src/auth/passdb-ldap.c Sat Nov 04 17:00:32 2006 +0200 @@ -34,84 +34,108 @@ } callback; }; +struct ldap_query_save_context { + struct ldap_connection *conn; + struct auth_request *auth_request; + LDAPMessage *entry; + + string_t *debug; + unsigned int userdb_fields:1; + unsigned int add_userdb_uid:1; + unsigned int add_userdb_gid:1; +}; + +static void +ldap_query_save_attr(struct ldap_query_save_context *ctx, const char *attr) +{ + struct auth *auth = ctx->auth_request->auth; + const char *name; + char **vals; + unsigned int i; + + name = hash_lookup(ctx->conn->pass_attr_map, attr); + + if (auth->verbose_debug) { + if (ctx->debug == NULL) + ctx->debug = t_str_new(256); + else + str_append_c(ctx->debug, ' '); + str_append(ctx->debug, attr); + str_printfa(ctx->debug, "(%s)=", + name != NULL ? name : "?unknown?"); + } + + if (name == NULL) + return; + + if (strncmp(name, "userdb_", 7) == 0) { + /* in case we're trying to use prefetch userdb, + see if we need to add global uid/gid */ + if (!ctx->userdb_fields) { + ctx->add_userdb_uid = ctx->add_userdb_gid = TRUE; + ctx->userdb_fields = TRUE; + } + if (strcmp(name, "userdb_uid") == 0) + ctx->add_userdb_uid = FALSE; + else if (strcmp(name, "userdb_gid") == 0) + ctx->add_userdb_gid = FALSE; + } + + vals = ldap_get_values(ctx->conn->ld, ctx->entry, attr); + if (vals != NULL && *name != '\0') { + for (i = 0; vals[i] != NULL; i++) { + if (ctx->debug != NULL) { + if (i != 0) + str_append_c(ctx->debug, '/'); + if (auth->verbose_debug_passwords || + strcmp(name, "password") != 0) + str_append(ctx->debug, vals[i]); + else { + str_append(ctx->debug, + PASSWORD_HIDDEN_STR); + } + } + auth_request_set_field(ctx->auth_request, name, vals[i], + ctx->conn->set.default_pass_scheme); + } + } + + ldap_value_free(vals); +} + static void ldap_query_save_result(struct ldap_connection *conn, LDAPMessage *entry, struct auth_request *auth_request) { - struct auth *auth = auth_request->auth; + struct ldap_query_save_context ctx; BerElement *ber; - const char *name; - char *attr, **vals; - unsigned int i; - string_t *debug = NULL; - bool userdb_fields = FALSE; - bool add_userdb_uid = FALSE, add_userdb_gid = FALSE; + char *attr; + + memset(&ctx, 0, sizeof(ctx)); + ctx.conn = conn; + ctx.auth_request = auth_request; + ctx.entry = entry; attr = ldap_first_attribute(conn->ld, entry, &ber); while (attr != NULL) { - name = hash_lookup(conn->pass_attr_map, attr); - vals = ldap_get_values(conn->ld, entry, attr); - - if (auth->verbose_debug) { - if (debug == NULL) - debug = t_str_new(256); - else - str_append_c(debug, ' '); - str_append(debug, attr); - str_printfa(debug, "(%s)=", - name != NULL ? name : "?unknown?"); - } - - if (strncmp(name, "userdb_", 7) == 0) { - /* in case we're trying to use prefetch userdb, - see if we need to add global uid/gid */ - if (!userdb_fields) { - add_userdb_uid = add_userdb_gid = TRUE; - userdb_fields = TRUE; - } - if (strcmp(name, "userdb_uid") == 0) - add_userdb_uid = FALSE; - else if (strcmp(name, "userdb_gid") == 0) - add_userdb_gid = FALSE; - } - - if (name != NULL && vals != NULL && *name != '\0') { - for (i = 0; vals[i] != NULL; i++) { - if (debug != NULL) { - if (i != 0) - str_append_c(debug, '/'); - if (auth->verbose_debug_passwords || - strcmp(name, "password") != 0) - str_append(debug, vals[i]); - else { - str_append(debug, - PASSWORD_HIDDEN_STR); - } - } - auth_request_set_field(auth_request, - name, vals[i], - conn->set.default_pass_scheme); - } - } - - ldap_value_free(vals); + ldap_query_save_attr(&ctx, attr); ldap_memfree(attr); attr = ldap_next_attribute(conn->ld, entry, ber); } - if (add_userdb_uid && conn->set.uid != (uid_t)-1) { + if (ctx.add_userdb_uid && conn->set.uid != (uid_t)-1) { auth_request_set_field(auth_request, "userdb_uid", dec2str(conn->set.uid), NULL); } - if (add_userdb_gid && conn->set.gid != (gid_t)-1) { + if (ctx.add_userdb_gid && conn->set.gid != (gid_t)-1) { auth_request_set_field(auth_request, "userdb_gid", dec2str(conn->set.gid), NULL); } - if (debug != NULL) { + if (ctx.debug != NULL) { auth_request_log_debug(auth_request, "ldap", - "%s", str_c(debug)); + "%s", str_c(ctx.debug)); } } @@ -218,13 +242,41 @@ auth_request_unref(&auth_request); } +static void authbind_start(struct ldap_connection *conn, + struct ldap_request *ldap_request) +{ + struct passdb_ldap_request *passdb_ldap_request = + (struct passdb_ldap_request *)ldap_request; + struct auth_request *auth_request = ldap_request->context; + int msgid; + + /* switch back to the default dn before doing the next search request */ + conn->last_auth_bind = TRUE; + + /* the DN is kept in base variable, a bit ugly.. */ + msgid = ldap_bind(conn->ld, ldap_request->base, + auth_request->mech_password, LDAP_AUTH_SIMPLE); + if (msgid == -1) { + i_error("ldap_bind(%s) failed: %s", + ldap_request->base, ldap_get_error(conn)); + passdb_ldap_request->callback. + verify_plain(PASSDB_RESULT_INTERNAL_FAILURE, + auth_request); + return; + } + + /* Bind started */ + auth_request_ref(auth_request); + hash_insert(conn->requests, POINTER_CAST(msgid), ldap_request); +} + static void handle_request_authbind(struct ldap_connection *conn, - struct ldap_request *request, LDAPMessage *res) + struct ldap_request *ldap_request, LDAPMessage *res) { struct passdb_ldap_request *passdb_ldap_request = - (struct passdb_ldap_request *)request; - struct auth_request *auth_request = request->context; + (struct passdb_ldap_request *)ldap_request; + struct auth_request *auth_request = ldap_request->context; enum passdb_result passdb_result; int ret; @@ -237,39 +289,22 @@ else if (ret == LDAP_INVALID_CREDENTIALS) passdb_result = PASSDB_RESULT_PASSWORD_MISMATCH; else { - auth_request_log_error(request->context, "ldap", + auth_request_log_error(auth_request, "ldap", "ldap_bind() failed: %s", ldap_err2string(ret)); } } - passdb_ldap_request->callback.verify_plain(passdb_result, auth_request); + if (conn->retrying && res == NULL) { + /* reconnected, retry binding */ + authbind_start(conn, ldap_request); + } else { + passdb_ldap_request->callback. + verify_plain(passdb_result, auth_request); + } auth_request_unref(&auth_request); } -static void authbind_start(struct ldap_connection *conn, - struct ldap_request *ldap_request, const char *dn) -{ - struct passdb_ldap_request *passdb_ldap_request = - (struct passdb_ldap_request *)ldap_request; - struct auth_request *auth_request = ldap_request->context; - int msgid; - - msgid = ldap_bind(conn->ld, dn, auth_request->mech_password, - LDAP_AUTH_SIMPLE); - if (msgid == -1) { - i_error("ldap_bind(%s) failed: %s", dn, ldap_get_error(conn)); - passdb_ldap_request->callback. - verify_plain(PASSDB_RESULT_INTERNAL_FAILURE, - auth_request); - return; - } - - /* Bind started */ - auth_request_ref(auth_request); - hash_insert(conn->requests, POINTER_CAST(msgid), ldap_request); -} - static void handle_request_authbind_search(struct ldap_connection *conn, struct ldap_request *ldap_request, @@ -285,10 +320,15 @@ if (entry == NULL) return; + ldap_query_save_result(conn, entry, auth_request); + /* switch the handler to the authenticated bind handler */ + ldap_request->base = + p_strdup(auth_request->pool, ldap_get_dn(conn->ld, entry)); + ldap_request->filter = NULL; ldap_request->callback = handle_request_authbind; - authbind_start(conn, ldap_request, ldap_get_dn(conn->ld, entry)); + authbind_start(conn, ldap_request); auth_request_unref(&auth_request); } @@ -345,7 +385,8 @@ ldap_request->callback = handle_request_authbind; ldap_request->context = auth_request; - authbind_start(conn, ldap_request, str_c(dn)); + ldap_request->base = p_strdup(auth_request->pool, str_c(dn)); + authbind_start(conn, ldap_request); } static void @@ -369,9 +410,10 @@ var_expand(str, conn->set.pass_filter, vars); ldap_request->filter = p_strdup(auth_request->pool, str_c(str)); - /* we don't want any attributes in our search results; - we only need the DN. */ - ldap_request->attributes = p_new(auth_request->pool, char *, 1); + /* we don't need the attributes to perform authentication, but they + may contain some extra parameters. if a password is returned, + it's just ignored. */ + ldap_request->attributes = conn->pass_attr_names; auth_request_ref(auth_request); ldap_request->context = auth_request; @@ -395,6 +437,15 @@ struct ldap_connection *conn = module->conn; struct passdb_ldap_request *ldap_request; + /* reconnect if needed. this is also done by db_ldap_search(), but + with auth binds we'll have to do it ourself */ + if (!conn->connected && !conn->connecting) { + if (db_ldap_connect(conn)< 0) { + callback(PASSDB_RESULT_INTERNAL_FAILURE, request); + return; + } + } + ldap_request = p_new(request->pool, struct passdb_ldap_request, 1); ldap_request->callback.verify_plain = callback; @@ -429,8 +480,11 @@ hash_create(default_pool, conn->pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); + if (conn->set.auth_bind_userdn != NULL) + conn->set.auth_bind = TRUE; db_ldap_set_attrs(conn, conn->set.pass_attrs, &conn->pass_attr_names, - conn->pass_attr_map, default_attr_map); + conn->pass_attr_map, default_attr_map, + conn->set.auth_bind ? "password" : NULL); module->module.cache_key = auth_cache_parse_key(auth_passdb->auth->pool, conn->set.pass_filter);
--- a/src/auth/userdb-ldap.c Sat Nov 04 14:05:13 2006 +0200 +++ b/src/auth/userdb-ldap.c Sat Nov 04 17:00:32 2006 +0200 @@ -219,7 +219,7 @@ (hash_cmp_callback_t *)strcmp); db_ldap_set_attrs(conn, conn->set.user_attrs, &conn->user_attr_names, - conn->user_attr_map, default_attr_map); + conn->user_attr_map, default_attr_map, NULL); return &module->module; }