db-ldap.c revision 37b5289a049b20bab43f414f647d37b17aa2c3b2
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek/* Copyright (c) 2003-2008 Dovecot authors, see the included COPYING file */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#if defined(PASSDB_LDAP) || defined(USERDB_LDAP)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina# define LDAP_SASL_QUIET 0 /* Doesn't exist in Solaris LDAP */
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina/* Older versions may require calling ldap_result() twice */
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina/* Solaris LDAP library doesn't have LDAP_OPT_SUCCESS */
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina const char *name, *value, *template, *val_1_arr[2];
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina const char *const *static_attrs;
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek#define DEF_STR(name) DEF_STRUCT_STR(name, ldap_settings)
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek#define DEF_INT(name) DEF_STRUCT_INT(name, ldap_settings)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, ldap_settings)
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek MEMBER(user_attrs) "homeDirectory=home,uidNumber=uid,gidNumber=gid",
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek MEMBER(user_filter) "(&(objectClass=posixAccount)(uid=%u))",
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek MEMBER(pass_attrs) "uid=user,userPassword=password",
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek MEMBER(pass_filter) "(&(objectClass=posixAccount)(uid=%u))",
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozekstatic struct ldap_connection *ldap_connections = NULL;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int db_ldap_bind(struct ldap_connection *conn);
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic void db_ldap_conn_close(struct ldap_connection *conn);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_fatal("LDAP: Unknown deref option '%s'", str);
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina i_fatal("LDAP: Unknown scope option '%s'", str);
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int tls_require_cert2str(const char *str)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina i_fatal("LDAP: Unknown tls_require_cert value '%s'", str);
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int ldap_get_errno(struct ldap_connection *conn)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek ret = ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, (void *) &err);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekconst char *ldap_get_error(struct ldap_connection *conn)
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březinastatic void ldap_conn_reconnect(struct ldap_connection *conn)
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březinastatic int ldap_handle_error(struct ldap_connection *conn)
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina /* invalid input */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* connection problems */
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březinastatic int db_ldap_request_bind(struct ldap_connection *conn,
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina i_assert(request->type == LDAP_REQUEST_TYPE_BIND);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_AUTH ||
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina request->msgid = ldap_bind(conn->ld, brequest->dn,
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina auth_request_log_error(request->auth_request, "ldap",
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina "ldap_bind(%s) failed: %s",
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* broken request, remove it */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekstatic int db_ldap_request_search(struct ldap_connection *conn,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek ldap_search(conn->ld, srequest->base, conn->set.ldap_scope,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek auth_request_log_error(request->auth_request, "ldap",
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek "ldap_search() failed (filter %s): %s",
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* broken request, remove it */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekstatic bool db_ldap_request_queue_next(struct ldap_connection *conn)
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina struct ldap_request *const *requestp, *request;
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina unsigned int queue_size = aqueue_count(conn->request_queue);
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina /* no non-pending requests */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (queue_size > DB_LDAP_MAX_PENDING_REQUESTS) {
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina /* wait until server has replied to some requests */
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina /* we can't do binds until all existing requests are finished */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* wait until we're in bound state */
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina /* bind to default dn first */
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina /* we can do anything in this state */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* success */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek } else if (ret < 0) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* disconnected */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* broken request, remove from queue */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekvoid db_ldap_request(struct ldap_connection *conn,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek aqueue_count(conn->request_queue) >= DB_LDAP_MAX_QUEUE_SIZE) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* Queue is full already, fail this request */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina auth_request_log_error(request->auth_request, "ldap",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina "Request queue is full");
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinastatic int db_ldap_connect_finish(struct ldap_connection *conn, int ret)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->set.dn == NULL ? "(none)" : conn->set.dn,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinastatic void db_ldap_default_bind_finished(struct ldap_connection *conn,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina ret = ldap_result2error(conn->ld, res, FALSE);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* lost connection, close it */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinastatic void db_ldap_abort_requests(struct ldap_connection *conn,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina struct ldap_request *const *requestp, *request;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina while (aqueue_count(conn->request_queue) > 0 && max_count > 0) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* timed out, abort */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina auth_request_log_error(request->auth_request, "ldap",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina auth_request_log_info(request->auth_request, "ldap",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinadb_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina struct ldap_request *const *requests, *request = NULL;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina unsigned int i, count;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina requests = count == 0 ? NULL : array_idx(&conn->request_array, 0);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina for (i = 0; i < count; i++) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina request = requests[aqueue_idx(conn->request_queue, i)];
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina i_error("LDAP: Reply with unknown msgid %d", msgid);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (request->type == LDAP_REQUEST_TYPE_BIND) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->conn_state = LDAP_CONN_STATE_BOUND_AUTH;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (ret != LDAP_SUCCESS && request->type == LDAP_REQUEST_TYPE_SEARCH) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* handle search failures here */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina auth_request_log_error(request->auth_request, "ldap",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina "ldap_search(%s) failed: %s",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* see if there are timed out requests */
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březinastatic void ldap_input(struct ldap_connection *conn)
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina ret = ldap_result(conn->ld, LDAP_RES_ANY, 1, &timeout, &res);
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina /* try again, there may be another in buffer */
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina /* send more requests */
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina } else if (ldap_get_errno(conn) != LDAP_SERVER_DOWN) {
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina i_error("LDAP: ldap_result() failed: %s", ldap_get_error(conn));
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina } else if (aqueue_count(conn->request_queue) > 0 ||
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_error("LDAP: Connection lost to LDAP server, reconnecting");
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* server probably disconnected an idle connection. don't
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek reconnect until the next request comes. */
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březinasasl_interact(LDAP *ld ATTR_UNUSED, unsigned flags ATTR_UNUSED,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek struct db_ldap_sasl_bind_context *context = defaults;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek const char *str;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek for (in = interact; in->id != SASL_CB_LIST_END; in++) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekstatic int db_ldap_bind(struct ldap_connection *conn)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(conn->conn_state != LDAP_CONN_STATE_BINDING);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek msgid = ldap_bind(conn->ld, conn->set.dn, conn->set.dnpass,
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina i_assert(ldap_get_errno(conn) != LDAP_SUCCESS);
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina if (db_ldap_connect_finish(conn, ldap_get_errno(conn)) < 0) {
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina /* lost connection, close it */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekstatic void db_ldap_get_fd(struct ldap_connection *conn)
int ret;
int ret;
const char *optname)
#ifdef OPENLDAP_TLS_OPTIONS
unsigned int ldap_version;
int value;
#ifdef LDAP_OPT_DEBUG_LEVEL
if (value != 0) {
int ret;
#ifdef LDAP_HAVE_INITIALIZE
#ifdef LDAP_HAVE_START_TLS_S
#ifdef HAVE_LDAP_SASL
const char *skip_attr)
unsigned int i, j, size;
for (i = j = 0; i < size; i++) {
if (p == NULL)
else if (p != attr_data) {
struct var_expand_table *
unsigned int count;
count++;
return table;
#define IS_LDAP_ESCAPED_CHAR(c) \
if (IS_LDAP_ESCAPED_CHAR(*p))
return str;
if (IS_LDAP_ESCAPED_CHAR(*p))
struct db_ldap_result_iterate_context *
const char *static_data;
return ctx;
if (!first)
return TRUE;
if (p == NULL) {
return TRUE;
return FALSE;
return FALSE;
return TRUE;
const char **name_r,
const char *const **values_r)
return FALSE;
return TRUE;
return conn;
return NULL;
const char *str;
return conn;
#ifndef LDAP_HAVE_INITIALIZE
return conn;
struct ldap_connection **p;
if (*p == conn) {
#ifndef BUILTIN_LDAP
void authdb_ldap_init(void);
void authdb_ldap_deinit(void);
void authdb_ldap_init(void)
void authdb_ldap_deinit(void)