diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/configure.in dovecot-1.2.10/configure.in --- ORIG/dovecot-1.2.10/configure.in 2010-01-24 23:31:57.000000000 +0000 +++ dovecot-1.2.10/configure.in 2010-02-05 07:19:44.000000000 +0000 @@ -1936,10 +1936,13 @@ userdb="$userdb (plugin)" passdb="$passdb (plugin)" fi +# Need to run some specific initialization if LDAP is built, +# see lib-dict/Makefile.am which builds dict-drivers-register.c instead +# dict_drivers="$dict_drivers ldap" fi AM_CONDITIONAL(LDAP_PLUGIN, test "$have_ldap_plugin" = "yes") -dict_drivers=client +dict_drivers="$dict_drivers client" if test $want_db != no; then AC_MSG_CHECKING([db_env_create in -ldb]) @@ -2456,6 +2459,7 @@ src/Makefile src/lib/Makefile src/lib-sql/Makefile +src/lib-ldap/Makefile src/lib-auth/Makefile src/lib-charset/Makefile src/lib-dict/Makefile diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/doc/wiki/Makefile.am.in dovecot-1.2.10/doc/wiki/Makefile.am.in --- ORIG/dovecot-1.2.10/doc/wiki/Makefile.am.in 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-1.2.10/doc/wiki/Makefile.am.in 2010-02-05 11:59:29.000000000 +0000 @@ -0,0 +1,9 @@ +if BUILD_DOCS +wikidir = $(docdir)/wiki +wiki_DATA = $(wikifiles) +endif + +EXTRA_DIST = \ + $(wikifiles) + +wikifiles = \ No newline at end of file diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/Makefile.am dovecot-1.2.10/src/Makefile.am --- ORIG/dovecot-1.2.10/src/Makefile.am 2009-07-10 14:47:39.000000000 +0000 +++ dovecot-1.2.10/src/Makefile.am 2010-02-05 07:19:44.000000000 +0000 @@ -1,10 +1,11 @@ SUBDIRS = \ lib \ - lib-dict \ lib-sql \ lib-ntlm \ lib-otp \ lib-settings \ + lib-ldap \ + lib-dict \ lib-charset \ lib-mail \ lib-imap \ diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/deliver/Makefile.am dovecot-1.2.10/src/deliver/Makefile.am --- ORIG/dovecot-1.2.10/src/deliver/Makefile.am 2010-01-19 11:56:56.000000000 +0000 +++ dovecot-1.2.10/src/deliver/Makefile.am 2010-02-05 07:19:44.000000000 +0000 @@ -1,18 +1,21 @@ pkglibexecdir = $(libexecdir)/dovecot + pkglibexec_PROGRAMS = deliver AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-settings \ -I$(top_srcdir)/src/lib-auth \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-mail \ -I$(top_srcdir)/src/lib-imap \ -I$(top_srcdir)/src/lib-index \ - -I$(top_srcdir)/src/lib-settings \ + -I$(top_srcdir)/src/lib-ldap \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ -I$(top_srcdir)/src/lib-storage/index/raw \ + -DDICT_MODULE_DIR=\""$(moduledir)/dict"\" \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DPKG_RUNDIR=\""$(rundir)"\" \ -DMODULEDIR=\""$(moduledir)"\" @@ -29,6 +32,10 @@ libs = \ $(STORAGE_LIBS) \ ../lib-dict/libdict.a \ + ../lib-dict/libdict_backend.a \ + ../lib-ldap/libldap.a \ + ../lib-settings/libsettings.a \ + ../lib/liblib.a \ $(unused_objects) deliver_LDADD = \ diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/deliver/deliver.c dovecot-1.2.10/src/deliver/deliver.c --- ORIG/dovecot-1.2.10/src/deliver/deliver.c 2010-01-24 23:14:17.000000000 +0000 +++ dovecot-1.2.10/src/deliver/deliver.c 2010-02-05 07:19:44.000000000 +0000 @@ -30,6 +30,7 @@ #include "raw-storage.h" #include "imap-utf7.h" #include "dict.h" +#include "dict-ldap.h" #include "auth-client.h" #include "mail-send.h" #include "duplicate.h" @@ -1169,9 +1170,11 @@ deliver_set->log_format = DEFAULT_LOG_FORMAT; dict_drivers_register_builtin(); - duplicate_init(); + // FIXME: should also add dict-sql here + dict_ldap_register(); + duplicate_init(); mail_users_init(getenv("AUTH_SOCKET_PATH"), getenv("DEBUG") != NULL); - mail_storage_init(); + mail_storage_init(); mail_storage_register_all(); mailbox_list_register_all(); @@ -1310,6 +1313,7 @@ duplicate_deinit(); dict_drivers_unregister_builtin(); + dict_ldap_unregister(); lib_signals_deinit(); io_loop_destroy(&ioloop); diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/dict/Makefile.am dovecot-1.2.10/src/dict/Makefile.am --- ORIG/dovecot-1.2.10/src/dict/Makefile.am 2008-08-27 08:43:56.000000000 +0000 +++ dovecot-1.2.10/src/dict/Makefile.am 2010-02-05 07:19:44.000000000 +0000 @@ -5,6 +5,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-dict \ + -I$(top_srcdir)/src/lib-ldap \ -I$(top_srcdir)/src/lib-sql \ -DDICT_MODULE_DIR=\""$(moduledir)/dict"\" \ -DPKG_RUNDIR=\""$(rundir)"\" @@ -14,6 +15,7 @@ libs = \ ../lib-dict/libdict_backend.a \ ../lib-dict/libdict.a \ + ../lib-ldap/libldap.a \ ../lib-sql/libsql.a \ ../lib-settings/libsettings.a \ ../lib/liblib.a diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib/failures.c dovecot-1.2.10/src/lib/failures.c --- ORIG/dovecot-1.2.10/src/lib/failures.c 2010-01-24 23:14:17.000000000 +0000 +++ dovecot-1.2.10/src/lib/failures.c 2010-02-09 05:21:43.000000000 +0000 @@ -16,6 +16,7 @@ #include const char *failure_log_type_prefixes[] = { + "Debug: ", "Info: ", "Warning: ", "Error: ", @@ -23,7 +24,7 @@ "Panic: " }; static char log_type_internal_chars[] = { - 'I', 'W', 'E', 'F', 'P' + 'D', 'I', 'W', 'E', 'F', 'P' }; /* Initialize working defaults */ @@ -262,6 +263,18 @@ errno = old_errno; } +void i_debug(const char *format, ...) +{ + int old_errno = errno; + va_list args; + + va_start(args, format); + info_handler(LOG_TYPE_DEBUG, format, args); + va_end(args); + + errno = old_errno; +} + void i_set_fatal_handler(fatal_failure_callback_t *callback ATTR_NORETURN) { if (callback == NULL) @@ -320,6 +333,8 @@ int level = LOG_ERR; switch (type) { + case LOG_TYPE_DEBUG: + level = LOG_DEBUG; case LOG_TYPE_INFO: level = LOG_INFO; break; diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib/failures.h dovecot-1.2.10/src/lib/failures.h --- ORIG/dovecot-1.2.10/src/lib/failures.h 2009-02-21 19:17:03.000000000 +0000 +++ dovecot-1.2.10/src/lib/failures.h 2010-02-09 05:02:01.000000000 +0000 @@ -15,6 +15,7 @@ }; enum log_type { + LOG_TYPE_DEBUG, LOG_TYPE_INFO, LOG_TYPE_WARNING, LOG_TYPE_ERROR, @@ -37,6 +38,7 @@ void i_error(const char *format, ...) ATTR_FORMAT(1, 2); void i_warning(const char *format, ...) ATTR_FORMAT(1, 2); void i_info(const char *format, ...) ATTR_FORMAT(1, 2); +void i_debug(const char *format, ...) ATTR_FORMAT(1, 2); void i_fatal_status(int status, const char *format, ...) ATTR_FORMAT(2, 3) ATTR_NORETURN; diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib/macros.h dovecot-1.2.10/src/lib/macros.h --- ORIG/dovecot-1.2.10/src/lib/macros.h 2008-06-20 06:52:12.000000000 +0000 +++ dovecot-1.2.10/src/lib/macros.h 2010-02-05 07:19:44.000000000 +0000 @@ -138,6 +138,9 @@ # define MEMBER(name) #endif +// FIXME: help, no idea if this is the right spot +#define S_MEMBER(s, name) s->name = + /* Macros to provide type safety for callback functions' context parameters */ #ifdef __GNUC__ # define CONTEXT_TYPE_SAFETY diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib-dict/Makefile.am dovecot-1.2.10/src/lib-dict/Makefile.am --- ORIG/dovecot-1.2.10/src/lib-dict/Makefile.am 2009-07-10 14:47:39.000000000 +0000 +++ dovecot-1.2.10/src/lib-dict/Makefile.am 2010-02-05 07:19:44.000000000 +0000 @@ -2,8 +2,10 @@ dict_drivers = @dict_drivers@ +# DICTLDAP: add auth modules AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-ldap \ -I$(top_srcdir)/src/lib-sql \ -I$(top_srcdir)/src/lib-settings \ $(SQL_CFLAGS) @@ -15,12 +17,18 @@ backend_sources = \ dict-db.c \ + dict-ldap.c \ + dict-ldap-settings.c \ dict-sql.c \ dict-sql-settings.c +#libdict_a_LIBADD = \ +# ../lib-ldap/libldap.a libdict_a_SOURCES = \ $(base_sources) +#libdict_backend_a_LIBADD = \ +# ../lib-ldap/libldap.a libdict_backend_a_SOURCES = \ $(backend_sources) \ dict-drivers-register.c @@ -28,6 +36,8 @@ headers = \ dict.h \ dict-client.h \ + dict-ldap.h \ + dict-ldap-settings.h \ dict-private.h \ dict-sql.h \ dict-sql-settings.h @@ -44,6 +54,7 @@ echo '/* this file automatically generated by Makefile */' >$@ echo '#include "lib.h"' >>$@ echo '#include "dict.h"' >>$@ + echo '#include "dict-ldap.h"' >>$@ echo '#include "dict-sql.h"' >>$@ for i in $(dict_drivers) null; do \ if [ "$${i}" != "null" ]; then \ @@ -51,6 +62,7 @@ fi; \ done echo 'void dict_drivers_register_all(void) {' >>$@ + echo 'dict_ldap_register();' >>$@ echo 'dict_sql_register();' >>$@ for i in $(dict_drivers) null; do \ if [ "$${i}" != "null" ]; then \ @@ -59,6 +71,7 @@ done echo '}' >>$@ echo 'void dict_drivers_unregister_all(void) {' >>$@ + echo 'dict_ldap_unregister();' >>$@ echo 'dict_sql_unregister();' >>$@ for i in $(dict_drivers) null; do \ if [ "$${i}" != "null" ]; then \ diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib-dict/dict-ldap-settings.c dovecot-1.2.10/src/lib-dict/dict-ldap-settings.c --- ORIG/dovecot-1.2.10/src/lib-dict/dict-ldap-settings.c 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-1.2.10/src/lib-dict/dict-ldap-settings.c 2010-02-11 15:01:08.000000000 +0000 @@ -0,0 +1,254 @@ +/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */ + +/* based on (copied from) lib-dict/lib-sql-settings.c */ + +#include "dict-ldap-settings.h" + +/* FIXME: a lot of this stuff should be generic for SQL, LDAP, and FILE (?) maps + * - we probably don't need two parsers + */ + +/* dict-ldap config file elements + */ + +#define DEF_STR(name) DEF_STRUCT_STR(name, ldap_settings) +#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, ldap_settings) +#define DEF_INT(name) DEF_STRUCT_INT(name, ldap_settings) + +// FIXME: resolve settings differences between dict-ldap & db-ldap (auth, dovecot-ldap.conf) +// struct setting_def dict_ldap_setting_defs[] >> define here, not in dict-ldap-settings.h +static struct setting_def dict_ldap_setting_defs[] = { + DEF_STR(pattern), + DEF_STR(filter), + DEF_STR(attrlist), + DEF_STR(encoding), + + DEF_STR(hosts), + DEF_STR(uris), + DEF_STR(dn), + DEF_STR(dnpass), + DEF_BOOL(auth_bind), + DEF_STR(auth_bind_userdn), + DEF_BOOL(tls), + DEF_BOOL(sasl_bind), + DEF_STR(sasl_mech), + DEF_STR(sasl_realm), + DEF_STR(sasl_authz_id), + DEF_STR(tls_ca_cert_file), + DEF_STR(tls_ca_cert_dir), + DEF_STR(tls_cert_file), + DEF_STR(tls_key_file), + DEF_STR(tls_cipher_suite), + DEF_STR(tls_require_cert), + DEF_STR(deref), + DEF_STR(scope), + DEF_STR(base), + DEF_INT(ldap_version), + DEF_STR(debug_level), + DEF_STR(ldaprc_path), + DEF_STR(user_attrs), + DEF_STR(user_filter), + DEF_STR(pass_attrs), + DEF_STR(pass_filter), + DEF_STR(default_pass_scheme), + + { 0, NULL, 0 } +}; + + +/* config file parsing + */ +static const char *ldap_pattern_read_name(const char **pattern) +{ + const char *p = *pattern, *name; + + if (*p == '{') { + /* ${name} */ + name = ++p; + p = strchr(p, '}'); + if (p == NULL) { + /* error, but allow anyway */ + *pattern += strlen(*pattern); + return ""; + } + *pattern = p + 1; + } else { + /* $name - ends at the first non-alnum_ character */ + name = p; + for (; *p != '\0'; p++) { + if (!i_isalnum(*p) && *p != '_') + break; + } + *pattern = p; + } + name = t_strdup_until(name, p); + return name; +} + +static const char *dict_ldap_fields_map(struct ldap_setting_parser_ctx *ctx) +{ + struct dict_ldap_map_field *fields; + string_t *pattern; + const char *p, *name; + unsigned int i, count; + + /* go through the variables in the pattern, replace them with plain + '$' character and add its field */ + pattern = t_str_new(strlen(ctx->cur_conn.set.pattern) + 1); + fields = array_get_modifiable(&ctx->cur_fields, &count); + + p_array_init(&ctx->cur_conn.set.ldap_fields, ctx->pool, count); + for (p = ctx->cur_conn.set.pattern; *p != '\0';) { + if (*p != '$') { + str_append_c(pattern, *p); + p++; + continue; + } + p++; + str_append_c(pattern, '$'); + + name = ldap_pattern_read_name(&p); + for (i = 0; i < count; i++) { + if (fields[i].variable != NULL && + strcmp(fields[i].variable, name) == 0) + break; + } + if (i == count) { + return t_strconcat("Missing LDAP field for variable: ", + name, NULL); + } + + /* mark this field as used */ + fields[i].variable = NULL; + array_append(&ctx->cur_conn.set.ldap_fields, + &fields[i].ldap_field, 1); + } + + /* make sure there aren't any unused fields */ + for (i = 0; i < count; i++) { + if (fields[i].variable != NULL) { + return t_strconcat("Unused variable: ", + fields[i].variable, NULL); + } + } + + if (ctx->set->max_field_count < count) + ctx->set->max_field_count = count; + ctx->cur_conn.set.pattern = p_strdup(ctx->pool, str_c(pattern)); + return NULL; +} + +static const char *dict_ldap_map_finish(struct ldap_setting_parser_ctx *ctx) +{ + if (ctx->cur_conn.set.pattern == NULL) + return "Missing setting: pattern"; + if (ctx->cur_conn.set.filter == NULL) + return "Missing setting: filter"; + if (ctx->cur_conn.set.attrlist == NULL) + return "Missing setting: attrlist"; + + if (ctx->cur_conn.set.encoding == NULL) { + /* DEFAULT: none, not all queries require this */ + ctx->cur_conn.set.encoding = "none"; + } + + if (!array_is_created(&ctx->cur_conn.set.ldap_fields)) { + /* no fields besides value. allocate the array anyway. */ + p_array_init(&ctx->cur_conn.set.ldap_fields, ctx->pool, 1); + if (strchr(ctx->cur_conn.set.pattern, '$') != NULL) + return "Missing fields for pattern variables"; + } + array_append(&ctx->set->conns, &ctx->cur_conn, 1); + memset(&ctx->cur_conn, 0, sizeof(ctx->cur_conn)); + return NULL; +} + +static const char * +ldap_parse_setting(const char *key, const char *value, + struct ldap_setting_parser_ctx *ctx) +{ + struct dict_ldap_map_field *field; + + switch (ctx->type) { + case SECTION_ROOT: + /* nothing in the root section that we want, all per-map */ +// From SQL, for comparison: +// if (strcmp(key, "connect") == 0) { +// ctx->set->connect = p_strdup(ctx->pool, value); +// return NULL; +// } + break; + case SECTION_MAP: + return parse_setting_from_defs(ctx->pool, + dict_ldap_setting_defs, + &ctx->cur_conn.set, key, value); + case SECTION_FIELDS: + if (*value != '$') { + return t_strconcat("Value is missing '$' for field: ", + key, NULL); + } + field = array_append_space(&ctx->cur_fields); + field->ldap_field = p_strdup(ctx->pool, key); + field->variable = p_strdup(ctx->pool, value + 1); + return NULL; + } + return t_strconcat("Unknown setting: ", key, NULL); +} + +static bool +ldap_parse_section(const char *type, const char *name ATTR_UNUSED, + struct ldap_setting_parser_ctx *ctx, const char **error_r) +{ + switch (ctx->type) { + case SECTION_ROOT: + if (type == NULL) + return FALSE; + if (strcmp(type, "map") == 0) { + array_clear(&ctx->cur_fields); + ctx->type = SECTION_MAP; + /* about to enter a new map, which holds ldap_connection->ldap_setting data. + init settings with defaults */ + libldap_set_default_ldap_settings(&ctx->cur_conn.set); + return TRUE; + } + break; + case SECTION_MAP: + if (type == NULL) { + ctx->type = SECTION_ROOT; + *error_r = dict_ldap_map_finish(ctx); + return FALSE; + } + if (strcmp(type, "fields") == 0) { + ctx->type = SECTION_FIELDS; + return TRUE; + } + break; + case SECTION_FIELDS: + if (type == NULL) { + ctx->type = SECTION_MAP; + *error_r = dict_ldap_fields_map(ctx); + return FALSE; + } + break; + } + *error_r = t_strconcat("Unknown section: ", type, NULL); + return FALSE; +} + +/* LDAP vs SQL: LDAP has no settings, all in the map section */ +struct dict_ldap_settings * +dict_ldap_settings_read(pool_t pool, const char *path) +{ + struct ldap_setting_parser_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + ctx.pool = pool; + ctx.set = p_new(pool, struct dict_ldap_settings, 1); + t_array_init(&ctx.cur_fields, 16); + p_array_init(&ctx.set->conns, pool, 8); + + if (!settings_read(path, NULL, ldap_parse_setting, ldap_parse_section, &ctx)) + return NULL; + + return ctx.set; +} diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib-dict/dict-ldap-settings.h dovecot-1.2.10/src/lib-dict/dict-ldap-settings.h --- ORIG/dovecot-1.2.10/src/lib-dict/dict-ldap-settings.h 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-1.2.10/src/lib-dict/dict-ldap-settings.h 2010-02-05 07:19:44.000000000 +0000 @@ -0,0 +1,49 @@ +#ifndef DICT_LDAP_SETTINGS_H +#define DICT_LDAP_SETTINGS_H + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "settings.h" +#include "lib-ldap.h" +#include "dict-ldap-settings.h" + +#include + +/* difference vs dict-sql-settings.h + * 1. no root fields (like "connect" for SQL) + * 2. many potential maps, each map has its own fields from ldap_settings struct (see: lib-ldap/lib-ldap.h) + * 3. there is no dict_ldap_map (as with dict-sql-settings.h), use an expanded ldap_settings struct instead + */ + +enum ldap_section_type { + SECTION_ROOT = 0, + SECTION_MAP, + SECTION_FIELDS +}; + +struct dict_ldap_map_field { + const char *ldap_field; + const char *variable; +}; + +struct ldap_setting_parser_ctx { + pool_t pool; + struct dict_ldap_settings *set; + enum ldap_section_type type; + + struct ldap_connection cur_conn; + ARRAY_DEFINE(cur_fields, struct dict_ldap_map_field); +}; + +struct dict_ldap_settings { + unsigned int max_field_count; + ARRAY_DEFINE(conns, struct ldap_connection); +}; + +// FIXME: resolve settings differences between dict-ldap & db-ldap (auth, dovecot-ldap.conf) +// struct setting_def dict_ldap_setting_defs[] >> moved to dict-ldap-settings.c + +struct dict_ldap_settings * +dict_ldap_settings_read(pool_t pool, const char *path); +#endif diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib-dict/dict-ldap.c dovecot-1.2.10/src/lib-dict/dict-ldap.c --- ORIG/dovecot-1.2.10/src/lib-dict/dict-ldap.c 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-1.2.10/src/lib-dict/dict-ldap.c 2010-02-11 14:58:26.000000000 +0000 @@ -0,0 +1,324 @@ +/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */ + +#include "dict-ldap.h" + +#define LDAP_MODULE_NAME "ldap" + +/* ldap_conn_init + * - initialize ldap connections, dict->set->conns[]->conn + * - remember, each dict-ldap map can use a different set of ldap_settings + * - uses overloaded lib-ldap/lib-ldap.c:libldap_init(struct ldap_connection *conn) + * FIXME: + * - conns[i]->conn should be initialized with defaults first? + */ +static int +ldap_conn_init( + struct ldap_dict *dict) +{ + struct ldap_connection *conn; + const struct ldap_connection *conns; + pool_t pool; + unsigned int i, count; + int ret=1; + + // FIXME: 2.0 has array_foreach_modifiable, 1.x doesn't. + conns = array_get_modifiable(&dict->dls->conns, &count); + for (i=0; ipool = pool; + conn->refcount = 1; + conn->conn_state = LDAP_CONN_STATE_DISCONNECTED; + conn->default_bind_msgid = -1; + conn->fd = -1; + + if (libldap_init(conn) == NULL) { + i_error("ldap conn init: failed to initialize map number '%d'.", i); + ret--; + } + + } + + return ret; +} + +/* ldap_dict_init + * - dict RO + */ +static struct dict * +ldap_dict_init( + struct dict *dict_driver, + const char *uri, + enum dict_data_type value_type, + const char *username, + const char *base_dir) +{ + + struct ldap_dict *dict; + pool_t pool; + + pool = pool_alloconly_create("ldap dict", 2048); + dict = p_new(pool, struct ldap_dict, 1); + dict->pool = pool; + dict->dict = *dict_driver; + dict->username = p_strdup(pool, username); + + /* settings: dict-ldap-settings.c calls lib-settings/settings.c + * dict->conns is an array of ldap_connection + */ + dict->dls = dict_ldap_settings_read(pool, uri); + if (dict->dls == NULL) { + pool_unref(&pool); + return NULL; + } + + ldap_conn_init(dict); + return &dict->dict; +} + +/* ldap_dict_deinit + * - dict RO + */ +static void +ldap_dict_deinit( + struct dict *_dict) +{ + struct ldap_dict *dict = (struct ldap_dict *)_dict; + // FIXME: incomplete. eg: ldap_conn_deinit() ? + pool_unref(&dict->pool); +} + + +/* ldap_dict_lookup + * - dict RO + */ +static int +ldap_dict_lookup( + struct dict *_dict, + pool_t pool, + const char *key, + const char **value_r) +{ + struct ldap_dict *dict = (struct ldap_dict *)_dict; + struct ldap_connection *conn = NULL; + struct ldap_result_context *rctx; + struct ldap_request *req; + struct ldap_request_search *sreq; + const struct var_expand_table *vars; + const struct ldap_connection *conns; + string_t *str; + LDAPMessage *res; + unsigned int count, i; + int ret; + + // Find the conn where key==pattern + conns = array_get(&dict->dls->conns, &count); + for (i=0; iconn_state = LDAP_CONN_STATE_DISCONNECTED; // FIXME: init should do this + + /* STEP 1: CONNECT */ + ret = libldap_connect_s(conn); + + if (ret != 0) { + i_error("ldap_dict_lookup: connection failure: %s", libldap_get_error(conn)); + *value_r = NULL; + return -1; + } + + /* STEP 2: Build search request, expand filter with username data (for %u -> user@domain) */ + req = libldap_init_request(LDAP_REQUEST_TYPE_SEARCH_S); + sreq = libldap_init_request_search_s(req, conn->set.base, conn->set.filter, conn->set.attrlist); + + vars = libldap_request_search_var_expand_string(sreq, dict->username); + str = t_str_new(512); + var_expand( str, conn->set.filter, vars); + sreq->filter = p_strdup(sreq->request.pool, str_c(str)); + str_free(&str); + + /* STEP 3: search */ + ret = libldap_search_s(conn, sreq, &rctx); + + /* STEP 4: get first value. API say:s key<>value, not: key<>val1,val2,... */ + /* use calling pool, value can persist outside this context */ + *value_r = p_strdup(pool, libldap_result_context_first_value(rctx)); + + /* STEP 5: clean up */ + libldap_unref_request(req); + libldap_unref_result_context(rctx); + + // FIXME: maybe there should be a warning for >1 results? + i_debug("ldap_dict_lookup: FOUND: username: '%s' key: '%s' value: '%s'", + dict->username, key, *value_r); + return ret; + } + } + if (conn == NULL) { + i_debug("ldap_dict_lookup: NOTFOUND: username: '%s' key: '%s'", + dict->username, key); + *value_r = NULL; + return 0; + } + + // Should not reach + i_unreached(); + *value_r = NULL; + return -1; +} + +/* ldap_dict_iterate_init + * - dict RO + */ +static struct dict_iterate_context * +ldap_dict_iterate_init( + struct dict *_dict, + const char *path, + enum dict_iterate_flags flags) +{ + struct ldap_dict *dict = (struct ldap_dict *)_dict; + i_error("ldap_dict_iterate_init: not implemented"); + + // FIXME + return NULL; +} + +/* ldap_dict_iterate + * - dict RO + */ +static int +ldap_dict_iterate( + struct dict_iterate_context *_ctx, + const char **key_r, + const char **value_r) +{ + i_error("ldap_dict_iterate: not implemented"); + + // FIXME + return -1; +} + +/* ldap_dict_iterate_deinit + * - dict RO + */ +static void +ldap_dict_iterate_deinit( + struct dict_iterate_context *_ctx) +{ + i_error("ldap_dict_iterate_deinit: not implemented"); +} + +/* ldap_dict_transaction_init + * - dict RW + */ +struct dict_transaction_context * +ldap_dict_transaction_init( + struct dict *_dict); + +/* ldap_dict_transaction_commit + * - dict RW + */ +static int +ldap_dict_transaction_commit( + struct dict_transaction_context *_ctx, + bool async ATTR_UNUSED, + dict_transaction_commit_callback_t *callback, + void *context); + +/* ldap_dict_transaction_rollback + * - dict RW + */ +static void +ldap_dict_transaction_rollback( + struct dict_transaction_context *_ctx); + +/* ldap_dict_set + * - dict RW + */ +static void +ldap_dict_set( + struct dict_transaction_context *_ctx, + const char *key, + const char *value); + +/* ldap_dict_unset + * - dict RW + */ +static void +ldap_dict_unset( + struct dict_transaction_context *_ctx, + const char *key); + +/* ldap_dict_atomic_inc + * - dict RW + */ +static void +ldap_dict_atomic_inc( + struct dict_transaction_context *_ctx, + const char *key, + long long diff); + + +/* register the dict type + see dict-private.h + name, struct dict_vfuncs +*/ +static struct dict dict_driver_ldap = { + MEMBER(name) LDAP_MODULE_NAME, + +/* + { + ldap_dict_init, // init + ldap_dict_deinit, // deinit + NULL, // wait + ldap_dict_lookup, // lookup + ldap_dict_iterate_init, // iterate_init + ldap_dict_iterate, // iterate + ldap_dict_iterate_deinit, // iterate_deinit + ldap_dict_transaction_init, // transaction_init + ldap_dict_transaction_commit, // transaction_commit + ldap_dict_transaction_rollback, // transaction_rollback + ldap_dict_set, // set + ldap_dict_unset, // unset + ldap_dict_atomic_inc // atomic_inc + } +*/ + +// bare minimum for lookup only? + { + ldap_dict_init, // init + ldap_dict_deinit, // deinit + NULL, // wait + ldap_dict_lookup, // lookup + ldap_dict_iterate_init, // iterate_init + ldap_dict_iterate, // iterate + ldap_dict_iterate_deinit, // iterate_deinit + NULL, // transaction_init + NULL, // transaction_commit + NULL, // transaction_rollback + NULL, // set + NULL, // unset + NULL // atomic_inc + } +}; + + +/* called from dict-drivers-register.c + * - generated by Makefile (see Makefile.am) + */ +void dict_ldap_register(void) +{ + // FIXME: should not happen unless --with-ldap ? + dict_driver_register(&dict_driver_ldap); +} + +void dict_ldap_unregister(void) +{ + dict_driver_unregister(&dict_driver_ldap); +} + diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib-dict/dict-ldap.h dovecot-1.2.10/src/lib-dict/dict-ldap.h --- ORIG/dovecot-1.2.10/src/lib-dict/dict-ldap.h 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-1.2.10/src/lib-dict/dict-ldap.h 2010-02-05 07:19:44.000000000 +0000 @@ -0,0 +1,32 @@ +#ifndef DICT_LDAP_H +#define DICT_LDAP_H + +#include "lib.h" +#include "module-dir.h" +#include "array.h" +#include "istream.h" +#include "str.h" +#include "dict.h" +#include "dict-client.h" +#include "dict-ldap-settings.h" +#include "dict-private.h" +#include "lib-ldap.h" +#include "settings.h" + +#include + +struct ldap_dict { + struct dict dict; + + pool_t pool; + const char *username; + + // FIXME: no need for anything other than the conns? (ldap_connection). impact on dict-ldap-settings.h:struct dict_ldap_settings + struct dict_ldap_settings *dls; +}; + +void dict_ldap_register(void); +void dict_ldap_unregister(void); + +#endif + diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib-ldap/Makefile.am dovecot-1.2.10/src/lib-ldap/Makefile.am --- ORIG/dovecot-1.2.10/src/lib-ldap/Makefile.am 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-1.2.10/src/lib-ldap/Makefile.am 2010-02-05 07:19:44.000000000 +0000 @@ -0,0 +1,21 @@ +noinst_LIBRARIES = libldap.a + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-settings + +libldap_a_LIBADD = \ + ../lib/liblib.a \ + ../lib-settings/libsettings.a +libldap_a_SOURCES = \ + lib-ldap.c + +headers = \ + lib-ldap.h + +if INSTALL_HEADERS + pkginc_libdir=$(pkgincludedir)/src/lib-ldap + pkginc_lib_HEADERS = $(headers) +else + noinst_HEADERS = $(headers) +endif diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib-ldap/lib-ldap.c dovecot-1.2.10/src/lib-ldap/lib-ldap.c --- ORIG/dovecot-1.2.10/src/lib-ldap/lib-ldap.c 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-1.2.10/src/lib-ldap/lib-ldap.c 2010-02-11 14:56:31.000000000 +0000 @@ -0,0 +1,816 @@ +/* see notes at the top of lib-ldap.h */ +#include "lib-ldap.h" + +#define MASTER_SOCKET_FD 0 +#define CLIENT_LISTEN_FD 3 +#define WORKER_SERVER_FD 4 + +/* functions */ + +static int +deref2str(const char *str) +{ + if (strcasecmp(str, "never") == 0) + return LDAP_DEREF_NEVER; + if (strcasecmp(str, "searching") == 0) + return LDAP_DEREF_SEARCHING; + if (strcasecmp(str, "finding") == 0) + return LDAP_DEREF_FINDING; + if (strcasecmp(str, "always") == 0) + return LDAP_DEREF_ALWAYS; + + i_fatal("LDAP: Unknown deref option '%s'", str); +} + +static int +scope2str(const char *str) +{ + if (strcasecmp(str, "base") == 0) + return LDAP_SCOPE_BASE; + if (strcasecmp(str, "onelevel") == 0) + return LDAP_SCOPE_ONELEVEL; + if (strcasecmp(str, "subtree") == 0) + return LDAP_SCOPE_SUBTREE; + + i_fatal("LDAP: Unknown scope option '%s'", str); +} + +#ifdef OPENLDAP_TLS_OPTIONS +static int +tls_require_cert2str(const char *str) +{ + if (strcasecmp(str, "never") == 0) + return LDAP_OPT_X_TLS_NEVER; + if (strcasecmp(str, "hard") == 0) + return LDAP_OPT_X_TLS_HARD; + if (strcasecmp(str, "demand") == 0) + return LDAP_OPT_X_TLS_DEMAND; + if (strcasecmp(str, "allow") == 0) + return LDAP_OPT_X_TLS_ALLOW; + if (strcasecmp(str, "try") == 0) + return LDAP_OPT_X_TLS_TRY; + + i_fatal("LDAP: Unknown tls_require_cert value '%s'", str); +} +#endif + +void +libldap_set_default_ldap_settings(struct ldap_settings *set) +{ + S_MEMBER(set, pattern) NULL; + S_MEMBER(set, filter) NULL; + S_MEMBER(set, attrlist) NULL; + S_MEMBER(set, encoding) NULL; + + S_MEMBER(set, hosts) NULL; + S_MEMBER(set, uris) NULL; + S_MEMBER(set, dn) NULL; + S_MEMBER(set, dnpass) NULL; + S_MEMBER(set, auth_bind) FALSE; + S_MEMBER(set, auth_bind_userdn) NULL; + S_MEMBER(set, tls) FALSE; + S_MEMBER(set, sasl_bind) FALSE; + S_MEMBER(set, sasl_mech) NULL; + S_MEMBER(set, sasl_realm) NULL; + S_MEMBER(set, sasl_authz_id) NULL; + S_MEMBER(set, tls_ca_cert_file) NULL; + S_MEMBER(set, tls_ca_cert_dir) NULL; + S_MEMBER(set, tls_cert_file) NULL; + S_MEMBER(set, tls_key_file) NULL; + S_MEMBER(set, tls_cipher_suite) NULL; + S_MEMBER(set, tls_require_cert) NULL; + S_MEMBER(set, deref) "never"; + S_MEMBER(set, scope) "subtree"; + S_MEMBER(set, base) NULL; + S_MEMBER(set, ldap_version) 3; + S_MEMBER(set, debug_level) "0"; + S_MEMBER(set, ldaprc_path) ""; + S_MEMBER(set, default_pass_scheme) "crypt"; +} + +static int libldap_bind(struct ldap_connection *conn); +static void libldap_conn_close(struct ldap_connection *conn); + +// FIXME: static const char * +const char * +libldap_conn_state2str(int state) { + switch (state) { + case LDAP_CONN_STATE_DISCONNECTED: + return "LDAP_CONN_STATE_DISCONNECTED"; + case LDAP_CONN_STATE_BINDING: + return "LDAP_CONN_STATE_BINDING"; + case LDAP_CONN_STATE_BOUND_AUTH: + return "LDAP_CONN_STATE_BOUND_AUTH"; + case LDAP_CONN_STATE_BOUND_DEFAULT: + return "LDAP_CONN_STATE_BOUND_DEFAULT"; + } + return "LDAP_CONN_STATE_UNKNOWN"; +} + +static int +libldap_get_errno(struct ldap_connection *conn) +{ + int ret, err; + + ret = ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, (void *) &err); + if (ret != LDAP_SUCCESS) { + i_error("LDAP: Can't get error number: %s", + ldap_err2string(ret)); + return LDAP_UNAVAILABLE; + } + + return err; +} + +static void +libldap_conn_reconnect(struct ldap_connection *conn) +{ + libldap_conn_close(conn); + if (libldap_connect(conn) < 0) + libldap_conn_close(conn); +} + +const char * +libldap_get_error(struct ldap_connection *conn) +{ + const char *ret; + char *str = NULL; + + ret = ldap_err2string(libldap_get_errno(conn)); + + ldap_get_option(conn->ld, LDAP_OPT_ERROR_STRING, (void *)&str); + if (str != NULL) { + ret = t_strconcat(ret, ", ", str, NULL); + ldap_memfree(str); + } + ldap_set_option(conn->ld, LDAP_OPT_ERROR_STRING, NULL); + return ret; +} + + +static int +libldap_handle_error(struct ldap_connection *conn) +{ + int err = libldap_get_errno(conn); + + switch (err) { + case LDAP_SUCCESS: + i_unreached(); + case LDAP_SIZELIMIT_EXCEEDED: + case LDAP_TIMELIMIT_EXCEEDED: + case LDAP_NO_SUCH_ATTRIBUTE: + case LDAP_UNDEFINED_TYPE: + case LDAP_INAPPROPRIATE_MATCHING: + case LDAP_CONSTRAINT_VIOLATION: + case LDAP_TYPE_OR_VALUE_EXISTS: + case LDAP_INVALID_SYNTAX: + case LDAP_NO_SUCH_OBJECT: + case LDAP_ALIAS_PROBLEM: + case LDAP_INVALID_DN_SYNTAX: + case LDAP_IS_LEAF: + case LDAP_ALIAS_DEREF_PROBLEM: + case LDAP_FILTER_ERROR: + /* invalid input */ + return -1; + case LDAP_SERVER_DOWN: + case LDAP_TIMEOUT: + case LDAP_UNAVAILABLE: + case LDAP_BUSY: +#ifdef LDAP_CONNECT_ERROR + case LDAP_CONNECT_ERROR: +#endif + case LDAP_LOCAL_ERROR: + case LDAP_INVALID_CREDENTIALS: + default: + /* connection problems */ + libldap_conn_reconnect(conn); + return 0; + } +} + +static int +libldap_connect_finish(struct ldap_connection *conn, int ret) +{ + if (ret == LDAP_SERVER_DOWN) { + i_error("LDAP: Can't connect to server: %s", + conn->set.uris != NULL ? + conn->set.uris : conn->set.hosts); + return -1; + } + if (ret != LDAP_SUCCESS) { + i_error("LDAP: binding failed (dn %s): %s", + conn->set.dn == NULL ? "(none)" : conn->set.dn, + libldap_get_error(conn)); + return -1; + } + + conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT; + return 0; +} + + +#ifdef HAVE_LDAP_SASL +static int +sasl_interact(LDAP *ld ATTR_UNUSED, unsigned flags ATTR_UNUSED, + void *defaults, void *interact) +{ + struct libldap_sasl_bind_context *context = defaults; + sasl_interact_t *in; + const char *str; + + for (in = interact; in->id != SASL_CB_LIST_END; in++) { + switch (in->id) { + case SASL_CB_GETREALM: + str = context->realm; + break; + case SASL_CB_AUTHNAME: + str = context->authcid; + break; + case SASL_CB_USER: + str = context->authzid; + break; + case SASL_CB_PASS: + str = context->passwd; + break; + default: + str = NULL; + break; + } + if (str != NULL) { + in->len = strlen(str); + in->result = str; + } + + } + return LDAP_SUCCESS; +} +#endif + +static int +libldap_request_bind_s(struct ldap_connection *conn) +{ + i_assert(conn->conn_state != LDAP_CONN_STATE_BINDING); + i_assert(conn->default_bind_msgid == -1); + + ldap_bind_s(conn->ld, conn->set.dn, conn->set.dnpass, + LDAP_AUTH_SIMPLE); + /* ldap_bind_s indicates error vi ld_errno, no useful return */ + if (libldap_get_errno(conn) != LDAP_SUCCESS) { + if (libldap_connect_finish(conn, libldap_get_errno(conn)) < 0) { + /* lost connection, close it */ + libldap_conn_close(conn); + } + return -1; + } + + conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT; + return 0; +} + +static void +libldap_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 +libldap_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 +libldap_set_opt_str(struct ldap_connection *conn, int opt, const char *value, + const char *optname) +{ + if (value != NULL) + libldap_set_opt(conn, opt, value, optname, value); +} + +static void +libldap_set_tls_options(struct ldap_connection *conn) +{ + if (!conn->set.tls) + return; + +#ifdef OPENLDAP_TLS_OPTIONS + libldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CACERTFILE, + conn->set.tls_ca_cert_file, "tls_ca_cert_file"); + libldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CACERTDIR, + conn->set.tls_ca_cert_dir, "tls_ca_cert_dir"); + libldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CERTFILE, + conn->set.tls_cert_file, "tls_cert_file"); + libldap_set_opt_str(NULL, LDAP_OPT_X_TLS_KEYFILE, + conn->set.tls_key_file, "tls_key_file"); + libldap_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); + libldap_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 +libldap_set_options(struct ldap_connection *conn) +{ + unsigned int ldap_version; + int value; + + libldap_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) { + libldap_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; + libldap_set_opt(conn, LDAP_OPT_PROTOCOL_VERSION, &ldap_version, + "protocol_version", dec2str(ldap_version)); + libldap_set_tls_options(conn); +} + +int +libldap_connect_s(struct ldap_connection *conn) +{ + int ret; + + if (conn->conn_state != LDAP_CONN_STATE_DISCONNECTED) { + return 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); + + libldap_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 libldap_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 (libldap_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 (libldap_request_bind_s(conn) < 0) + return -1; + } + + libldap_get_fd(conn); + return 0; +} + +static void +libldap_conn_close(struct ldap_connection *conn) +{ + conn->conn_state = LDAP_CONN_STATE_DISCONNECTED; + conn->default_bind_msgid = -1; + + if (conn->ld != NULL) { + ldap_unbind(conn->ld); + conn->ld = NULL; + } + conn->fd = -1; + + if (conn->io != NULL) { + /* the fd may have already been closed before ldap_unbind(), + so we'll have to use io_remove_closed(). */ + io_remove_closed(&conn->io); + } +} + +// this is what takes '(uid=%u)' and changes it to '(uid=someperson@domain.com)' +// uses libldap_escape only +struct var_expand_table * +libldap_request_search_var_expand_string(struct ldap_request_search *request_search, + const char *string) +{ + // limited set of vars for LDAP + static struct var_expand_table static_tab[] = { + { 'u', NULL, "user" }, + { 'n', NULL, "username" }, + { 'd', NULL, "domain" }, + { '\0', NULL, NULL } + }; + struct var_expand_table *tab; + + tab = t_malloc(sizeof(static_tab)); + memcpy(tab, static_tab, sizeof(static_tab)); + + tab[0].value = libldap_escape(string); + tab[1].value = libldap_escape(t_strcut(string, '@')); + tab[2].value = strchr(string, '@'); + if (tab[2].value != NULL) + tab[2].value = libldap_escape(tab[2].value+1); + + return tab; +} + +#define IS_LDAP_ESCAPED_CHAR(c) \ + ((c) == '*' || (c) == '(' || (c) == ')' || (c) == '\\') + +const char * +libldap_escape(const char *str) +{ + 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); +} + +// FIXME: not sure if I like *attrlist vs **attributes +struct ldap_request_search * +libldap_init_request_search_s(struct ldap_request *r, char *base, char *filter, char *attrlist) +{ + struct ldap_request_search *sr; + + sr = p_new(r->pool, struct ldap_request_search, 1); + sr->request = *r; + sr->request.type = LDAP_REQUEST_TYPE_SEARCH_S; + + sr->base = p_strdup(sr->request.pool, base); + sr->filter = p_strdup(sr->request.pool, filter); + + if (attrlist != NULL) { + sr->attributes = p_strsplit_spaces(sr->request.pool, attrlist, ","); + } + + return sr; +} + +struct ldap_request * +libldap_init_request(int type) +{ + struct ldap_request *r; + pool_t pool; + + pool = pool_alloconly_create("ldap request", 1024); + r = p_new(pool, struct ldap_request, 1); + r->pool = pool; + r->type = type; + + if ((type == LDAP_REQUEST_TYPE_BIND_S) || + (type == LDAP_REQUEST_TYPE_SEARCH_S)) + { + r->err = LDAP_SUCCESS; + r->timeout.tv_sec = LIBLDAP_SYNCH_TIMEOUT_SECS; + r->timeout.tv_usec = 0; + } + + return r; +} + +void +libldap_unref_request(struct ldap_request *request) +{ + pool_unref(&request->pool); +} + +static void +libldap_init_result_attribute_context(struct ldap_result_context *rctx, + struct ldap_result_entry_context *ectx, int no_attributes) +{ + struct ldap_result_attribute_context **actx; + int i; + + ectx->no_attributes = no_attributes; + if (no_attributes > 0) { + actx = p_new(rctx->pool, struct ldap_result_attribute_context *, no_attributes); + } + ectx->actx = actx; + + for(i = 0; iactx[i] = p_new(rctx->pool, struct ldap_result_attribute_context, no_attributes); + ectx->actx[i]->no_values = 0; + ectx->actx[i]->values = NULL; + } +} + +static void +libldap_init_result_entry_context(struct ldap_result_context *rctx, int no_entries) +{ + struct ldap_result_entry_context **ectx; + int i; + + rctx->no_entries = no_entries; + if (no_entries > 0) { + ectx = p_new(rctx->pool, struct ldap_result_entry_context *, no_entries); + } + rctx->ectx = ectx; + + for(i = 0; iectx[i] = p_new(rctx->pool, struct ldap_result_entry_context, 1); + rctx->ectx[i]->no_attributes = 0; + rctx->ectx[i]->actx = NULL; + } +} + +void +libldap_init_result_context(struct ldap_result_context **rctx) +{ + pool_t pool; + + pool = pool_alloconly_create("ldap result context", 2048); + *rctx = p_new(pool, struct ldap_result_context, 1); + (*rctx)->pool = pool; + (*rctx)->no_entries = 0; +} + +void +libldap_unref_result_context(struct ldap_result_context *rctx) +{ + pool_unref(&rctx->pool); +} + +/* number of attributes in an entry */ +static int +libldap_attribute_count(LDAP *ld, LDAPMessage *entry) +{ + BerElement *ber; + char *attr; + int iatt = 0; + + for (attr = ldap_first_attribute(ld, entry, &ber); attr != NULL; + attr = ldap_next_attribute(ld, entry, ber)) { + iatt++; + } + + ber_free(ber, 0); + return iatt; +} + +/* accross all entries in a ldap_result_context, how many values for attr X? */ +int +libldap_result_count_values_for_attr(struct ldap_result_context *rctx, char *attribute) +{ + int ret, e, a; + + i_assert(attribute != NULL); + ret = 0; + for (e = 0; e < rctx->no_entries; e++) { + for (a = 0; a < rctx->ectx[e]->no_attributes; a++) { + if (strcmp(attribute, rctx->ectx[e]->actx[a]->attr) == 0) { + ret += rctx->ectx[e]->actx[a]->no_values; + } + } + } + + return ret; +} + +/* return the first value found in the context only. not an iterator */ +char * +libldap_result_context_first_value(struct ldap_result_context *rctx) +{ + int e, a, v; + + for (e = 0; e < rctx->no_entries; e++) { + for (a = 0; a < rctx->ectx[e]->no_attributes; a++) { + for (v = 0; v < rctx->ectx[e]->actx[v]->no_values; v++) { + if (rctx->ectx[e]->actx[v]->values[v] != NULL) { + return rctx->ectx[e]->actx[v]->values[v]; + } + } + } + } + return NULL; +} + +/* search, synchronous + - requires a LDAP_REQUEST_TYPE_SEARCH_S ldap_request, with no errors (err=0) + - conn must be LDAP_CONN_STATE_BOUND_DEFAULT + - request->filter must already be expanded + i.e. conn->set.filter '(uid=%u)', request->filter '(uid=userfoo@domainbar.com)' + FIXME: + - the (*rctx)-> notation is gross, sorry + */ +int +libldap_search_s(struct ldap_connection *conn, struct ldap_request_search *sr, struct ldap_result_context **rctx) +{ + + LDAPMessage *res, *entry; + BerElement *ber; + int ecount, vcount, acount, ient, ival, iatt; + char *attr; + char **vals; + + /* must be bound before search */ + i_assert(sr->request.type == LDAP_REQUEST_TYPE_SEARCH_S); + i_assert(sr->request.err == 0); + i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT); + + /* attrsonly = 0, we want attribute values */ + sr->request.err = ldap_search_st(conn->ld, sr->base, scope2str(conn->set.scope), sr->filter, + sr->attributes, 0, &sr->request.timeout, &res); + + if (sr->request.err != LDAP_SUCCESS) { + i_error("libldap_search_s: ldap_search_st: base: %s, scope[%d]: %s, filter: %s, attrs: %s: %s", + sr->base, scope2str(conn->set.scope), conn->set.scope, sr->filter, conn->set.attrlist, ldap_err2string(sr->request.err)); + return -1; + } + + libldap_init_result_context(rctx); + + ecount = ldap_count_entries(conn->ld, res); + libldap_init_result_entry_context((*rctx), ecount); + + i_debug("libldap_search_s: base: %s, scope[%d]: %s, filter: %s, attrs: %s: %d entries returned", + sr->base, scope2str(conn->set.scope), conn->set.scope, sr->filter, conn->set.attrlist, ecount); + // STEP 1: cycle through entries + ient = -1; + for( entry = ldap_first_entry(conn->ld, res); entry != NULL; + entry = ldap_next_entry(conn->ld, entry)) + { + ient++; + // STEP 2: cycle through attributes for entry + iatt = -1; + acount = libldap_attribute_count(conn->ld, entry); + libldap_init_result_attribute_context((*rctx), (*rctx)->ectx[ient], acount); + + for ( attr = ldap_first_attribute(conn->ld, entry, &ber); attr != NULL; + attr = ldap_next_attribute(conn->ld, entry, ber)) + { + // STEP 3: cycle through values for attribute + // FIXME: handle binary? + iatt++; + vals = ldap_get_values(conn->ld, entry, attr); + vcount = ldap_count_values(vals); + + (*rctx)->ectx[ient]->actx[iatt]->attr = p_strdup((*rctx)->pool, attr); + + if (vcount < 0) { + (*rctx)->ectx[ient]->actx[iatt]->no_values = -1; + i_error("libldap_search_s: negative ldap_count_values(): %s", libldap_get_error(conn->ld)); + return -1; + } else if (vcount == 0) { + (*rctx)->ectx[ient]->actx[iatt]->no_values = vcount; + } else { + if (vals != NULL) { + (*rctx)->ectx[ient]->actx[iatt]->no_values = vcount; + (*rctx)->ectx[ient]->actx[iatt]->values = p_new((*rctx)->pool, char *, vcount); + for(ival=0; ival < vcount; ival++) { + // value loading happens here + (*rctx)->ectx[ient]->actx[iatt]->values[ival] = p_strdup((*rctx)->pool, vals[ival]); + } + } else { + (*rctx)->ectx[ient]->actx[iatt]->no_values = -1; + i_error("libldap_search_s: NULL ldap_get_values(), yet positive ldap_count_values()"); + return -1; + } + } + } + /* ldap_first_attribute() caller solely responsible for freeing BerElement */ + ber_free(ber,0); + } + return ecount; +} + +/* reads conn->set, which must be loaded by lib-config beforehand */ +struct ldap_connection * +libldap_init(struct ldap_connection *conn) +{ + const char *str; + + 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); + + return conn; +} + +void +libldap_unref(struct ldap_connection **_conn) +{ + struct ldap_connection *conn = *_conn; + + *_conn = NULL; + libldap_conn_close(conn); + + pool_unref(&conn->pool); +} + diff -urN --exclude-from=exclude-file ORIG/dovecot-1.2.10/src/lib-ldap/lib-ldap.h dovecot-1.2.10/src/lib-ldap/lib-ldap.h --- ORIG/dovecot-1.2.10/src/lib-ldap/lib-ldap.h 1970-01-01 00:00:00.000000000 +0000 +++ dovecot-1.2.10/src/lib-ldap/lib-ldap.h 2010-02-11 14:40:17.000000000 +0000 @@ -0,0 +1,288 @@ +#ifndef LIB_LDAP_H +#define LIB_LDAP_H + +/* based on auth/db-ldap.[ch] but for generic ldap queries only + (no auth, dict, etc depencies) + + notes: + - no async support for now, so no request queue + - no support for openldap <2.2 quirks + - functions are ldap_*, lib-ldap functions are libldap_* + + workflow: + - init the ldap_connection + conn->conn_state + */ + + +#include "lib.h" +#include "network.h" +#include "ioloop.h" +#include "array.h" +#include "hash.h" +#include "aqueue.h" +#include "str.h" +#include "env-util.h" +#include "var-expand.h" +#include "settings.h" + +#include +#include +#include + +#define HAVE_LDAP_SASL +#ifdef HAVE_SASL_SASL_H +# include +#elif defined (HAVE_SASL_H) +# include +#else +# undef HAVE_LDAP_SASL +#endif +#ifdef LDAP_OPT_X_TLS +# define OPENLDAP_TLS_OPTIONS +#endif +#if SASL_VERSION_MAJOR < 2 +# undef HAVE_LDAP_SASL +#endif + +#ifndef LDAP_SASL_QUIET +# define LDAP_SASL_QUIET 0 /* Doesn't exist in Solaris LDAP */ +#endif + +/* Solaris LDAP library doesn't have LDAP_OPT_SUCCESS */ +#ifndef LDAP_OPT_SUCCESS +# define LDAP_OPT_SUCCESS LDAP_SUCCESS +#endif + +/* Functions like ldap_bind() have been deprecated in OpenLDAP 2.3 + This define enables them until the code here can be refactored. + Update 2010/02: keep using this until all major UNIX/Linux distributions move up */ +#define LDAP_DEPRECATED 1 + +/* Maximum number of requests in queue. After this new requests are dropped. */ +//#define LIBLDAP_MAX_QUEUE_SIZE 1024 +/* Maximum number of pending requests before delaying new requests. */ +//#define LIBLDAP_MAX_PENDING_REQUESTS 128 +/* If LDAP connection is down, fail requests after waiting for this long. */ +//#define LIBLDAP_REQUEST_DISCONNECT_TIMEOUT_SECS 4 +/* If request is still in queue after this many seconds and other requests + have been replied, assume the request was lost and abort it. */ +//#define LIBLDAP_REQUEST_LOST_TIMEOUT_SECS 60 +/* If server disconnects us, don't reconnect if no requests have been sent + for this many seconds. */ +//#define LIBLDAP_IDLE_RECONNECT_SECS 60 +/* Timeout for synchronous operations */ +#define LIBLDAP_SYNCH_TIMEOUT_SECS 10 + +/* macros */ + +/* forward declarations */ +struct ldap_connection; +struct ldap_request; + +typedef void +libldap_search_callback_t(struct ldap_connection *conn, + struct ldap_request *request, + LDAPMessage *res); + +/* generic ldap server settings */ +struct ldap_settings { + const char *hosts; + const char *uris; + const char *dn; + const char *dnpass; + bool auth_bind; + const char *auth_bind_userdn; + + bool tls; + bool sasl_bind; + const char *sasl_mech; + const char *sasl_realm; + const char *sasl_authz_id; + + const char *tls_ca_cert_file; + const char *tls_ca_cert_dir; + const char *tls_cert_file; + const char *tls_key_file; + const char *tls_cipher_suite; + const char *tls_require_cert; + + const char *deref; + const char *scope; + const char *base; + unsigned int ldap_version; + + const char *ldaprc_path; + const char *debug_level; + + const char *user_attrs; + const char *user_filter; + const char *pass_attrs; + const char *pass_filter; + + const char *default_pass_scheme; + + /* ... */ + int ldap_deref, ldap_scope; + uid_t uid; + gid_t gid; + + /* dict ldap map support */ + const char *pattern; + const char *filter; + const char *attrlist; + const char *encoding; + + ARRAY_TYPE(const_string) ldap_fields; +}; + +enum ldap_request_type { + LDAP_REQUEST_TYPE_SEARCH_A, + LDAP_REQUEST_TYPE_SEARCH_S, + LDAP_REQUEST_TYPE_BIND_A, + LDAP_REQUEST_TYPE_BIND_S +}; + +enum ldap_connection_state { + /* Not connected */ + LDAP_CONN_STATE_DISCONNECTED, + /* Binding - either to default dn or doing auth bind */ + LDAP_CONN_STATE_BINDING, + /* Bound to auth dn */ + LDAP_CONN_STATE_BOUND_AUTH, + /* Bound to default dn */ + LDAP_CONN_STATE_BOUND_DEFAULT +}; + +struct ldap_request { + pool_t pool; + enum ldap_request_type type; + + /* sync: err is ldap_error */ + int err; + struct timeval timeout; + + /* async: msgid for sent requests, -1 if not sent. */ + int msgid; + time_t create_time; + libldap_search_callback_t *callback; + +}; + +struct ldap_request_search { + struct ldap_request request; + + const char *base; + const char *filter; + char **attributes; +}; + +struct ldap_request_bind { + struct ldap_request request; + + const char *dn; + const char *dnpass; +}; + +struct ldap_result_attribute_context { + int no_values; + char *attr; + char **values; +}; + +struct ldap_result_entry_context { + int no_attributes; + //FIXME: add dn here? + struct ldap_result_attribute_context **actx; +}; + +struct ldap_result_context { + int no_entries; + struct ldap_result_entry_context **ectx; + + pool_t pool; +}; + + +struct libldap_sasl_bind_context { + const char *authcid; + const char *passwd; + const char *realm; + const char *authzid; +}; + +struct ldap_connection { + struct ldap_connection *next; + + pool_t pool; + int refcount; + + char *config_path; + struct ldap_settings set; + + LDAP *ld; + enum ldap_connection_state conn_state; + int default_bind_msgid; + + int fd; + struct io *io; + struct timeout *to; +}; + +/* libldap public interface */ +int +libldap_connect_s(struct ldap_connection *conn); + +const char * +libldap_conn_state2str(int state); + +const char * +libldap_escape(const char *str); + +const char * +libldap_get_error(struct ldap_connection *conn); + +struct ldap_connection * +libldap_init(struct ldap_connection *conn); + +void +libldap_init_result_context(struct ldap_result_context **); + +struct ldap_request * +libldap_init_request(int type); + +struct ldap_request_search * +libldap_init_request_search_s(struct ldap_request *r, char *base, char *filter, char *attrlist); + +void +libldap_request(struct ldap_connection *conn, struct ldap_request *request); + +char * +libldap_result_context_first_value(struct ldap_result_context *rctx); + +int +libldap_result_count_values_for_attr(struct ldap_result_context *rctx, char *attribute); + +int +libldap_search_s(struct ldap_connection *conn, struct ldap_request_search *sr, struct ldap_result_context **rctx); + +void +libldap_set_attrs(struct ldap_connection *conn, char ***attr_names_r); + +void +libldap_set_default_ldap_settings(struct ldap_settings *set); + +void +libldap_unref(struct ldap_connection **conn); + +void +libldap_unref_request(struct ldap_request *request); + +void +libldap_unref_result_context(struct ldap_result_context *rctx); + +struct var_expand_table * +libldap_request_search_var_expand_string(struct ldap_request_search *request_search, + const char *string); + +#endif