db-ldap.c revision 0e90e1b11b699166a4a4c5e01d132a28c3e26aff
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen/* Copyright (C) 2003 Timo Sirainen */
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "config.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#undef HAVE_CONFIG_H
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#if defined(PASSDB_LDAP) || defined(USERDB_LDAP)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "common.h"
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen#include "network.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "ioloop.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "hash.h"
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen#include "str.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "settings.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "db-ldap.h"
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include <stddef.h>
0cb57ee35d4cab9c03434d7abf312c081ed554d4Timo Sirainen#include <stdlib.h>
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
a8fc29f19ea6e2d472ba779b2dd5ca4e1f3dac79Timo Sirainen/* Older versions may require calling ldap_result() twice */
a8fc29f19ea6e2d472ba779b2dd5ca4e1f3dac79Timo Sirainen#if LDAP_VENDOR_VERSION <= 20112
3e1ded79bbc9166aa221bcf62d8eb2bee179c557Timo Sirainen# define OPENLDAP_ASYNC_WORKAROUND
3e1ded79bbc9166aa221bcf62d8eb2bee179c557Timo Sirainen#endif
3e1ded79bbc9166aa221bcf62d8eb2bee179c557Timo Sirainen
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen/* Solaris LDAP library doesn't have LDAP_OPT_SUCCESS */
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen#ifndef LDAP_OPT_SUCCESS
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen# define LDAP_OPT_SUCCESS LDAP_SUCCESS
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen#endif
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#define DEF(type, name) \
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen { type, #name, offsetof(struct ldap_settings, name) }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstatic struct setting_def setting_defs[] = {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen DEF(SET_STR, hosts),
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen DEF(SET_STR, uris),
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen DEF(SET_STR, dn),
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen DEF(SET_STR, dnpass),
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen DEF(SET_STR, deref),
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen DEF(SET_STR, scope),
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen DEF(SET_STR, base),
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen DEF(SET_INT, ldap_version),
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainen DEF(SET_STR, user_attrs),
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainen DEF(SET_STR, user_filter),
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainen DEF(SET_STR, pass_attrs),
1f18053d463f0294387b5e4dd11f9010bda9a24eTimo Sirainen DEF(SET_STR, pass_filter),
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen DEF(SET_STR, default_pass_scheme),
c65695787dbb712fc403c4b9f90516b2598377b9Timo Sirainen DEF(SET_INT, user_global_uid),
c65695787dbb712fc403c4b9f90516b2598377b9Timo Sirainen DEF(SET_INT, user_global_gid)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen};
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstruct ldap_settings default_ldap_settings = {
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen MEMBER(hosts) NULL,
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen MEMBER(uris) NULL,
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen MEMBER(dn) NULL,
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen MEMBER(dnpass) NULL,
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen MEMBER(deref) "never",
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen MEMBER(scope) "subtree",
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen MEMBER(base) NULL,
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen MEMBER(ldap_version) 2,
5a8b0ce25f7838652b4a0cb9dab0ad19ec0fab25Timo Sirainen MEMBER(user_attrs) "uid,homeDirectory,,,uidNumber,gidNumber",
5a8b0ce25f7838652b4a0cb9dab0ad19ec0fab25Timo Sirainen MEMBER(user_filter) "(&(objectClass=posixAccount)(uid=%u))",
5a8b0ce25f7838652b4a0cb9dab0ad19ec0fab25Timo Sirainen MEMBER(pass_attrs) "uid,userPassword",
5a8b0ce25f7838652b4a0cb9dab0ad19ec0fab25Timo Sirainen MEMBER(pass_filter) "(&(objectClass=posixAccount)(uid=%u))",
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen MEMBER(default_pass_scheme) "crypt",
002bb664e4b88a808e8a07cf595bfbbf1e7194ecTimo Sirainen MEMBER(user_global_uid) (uid_t)-1,
002bb664e4b88a808e8a07cf595bfbbf1e7194ecTimo Sirainen MEMBER(user_global_gid) (gid_t)-1
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen};
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
c4457e497e01b57565d24da624968699b166e02aTimo Sirainenstatic struct ldap_connection *ldap_connections = NULL;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen
985375eafa124c948eee326eb6bfbe2b268514b5Timo Sirainenstatic void ldap_conn_close(struct ldap_connection *conn);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstatic int deref2str(const char *str)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (strcasecmp(str, "never") == 0)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return LDAP_DEREF_NEVER;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (strcasecmp(str, "searching") == 0)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return LDAP_DEREF_SEARCHING;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (strcasecmp(str, "finding") == 0)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return LDAP_DEREF_FINDING;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (strcasecmp(str, "always") == 0)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return LDAP_DEREF_ALWAYS;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_fatal("LDAP: Unknown deref option '%s'", str);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainenstatic int scope2str(const char *str)
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen{
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen if (strcasecmp(str, "base") == 0)
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen return LDAP_SCOPE_BASE;
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen if (strcasecmp(str, "onelevel") == 0)
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen return LDAP_SCOPE_ONELEVEL;
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen if (strcasecmp(str, "subtree") == 0)
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen return LDAP_SCOPE_SUBTREE;
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen i_fatal("LDAP: Unknown scope option '%s'", str);
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen}
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainenconst char *ldap_get_error(struct ldap_connection *conn)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen int ret, err;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen ret = ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, (void *) &err);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (ret != LDAP_SUCCESS) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_error("LDAP: Can't get error number: %s",
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen ldap_err2string(ret));
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return "??";
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return ldap_err2string(err);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenvoid db_ldap_search(struct ldap_connection *conn, const char *base, int scope,
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen const char *filter, char **attributes,
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen struct ldap_request *request)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen int msgid;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (!conn->connected) {
08aea01ef9a9d20703e0fcf8618e6195c0037a44Timo Sirainen if (!db_ldap_connect(conn)) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen request->callback(conn, request, NULL);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen msgid = ldap_search(conn->ld, base, scope, filter, attributes, 0);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (msgid == -1) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_error("LDAP: ldap_search() failed (filter %s): %s",
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen filter, ldap_get_error(conn));
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen request->callback(conn, request, NULL);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen hash_insert(conn->requests, POINTER_CAST(msgid), request);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstatic void ldap_input(void *context)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen struct ldap_connection *conn = context;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen struct ldap_request *request;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen struct timeval timeout;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen LDAPMessage *res;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen int ret, msgid;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen while (conn->ld != NULL) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen memset(&timeout, 0, sizeof(timeout));
a8fc29f19ea6e2d472ba779b2dd5ca4e1f3dac79Timo Sirainen ret = ldap_result(conn->ld, LDAP_RES_ANY, 1, &timeout, &res);
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen#ifdef OPENLDAP_ASYNC_WORKAROUND
a8fc29f19ea6e2d472ba779b2dd5ca4e1f3dac79Timo Sirainen if (ret == 0) {
a8fc29f19ea6e2d472ba779b2dd5ca4e1f3dac79Timo Sirainen /* try again, there may be another in buffer */
a8fc29f19ea6e2d472ba779b2dd5ca4e1f3dac79Timo Sirainen ret = ldap_result(conn->ld, LDAP_RES_ANY, 1,
a8fc29f19ea6e2d472ba779b2dd5ca4e1f3dac79Timo Sirainen &timeout, &res);
a8fc29f19ea6e2d472ba779b2dd5ca4e1f3dac79Timo Sirainen }
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen#endif
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (ret <= 0) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (ret < 0) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_error("LDAP: ldap_result() failed: %s",
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen ldap_get_error(conn));
985375eafa124c948eee326eb6bfbe2b268514b5Timo Sirainen /* reconnect */
985375eafa124c948eee326eb6bfbe2b268514b5Timo Sirainen ldap_conn_close(conn);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen msgid = ldap_msgid(res);
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen request = hash_lookup(conn->requests, POINTER_CAST(msgid));
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen if (request == NULL) {
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen i_error("LDAP: Reply with unknown msgid %d",
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen msgid);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen } else {
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen hash_remove(conn->requests, POINTER_CAST(msgid));
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen request->callback(conn, request, res);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen ldap_msgfree(res);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
08aea01ef9a9d20703e0fcf8618e6195c0037a44Timo Sirainenint db_ldap_connect(struct ldap_connection *conn)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen int ret, fd;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (conn->connected)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return TRUE;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (conn->ld == NULL) {
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen if (conn->set.uris != NULL) {
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen#ifdef LDAP_HAVE_INITIALIZE
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen if (ldap_initialize(&conn->ld, conn->set.uris) != LDAP_SUCCESS)
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen conn->ld = NULL;
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen#else
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen i_fatal("LDAP: Your LDAP library doesn't support "
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen "'uris' setting, use 'hosts' instead.");
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen#endif
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen } else
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen conn->ld = ldap_init(conn->set.hosts, LDAP_PORT);
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (conn->ld == NULL)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_fatal("LDAP: ldap_init() failed with hosts: %s",
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->set.hosts);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen ret = ldap_set_option(conn->ld, LDAP_OPT_DEREF,
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen (void *) &conn->set.ldap_deref);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (ret != LDAP_SUCCESS) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_fatal("LDAP: Can't set deref option: %s",
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen ldap_err2string(ret));
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen ret = ldap_set_option(conn->ld, LDAP_OPT_PROTOCOL_VERSION,
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen (void *) &conn->set.ldap_version);
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen if (ret != LDAP_OPT_SUCCESS) {
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen i_fatal("LDAP: Can't set protocol version %u: %s",
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen conn->set.ldap_version, ldap_err2string(ret));
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen /* NOTE: we use blocking connect, we couldn't do anything anyway
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen until it's done. */
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen ret = ldap_simple_bind_s(conn->ld, conn->set.dn, conn->set.dnpass);
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen if (ret == LDAP_SERVER_DOWN) {
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen i_error("LDAP: Can't connect to server: %s", conn->set.hosts);
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen return FALSE;
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (ret != LDAP_SUCCESS) {
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen i_error("LDAP: ldap_simple_bind_s() failed (dn %s): %s",
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen conn->set.dn == NULL ? "(none)" : conn->set.dn,
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen ldap_get_error(conn));
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return FALSE;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->connected = TRUE;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen /* register LDAP input to ioloop */
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen ret = ldap_get_option(conn->ld, LDAP_OPT_DESC, (void *) &fd);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (ret != LDAP_SUCCESS) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_fatal("LDAP: Can't get connection fd: %s",
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen ldap_err2string(ret));
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen net_set_nonblock(fd, TRUE);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->io = io_add(fd, IO_READ, ldap_input, conn);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return TRUE;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
9e7182d6fa1940ec14cc2938699820b68ee1dc0dTimo Sirainenstatic void ldap_conn_close(struct ldap_connection *conn)
095034a7699bfc464a07883f633551b5c313c4e7Timo Sirainen{
9e7182d6fa1940ec14cc2938699820b68ee1dc0dTimo Sirainen struct hash_iterate_context *iter;
9e7182d6fa1940ec14cc2938699820b68ee1dc0dTimo Sirainen void *key, *value;
095034a7699bfc464a07883f633551b5c313c4e7Timo Sirainen
9e7182d6fa1940ec14cc2938699820b68ee1dc0dTimo Sirainen iter = hash_iterate_init(conn->requests);
9e7182d6fa1940ec14cc2938699820b68ee1dc0dTimo Sirainen while (hash_iterate(iter, &key, &value)) {
9e7182d6fa1940ec14cc2938699820b68ee1dc0dTimo Sirainen struct ldap_request *request = value;
095034a7699bfc464a07883f633551b5c313c4e7Timo Sirainen
9e7182d6fa1940ec14cc2938699820b68ee1dc0dTimo Sirainen request->callback(conn, request, NULL);
9e7182d6fa1940ec14cc2938699820b68ee1dc0dTimo Sirainen }
9e7182d6fa1940ec14cc2938699820b68ee1dc0dTimo Sirainen hash_iterate_deinit(iter);
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen hash_clear(conn->requests, FALSE);
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen conn->connected = FALSE;
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen if (conn->io != NULL) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen io_remove(conn->io);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->io = NULL;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (conn->ld != NULL) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen ldap_unbind(conn->ld);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->ld = NULL;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainenvoid db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen const char *const default_attr_map[])
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen const char *const *attr;
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen char *name, *value, *p;
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen unsigned int i, size;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen if (*attrlist == '\0')
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen return;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen t_push();
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen attr = t_strsplit(attrlist, ",");
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
0e90e1b11b699166a4a4c5e01d132a28c3e26affTimo Sirainen /* @UNSAFE */
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen for (size = 0; attr[size] != NULL; size++) ;
0e90e1b11b699166a4a4c5e01d132a28c3e26affTimo Sirainen conn->attr_names = p_new(conn->pool, char *, size + 1);
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen for (i = 0; i < size; i++) {
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen p = strchr(attr[i], '=');
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen if (p == NULL) {
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen name = p_strdup(conn->pool, attr[i]);
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen value = *default_attr_map == NULL ? name :
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen p_strdup(conn->pool, *default_attr_map);
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen } else {
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen name = p_strdup_until(conn->pool, attr[i], p);
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen value = p_strdup(conn->pool, p + 1);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen conn->attr_names[i] = name;
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen if (*name != '\0')
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen hash_insert(conn->attr_map, name, value);
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen if (*default_attr_map != NULL)
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen default_attr_map++;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen t_pop();
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen#define IS_LDAP_ESCAPED_CHAR(c) \
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen ((c) == '*' || (c) == '(' || (c) == ')' || (c) == '\\')
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainenconst char *ldap_escape(const char *str)
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen{
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen const char *p;
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen string_t *ret;
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen for (p = str; *p != '\0'; p++) {
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen if (IS_LDAP_ESCAPED_CHAR(*p))
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen break;
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen }
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen if (*p == '\0')
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen return str;
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen ret = t_str_new((size_t) (p - str) + 64);
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen str_append_n(ret, str, (size_t) (p - str));
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen for (; *p != '\0'; p++) {
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen if (IS_LDAP_ESCAPED_CHAR(*p))
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen str_append_c(ret, '\\');
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen str_append_c(ret, *p);
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen }
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen return str_c(ret);
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen}
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstatic const char *parse_setting(const char *key, const char *value,
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen void *context)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen struct ldap_connection *conn = context;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return parse_setting_from_defs(conn->pool, setting_defs,
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen &conn->set, key, value);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
c4457e497e01b57565d24da624968699b166e02aTimo Sirainenstatic struct ldap_connection *ldap_conn_find(const char *config_path)
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen{
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen struct ldap_connection *conn;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen for (conn = ldap_connections; conn != NULL; conn = conn->next) {
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen if (strcmp(conn->config_path, config_path) == 0)
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen return conn;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen }
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen return NULL;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen}
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstruct ldap_connection *db_ldap_init(const char *config_path)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen struct ldap_connection *conn;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen pool_t pool;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen /* see if it already exists */
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen conn = ldap_conn_find(config_path);
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen if (conn != NULL) {
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen conn->refcount++;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen return conn;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen }
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen pool = pool_alloconly_create("ldap_connection", 1024);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn = p_new(pool, struct ldap_connection, 1);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->pool = pool;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->refcount = 1;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->requests = hash_create(default_pool, pool, 0, NULL, NULL);
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen conn->attr_map = hash_create(default_pool, pool, 0, str_hash,
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen (hash_cmp_callback_t *)strcmp);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen conn->config_path = p_strdup(pool, config_path);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->set = default_ldap_settings;
0cb57ee35d4cab9c03434d7abf312c081ed554d4Timo Sirainen if (!settings_read(config_path, NULL, parse_setting, NULL, conn))
0cb57ee35d4cab9c03434d7abf312c081ed554d4Timo Sirainen exit(FATAL_DEFAULT);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (conn->set.base == NULL)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_fatal("LDAP: No base given");
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->set.ldap_deref = deref2str(conn->set.deref);
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen conn->set.ldap_scope = scope2str(conn->set.scope);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen conn->next = ldap_connections;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen ldap_connections = conn;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return conn;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenvoid db_ldap_unref(struct ldap_connection *conn)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (--conn->refcount > 0)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return;
0e90e1b11b699166a4a4c5e01d132a28c3e26affTimo Sirainen i_assert(conn->refcount == 0);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen ldap_conn_close(conn);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen hash_destroy(conn->requests);
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen hash_destroy(conn->attr_map);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen pool_unref(conn->pool);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#endif