db-ldap.c revision e434abb86a137bbe710320b5f5431804f05c6e26
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "auth-common.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
f242fa00989e452ecffba917f20f885f509d0f8fTimo Sirainen#if defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
bca919b207e27d0d08b431bdb0f2ac099ef8b512Timo Sirainen#include "network.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ioloop.h"
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen#include "array.h"
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen#include "hash.h"
7a7d2aa11e46195e2d92d6c337d7e78052a5ce67Timo Sirainen#include "aqueue.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "str.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "env-util.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "var-expand.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "settings.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "userdb.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "db-ldap.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <stddef.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <stdlib.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <unistd.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#define HAVE_LDAP_SASL
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#ifdef HAVE_SASL_SASL_H
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# include <sasl/sasl.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#elif defined (HAVE_SASL_H)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# include <sasl.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#else
4c1deab456fe8877bf025d11843167ac1f36327aTimo Sirainen# undef HAVE_LDAP_SASL
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#ifdef LDAP_OPT_X_TLS
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# define OPENLDAP_TLS_OPTIONS
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#if SASL_VERSION_MAJOR < 2
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# undef HAVE_LDAP_SASL
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#ifndef LDAP_SASL_QUIET
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# define LDAP_SASL_QUIET 0 /* Doesn't exist in Solaris LDAP */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* Older versions may require calling ldap_result() twice */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#if LDAP_VENDOR_VERSION <= 20112
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# define OPENLDAP_ASYNC_WORKAROUND
f6aaada6101dd43cd80fe965ff1ab9bfaf776252Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* Solaris LDAP library doesn't have LDAP_OPT_SUCCESS */
ddb018bc886680f462463b2c87f983fdedbf6cf0Timo Sirainen#ifndef LDAP_OPT_SUCCESS
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen# define LDAP_OPT_SUCCESS LDAP_SUCCESS
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct db_ldap_result_iterate_context {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct ldap_connection *conn;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen LDAPMessage *entry;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen struct auth_request *auth_request;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct hash_table *attr_map;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct var_expand_table *var_table;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen char *attr, **vals;
04c3ac276103b56185119bcff9a66de7a8bb0e68Timo Sirainen const char *name, *value, *template, *val_1_arr[2];
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *const *static_attrs;
02d72ab3d606033e9a720274ddc3dd83a0ad070dTimo Sirainen BerElement *ber;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen string_t *var, *debug;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int value_idx;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen};
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct db_ldap_sasl_bind_context {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *authcid;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *passwd;
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen const char *realm;
04c3ac276103b56185119bcff9a66de7a8bb0e68Timo Sirainen const char *authzid;
04c3ac276103b56185119bcff9a66de7a8bb0e68Timo Sirainen};
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen#define DEF_STR(name) DEF_STRUCT_STR(name, ldap_settings)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen#define DEF_INT(name) DEF_STRUCT_INT(name, ldap_settings)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, ldap_settings)
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic struct setting_def setting_defs[] = {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen DEF_STR(hosts),
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen DEF_STR(uris),
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen DEF_STR(dn),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(dnpass),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_BOOL(auth_bind),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(auth_bind_userdn),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_BOOL(tls),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_BOOL(sasl_bind),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(sasl_mech),
860e6dd603921f61b0cd53c1cc16e1d66d312699Timo Sirainen DEF_STR(sasl_realm),
860e6dd603921f61b0cd53c1cc16e1d66d312699Timo Sirainen DEF_STR(sasl_authz_id),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(tls_ca_cert_file),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(tls_ca_cert_dir),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(tls_cert_file),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(tls_key_file),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(tls_cipher_suite),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(tls_require_cert),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(deref),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(scope),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(base),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_INT(ldap_version),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(debug_level),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(ldaprc_path),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(user_attrs),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(user_filter),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(pass_attrs),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(pass_filter),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen DEF_STR(default_pass_scheme),
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen { 0, NULL, 0 }
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen};
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainenstatic struct ldap_settings default_ldap_settings = {
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(hosts) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(uris) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(dn) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(dnpass) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(auth_bind) FALSE,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(auth_bind_userdn) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(tls) FALSE,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(sasl_bind) FALSE,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(sasl_mech) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(sasl_realm) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(sasl_authz_id) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(tls_ca_cert_file) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(tls_ca_cert_dir) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(tls_cert_file) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(tls_key_file) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(tls_cipher_suite) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(tls_require_cert) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(deref) "never",
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(scope) "subtree",
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(base) NULL,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(ldap_version) 3,
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(debug_level) "0",
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(ldaprc_path) "",
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(user_attrs) "homeDirectory=home,uidNumber=uid,gidNumber=gid",
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(user_filter) "(&(objectClass=posixAccount)(uid=%u))",
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen MEMBER(pass_attrs) "uid=user,userPassword=password",
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen MEMBER(pass_filter) "(&(objectClass=posixAccount)(uid=%u))",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen MEMBER(default_pass_scheme) "crypt"
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen};
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic struct ldap_connection *ldap_connections = NULL;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int db_ldap_bind(struct ldap_connection *conn);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void db_ldap_conn_close(struct ldap_connection *conn);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int deref2str(const char *str)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
3b32bc12710240f86465a00fbb2bd1ef030e6c40Timo Sirainen if (strcasecmp(str, "never") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return LDAP_DEREF_NEVER;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (strcasecmp(str, "searching") == 0)
d22301419109ed4a38351715e6760011421dadecTimo Sirainen return LDAP_DEREF_SEARCHING;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (strcasecmp(str, "finding") == 0)
d22301419109ed4a38351715e6760011421dadecTimo Sirainen return LDAP_DEREF_FINDING;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (strcasecmp(str, "always") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return LDAP_DEREF_ALWAYS;
8c909e451d14075c05d90382cf8eebc4e354f569Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_fatal("LDAP: Unknown deref option '%s'", str);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen}
d22301419109ed4a38351715e6760011421dadecTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int scope2str(const char *str)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcasecmp(str, "base") == 0)
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen return LDAP_SCOPE_BASE;
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen if (strcasecmp(str, "onelevel") == 0)
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen return LDAP_SCOPE_ONELEVEL;
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen if (strcasecmp(str, "subtree") == 0)
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen return LDAP_SCOPE_SUBTREE;
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen i_fatal("LDAP: Unknown scope option '%s'", str);
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen}
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen#ifdef OPENLDAP_TLS_OPTIONS
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int tls_require_cert2str(const char *str)
d22301419109ed4a38351715e6760011421dadecTimo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcasecmp(str, "never") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return LDAP_OPT_X_TLS_NEVER;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcasecmp(str, "hard") == 0)
d22301419109ed4a38351715e6760011421dadecTimo Sirainen return LDAP_OPT_X_TLS_HARD;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (strcasecmp(str, "demand") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return LDAP_OPT_X_TLS_DEMAND;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (strcasecmp(str, "allow") == 0)
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen return LDAP_OPT_X_TLS_ALLOW;
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen if (strcasecmp(str, "try") == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return LDAP_OPT_X_TLS_TRY;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1dd054126238349e1a7d3d1ffe7f8bc5fdbacb7aTimo Sirainen i_fatal("LDAP: Unknown tls_require_cert value '%s'", str);
1dd054126238349e1a7d3d1ffe7f8bc5fdbacb7aTimo Sirainen}
1dd054126238349e1a7d3d1ffe7f8bc5fdbacb7aTimo Sirainen#endif
1dd054126238349e1a7d3d1ffe7f8bc5fdbacb7aTimo Sirainen
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainenstatic int ldap_get_errno(struct ldap_connection *conn)
1dd054126238349e1a7d3d1ffe7f8bc5fdbacb7aTimo Sirainen{
1dd054126238349e1a7d3d1ffe7f8bc5fdbacb7aTimo Sirainen int ret, err;
1dd054126238349e1a7d3d1ffe7f8bc5fdbacb7aTimo Sirainen
1dd054126238349e1a7d3d1ffe7f8bc5fdbacb7aTimo Sirainen ret = ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, (void *) &err);
1dd054126238349e1a7d3d1ffe7f8bc5fdbacb7aTimo Sirainen if (ret != LDAP_SUCCESS) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_error("LDAP: Can't get error number: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ldap_err2string(ret));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return LDAP_UNAVAILABLE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return err;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen}
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainenconst char *ldap_get_error(struct ldap_connection *conn)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const char *ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen char *str = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
ddb018bc886680f462463b2c87f983fdedbf6cf0Timo Sirainen ret = ldap_err2string(ldap_get_errno(conn));
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen ldap_get_option(conn->ld, LDAP_OPT_ERROR_STRING, (void *)&str);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (str != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = t_strconcat(ret, ", ", str, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ldap_memfree(str);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ldap_set_option(conn->ld, LDAP_OPT_ERROR_STRING, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void ldap_conn_reconnect(struct ldap_connection *conn)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen db_ldap_conn_close(conn);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (db_ldap_connect(conn) < 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen db_ldap_conn_close(conn);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int ldap_handle_error(struct ldap_connection *conn)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int err = ldap_get_errno(conn);
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen switch (err) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_SUCCESS:
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen i_unreached();
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen case LDAP_SIZELIMIT_EXCEEDED:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_TIMELIMIT_EXCEEDED:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_NO_SUCH_ATTRIBUTE:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_UNDEFINED_TYPE:
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen case LDAP_INAPPROPRIATE_MATCHING:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_CONSTRAINT_VIOLATION:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_TYPE_OR_VALUE_EXISTS:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_INVALID_SYNTAX:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_NO_SUCH_OBJECT:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_ALIAS_PROBLEM:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_INVALID_DN_SYNTAX:
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen case LDAP_IS_LEAF:
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen case LDAP_ALIAS_DEREF_PROBLEM:
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen case LDAP_FILTER_ERROR:
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen /* invalid input */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_SERVER_DOWN:
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen case LDAP_TIMEOUT:
14e4afa9f52bc2150fe92b5cc737ad0354078391Timo Sirainen case LDAP_UNAVAILABLE:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_BUSY:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#ifdef LDAP_CONNECT_ERROR
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_CONNECT_ERROR:
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen#endif
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_LOCAL_ERROR:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_INVALID_CREDENTIALS:
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen default:
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen /* connection problems */
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen ldap_conn_reconnect(conn);
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
14e4afa9f52bc2150fe92b5cc737ad0354078391Timo Sirainen}
14e4afa9f52bc2150fe92b5cc737ad0354078391Timo Sirainen
6d2c938f017a2fc55ae476f88839f03a3d3c5fbdTimo Sirainenstatic int db_ldap_request_bind(struct ldap_connection *conn,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct ldap_request *request)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
6d2c938f017a2fc55ae476f88839f03a3d3c5fbdTimo Sirainen struct ldap_request_bind *brequest =
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (struct ldap_request_bind *)request;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(request->type == LDAP_REQUEST_TYPE_BIND);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(request->msgid == -1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_AUTH ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(conn->pending_count == 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen request->msgid = ldap_bind(conn->ld, brequest->dn,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen request->auth_request->mech_password,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen LDAP_AUTH_SIMPLE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (request->msgid == -1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen auth_request_log_error(request->auth_request, "ldap",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "ldap_bind(%s) failed: %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen brequest->dn, ldap_get_error(conn));
ee9254cc7875519a9c71cc58a40610e6f320e907Timo Sirainen if (ldap_handle_error(conn) < 0) {
29337701451b9c9f9dd26b2aec23a31ab5203822Timo Sirainen /* broken request, remove it */
29337701451b9c9f9dd26b2aec23a31ab5203822Timo Sirainen return 0;
29337701451b9c9f9dd26b2aec23a31ab5203822Timo Sirainen }
29337701451b9c9f9dd26b2aec23a31ab5203822Timo Sirainen return -1;
29337701451b9c9f9dd26b2aec23a31ab5203822Timo Sirainen }
29337701451b9c9f9dd26b2aec23a31ab5203822Timo Sirainen conn->conn_state = LDAP_CONN_STATE_BINDING;
29337701451b9c9f9dd26b2aec23a31ab5203822Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
14e4afa9f52bc2150fe92b5cc737ad0354078391Timo Sirainen
fa7c76955c6bc62689fbdf39318194f85905e6e2Timo Sirainenstatic int db_ldap_request_search(struct ldap_connection *conn,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct ldap_request *request)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct ldap_request_search *srequest =
14e4afa9f52bc2150fe92b5cc737ad0354078391Timo Sirainen (struct ldap_request_search *)request;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
46219292a55094fa49aae33eee681ed075d30e17Timo Sirainen i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen i_assert(request->msgid == -1);
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen
1f1ee8db68d9ae1604350801cd8dc33ebe29fe8aTimo Sirainen request->msgid =
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen ldap_search(conn->ld, srequest->base, conn->set.ldap_scope,
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen srequest->filter, srequest->attributes, 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (request->msgid == -1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen auth_request_log_error(request->auth_request, "ldap",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "ldap_search() failed (filter %s): %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen srequest->filter, ldap_get_error(conn));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ldap_handle_error(conn) < 0) {
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen /* broken request, remove it */
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
888ab4e17f7441b4dcca4a01886d055b57f4586dTimo Sirainen}
dd4f30895ebbddd77e000472fbadcb3128ae2883Timo Sirainen
888ab4e17f7441b4dcca4a01886d055b57f4586dTimo Sirainenstatic bool db_ldap_request_queue_next(struct ldap_connection *conn)
888ab4e17f7441b4dcca4a01886d055b57f4586dTimo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct ldap_request *const *requestp, *request;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int queue_size = aqueue_count(conn->request_queue);
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen int ret = -1;
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen if (conn->pending_count == queue_size) {
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen /* no non-pending requests */
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen return FALSE;
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen }
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen if (queue_size > DB_LDAP_MAX_PENDING_REQUESTS) {
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen /* wait until server has replied to some requests */
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen return FALSE;
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen }
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen
c18ff860dc22960fd37c272d929f889c7939a2c8Timo Sirainen if (db_ldap_connect(conn) < 0)
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen return FALSE;
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen requestp = array_idx(&conn->request_array,
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen aqueue_idx(conn->request_queue,
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen conn->pending_count));
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen request = *requestp;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (conn->pending_count > 0 &&
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen request->type == LDAP_REQUEST_TYPE_BIND) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* we can't do binds until all existing requests are finished */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return FALSE;
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen }
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen switch (conn->conn_state) {
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen case LDAP_CONN_STATE_DISCONNECTED:
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen case LDAP_CONN_STATE_BINDING:
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen /* wait until we're in bound state */
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen return FALSE;
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen case LDAP_CONN_STATE_BOUND_AUTH:
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen if (request->type == LDAP_REQUEST_TYPE_BIND)
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen break;
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen /* bind to default dn first */
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen i_assert(conn->pending_count == 0);
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen (void)db_ldap_bind(conn);
fbd918f47f591f8084fd52b207ef29515ddd11b9Timo Sirainen return FALSE;
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen case LDAP_CONN_STATE_BOUND_DEFAULT:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* we can do anything in this state */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen switch (request->type) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen case LDAP_REQUEST_TYPE_BIND:
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen ret = db_ldap_request_bind(conn, request);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen case LDAP_REQUEST_TYPE_SEARCH:
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ret = db_ldap_request_search(conn, request);
fbd918f47f591f8084fd52b207ef29515ddd11b9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen
fbd918f47f591f8084fd52b207ef29515ddd11b9Timo Sirainen if (ret > 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* success */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(request->msgid != -1);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen conn->pending_count++;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen return TRUE;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen } else if (ret < 0) {
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen /* disconnected */
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen return FALSE;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen } else {
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen /* broken request, remove from queue */
206ed2f6fa3a6fb291498627b2da626581c07a18Timo Sirainen aqueue_delete_tail(conn->request_queue);
206ed2f6fa3a6fb291498627b2da626581c07a18Timo Sirainen request->callback(conn, request, NULL);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen return TRUE;
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen }
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen}
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen
206ed2f6fa3a6fb291498627b2da626581c07a18Timo Sirainenstatic bool
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainendb_ldap_check_limits(struct ldap_connection *conn, struct ldap_request *request)
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct ldap_request *const *first_requestp;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen time_t secs_diff;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
84e49ad7d7a840d600a961daeca60802e3d69cd0Timo Sirainen count = aqueue_count(conn->request_queue);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (count == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen first_requestp = array_idx(&conn->request_array,
f46885a5b78b15a8d2419f6e5d13b643bd85e41fTimo Sirainen aqueue_idx(conn->request_queue, 0));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen secs_diff = ioloop_time - (*first_requestp)->create_time;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (secs_diff > DB_LDAP_REQUEST_LOST_TIMEOUT_SECS) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen auth_request_log_error(request->auth_request, "ldap",
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen "Connection appears to be hanging, reconnecting");
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen ldap_conn_reconnect(conn);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (conn->request_queue->full && count >= DB_LDAP_MAX_QUEUE_SIZE) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* Queue is full already, fail this request */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen auth_request_log_error(request->auth_request, "ldap",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Request queue is full (oldest added %d secs ago)",
dca6d617a23e3f93af3b8df59acb46478179fe55Timo Sirainen (int)secs_diff);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
13307d0b3f41c6d940ae263f4828cf557258f9b9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
5fb3f13537dffd15a31e997da133a721c0728af8Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid db_ldap_request(struct ldap_connection *conn,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct ldap_request *request)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen i_assert(request->auth_request != NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen request->msgid = -1;
306cfd77100131c08b243de10f6d40500f4c27c6Timo Sirainen request->create_time = ioloop_time;
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen
1dd054126238349e1a7d3d1ffe7f8bc5fdbacb7aTimo Sirainen if (!db_ldap_check_limits(conn, request)) {
438f12d7a776da695019114884b48188d94613efTimo Sirainen request->callback(conn, request, NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
af9edddbb704a640055898846add4e386e83fe43Timo Sirainen
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen aqueue_append(conn->request_queue, &request);
fbd918f47f591f8084fd52b207ef29515ddd11b9Timo Sirainen (void)db_ldap_request_queue_next(conn);
51327f2489a4e0e615eb9f7d921473cf8512bb79Timo Sirainen}
6469cf211a57433335641725dc236ebb2b9fdd3bTimo Sirainen
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainenstatic int db_ldap_connect_finish(struct ldap_connection *conn, int ret)
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen{
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen if (ret == LDAP_SERVER_DOWN) {
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen i_error("LDAP: Can't connect to server: %s",
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen conn->set.uris != NULL ?
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen conn->set.uris : conn->set.hosts);
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret != LDAP_SUCCESS) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_error("LDAP: binding failed (dn %s): %s",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen conn->set.dn == NULL ? "(none)" : conn->set.dn,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ldap_get_error(conn));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen while (db_ldap_request_queue_next(conn))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void db_ldap_default_bind_finished(struct ldap_connection *conn,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen LDAPMessage *res)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(conn->pending_count == 0);
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen conn->default_bind_msgid = -1;
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen ret = ldap_result2error(conn->ld, res, FALSE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (db_ldap_connect_finish(conn, ret) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* lost connection, close it */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen db_ldap_conn_close(conn);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void db_ldap_abort_requests(struct ldap_connection *conn,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int max_count,
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen unsigned int timeout_secs,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen bool error, const char *reason)
57bf90f66f393c2807b2fc543655013f61d1d9e4Timo Sirainen{
5b62dea2f88165f3f4d87bba9011343f3ff415ffTimo Sirainen struct ldap_request *const *requestp, *request;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen time_t diff;
while (aqueue_count(conn->request_queue) > 0 && max_count > 0) {
requestp = array_idx(&conn->request_array,
aqueue_idx(conn->request_queue, 0));
request = *requestp;
diff = ioloop_time - request->create_time;
if (diff < (time_t)timeout_secs)
break;
/* timed out, abort */
aqueue_delete_tail(conn->request_queue);
if (request->msgid != -1) {
i_assert(conn->pending_count > 0);
conn->pending_count--;
}
if (error) {
auth_request_log_error(request->auth_request, "ldap",
"%s", reason);
} else {
auth_request_log_info(request->auth_request, "ldap",
"%s", reason);
}
request->callback(conn, request, NULL);
max_count--;
}
}
static void
db_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res)
{
struct ldap_request *const *requests, *request = NULL;
unsigned int i, count;
int msgid, ret;
msgid = ldap_msgid(res);
if (msgid == conn->default_bind_msgid) {
db_ldap_default_bind_finished(conn, res);
return;
}
count = aqueue_count(conn->request_queue);
requests = count == 0 ? NULL : array_idx(&conn->request_array, 0);
for (i = 0; i < count; i++) {
request = requests[aqueue_idx(conn->request_queue, i)];
if (request->msgid == msgid)
break;
if (request->msgid == -1) {
request = NULL;
break;
}
}
if (request == NULL) {
i_error("LDAP: Reply with unknown msgid %d", msgid);
return;
}
if (request->type == LDAP_REQUEST_TYPE_BIND) {
i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING);
i_assert(conn->pending_count == 1);
conn->conn_state = LDAP_CONN_STATE_BOUND_AUTH;
}
i_assert(conn->pending_count > 0);
conn->pending_count--;
aqueue_delete(conn->request_queue, i);
ret = ldap_result2error(conn->ld, res, 0);
if (ret != LDAP_SUCCESS && request->type == LDAP_REQUEST_TYPE_SEARCH) {
/* handle search failures here */
struct ldap_request_search *srequest =
(struct ldap_request_search *)request;
auth_request_log_error(request->auth_request, "ldap",
"ldap_search(%s) failed: %s",
srequest->filter, ldap_err2string(ret));
res = NULL;
}
T_BEGIN {
request->callback(conn, request, res);
} T_END;
if (i > 0) {
/* see if there are timed out requests */
db_ldap_abort_requests(conn, i,
DB_LDAP_REQUEST_LOST_TIMEOUT_SECS,
TRUE, "Request lost");
}
}
static void ldap_input(struct ldap_connection *conn)
{
struct timeval timeout;
LDAPMessage *res;
int ret;
for (;;) {
if (conn->ld == NULL)
return;
memset(&timeout, 0, sizeof(timeout));
ret = ldap_result(conn->ld, LDAP_RES_ANY, 1, &timeout, &res);
#ifdef OPENLDAP_ASYNC_WORKAROUND
if (ret == 0) {
/* try again, there may be another in buffer */
ret = ldap_result(conn->ld, LDAP_RES_ANY, 1,
&timeout, &res);
}
#endif
if (ret <= 0)
break;
db_ldap_handle_result(conn, res);
ldap_msgfree(res);
}
conn->last_reply_stamp = ioloop_time;
if (ret == 0) {
/* send more requests */
while (db_ldap_request_queue_next(conn))
;
} else if (ldap_get_errno(conn) != LDAP_SERVER_DOWN) {
i_error("LDAP: ldap_result() failed: %s", ldap_get_error(conn));
ldap_conn_reconnect(conn);
} else if (aqueue_count(conn->request_queue) > 0 ||
ioloop_time - conn->last_reply_stamp <
DB_LDAP_IDLE_RECONNECT_SECS) {
i_error("LDAP: Connection lost to LDAP server, reconnecting");
ldap_conn_reconnect(conn);
} else {
/* server probably disconnected an idle connection. don't
reconnect until the next request comes. */
db_ldap_conn_close(conn);
}
}
#ifdef HAVE_LDAP_SASL
static int
sasl_interact(LDAP *ld ATTR_UNUSED, unsigned flags ATTR_UNUSED,
void *defaults, void *interact)
{
struct db_ldap_sasl_bind_context *context = defaults;
sasl_interact_t *in;
const char *str;
for (in = interact; in->id != SASL_CB_LIST_END; in++) {
switch (in->id) {
case SASL_CB_GETREALM:
str = context->realm;
break;
case SASL_CB_AUTHNAME:
str = context->authcid;
break;
case SASL_CB_USER:
str = context->authzid;
break;
case SASL_CB_PASS:
str = context->passwd;
break;
default:
str = NULL;
break;
}
if (str != NULL) {
in->len = strlen(str);
in->result = str;
}
}
return LDAP_SUCCESS;
}
#endif
static int db_ldap_bind(struct ldap_connection *conn)
{
int msgid;
i_assert(conn->conn_state != LDAP_CONN_STATE_BINDING);
i_assert(conn->default_bind_msgid == -1);
i_assert(conn->pending_count == 0);
msgid = ldap_bind(conn->ld, conn->set.dn, conn->set.dnpass,
LDAP_AUTH_SIMPLE);
if (msgid == -1) {
i_assert(ldap_get_errno(conn) != LDAP_SUCCESS);
if (db_ldap_connect_finish(conn, ldap_get_errno(conn)) < 0) {
/* lost connection, close it */
db_ldap_conn_close(conn);
}
return -1;
}
conn->conn_state = LDAP_CONN_STATE_BINDING;
conn->default_bind_msgid = msgid;
return 0;
}
static void db_ldap_get_fd(struct ldap_connection *conn)
{
int 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));
}
if (conn->fd <= STDERR_FILENO) {
/* Solaris LDAP library seems to be broken */
i_fatal("LDAP: Buggy LDAP library returned wrong fd: %d",
conn->fd);
}
i_assert(conn->fd != -1);
net_set_nonblock(conn->fd, TRUE);
}
static void
db_ldap_set_opt(struct ldap_connection *conn, int opt, const void *value,
const char *optname, const char *value_str)
{
int ret;
ret = ldap_set_option(conn == NULL ? NULL : conn->ld, opt, value);
if (ret != LDAP_SUCCESS) {
i_fatal("LDAP: Can't set option %s to %s: %s",
optname, value_str, ldap_err2string(ret));
}
}
static void
db_ldap_set_opt_str(struct ldap_connection *conn, int opt, const char *value,
const char *optname)
{
if (value != NULL)
db_ldap_set_opt(conn, opt, value, optname, value);
}
static void db_ldap_set_tls_options(struct ldap_connection *conn)
{
if (!conn->set.tls)
return;
#ifdef OPENLDAP_TLS_OPTIONS
db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CACERTFILE,
conn->set.tls_ca_cert_file, "tls_ca_cert_file");
db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CACERTDIR,
conn->set.tls_ca_cert_dir, "tls_ca_cert_dir");
db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CERTFILE,
conn->set.tls_cert_file, "tls_cert_file");
db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_KEYFILE,
conn->set.tls_key_file, "tls_key_file");
db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
conn->set.tls_cipher_suite, "tls_cipher_suite");
if (conn->set.tls_require_cert != NULL) {
int value = tls_require_cert2str(conn->set.tls_require_cert);
db_ldap_set_opt(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &value,
"tls_require_cert", conn->set.tls_require_cert);
}
#else
if (conn->set.tls_ca_cert_file != NULL ||
conn->set.tls_ca_cert_dir != NULL ||
conn->set.tls_cert_file != NULL ||
conn->set.tls_key_file != NULL ||
conn->set.tls_cipher_suite != NULL)
i_warning("LDAP: tls_* settings ignored, "
"your LDAP library doesn't seem to support them");
#endif
}
static void db_ldap_set_options(struct ldap_connection *conn)
{
unsigned int ldap_version;
int value;
db_ldap_set_opt(conn, LDAP_OPT_DEREF, &conn->set.ldap_deref,
"deref", conn->set.deref);
#ifdef LDAP_OPT_DEBUG_LEVEL
value = atoi(conn->set.debug_level);
if (value != 0) {
db_ldap_set_opt(NULL, LDAP_OPT_DEBUG_LEVEL, &value,
"debug_level", conn->set.debug_level);
}
#endif
if (conn->set.ldap_version < 3) {
if (conn->set.sasl_bind)
i_fatal("LDAP: sasl_bind=yes requires ldap_version=3");
if (conn->set.tls)
i_fatal("LDAP: tls=yes requires ldap_version=3");
}
ldap_version = conn->set.ldap_version;
db_ldap_set_opt(conn, LDAP_OPT_PROTOCOL_VERSION, &ldap_version,
"protocol_version", dec2str(ldap_version));
db_ldap_set_tls_options(conn);
}
int db_ldap_connect(struct ldap_connection *conn)
{
int ret;
if (conn->conn_state != LDAP_CONN_STATE_DISCONNECTED)
return 0;
i_assert(conn->pending_count == 0);
if (conn->ld == NULL) {
if (conn->set.uris != NULL) {
#ifdef LDAP_HAVE_INITIALIZE
if (ldap_initialize(&conn->ld, conn->set.uris) != LDAP_SUCCESS)
conn->ld = NULL;
#else
i_fatal("LDAP: Your LDAP library doesn't support "
"'uris' setting, use 'hosts' instead.");
#endif
} else
conn->ld = ldap_init(conn->set.hosts, LDAP_PORT);
if (conn->ld == NULL)
i_fatal("LDAP: ldap_init() failed with hosts: %s",
conn->set.hosts);
db_ldap_set_options(conn);
}
if (conn->set.tls) {
#ifdef LDAP_HAVE_START_TLS_S
ret = ldap_start_tls_s(conn->ld, NULL, NULL);
if (ret != LDAP_SUCCESS) {
if (ret == LDAP_OPERATIONS_ERROR &&
strncmp(conn->set.uris, "ldaps:", 6) == 0) {
i_fatal("LDAP: Don't use both tls=yes "
"and ldaps URI");
}
i_error("LDAP: ldap_start_tls_s() failed: %s",
ldap_err2string(ret));
return -1;
}
#else
i_error("LDAP: Your LDAP library doesn't support TLS");
return -1;
#endif
}
if (conn->set.sasl_bind) {
#ifdef HAVE_LDAP_SASL
struct db_ldap_sasl_bind_context context;
memset(&context, 0, sizeof(context));
context.authcid = conn->set.dn;
context.passwd = conn->set.dnpass;
context.realm = conn->set.sasl_realm;
context.authzid = conn->set.sasl_authz_id;
/* There doesn't seem to be a way to do SASL binding
asynchronously.. */
ret = ldap_sasl_interactive_bind_s(conn->ld, NULL,
conn->set.sasl_mech,
NULL, NULL, LDAP_SASL_QUIET,
sasl_interact, &context);
if (db_ldap_connect_finish(conn, ret) < 0)
return -1;
#else
i_fatal("LDAP: sasl_bind=yes but no SASL support compiled in");
#endif
conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT;
} else {
if (db_ldap_bind(conn) < 0)
return -1;
}
db_ldap_get_fd(conn);
conn->io = io_add(conn->fd, IO_READ, ldap_input, conn);
return 0;
}
static void db_ldap_disconnect_timeout(struct ldap_connection *conn)
{
db_ldap_abort_requests(conn, -1U,
DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS, FALSE,
"Aborting (timeout), we're not connected to LDAP server");
if (aqueue_count(conn->request_queue) == 0) {
/* no requests left, remove this timeout handler */
timeout_remove(&conn->to);
}
}
static void db_ldap_conn_close(struct ldap_connection *conn)
{
struct ldap_request *const *requests, *request;
unsigned int i;
conn->conn_state = LDAP_CONN_STATE_DISCONNECTED;
conn->default_bind_msgid = -1;
if (conn->pending_count != 0) {
requests = array_idx(&conn->request_array, 0);
for (i = 0; i < conn->pending_count; i++) {
request = requests[aqueue_idx(conn->request_queue, i)];
i_assert(request->msgid != -1);
request->msgid = -1;
}
conn->pending_count = 0;
}
if (conn->ld != NULL) {
ldap_unbind(conn->ld);
conn->ld = NULL;
}
conn->fd = -1;
if (conn->io != NULL) {
/* the fd may have already been closed before ldap_unbind(),
so we'll have to use io_remove_closed(). */
io_remove_closed(&conn->io);
}
if (aqueue_count(conn->request_queue) == 0) {
if (conn->to != NULL)
timeout_remove(&conn->to);
} else if (conn->to == NULL) {
conn->to = timeout_add(DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS *
1000/2, db_ldap_disconnect_timeout, conn);
}
}
void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
char ***attr_names_r, struct hash_table *attr_map,
const char *skip_attr)
{
const char *const *attr, *attr_data, *p;
string_t *static_data;
char *name, *value;
unsigned int i, j, size;
if (*attrlist == '\0')
return;
attr = t_strsplit(attrlist, ",");
static_data = t_str_new(128);
/* @UNSAFE */
for (size = 0; attr[size] != NULL; size++) ;
*attr_names_r = p_new(conn->pool, char *, size + 1);
for (i = j = 0; i < size; i++) {
/* allow spaces here so "foo=1, bar=2" works */
attr_data = attr[i];
while (*attr_data == ' ') attr_data++;
p = strchr(attr_data, '=');
if (p == NULL)
name = value = p_strdup(conn->pool, attr_data);
else if (p != attr_data) {
name = p_strdup_until(conn->pool, attr_data, p);
value = p_strdup(conn->pool, p + 1);
} else {
/* =<static key>=<static value> */
if (str_len(static_data) > 0)
str_append_c(static_data, ',');
str_append(static_data, p + 1);
continue;
}
if (*name != '\0' &&
(skip_attr == NULL || strcmp(skip_attr, value) != 0)) {
if (hash_table_lookup(attr_map, name) != NULL) {
i_fatal("ldap: LDAP attribute '%s' used multiple times. This is currently unsupported.",
name);
}
hash_table_insert(attr_map, name, value);
(*attr_names_r)[j++] = name;
}
}
if (str_len(static_data) > 0) {
hash_table_insert(attr_map, "",
p_strdup(conn->pool, str_c(static_data)));
}
}
struct var_expand_table *
db_ldap_value_get_var_expand_table(struct auth_request *auth_request)
{
const struct var_expand_table *auth_table;
struct var_expand_table *table;
unsigned int count;
auth_table = auth_request_get_var_expand_table(auth_request, NULL);
for (count = 0; auth_table[count].key != '\0'; count++) ;
count++;
table = t_new(struct var_expand_table, count + 1);
table[0].key = '$';
memcpy(table + 1, auth_table, sizeof(*table) * count);
return table;
}
#define IS_LDAP_ESCAPED_CHAR(c) \
((c) == '*' || (c) == '(' || (c) == ')' || (c) == '\\')
const char *ldap_escape(const char *str,
const struct auth_request *auth_request ATTR_UNUSED)
{
const char *p;
string_t *ret;
for (p = str; *p != '\0'; p++) {
if (IS_LDAP_ESCAPED_CHAR(*p))
break;
}
if (*p == '\0')
return str;
ret = t_str_new((size_t) (p - str) + 64);
str_append_n(ret, str, (size_t) (p - str));
for (; *p != '\0'; p++) {
if (IS_LDAP_ESCAPED_CHAR(*p))
str_append_c(ret, '\\');
str_append_c(ret, *p);
}
return str_c(ret);
}
struct db_ldap_result_iterate_context *
db_ldap_result_iterate_init(struct ldap_connection *conn, LDAPMessage *entry,
struct auth_request *auth_request,
struct hash_table *attr_map)
{
struct db_ldap_result_iterate_context *ctx;
const char *static_data;
ctx = t_new(struct db_ldap_result_iterate_context, 1);
ctx->conn = conn;
ctx->entry = entry;
ctx->auth_request = auth_request;
ctx->attr_map = attr_map;
static_data = hash_table_lookup(attr_map, "");
if (static_data != NULL) {
const struct var_expand_table *table;
string_t *str;
table = auth_request_get_var_expand_table(auth_request, NULL);
str = t_str_new(256);
var_expand(str, static_data, table);
ctx->static_attrs = t_strsplit(str_c(str), ",");
}
if (auth_request->auth->set->debug)
ctx->debug = t_str_new(256);
ctx->attr = ldap_first_attribute(conn->ld, entry, &ctx->ber);
return ctx;
}
static void
db_ldap_result_iterate_finish(struct db_ldap_result_iterate_context *ctx)
{
if (ctx->debug != NULL) {
if (str_len(ctx->debug) > 0) {
auth_request_log_debug(ctx->auth_request, "ldap",
"result: %s", str_c(ctx->debug) + 1);
} else {
auth_request_log_debug(ctx->auth_request, "ldap",
"no fields returned by the server");
}
}
ber_free(ctx->ber, 0);
}
static void
db_ldap_result_change_attr(struct db_ldap_result_iterate_context *ctx)
{
ctx->name = hash_table_lookup(ctx->attr_map, ctx->attr);
ctx->template = NULL;
if (ctx->debug != NULL) {
str_printfa(ctx->debug, " %s(%s)=", ctx->attr,
ctx->name != NULL ? ctx->name : "?unknown?");
}
if (ctx->name == NULL || *ctx->name == '\0') {
ctx->value = NULL;
return;
}
if (strchr(ctx->name, '%') != NULL &&
(ctx->template = strchr(ctx->name, '=')) != NULL) {
/* we want to use variables */
ctx->name = t_strdup_until(ctx->name, ctx->template);
ctx->template++;
if (ctx->var_table == NULL) {
ctx->var_table = db_ldap_value_get_var_expand_table(
ctx->auth_request);
ctx->var = t_str_new(256);
}
}
ctx->vals = ldap_get_values(ctx->conn->ld, ctx->entry,
ctx->attr);
ctx->value = ctx->vals[0];
ctx->value_idx = 0;
}
static void
db_ldap_result_return_value(struct db_ldap_result_iterate_context *ctx)
{
bool first = ctx->value_idx == 0;
if (ctx->template != NULL) {
ctx->var_table[0].value = ctx->value;
str_truncate(ctx->var, 0);
var_expand(ctx->var, ctx->template, ctx->var_table);
ctx->value = str_c(ctx->var);
}
if (ctx->debug != NULL) {
if (!first)
str_append_c(ctx->debug, '/');
if (ctx->auth_request->auth->set->debug_passwords ||
strcmp(ctx->name, "password") != 0)
str_append(ctx->debug, ctx->value);
else
str_append(ctx->debug, PASSWORD_HIDDEN_STR);
}
}
static bool db_ldap_result_int_next(struct db_ldap_result_iterate_context *ctx)
{
const char *p;
while (ctx->attr != NULL) {
if (ctx->vals == NULL) {
/* a new attribute */
db_ldap_result_change_attr(ctx);
} else {
/* continuing existing attribute */
if (ctx->value != NULL)
ctx->value = ctx->vals[++ctx->value_idx];
}
if (ctx->value != NULL) {
db_ldap_result_return_value(ctx);
return TRUE;
}
ldap_value_free(ctx->vals); ctx->vals = NULL;
ldap_memfree(ctx->attr);
ctx->attr = ldap_next_attribute(ctx->conn->ld, ctx->entry,
ctx->ber);
}
if (ctx->static_attrs != NULL && *ctx->static_attrs != NULL) {
p = strchr(*ctx->static_attrs, '=');
if (p == NULL) {
ctx->name = *ctx->static_attrs;
ctx->value = "";
} else {
ctx->name = t_strdup_until(*ctx->static_attrs, p);
ctx->value = p + 1;
}
/* make _next_all() return correct values */
ctx->template = "";
ctx->val_1_arr[0] = ctx->value;
ctx->static_attrs++;
return TRUE;
}
db_ldap_result_iterate_finish(ctx);
return FALSE;
}
bool db_ldap_result_iterate_next(struct db_ldap_result_iterate_context *ctx,
const char **name_r, const char **value_r)
{
if (!db_ldap_result_int_next(ctx))
return FALSE;
*name_r = ctx->name;
*value_r = ctx->value;
return TRUE;
}
bool db_ldap_result_iterate_next_all(struct db_ldap_result_iterate_context *ctx,
const char **name_r,
const char *const **values_r)
{
if (!db_ldap_result_int_next(ctx))
return FALSE;
if (ctx->template != NULL) {
/* we can use only one value with templates */
ctx->val_1_arr[0] = ctx->value;
*values_r = ctx->val_1_arr;
} else {
*values_r = (const char *const *)ctx->vals;
}
ctx->value = NULL;
*name_r = ctx->name;
return TRUE;
}
static const char *parse_setting(const char *key, const char *value,
struct ldap_connection *conn)
{
return parse_setting_from_defs(conn->pool, setting_defs,
&conn->set, key, value);
}
static struct ldap_connection *ldap_conn_find(const char *config_path)
{
struct ldap_connection *conn;
for (conn = ldap_connections; conn != NULL; conn = conn->next) {
if (strcmp(conn->config_path, config_path) == 0)
return conn;
}
return NULL;
}
struct ldap_connection *db_ldap_init(const char *config_path)
{
struct ldap_connection *conn;
const char *str;
pool_t pool;
/* see if it already exists */
conn = ldap_conn_find(config_path);
if (conn != NULL) {
conn->refcount++;
return conn;
}
if (*config_path == '\0')
i_fatal("LDAP: Configuration file path not given");
pool = pool_alloconly_create("ldap_connection", 1024);
conn = p_new(pool, struct ldap_connection, 1);
conn->pool = pool;
conn->refcount = 1;
conn->conn_state = LDAP_CONN_STATE_DISCONNECTED;
conn->default_bind_msgid = -1;
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_settings_section_callback, conn))
exit(FATAL_DEFAULT);
if (conn->set.base == NULL)
i_fatal("LDAP: No base given");
if (conn->set.uris == NULL && conn->set.hosts == NULL)
i_fatal("LDAP: No uris or hosts set");
#ifndef LDAP_HAVE_INITIALIZE
if (conn->set.uris != NULL) {
i_fatal("LDAP: Dovecot compiled without support for LDAP uris "
"(ldap_initialize not found)");
}
#endif
if (*conn->set.ldaprc_path != '\0') {
str = getenv("LDAPRC");
if (str != NULL && strcmp(str, conn->set.ldaprc_path) != 0) {
i_fatal("LDAP: Multiple different ldaprc_path "
"settings not allowed (%s and %s)",
str, conn->set.ldaprc_path);
}
env_put(t_strconcat("LDAPRC=", conn->set.ldaprc_path, NULL));
}
conn->set.ldap_deref = deref2str(conn->set.deref);
conn->set.ldap_scope = scope2str(conn->set.scope);
i_array_init(&conn->request_array, DB_LDAP_MAX_QUEUE_SIZE);
conn->request_queue = aqueue_init(&conn->request_array.arr);
conn->next = ldap_connections;
ldap_connections = conn;
return conn;
}
void db_ldap_unref(struct ldap_connection **_conn)
{
struct ldap_connection *conn = *_conn;
struct ldap_connection **p;
*_conn = NULL;
i_assert(conn->refcount >= 0);
if (--conn->refcount > 0)
return;
for (p = &ldap_connections; *p != NULL; p = &(*p)->next) {
if (*p == conn) {
*p = conn->next;
break;
}
}
db_ldap_abort_requests(conn, -1U, 0, FALSE, "Shutting down");
i_assert(conn->pending_count == 0);
db_ldap_conn_close(conn);
i_assert(conn->to == NULL);
array_free(&conn->request_array);
aqueue_deinit(&conn->request_queue);
if (conn->pass_attr_map != NULL)
hash_table_destroy(&conn->pass_attr_map);
if (conn->user_attr_map != NULL)
hash_table_destroy(&conn->user_attr_map);
pool_unref(&conn->pool);
}
#ifndef BUILTIN_LDAP
/* Building a plugin */
extern struct passdb_module_interface passdb_ldap;
extern struct userdb_module_interface userdb_ldap;
void authdb_ldap_init(void);
void authdb_ldap_deinit(void);
void authdb_ldap_init(void)
{
passdb_register_module(&passdb_ldap);
userdb_register_module(&userdb_ldap);
}
void authdb_ldap_deinit(void)
{
passdb_unregister_module(&passdb_ldap);
userdb_unregister_module(&userdb_ldap);
}
#endif
#endif