db-ldap.c revision 37b5289a049b20bab43f414f647d37b17aa2c3b2
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek/* Copyright (c) 2003-2008 Dovecot authors, see the included COPYING file */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "common.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#if defined(PASSDB_LDAP) || defined(USERDB_LDAP)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "network.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "ioloop.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "array.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "hash.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "aqueue.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "str.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "env-util.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "var-expand.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "settings.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "userdb.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include "db-ldap.h"
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include <stddef.h>
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#include <stdlib.h>
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina#define HAVE_LDAP_SASL
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#ifdef HAVE_SASL_SASL_H
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina# include <sasl/sasl.h>
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#elif defined (HAVE_SASL_H)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek# include <sasl.h>
1a542b3698d8c42cf075b722f8838f106eb09fccPavel Březina#else
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek# undef HAVE_LDAP_SASL
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#endif
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#ifdef LDAP_OPT_X_TLS
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek# define OPENLDAP_TLS_OPTIONS
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#endif
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#if SASL_VERSION_MAJOR < 2
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek# undef HAVE_LDAP_SASL
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#endif
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek#ifndef LDAP_SASL_QUIET
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina# define LDAP_SASL_QUIET 0 /* Doesn't exist in Solaris LDAP */
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina#endif
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina/* Older versions may require calling ldap_result() twice */
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina#if LDAP_VENDOR_VERSION <= 20112
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina# define OPENLDAP_ASYNC_WORKAROUND
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina#endif
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina/* Solaris LDAP library doesn't have LDAP_OPT_SUCCESS */
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek#ifndef LDAP_OPT_SUCCESS
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek# define LDAP_OPT_SUCCESS LDAP_SUCCESS
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina#endif
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastruct db_ldap_result_iterate_context {
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina struct ldap_connection *conn;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina LDAPMessage *entry;
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek struct auth_request *auth_request;
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek struct hash_table *attr_map;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina struct var_expand_table *var_table;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina char *attr, **vals;
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina const char *name, *value, *template, *val_1_arr[2];
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina const char *const *static_attrs;
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina BerElement *ber;
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina string_t *var, *debug;
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina unsigned int value_idx;
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina};
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastruct db_ldap_sasl_bind_context {
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina const char *authcid;
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek const char *passwd;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina const char *realm;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina const char *authzid;
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek};
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek
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)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic struct setting_def setting_defs[] = {
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina DEF_STR(hosts),
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina DEF_STR(uris),
ed44814e0e7ff9f0ef7ffc98fab7d9542a7822dfPavel Březina DEF_STR(dn),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_STR(dnpass),
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina DEF_BOOL(auth_bind),
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina DEF_STR(auth_bind_userdn),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_BOOL(tls),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_BOOL(sasl_bind),
ed44814e0e7ff9f0ef7ffc98fab7d9542a7822dfPavel Březina DEF_STR(sasl_mech),
ed44814e0e7ff9f0ef7ffc98fab7d9542a7822dfPavel Březina DEF_STR(sasl_realm),
ed44814e0e7ff9f0ef7ffc98fab7d9542a7822dfPavel Březina DEF_STR(sasl_authz_id),
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina DEF_STR(tls_ca_cert_file),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_STR(tls_ca_cert_dir),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_STR(tls_cert_file),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_STR(tls_key_file),
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina DEF_STR(tls_cipher_suite),
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina DEF_STR(tls_require_cert),
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina DEF_STR(deref),
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina DEF_STR(scope),
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina DEF_STR(base),
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina DEF_INT(ldap_version),
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina DEF_STR(debug_level),
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina DEF_STR(ldaprc_path),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_STR(user_attrs),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_STR(user_filter),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_STR(pass_attrs),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_STR(pass_filter),
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek DEF_STR(default_pass_scheme),
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina { 0, NULL, 0 }
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina};
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastruct ldap_settings default_ldap_settings = {
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina MEMBER(hosts) NULL,
ed44814e0e7ff9f0ef7ffc98fab7d9542a7822dfPavel Březina MEMBER(uris) NULL,
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek MEMBER(dn) NULL,
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina MEMBER(dnpass) NULL,
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina MEMBER(auth_bind) FALSE,
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek MEMBER(auth_bind_userdn) NULL,
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek MEMBER(tls) FALSE,
ed44814e0e7ff9f0ef7ffc98fab7d9542a7822dfPavel Březina MEMBER(sasl_bind) FALSE,
ed44814e0e7ff9f0ef7ffc98fab7d9542a7822dfPavel Březina MEMBER(sasl_mech) NULL,
ed44814e0e7ff9f0ef7ffc98fab7d9542a7822dfPavel Březina MEMBER(sasl_realm) NULL,
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina MEMBER(sasl_authz_id) NULL,
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina MEMBER(tls_ca_cert_file) NULL,
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek MEMBER(tls_ca_cert_dir) NULL,
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek MEMBER(tls_cert_file) NULL,
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina MEMBER(tls_key_file) NULL,
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina MEMBER(tls_cipher_suite) NULL,
db419c61035cb262010cc8d5a4047191c2b60f05Pavel Březina MEMBER(tls_require_cert) NULL,
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina MEMBER(deref) "never",
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina MEMBER(scope) "subtree",
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina MEMBER(base) NULL,
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina MEMBER(ldap_version) 2,
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina MEMBER(debug_level) "0",
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek MEMBER(ldaprc_path) "",
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))",
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina MEMBER(default_pass_scheme) "crypt"
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina};
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozekstatic struct ldap_connection *ldap_connections = NULL;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int db_ldap_bind(struct ldap_connection *conn);
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic void db_ldap_conn_close(struct ldap_connection *conn);
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozek
4be402505ba20b43361753f0e6e1589c9b029e81Jakub Hrozekstatic int deref2str(const char *str)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina{
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "never") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_DEREF_NEVER;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "searching") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_DEREF_SEARCHING;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "finding") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_DEREF_FINDING;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "always") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_DEREF_ALWAYS;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_fatal("LDAP: Unknown deref option '%s'", str);
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina}
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int scope2str(const char *str)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina{
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "base") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_SCOPE_BASE;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "onelevel") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_SCOPE_ONELEVEL;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "subtree") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_SCOPE_SUBTREE;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina i_fatal("LDAP: Unknown scope option '%s'", str);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek}
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina#ifdef OPENLDAP_TLS_OPTIONS
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int tls_require_cert2str(const char *str)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina{
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "never") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_OPT_X_TLS_NEVER;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "hard") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_OPT_X_TLS_HARD;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "demand") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_OPT_X_TLS_DEMAND;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina if (strcasecmp(str, "allow") == 0)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return LDAP_OPT_X_TLS_ALLOW;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (strcasecmp(str, "try") == 0)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina return LDAP_OPT_X_TLS_TRY;
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina i_fatal("LDAP: Unknown tls_require_cert value '%s'", str);
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina}
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina#endif
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březinastatic int ldap_get_errno(struct ldap_connection *conn)
c9aab1c04c399ca2d1abef74f6df22ced34983dcPavel Březina{
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek int ret, err;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek ret = ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, (void *) &err);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (ret != LDAP_SUCCESS) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_error("LDAP: Can't get error number: %s",
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek ldap_err2string(ret));
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return LDAP_UNAVAILABLE;
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina }
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina return err;
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina}
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekconst char *ldap_get_error(struct ldap_connection *conn)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek{
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return ldap_err2string(ldap_get_errno(conn));
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březina}
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březinastatic void ldap_conn_reconnect(struct ldap_connection *conn)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek{
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina db_ldap_conn_close(conn);
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina if (db_ldap_connect(conn) < 0)
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina db_ldap_conn_close(conn);
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina}
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březinastatic int ldap_handle_error(struct ldap_connection *conn)
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina{
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek int err = ldap_get_errno(conn);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina switch (err) {
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_SUCCESS:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina i_unreached();
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_SIZELIMIT_EXCEEDED:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_TIMELIMIT_EXCEEDED:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case LDAP_NO_SUCH_ATTRIBUTE:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_UNDEFINED_TYPE:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_INAPPROPRIATE_MATCHING:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_CONSTRAINT_VIOLATION:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_TYPE_OR_VALUE_EXISTS:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_INVALID_SYNTAX:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_NO_SUCH_OBJECT:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_ALIAS_PROBLEM:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_INVALID_DN_SYNTAX:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_IS_LEAF:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_ALIAS_DEREF_PROBLEM:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_FILTER_ERROR:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina /* invalid input */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return -1;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case LDAP_SERVER_DOWN:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_TIMEOUT:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_UNAVAILABLE:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_BUSY:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina#ifdef LDAP_CONNECT_ERROR
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina case LDAP_CONNECT_ERROR:
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina#endif
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case LDAP_LOCAL_ERROR:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case LDAP_INVALID_CREDENTIALS:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek default:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* connection problems */
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina ldap_conn_reconnect(conn);
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina return 0;
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina }
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek}
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březinastatic int db_ldap_request_bind(struct ldap_connection *conn,
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina struct ldap_request *request)
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina{
fb4e4c4eb6a6dc732370584f70d23dd4a2c5c7b6Pavel Březina struct ldap_request_bind *brequest =
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina (struct ldap_request_bind *)request;
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina i_assert(request->type == LDAP_REQUEST_TYPE_BIND);
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina i_assert(request->msgid == -1);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_AUTH ||
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(conn->pending_count == 0);
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina request->msgid = ldap_bind(conn->ld, brequest->dn,
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina request->auth_request->mech_password,
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina LDAP_AUTH_SIMPLE);
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina if (request->msgid == -1) {
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina auth_request_log_error(request->auth_request, "ldap",
f7af8c5b369938725e47585c641ae5b017d442a1Pavel Březina "ldap_bind(%s) failed: %s",
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina brequest->dn, ldap_get_error(conn));
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (ldap_handle_error(conn) < 0) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* broken request, remove it */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return 0;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek }
f6171b2bc954a367f316853ab71090eb213bdee3Pavel Březina return -1;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek }
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek conn->conn_state = LDAP_CONN_STATE_BINDING;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return 1;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek}
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekstatic int db_ldap_request_search(struct ldap_connection *conn,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek struct ldap_request *request)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek{
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek struct ldap_request_search *srequest =
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek (struct ldap_request_search *)request;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(request->msgid == -1);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek request->msgid =
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek ldap_search(conn->ld, srequest->base, conn->set.ldap_scope,
8bbf89c5ab798c112773fe23515c3a9df56dde71Nick Guay srequest->filter, srequest->attributes, 0);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (request->msgid == -1) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek auth_request_log_error(request->auth_request, "ldap",
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek "ldap_search() failed (filter %s): %s",
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březina srequest->filter, ldap_get_error(conn));
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (ldap_handle_error(conn) < 0) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* broken request, remove it */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return 0;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek }
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return -1;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek }
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return 1;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek}
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekstatic bool db_ldap_request_queue_next(struct ldap_connection *conn)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek{
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina struct ldap_request *const *requestp, *request;
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina unsigned int queue_size = aqueue_count(conn->request_queue);
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina int ret = -1;
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina if (conn->pending_count == queue_size) {
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina /* no non-pending requests */
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina return FALSE;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek }
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (queue_size > DB_LDAP_MAX_PENDING_REQUESTS) {
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina /* wait until server has replied to some requests */
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina return FALSE;
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina }
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina if (db_ldap_connect(conn) < 0)
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina return FALSE;
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina requestp = array_idx(&conn->request_array,
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina aqueue_idx(conn->request_queue,
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina conn->pending_count));
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina request = *requestp;
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina if (conn->pending_count > 0 &&
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina request->type == LDAP_REQUEST_TYPE_BIND) {
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina /* we can't do binds until all existing requests are finished */
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina return FALSE;
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina }
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina switch (conn->conn_state) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case LDAP_CONN_STATE_DISCONNECTED:
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina case LDAP_CONN_STATE_BINDING:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* wait until we're in bound state */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return FALSE;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case LDAP_CONN_STATE_BOUND_AUTH:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (request->type == LDAP_REQUEST_TYPE_BIND)
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina break;
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina /* bind to default dn first */
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina i_assert(conn->pending_count == 0);
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina (void)db_ldap_bind(conn);
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina return FALSE;
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina case LDAP_CONN_STATE_BOUND_DEFAULT:
cda8ff6cfdef22356dc3c06ec5204344912f0f0bPavel Březina /* we can do anything in this state */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek break;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek }
69e7d6649b58c66675ef38084868fc5356c5a240Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek switch (request->type) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case LDAP_REQUEST_TYPE_BIND:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek ret = db_ldap_request_bind(conn, request);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek break;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case LDAP_REQUEST_TYPE_SEARCH:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek ret = db_ldap_request_search(conn, request);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek break;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek }
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (ret > 0) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* success */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(request->msgid != -1);
fb4e4c4eb6a6dc732370584f70d23dd4a2c5c7b6Pavel Březina conn->pending_count++;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return TRUE;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek } else if (ret < 0) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* disconnected */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return FALSE;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek } else {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* broken request, remove from queue */
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek aqueue_delete_tail(conn->request_queue);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek request->callback(conn, request, NULL);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return TRUE;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek }
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek}
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekvoid db_ldap_request(struct ldap_connection *conn,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek struct ldap_request *request)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek{
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(request->auth_request != NULL);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek request->msgid = -1;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek request->create_time = ioloop_time;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek if (conn->request_queue->full &&
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řezina request->callback(conn, request, NULL);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina return;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina aqueue_append(conn->request_queue, &request);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina (void)db_ldap_request_queue_next(conn);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina}
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinastatic int db_ldap_connect_finish(struct ldap_connection *conn, int ret)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina{
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (ret == LDAP_SERVER_DOWN) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina i_error("LDAP: Can't connect to server: %s",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->set.uris != NULL ?
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->set.uris : conn->set.hosts);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina return -1;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (ret != LDAP_SUCCESS) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina i_error("LDAP: binding failed (dn %s): %s",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->set.dn == NULL ? "(none)" : conn->set.dn,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina ldap_get_error(conn));
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina return -1;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina while (db_ldap_request_queue_next(conn))
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina ;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina return 0;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina}
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinastatic void db_ldap_default_bind_finished(struct ldap_connection *conn,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina LDAPMessage *res)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina{
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina int ret;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina i_assert(conn->pending_count == 0);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->default_bind_msgid = -1;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina ret = ldap_result2error(conn->ld, res, FALSE);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (db_ldap_connect_finish(conn, ret) < 0) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* lost connection, close it */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina db_ldap_conn_close(conn);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina}
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinastatic void db_ldap_abort_requests(struct ldap_connection *conn,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina unsigned int max_count,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina unsigned int timeout_secs,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina bool error, const char *reason)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina{
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina struct ldap_request *const *requestp, *request;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina time_t diff;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina while (aqueue_count(conn->request_queue) > 0 && max_count > 0) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina requestp = array_idx(&conn->request_array,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina aqueue_idx(conn->request_queue, 0));
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina request = *requestp;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina diff = ioloop_time - request->create_time;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (diff < (time_t)timeout_secs)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina break;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* timed out, abort */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina aqueue_delete_tail(conn->request_queue);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (request->msgid != -1) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina i_assert(conn->pending_count > 0);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->pending_count--;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (error) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina auth_request_log_error(request->auth_request, "ldap",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina "%s", reason);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina } else {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina auth_request_log_info(request->auth_request, "ldap",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina "%s", reason);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina request->callback(conn, request, NULL);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina max_count--;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina}
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinastatic void
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březinadb_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina{
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina struct ldap_request *const *requests, *request = NULL;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina unsigned int i, count;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina int msgid, ret;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina msgid = ldap_msgid(res);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (msgid == conn->default_bind_msgid) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina db_ldap_default_bind_finished(conn, res);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina return;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina count = aqueue_count(conn->request_queue);
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 if (request->msgid == msgid)
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina break;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (request->msgid == -1) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina request = NULL;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina break;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (request == NULL) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina i_error("LDAP: Reply with unknown msgid %d", msgid);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina return;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
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 i_assert(conn->pending_count == 1);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->conn_state = LDAP_CONN_STATE_BOUND_AUTH;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina i_assert(conn->pending_count > 0);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina conn->pending_count--;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina aqueue_delete(conn->request_queue, i);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina ret = ldap_result2error(conn->ld, res, 0);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (ret != LDAP_SUCCESS && request->type == LDAP_REQUEST_TYPE_SEARCH) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* handle search failures here */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina struct ldap_request_search *srequest =
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina (struct ldap_request_search *)request;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina auth_request_log_error(request->auth_request, "ldap",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina "ldap_search(%s) failed: %s",
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina srequest->filter, ldap_err2string(ret));
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina res = NULL;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina T_BEGIN {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina request->callback(conn, request, res);
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina } T_END;
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina if (i > 0) {
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina /* see if there are timed out requests */
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina db_ldap_abort_requests(conn, i,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina DB_LDAP_REQUEST_LOST_TIMEOUT_SECS,
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina TRUE, "Request lost");
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina }
44749ce0c1fee9babee80060fa0db99eebb2ab51Pavel Březina}
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březinastatic void ldap_input(struct ldap_connection *conn)
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina{
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina struct timeval timeout;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina LDAPMessage *res;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina int ret;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina for (;;) {
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina if (conn->ld == NULL)
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina return;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina memset(&timeout, 0, sizeof(timeout));
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina ret = ldap_result(conn->ld, LDAP_RES_ANY, 1, &timeout, &res);
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina#ifdef OPENLDAP_ASYNC_WORKAROUND
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina if (ret == 0) {
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina /* try again, there may be another in buffer */
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina ret = ldap_result(conn->ld, LDAP_RES_ANY, 1,
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina &timeout, &res);
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina }
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina#endif
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina if (ret <= 0)
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina break;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina db_ldap_handle_result(conn, res);
2d34690ae92215d355b0272001d9e68214dc80f6Jakub Hrozek ldap_msgfree(res);
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina }
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina conn->last_reply_stamp = ioloop_time;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina if (ret == 0) {
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina /* send more requests */
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina while (db_ldap_request_queue_next(conn))
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina ;
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 ldap_conn_reconnect(conn);
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina } else if (aqueue_count(conn->request_queue) > 0 ||
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek ioloop_time - conn->last_reply_stamp <
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek DB_LDAP_IDLE_RECONNECT_SECS) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_error("LDAP: Connection lost to LDAP server, reconnecting");
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek ldap_conn_reconnect(conn);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek } else {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek /* server probably disconnected an idle connection. don't
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek reconnect until the next request comes. */
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina db_ldap_conn_close(conn);
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina }
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březina}
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březina
fb4e4c4eb6a6dc732370584f70d23dd4a2c5c7b6Pavel Březina#ifdef HAVE_LDAP_SASL
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březinastatic int
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březinasasl_interact(LDAP *ld ATTR_UNUSED, unsigned flags ATTR_UNUSED,
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek void *defaults, void *interact)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek{
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek struct db_ldap_sasl_bind_context *context = defaults;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina sasl_interact_t *in;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek const char *str;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek for (in = interact; in->id != SASL_CB_LIST_END; in++) {
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březina switch (in->id) {
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case SASL_CB_GETREALM:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek str = context->realm;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek break;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case SASL_CB_AUTHNAME:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek str = context->authcid;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina break;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek case SASL_CB_USER:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek str = context->authzid;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek break;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina case SASL_CB_PASS:
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina str = context->passwd;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina break;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek default:
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek str = NULL;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina break;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina }
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina if (str != NULL) {
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina in->len = strlen(str);
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina in->result = str;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina }
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek }
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek return LDAP_SUCCESS;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek}
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březina#endif
f643754db81eeade60485bbe3d80324d889cc4f3Pavel Březina
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekstatic int db_ldap_bind(struct ldap_connection *conn)
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek{
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina int msgid;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(conn->conn_state != LDAP_CONN_STATE_BINDING);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(conn->default_bind_msgid == -1);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek i_assert(conn->pending_count == 0);
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek msgid = ldap_bind(conn->ld, conn->set.dn, conn->set.dnpass,
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina LDAP_AUTH_SIMPLE);
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina if (msgid == -1) {
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 Hrozek db_ldap_conn_close(conn);
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina }
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina return -1;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina }
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina conn->conn_state = LDAP_CONN_STATE_BINDING;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina conn->default_bind_msgid = msgid;
f5d4b05027acce06e3509ecb68869d1c7ef37180Pavel Březina return 0;
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek}
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozek
3f98cdc011bb4e8cd22c088f288b0bcdb6452492Jakub Hrozekstatic 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 <= CLIENT_LISTEN_FD) {
/* 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, "LDAP server not connected");
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->io != NULL)
io_remove(&conn->io);
if (conn->ld != NULL) {
ldap_unbind(conn->ld);
conn->ld = NULL;
}
conn->fd = -1;
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)) {
hash_insert(attr_map, name, value);
(*attr_names_r)[j++] = name;
}
}
if (str_len(static_data) > 0) {
hash_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_lookup(attr_map, "");
if (static_data != NULL)
ctx->static_attrs = t_strsplit(static_data, ",");
if (auth_request->auth->verbose_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_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->verbose_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;
}
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_destroy(&conn->pass_attr_map);
if (conn->user_attr_map != NULL)
hash_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