passdb-ldap.c revision afa2af53ff06152efa65f6cc0e54fb1eb319d2fd
0c04407550130c0ea040b5675f2c214426b27718fuankg/* Copyright (C) 2003 Timo Sirainen */
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg#include "config.h"
0c04407550130c0ea040b5675f2c214426b27718fuankg#undef HAVE_CONFIG_H
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg#ifdef PASSDB_LDAP
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg#include "common.h"
0c04407550130c0ea040b5675f2c214426b27718fuankg#include "str.h"
0c04407550130c0ea040b5675f2c214426b27718fuankg#include "var-expand.h"
0662ed52e814f8f08ef0e09956413a792584eddffuankg#include "password-scheme.h"
0c04407550130c0ea040b5675f2c214426b27718fuankg#include "db-ldap.h"
0c04407550130c0ea040b5675f2c214426b27718fuankg#include "passdb.h"
0c04407550130c0ea040b5675f2c214426b27718fuankg#include "passdb-cache.h"
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg#include <ldap.h>
0c04407550130c0ea040b5675f2c214426b27718fuankg#include <stdlib.h>
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg/* using posixAccount */
0c04407550130c0ea040b5675f2c214426b27718fuankg#define DEFAULT_ATTRIBUTES "uid,userPassword"
44f575c8cb19a7a5cd61664a7848be6bc197df02fuankg
44f575c8cb19a7a5cd61664a7848be6bc197df02fuankgenum ldap_user_attr {
16b55a35cff91315d261d1baa776138af465c4e4fuankg ATTR_VIRTUAL_USER = 0,
0c04407550130c0ea040b5675f2c214426b27718fuankg ATTR_PASSWORD,
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg ATTR_COUNT
0c04407550130c0ea040b5675f2c214426b27718fuankg};
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankgstruct passdb_ldap_connection {
0c04407550130c0ea040b5675f2c214426b27718fuankg struct ldap_connection *conn;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg unsigned int *attrs;
0c04407550130c0ea040b5675f2c214426b27718fuankg char **attr_names;
0c04407550130c0ea040b5675f2c214426b27718fuankg};
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankgstruct passdb_ldap_request {
0c04407550130c0ea040b5675f2c214426b27718fuankg struct ldap_request request;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg enum passdb_credentials credentials;
0c04407550130c0ea040b5675f2c214426b27718fuankg union {
0c04407550130c0ea040b5675f2c214426b27718fuankg verify_plain_callback_t *verify_plain;
0c04407550130c0ea040b5675f2c214426b27718fuankg lookup_credentials_callback_t *lookup_credentials;
0c04407550130c0ea040b5675f2c214426b27718fuankg } callback;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg char password[1]; /* variable width */
0c04407550130c0ea040b5675f2c214426b27718fuankg};
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankgstatic struct passdb_ldap_connection *passdb_ldap_conn;
0c04407550130c0ea040b5675f2c214426b27718fuankgstatic char *passdb_ldap_cache_key;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankgstatic void handle_request(struct ldap_connection *conn,
0c04407550130c0ea040b5675f2c214426b27718fuankg struct ldap_request *request, LDAPMessage *res)
0c04407550130c0ea040b5675f2c214426b27718fuankg{
0c04407550130c0ea040b5675f2c214426b27718fuankg struct passdb_ldap_request *ldap_request =
0c04407550130c0ea040b5675f2c214426b27718fuankg (struct passdb_ldap_request *) request;
0c04407550130c0ea040b5675f2c214426b27718fuankg struct auth_request *auth_request = request->context;
0c04407550130c0ea040b5675f2c214426b27718fuankg enum passdb_result result;
0c04407550130c0ea040b5675f2c214426b27718fuankg LDAPMessage *entry;
0c04407550130c0ea040b5675f2c214426b27718fuankg BerElement *ber;
0c04407550130c0ea040b5675f2c214426b27718fuankg char *attr, **vals;
0c04407550130c0ea040b5675f2c214426b27718fuankg const char *user, *password, *scheme;
0c04407550130c0ea040b5675f2c214426b27718fuankg int ret;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg result = PASSDB_RESULT_USER_UNKNOWN;
0c04407550130c0ea040b5675f2c214426b27718fuankg user = auth_request->user;
0c04407550130c0ea040b5675f2c214426b27718fuankg password = NULL;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg if (res != NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg ret = ldap_result2error(conn->ld, res, 0);
0c04407550130c0ea040b5675f2c214426b27718fuankg if (ret != LDAP_SUCCESS) {
0c04407550130c0ea040b5675f2c214426b27718fuankg auth_request_log_error(auth_request, "ldap",
0c04407550130c0ea040b5675f2c214426b27718fuankg "ldap_search() failed: %s",
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_err2string(ret));
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg result = PASSDB_RESULT_INTERNAL_FAILURE;
0c04407550130c0ea040b5675f2c214426b27718fuankg res = NULL;
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg entry = res == NULL ? NULL : ldap_first_entry(conn->ld, res);
0c04407550130c0ea040b5675f2c214426b27718fuankg if (entry == NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg if (res != NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg auth_request_log_info(auth_request, "ldap",
0c04407550130c0ea040b5675f2c214426b27718fuankg "unknown user");
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg } else {
0c04407550130c0ea040b5675f2c214426b27718fuankg attr = ldap_first_attribute(conn->ld, entry, &ber);
0c04407550130c0ea040b5675f2c214426b27718fuankg while (attr != NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg vals = ldap_get_values(conn->ld, entry, attr);
0c04407550130c0ea040b5675f2c214426b27718fuankg if (vals != NULL && vals[0] != NULL &&
0c04407550130c0ea040b5675f2c214426b27718fuankg vals[1] == NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg if (strcasecmp(attr, passdb_ldap_conn->
0c04407550130c0ea040b5675f2c214426b27718fuankg attr_names[ATTR_PASSWORD]) == 0)
0c04407550130c0ea040b5675f2c214426b27718fuankg password = t_strdup(vals[0]);
ea1b70b7a14558cc058b9a1fc31d78afb093f529fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_value_free(vals);
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_memfree(attr);
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg attr = ldap_next_attribute(conn->ld, entry, ber);
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg if (password == NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg auth_request_log_error(auth_request, "ldap",
0c04407550130c0ea040b5675f2c214426b27718fuankg "No password in reply");
0c04407550130c0ea040b5675f2c214426b27718fuankg } else if (ldap_next_entry(conn->ld, entry) != NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg auth_request_log_error(auth_request, "ldap",
0c04407550130c0ea040b5675f2c214426b27718fuankg "Multiple password replies");
0c04407550130c0ea040b5675f2c214426b27718fuankg password = NULL;
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0662ed52e814f8f08ef0e09956413a792584eddffuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg /* LDAP result is freed now. we can check if auth_request is
0c04407550130c0ea040b5675f2c214426b27718fuankg even needed anymore */
0c04407550130c0ea040b5675f2c214426b27718fuankg if (!auth_request_unref(auth_request))
0c04407550130c0ea040b5675f2c214426b27718fuankg return;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg scheme = password_get_scheme(&password);
0c04407550130c0ea040b5675f2c214426b27718fuankg if (scheme == NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg scheme = conn->set.default_pass_scheme;
0c04407550130c0ea040b5675f2c214426b27718fuankg i_assert(scheme != NULL);
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg if (ldap_request->credentials != -1) {
0662ed52e814f8f08ef0e09956413a792584eddffuankg passdb_handle_credentials(result, ldap_request->credentials,
0c04407550130c0ea040b5675f2c214426b27718fuankg password, scheme,
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request->callback.lookup_credentials,
0c04407550130c0ea040b5675f2c214426b27718fuankg auth_request);
0c04407550130c0ea040b5675f2c214426b27718fuankg return;
0662ed52e814f8f08ef0e09956413a792584eddffuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg /* verify plain */
0c04407550130c0ea040b5675f2c214426b27718fuankg if (password == NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request->callback.verify_plain(result, auth_request);
0c04407550130c0ea040b5675f2c214426b27718fuankg return;
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg ret = password_verify(ldap_request->password, password, scheme, user);
0c04407550130c0ea040b5675f2c214426b27718fuankg if (ret < 0) {
0662ed52e814f8f08ef0e09956413a792584eddffuankg auth_request_log_error(auth_request, "ldap",
0c04407550130c0ea040b5675f2c214426b27718fuankg "Unknown password scheme %s", scheme);
0c04407550130c0ea040b5675f2c214426b27718fuankg } else if (ret == 0) {
0c04407550130c0ea040b5675f2c214426b27718fuankg auth_request_log_info(auth_request, "ldap",
0c04407550130c0ea040b5675f2c214426b27718fuankg "password mismatch");
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request->callback.verify_plain(ret > 0 ? PASSDB_RESULT_OK :
0c04407550130c0ea040b5675f2c214426b27718fuankg PASSDB_RESULT_PASSWORD_MISMATCH,
0c04407550130c0ea040b5675f2c214426b27718fuankg auth_request);
0c04407550130c0ea040b5675f2c214426b27718fuankg}
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankgstatic void ldap_lookup_pass(struct auth_request *auth_request,
0c04407550130c0ea040b5675f2c214426b27718fuankg struct ldap_request *ldap_request)
0c04407550130c0ea040b5675f2c214426b27718fuankg{
0c04407550130c0ea040b5675f2c214426b27718fuankg struct ldap_connection *conn = passdb_ldap_conn->conn;
0c04407550130c0ea040b5675f2c214426b27718fuankg const struct var_expand_table *vars;
0c04407550130c0ea040b5675f2c214426b27718fuankg const char **attr_names = (const char **)passdb_ldap_conn->attr_names;
0c04407550130c0ea040b5675f2c214426b27718fuankg const char *filter, *base;
0c04407550130c0ea040b5675f2c214426b27718fuankg string_t *str;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg vars = auth_request_get_var_expand_table(auth_request, ldap_escape);
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg str = t_str_new(512);
0c04407550130c0ea040b5675f2c214426b27718fuankg var_expand(str, conn->set.base, vars);
0c04407550130c0ea040b5675f2c214426b27718fuankg base = t_strdup(str_c(str));
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg if (conn->set.pass_filter == NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg filter = t_strdup_printf("(&(objectClass=posixAccount)(%s=%s))",
0c04407550130c0ea040b5675f2c214426b27718fuankg attr_names[ATTR_VIRTUAL_USER],
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_escape(auth_request->user));
0c04407550130c0ea040b5675f2c214426b27718fuankg } else {
0c04407550130c0ea040b5675f2c214426b27718fuankg var_expand(str, conn->set.pass_filter, vars);
0c04407550130c0ea040b5675f2c214426b27718fuankg filter = str_c(str);
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg
cf7ca2f9eaa6523fefcccba4287b91637391fb51fuankg auth_request_ref(auth_request);
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request->callback = handle_request;
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request->context = auth_request;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg auth_request_log_debug(auth_request, "ldap",
0c04407550130c0ea040b5675f2c214426b27718fuankg "base=%s scope=%s filter=%s fields=%s",
0c04407550130c0ea040b5675f2c214426b27718fuankg conn->set.base, conn->set.scope, filter,
0c04407550130c0ea040b5675f2c214426b27718fuankg t_strarray_join(attr_names, ","));
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg db_ldap_search(conn, base, conn->set.ldap_scope,
0c04407550130c0ea040b5675f2c214426b27718fuankg filter, passdb_ldap_conn->attr_names,
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request);
0c04407550130c0ea040b5675f2c214426b27718fuankg}
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankgstatic void
0c04407550130c0ea040b5675f2c214426b27718fuankgldap_verify_plain(struct auth_request *request, const char *password,
0c04407550130c0ea040b5675f2c214426b27718fuankg verify_plain_callback_t *callback)
0c04407550130c0ea040b5675f2c214426b27718fuankg{
0c04407550130c0ea040b5675f2c214426b27718fuankg struct ldap_connection *conn = passdb_ldap_conn->conn;
0c04407550130c0ea040b5675f2c214426b27718fuankg struct passdb_ldap_request *ldap_request;
0c04407550130c0ea040b5675f2c214426b27718fuankg enum passdb_result result;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg if (passdb_cache_verify_plain(request, passdb_ldap_cache_key, password,
0c04407550130c0ea040b5675f2c214426b27718fuankg conn->set.default_pass_scheme, &result)) {
0c04407550130c0ea040b5675f2c214426b27718fuankg callback(result, request);
0c04407550130c0ea040b5675f2c214426b27718fuankg return;
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request = i_malloc(sizeof(struct passdb_ldap_request) +
0c04407550130c0ea040b5675f2c214426b27718fuankg strlen(password));
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request->credentials = -1;
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request->callback.verify_plain = callback;
8ffac2c334103c0336602aaede650cb578611151fuankg strcpy(ldap_request->password, password);
8ffac2c334103c0336602aaede650cb578611151fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_lookup_pass(request, &ldap_request->request);
0c04407550130c0ea040b5675f2c214426b27718fuankg}
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankgstatic void ldap_lookup_credentials(struct auth_request *request,
0c04407550130c0ea040b5675f2c214426b27718fuankg enum passdb_credentials credentials,
0c04407550130c0ea040b5675f2c214426b27718fuankg lookup_credentials_callback_t *callback)
0c04407550130c0ea040b5675f2c214426b27718fuankg{
0c04407550130c0ea040b5675f2c214426b27718fuankg struct passdb_ldap_request *ldap_request;
0c04407550130c0ea040b5675f2c214426b27718fuankg const char *result, *scheme;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg if (passdb_cache_lookup_credentials(request, passdb_ldap_cache_key,
0c04407550130c0ea040b5675f2c214426b27718fuankg &result, &scheme)) {
0c04407550130c0ea040b5675f2c214426b27718fuankg if (scheme == NULL) {
0c04407550130c0ea040b5675f2c214426b27718fuankg scheme = passdb_ldap_conn->conn->set.
0c04407550130c0ea040b5675f2c214426b27718fuankg default_pass_scheme;
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg passdb_handle_credentials(result != NULL ? PASSDB_RESULT_OK :
0c04407550130c0ea040b5675f2c214426b27718fuankg PASSDB_RESULT_USER_UNKNOWN,
0c04407550130c0ea040b5675f2c214426b27718fuankg credentials, result, scheme,
0c04407550130c0ea040b5675f2c214426b27718fuankg callback, request);
0c04407550130c0ea040b5675f2c214426b27718fuankg return;
0c04407550130c0ea040b5675f2c214426b27718fuankg }
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request = i_new(struct passdb_ldap_request, 1);
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request->credentials = credentials;
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_request->callback.lookup_credentials = callback;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg ldap_lookup_pass(request, &ldap_request->request);
0c04407550130c0ea040b5675f2c214426b27718fuankg}
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankgstatic void passdb_ldap_preinit(const char *args)
0c04407550130c0ea040b5675f2c214426b27718fuankg{
0c04407550130c0ea040b5675f2c214426b27718fuankg struct ldap_connection *conn;
0c04407550130c0ea040b5675f2c214426b27718fuankg
0662ed52e814f8f08ef0e09956413a792584eddffuankg passdb_ldap_conn = i_new(struct passdb_ldap_connection, 1);
0c04407550130c0ea040b5675f2c214426b27718fuankg passdb_ldap_conn->conn = conn = db_ldap_init(args);
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankg db_ldap_set_attrs(conn, conn->set.pass_attrs ?
0c04407550130c0ea040b5675f2c214426b27718fuankg conn->set.pass_attrs : DEFAULT_ATTRIBUTES,
0c04407550130c0ea040b5675f2c214426b27718fuankg &passdb_ldap_conn->attrs,
0c04407550130c0ea040b5675f2c214426b27718fuankg &passdb_ldap_conn->attr_names);
0c04407550130c0ea040b5675f2c214426b27718fuankg passdb_ldap_cache_key = auth_cache_parse_key(conn->set.pass_filter);
0c04407550130c0ea040b5675f2c214426b27718fuankg}
0c04407550130c0ea040b5675f2c214426b27718fuankg
0c04407550130c0ea040b5675f2c214426b27718fuankgstatic void passdb_ldap_init(const char *args __attr_unused__)
0c04407550130c0ea040b5675f2c214426b27718fuankg{
0c04407550130c0ea040b5675f2c214426b27718fuankg (void)db_ldap_connect(passdb_ldap_conn->conn);
0c04407550130c0ea040b5675f2c214426b27718fuankg}
0c04407550130c0ea040b5675f2c214426b27718fuankg
0662ed52e814f8f08ef0e09956413a792584eddffuankgstatic void passdb_ldap_deinit(void)
0c04407550130c0ea040b5675f2c214426b27718fuankg{
0c04407550130c0ea040b5675f2c214426b27718fuankg db_ldap_unref(passdb_ldap_conn->conn);
i_free(passdb_ldap_cache_key);
i_free(passdb_ldap_conn);
}
struct passdb_module passdb_ldap = {
"ldap",
passdb_ldap_preinit,
passdb_ldap_init,
passdb_ldap_deinit,
ldap_verify_plain,
ldap_lookup_credentials
};
#endif