db-ldap.c revision 964c86de7158ccafdfe665853579d71232e2634e
8c294c1cd4d721818a59684cf7f2b36123f79163Stephen Gallagher/* Copyright (c) 2003-2011 Dovecot authors, see the included COPYING file */
8c294c1cd4d721818a59684cf7f2b36123f79163Stephen Gallagher#if defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher# define LDAP_SASL_QUIET 0 /* Doesn't exist in Solaris LDAP */
8a5e793a0576250da80371e53aa3e7eba15cdb63Sumit Bose/* Older versions may require calling ldap_result() twice */
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke/* Solaris LDAP library doesn't have LDAP_OPT_SUCCESS */
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer const char *const *static_attrs;
dbfc407eef1d9ba2469687c3ffbe7fd8bb111d94Jakub Hrozek#define DEF_STR(name) DEF_STRUCT_STR(name, ldap_settings)
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher#define DEF_INT(name) DEF_STRUCT_INT(name, ldap_settings)
4b6a0d0b3d42e5fdb457f47d9adfa5e66b160256Stephen Gallagher#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, ldap_settings)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic struct ldap_settings default_ldap_settings = {
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek .user_attrs = "homeDirectory=home,uidNumber=uid,gidNumber=gid",
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta .user_filter = "(&(objectClass=posixAccount)(uid=%u))",
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta .pass_attrs = "uid=user,userPassword=password",
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta .pass_filter = "(&(objectClass=posixAccount)(uid=%u))",
cb4d5b588e704114b7090678752d33512baa718eJakub Hrozek .iterate_filter = "(objectClass=posixAccount)",
f3a25949de81f80c136bb073e4a8f504b080c20cJakub Hrozekstatic struct ldap_connection *ldap_connections = NULL;
f3a25949de81f80c136bb073e4a8f504b080c20cJakub Hrozekstatic int db_ldap_bind(struct ldap_connection *conn);
45726939a48e605b0166521f94300ae04981a3a7Sumit Bosestatic void db_ldap_conn_close(struct ldap_connection *conn);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_fatal("LDAP: Unknown scope option '%s'", str);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int tls_require_cert2str(const char *str)
bbaba8b3ef9bc101863b8687f234f4ee956caacdPavel Březina i_fatal("LDAP: Unknown tls_require_cert value '%s'", str);
bbaba8b3ef9bc101863b8687f234f4ee956caacdPavel Březinastatic int ldap_get_errno(struct ldap_connection *conn)
4bd20c075f0f187db0181dc53d00ab6cd47fdb4dJakub Hrozek ret = ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, (void *) &err);
2a9af1f71887f02935e2fb6ad5023afba5b6d43eSumit Boseconst char *ldap_get_error(struct ldap_connection *conn)
461da2984c747708e8badd27fa55ef879f40e712Pallavi Jha const char *ret;
939246537b0b9a4af6862c513d3919501ad57d92Sumit Bose ldap_get_option(conn->ld, LDAP_OPT_ERROR_STRING, (void *)&str);
7caf7ed4f2eae1ec1c0717b4ee6ce78bdacd5926Jakub Hrozek ldap_set_option(conn->ld, LDAP_OPT_ERROR_STRING, NULL);
50b8a36b0932a510e825ed1ad8103f81ead2b7d8Pavel Reichlstatic void ldap_conn_reconnect(struct ldap_connection *conn)
802385896dc1c4e7b8bbd40dcfe3cd131f68e696Sumit Bosestatic int ldap_handle_error(struct ldap_connection *conn)
f92ace4a52602e8c38a34f2392bec3deeac2ddddJakub Hrozek /* invalid input */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* connection problems */
fb3c5cdfcda069a5fbeb7b9d200c0881911364b8Jakub Hrozekstatic int db_ldap_request_bind(struct ldap_connection *conn,
18372712592b30638772afb5b7e15bfca92c2058Lukas Slebodnik i_assert(request->type == LDAP_REQUEST_TYPE_BIND);
18372712592b30638772afb5b7e15bfca92c2058Lukas Slebodnik i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_AUTH ||
18372712592b30638772afb5b7e15bfca92c2058Lukas Slebodnik conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
18372712592b30638772afb5b7e15bfca92c2058Lukas Slebodnik request->msgid = ldap_bind(conn->ld, brequest->dn,
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher auth_request_log_error(request->auth_request, "ldap",
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher "ldap_bind(%s) failed: %s",
bf5a808fa92007c325c3996e79694badfab201d4Stephen Gallagher /* broken request, remove it */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher conn->conn_state = LDAP_CONN_STATE_BINDING;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int db_ldap_request_search(struct ldap_connection *conn,
a2e417f38c57ed87c956ddcecf4dafca93842b65Lukas Slebodnik i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ldap_search(conn->ld, srequest->base, conn->set.ldap_scope,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher srequest->filter, srequest->attributes, 0);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher auth_request_log_error(request->auth_request, "ldap",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "ldap_search(%s) parsing failed: %s",
8a5e793a0576250da80371e53aa3e7eba15cdb63Sumit Bose /* broken request, remove it */
90fd1bbd6035cdab46faa3a695a2fb2be6508b17Sumit Bosestatic bool db_ldap_request_queue_next(struct ldap_connection *conn)
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke struct ldap_request *const *requestp, *request;
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke /* connecting may call db_ldap_connect_finish(), which gets us back
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke here. so do the connection before checking the request queue. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (conn->pending_count == aqueue_count(conn->request_queue)) {
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik /* no non-pending requests */
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik if (conn->pending_count > DB_LDAP_MAX_PENDING_REQUESTS) {
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik /* wait until server has replied to some requests */
4a5a18f489f4d19aa0571528a7f0c7a8d35ac83fLukas Slebodnik /* we can't do binds until all existing requests are finished */
1467daed400d6c186bd0c99c057c42e764309ff3Stephen Gallagher /* wait until we're in bound state */
18372712592b30638772afb5b7e15bfca92c2058Lukas Slebodnik if (request->type == LDAP_REQUEST_TYPE_BIND)
6a6a821866091e0f722808566c25b951aa346d7cStephen Gallagher /* bind to default dn first */
3ce85a5f5264e7118beb6524e120fd8b53a13da4Nikolai Kondrashov /* we can do anything in this state */
cbff3fcdce5b0377a62fbe74f32e476efbf7ca9cNikolai Kondrashov ret = db_ldap_request_search(conn, request);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher } else if (ret < 0) {
17f08cbd0f909181536b93d6c12c7cd69995f09eSumit Bose /* disconnected */
3ce85a5f5264e7118beb6524e120fd8b53a13da4Nikolai Kondrashov /* broken request, remove from queue */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherdb_ldap_check_limits(struct ldap_connection *conn, struct ldap_request *request)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct ldap_request *const *first_requestp;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher count = aqueue_count(conn->request_queue);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher first_requestp = array_idx(&conn->request_array,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher secs_diff = ioloop_time - (*first_requestp)->create_time;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (secs_diff > DB_LDAP_REQUEST_LOST_TIMEOUT_SECS) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher auth_request_log_error(request->auth_request, "ldap",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "Connection appears to be hanging, reconnecting");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghervoid db_ldap_request(struct ldap_connection *conn,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int db_ldap_connect_finish(struct ldap_connection *conn, int ret)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_error("LDAP: binding failed (dn %s): %s",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher conn->set.dn == NULL ? "(none)" : conn->set.dn,
115de6d50f0d0bdd5745a5d8eb0d067be9128528Sumit Bose conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic void db_ldap_default_bind_finished(struct ldap_connection *conn,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = ldap_result2error(conn->ld, res, FALSE);
543676afec3c08fdc0a5a794976adc8dfdca974bJakub Hrozek /* lost connection, close it */
ca261795ce61c41d7e62217ccb2ee913923040ffPavel Březinastatic void db_ldap_abort_requests(struct ldap_connection *conn,
77d165f0629966db65753a3aee84a8b4971673afPavel Březina struct ldap_request *const *requestp, *request;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher while (aqueue_count(conn->request_queue) > 0 && max_count > 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher requestp = array_idx(&conn->request_array,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher diff = ioloop_time - request->create_time;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* timed out, abort */
4e0404ca1b19830dc0f729e59efd5bbd0a9d6103Lukas Slebodnik auth_request_log_error(request->auth_request, "ldap",
eaa723b4d06b4c1e588df67bef44a84bbfaebf1aLukas Slebodnik auth_request_log_info(request->auth_request, "ldap",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherdb_ldap_find_request(struct ldap_connection *conn, int msgid,
bfbf5cb0f00c60c0f000f56c282377b13b9a89abSumit Bose unsigned int *idx_r)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct ldap_request *const *requests, *request = NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int i, count;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher count = aqueue_count(conn->request_queue);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher requests = array_idx(&conn->request_array, 0);
3b08dec5ee634f83ee18e1753d5ffe0ac5e3c458Jakub Hrozek for (i = 0; i < count; i++) {
bf01e8179cbb2be476805340636098deda7e1366Sumit Bose request = requests[aqueue_idx(conn->request_queue, i)];
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherdb_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res)
748ba184db97b7534254f97018fa04e8aa458faeJan Cholasta unsigned int idx;
9959c512ac3ba36f7a0db7614f0357ce0bae748fJakub Hrozek request = db_ldap_find_request(conn, msgid, &idx);
918b2a5a91f1c551d48f4bffed2a28c36fdb4be1Simo Sorce i_error("LDAP: Reply with unknown msgid %d", msgid);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (request->type == LDAP_REQUEST_TYPE_BIND) {
7ac503a73a26abe49f9f7d175c74df705380898dPavel Březina i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher conn->conn_state = LDAP_CONN_STATE_BOUND_AUTH;
ef39c0adcb61b16f9edc7beb4cdc8f3b0d5a8f15Stephen Gallagher /* we're going to ignore this */
7ac503a73a26abe49f9f7d175c74df705380898dPavel Březina if (ldap_msgtype(res) == LDAP_RES_SEARCH_ENTRY)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret != LDAP_SUCCESS && request->type == LDAP_REQUEST_TYPE_SEARCH) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* handle search failures here */
364b3572bab5a9649e8f2d4da835d05d3c8ca7a9Pavel Březina auth_request_log_error(request->auth_request, "ldap",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "ldap_search(%s) failed: %s",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* see if there are timed out requests */
b79e0e50a935d108173ca3062f2afe16103fcb1dPavel Březinastatic void ldap_input(struct ldap_connection *conn)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = ldap_result(conn->ld, LDAP_RES_ANY, 0, &timeout, &res);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* try again, there may be another in buffer */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher prev_reply_diff = ioloop_time - conn->last_reply_stamp;
ad07ed37b6b51ef134d4524edaf2259e19ac984fJan Zeleny /* input disabled, continue once it's enabled */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher } else if (ret == 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* send more requests */
1a7d1977037864e52858058777af8ff8401547ddJan Cholasta } else if (ldap_get_errno(conn) != LDAP_SERVER_DOWN) {
65e8f538ad35ba7d86cd9e60a3d86aec34537027Stephen Gallagher i_error("LDAP: ldap_result() failed: %s", ldap_get_error(conn));
b407fe0474a674bb42f0f42ab47c7f530a07a367Pavel Březina } else if (aqueue_count(conn->request_queue) > 0 ||
4ddd5591c50e27dffa55f03fbce0dcc85cd50a8bPavel Březina prev_reply_diff < DB_LDAP_IDLE_RECONNECT_SECS) {
a679f0167b646cffdae86546ed77e105576991b0Pavel Březina i_error("LDAP: Connection lost to LDAP server, reconnecting");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher /* server probably disconnected an idle connection. don't
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher reconnect until the next request comes. */
b1a822a16e3ef97e31d167f9e97efec06fc121dcJakub Hrozeksasl_interact(LDAP *ld ATTR_UNUSED, unsigned flags ATTR_UNUSED,
7ac503a73a26abe49f9f7d175c74df705380898dPavel Březina struct db_ldap_sasl_bind_context *context = defaults;
b6dfbf81c61d4431aaa81687ec53e892f8b71edbSumit Bose const char *str;
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose for (in = interact; in->id != SASL_CB_LIST_END; in++) {
eaa723b4d06b4c1e588df67bef44a84bbfaebf1aLukas Slebodnikstatic void ldap_connection_timeout(struct ldap_connection *conn)
eaa723b4d06b4c1e588df67bef44a84bbfaebf1aLukas Slebodnik i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING);
86b61156743b7ebdc049450a6f88452890fd9a61Jakub Hrozek i_error("LDAP: Initial binding to LDAP server timed out");
77c0d1f6074059dafd2293f9c42ea0f9d60f8aadJakub Hrozekstatic int db_ldap_bind(struct ldap_connection *conn)
96453f402831275a39d5fb89c33c9776e148d03fStephen Gallagher i_assert(conn->conn_state != LDAP_CONN_STATE_BINDING);
11e8f3ecdddf8edd8b1bbe9f41b49ce8b709b92aPetr Cech msgid = ldap_bind(conn->ld, conn->set.dn, conn->set.dnpass,
654757bcead49427baaeb1b368c0e3433b67c51aJan Engelhardt i_assert(ldap_get_errno(conn) != LDAP_SUCCESS);
96453f402831275a39d5fb89c33c9776e148d03fStephen Gallagher if (db_ldap_connect_finish(conn, ldap_get_errno(conn)) < 0) {
96453f402831275a39d5fb89c33c9776e148d03fStephen Gallagher /* lost connection, close it */
87c07559af5cfcd2752295ef7c425bd3205f426fStephen Gallagher conn->to = timeout_add(DB_LDAP_REQUEST_LOST_TIMEOUT_SECS*1000,
69b46c32357ccf1aab9c0bd6d1afa33a8724ad77Lukas Slebodnikstatic void db_ldap_get_fd(struct ldap_connection *conn)
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik /* get the connection's fd */
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik ret = ldap_get_option(conn->ld, LDAP_OPT_DESC, (void *)&conn->fd);
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik i_fatal("LDAP: Can't get connection fd: %s",
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bose /* Solaris LDAP library seems to be broken */
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bose i_fatal("LDAP: Buggy LDAP library returned wrong fd: %d",
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bosedb_ldap_set_opt(struct ldap_connection *conn, int opt, const void *value,
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik ret = ldap_set_option(conn == NULL ? NULL : conn->ld, opt, value);
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik i_fatal("LDAP: Can't set option %s to %s: %s",
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bosedb_ldap_set_opt_str(struct ldap_connection *conn, int opt, const char *value,
0d5bb38364a6976e9c85d6349aa13a04d181a090Sumit Bose db_ldap_set_opt(conn, opt, value, optname, value);
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnikstatic void db_ldap_set_tls_options(struct ldap_connection *conn)
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CACERTFILE,
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik conn->set.tls_ca_cert_file, "tls_ca_cert_file");
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CACERTDIR,
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik conn->set.tls_ca_cert_dir, "tls_ca_cert_dir");
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CERTFILE,
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_KEYFILE,
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
25d4435998d0446f7699e7ab0874c7a6f610ab58Lukas Slebodnik conn->set.tls_cipher_suite, "tls_cipher_suite");
bf01e8179cbb2be476805340636098deda7e1366Sumit Bose int value = tls_require_cert2str(conn->set.tls_require_cert);
bf01e8179cbb2be476805340636098deda7e1366Sumit Bose db_ldap_set_opt(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &value,
bf01e8179cbb2be476805340636098deda7e1366Sumit Bose "your LDAP library doesn't seem to support them");
bf01e8179cbb2be476805340636098deda7e1366Sumit Bosestatic void db_ldap_set_options(struct ldap_connection *conn)
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher db_ldap_set_opt(conn, LDAP_OPT_DEREF, &conn->set.ldap_deref,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny db_ldap_set_opt(NULL, LDAP_OPT_DEBUG_LEVEL, &value,
817b1bcafff27cc67630dd0cbd36df708c05fcccStephen Gallagher i_fatal("LDAP: sasl_bind=yes requires ldap_version=3");
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher i_fatal("LDAP: tls=yes requires ldap_version=3");
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher db_ldap_set_opt(conn, LDAP_OPT_PROTOCOL_VERSION, &ldap_version,
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher "protocol_version", dec2str(ldap_version));
42c28b9424b6ef8a0021b124773e171dd5defaddJakub Hrozekint db_ldap_connect(struct ldap_connection *conn)
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (conn->conn_state != LDAP_CONN_STATE_DISCONNECTED)
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher if (ldap_initialize(&conn->ld, conn->set.uris) != LDAP_SUCCESS)
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher i_fatal("LDAP: Your LDAP library doesn't support "
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher "'uris' setting, use 'hosts' instead.");
dbea04f585a30d001b574317c068cd03a4fa332bJakub Hrozek conn->ld = ldap_init(conn->set.hosts, LDAP_PORT);
9959c512ac3ba36f7a0db7614f0357ce0bae748fJakub Hrozek i_fatal("LDAP: ldap_init() failed with hosts: %s",
022c6b90bb37851c0e8704c0e5388ebc113c6470Lukas Slebodnik strncmp(conn->set.uris, "ldaps:", 6) == 0) {
ac40d2f2b2b2fc35c95389f5e28febd580bd2b7aJakub Hrozek "and ldaps URI");
12805da52a93c268290cec7b8fbbdbd4ea8abc3eLukas Slebodnik i_error("LDAP: ldap_start_tls_s() failed: %s",
fe2091327ff44f80d6681c261494e4432404e9baStephen Gallagher i_error("LDAP: Your LDAP library doesn't support TLS");
1a7d1977037864e52858058777af8ff8401547ddJan Cholasta /* There doesn't seem to be a way to do SASL binding
96453f402831275a39d5fb89c33c9776e148d03fStephen Gallagher asynchronously.. */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = ldap_sasl_interactive_bind_s(conn->ld, NULL,
42ec8af02ecf1937e4db9b1ecc6216022634f0f9Michal Zidek i_fatal("LDAP: sasl_bind=yes but no SASL support compiled in");
42ec8af02ecf1937e4db9b1ecc6216022634f0f9Michal Zidek conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT;
42ec8af02ecf1937e4db9b1ecc6216022634f0f9Michal Zidek conn->io = io_add(conn->fd, IO_READ, ldap_input, conn);
577ba99b3150404533bd3d859522a2c994b17e76Lukas Slebodnikvoid db_ldap_enable_input(struct ldap_connection *conn, bool enable)
bbaba8b3ef9bc101863b8687f234f4ee956caacdPavel Březina conn->io = io_add(conn->fd, IO_READ, ldap_input, conn);
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březinastatic void db_ldap_disconnect_timeout(struct ldap_connection *conn)
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS, FALSE,
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina "Aborting (timeout), we're not connected to LDAP server");
3fc158e59eebbc2f538fe0076a03928d0d4eab9fPavel Březina /* no requests left, remove this timeout handler */
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bosestatic void db_ldap_conn_close(struct ldap_connection *conn)
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose unsigned int i;
4f6931e854c698dcb1c09f99eb330ce2fb97e7c6Lukas Slebodnik conn->conn_state = LDAP_CONN_STATE_DISCONNECTED;
654757bcead49427baaeb1b368c0e3433b67c51aJan Engelhardt requests = array_idx(&conn->request_array, 0);
65ce66c43141f7e5c8482a8f8e7e217a23791588Petr Cech request = requests[aqueue_idx(conn->request_queue, i)];
f28b09f887870c10c8c611beee3c17eaa9ef74f3Lukas Slebodnik /* the fd may have already been closed before ldap_unbind(),
4f6931e854c698dcb1c09f99eb330ce2fb97e7c6Lukas Slebodnik so we'll have to use io_remove_closed(). */
2a9af1f71887f02935e2fb6ad5023afba5b6d43eSumit Bose conn->to = timeout_add(DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS *
0d01e4f6cc21d8ca0e4fafe59c7cbfa1459fa47eSumit Bosevoid db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bose char ***attr_names_r, struct hash_table *attr_map,
a9c287bda3fc2a1e12cef2135ade96945f11ad01Sumit Bose unsigned int i, j, size;
1270ffe9f3809f2fd488ef4a320d344ae107ab87Sumit Bose /* @UNSAFE */
1270ffe9f3809f2fd488ef4a320d344ae107ab87Sumit Bose *attr_names_r = p_new(conn->pool, char *, size + 1);
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose for (i = j = 0; i < size; i++) {
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose /* allow spaces here so "foo=1, bar=2" works */
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose else if (p != attr_data) {
1270ffe9f3809f2fd488ef4a320d344ae107ab87Sumit Bose /* =<static key>=<static value> */
885386b7e3f1c3e74b354576b98a092b0835d64eSumit Bose (skip_attr == NULL || strcmp(skip_attr, value) != 0)) {
1270ffe9f3809f2fd488ef4a320d344ae107ab87Sumit Bose i_fatal("ldap: LDAP attribute '%s' used multiple times. This is currently unsupported.",
a7e27c11866a48742bb70564b88e15bf15e9367dPavel Březinadb_ldap_value_get_var_expand_table(struct auth_request *auth_request)
0bb98b7700b1b61f5b0a20b93279d5c2c391007fPavel Březina const struct var_expand_table *auth_table = NULL;
a7e27c11866a48742bb70564b88e15bf15e9367dPavel Březina unsigned int count;
a7e27c11866a48742bb70564b88e15bf15e9367dPavel Březina auth_table = auth_request_get_var_expand_table(auth_request, NULL);
a7e27c11866a48742bb70564b88e15bf15e9367dPavel Březina for (count = 0; auth_table[count].key != '\0'; count++) ;
efa6c1f75c4c18bcc148d6e7efd429c2d56499adPavel Březina table = t_new(struct var_expand_table, count + 1);
a7e27c11866a48742bb70564b88e15bf15e9367dPavel Březina memcpy(table + 1, auth_table, sizeof(*table) * count);
a7e27c11866a48742bb70564b88e15bf15e9367dPavel Březina ((c) == '*' || (c) == '(' || (c) == ')' || (c) == '\\')
a7e27c11866a48742bb70564b88e15bf15e9367dPavel Březina const struct auth_request *auth_request ATTR_UNUSED)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *p;
769347ad4d35d43488eb98f980143495b0db415dStef Walter if (*p == '\0')
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter for (; *p != '\0'; p++) {
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walterdb_ldap_result_iterate_init(struct ldap_connection *conn, LDAPMessage *entry,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx = t_new(struct db_ldap_result_iterate_context, 1);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher static_data = hash_table_lookup(attr_map, "");
769347ad4d35d43488eb98f980143495b0db415dStef Walter table = auth_request_get_var_expand_table(auth_request, NULL);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->static_attrs = t_strsplit(str_c(str), ",");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ctx->attr = ldap_first_attribute(conn->ld, entry, &ctx->ber);
8c3a4809b3420657289b42f028a1c9019b112991Stephen Gallagherdb_ldap_result_iterate_finish(struct db_ldap_result_iterate_context *ctx)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher auth_request_log_debug(ctx->auth_request, "ldap",
577ba99b3150404533bd3d859522a2c994b17e76Lukas Slebodnik auth_request_log_debug(ctx->auth_request, "ldap",
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "no fields returned by the server");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherdb_ldap_result_change_attr(struct db_ldap_result_iterate_context *ctx)
300c772767c1b12077cac1d148ac89738b058f97Jan Zeleny ctx->name = hash_table_lookup(ctx->attr_map, ctx->attr);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher str_printfa(ctx->debug, " %s(%s)=", ctx->attr,
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina (ctx->template = strchr(ctx->name, '=')) != NULL) {
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina /* we want to use variables */
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina ctx->name = t_strdup_until(ctx->name, ctx->template);
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina ctx->var_table = db_ldap_value_get_var_expand_table(
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek ctx->vals = ldap_get_values(ctx->conn->ld, ctx->entry,
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozekdb_ldap_result_return_value(struct db_ldap_result_iterate_context *ctx)
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta unsigned int i;
4de84af23db74e13e867985c9093f394c9fa8d51Sumit Bose auth_request_log_warning(ctx->auth_request, "ldap",
4de84af23db74e13e867985c9093f394c9fa8d51Sumit Bose "Multiple values found for '%s', "
4de84af23db74e13e867985c9093f394c9fa8d51Sumit Bose var_expand(ctx->var, ctx->template, ctx->var_table);
b9e5bd09a5ff7009537a18914dbebcf10498f592Sumit Bose /* no debugging */
e3f0014bb64b7e93979948936cf93cf869d3dc44Jan Zeleny } else if (ctx->auth_request->set->debug_passwords ||
b9e5bd09a5ff7009537a18914dbebcf10498f592Sumit Bosestatic bool db_ldap_result_int_next(struct db_ldap_result_iterate_context *ctx)
cb4d5b588e704114b7090678752d33512baa718eJakub Hrozek const char *p;
cb4d5b588e704114b7090678752d33512baa718eJakub Hrozek ctx->attr = ldap_next_attribute(ctx->conn->ld, ctx->entry,
827a016a07d5f911cc4195be89896a376fd71f59Sumit Bose if (ctx->static_attrs != NULL && *ctx->static_attrs != NULL) {
1a59af8245f183f22d87d067a90197d8e2ea958dJakub Hrozek ctx->name = t_strdup_until(*ctx->static_attrs, p);
8a1fd0633e85221da1fb63451516a70d66c0af31Pavel Březina /* make _next() return correct values */
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherbool db_ldap_result_iterate_next(struct db_ldap_result_iterate_context *ctx,
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek const char **name_r,
ac47e8854f3bc404f2a35c6682faf621673d6b32Pavel Březina const char *const **values_r)
ccf340e56364851f2e5b75e52d3d63701b662954Lukas Slebodnik /* we can use only one value with templates */
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozekstatic const char *parse_setting(const char *key, const char *value,
8359bf07a2e6c0181251ce8d5d9160dc57546c55Stephen Gallagher return parse_setting_from_defs(conn->pool, setting_defs,
530ba03ecabb472f17d5d1ab546aec9390492de1Jakub Hrozekstatic struct ldap_connection *ldap_conn_find(const char *config_path)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (conn = ldap_connections; conn != NULL; conn = conn->next) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (strcmp(conn->config_path, config_path) == 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstruct ldap_connection *db_ldap_init(const char *config_path, bool userdb)
577ba99b3150404533bd3d859522a2c994b17e76Lukas Slebodnik /* see if it already exists */
654757bcead49427baaeb1b368c0e3433b67c51aJan Engelhardt i_fatal("LDAP: Configuration file path not given");
42ec8af02ecf1937e4db9b1ecc6216022634f0f9Michal Zidek pool = pool_alloconly_create("ldap_connection", 1024);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher conn->conn_state = LDAP_CONN_STATE_DISCONNECTED;
577ba99b3150404533bd3d859522a2c994b17e76Lukas Slebodnik conn->config_path = p_strdup(pool, config_path);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (!settings_read(config_path, NULL, parse_setting,
a9eff330a7fbd231e8cc28a6828a1e5014ddb0d2Michal Zidek if (conn->set.uris == NULL && conn->set.hosts == NULL)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_fatal("LDAP: Dovecot compiled without support for LDAP uris "
2c0a971010596c122d7a0c0d76c8eb85f16f6d06Jakub Hrozek "(ldap_initialize not found)");
42ec8af02ecf1937e4db9b1ecc6216022634f0f9Michal Zidek if (str != NULL && strcmp(str, conn->set.ldaprc_path) != 0) {
654757bcead49427baaeb1b368c0e3433b67c51aJan Engelhardt i_fatal("LDAP: Multiple different ldaprc_path "
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher "settings not allowed (%s and %s)",
2c0a971010596c122d7a0c0d76c8eb85f16f6d06Jakub Hrozek env_put(t_strconcat("LDAPRC=", conn->set.ldaprc_path, NULL));
654757bcead49427baaeb1b368c0e3433b67c51aJan Engelhardt conn->set.ldap_deref = deref2str(conn->set.deref);
2c0a971010596c122d7a0c0d76c8eb85f16f6d06Jakub Hrozek conn->set.ldap_scope = scope2str(conn->set.scope);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher conn->request_queue = aqueue_init(&conn->request_array.arr);
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zelenyvoid db_ldap_unref(struct ldap_connection **_conn)
f1828234a850dd28465425248a83a993f262918fPavel Březina for (p = &ldap_connections; *p != NULL; p = &(*p)->next) {
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay db_ldap_abort_requests(conn, -1U, 0, FALSE, "Shutting down");
b69cb1787209e85cc246eb9a944242689bfe0c46Pavel Březinavoid db_ldap_check_userdb_warning(struct ldap_connection *conn)
23fb01bf67a6058fb508da6d81515e8b18634bebPavel Březina const struct ldap_settings *def = &default_ldap_settings;
b69cb1787209e85cc246eb9a944242689bfe0c46Pavel Březina if (worker || conn->userdb_used || conn->set.userdb_warning_disable)
b69cb1787209e85cc246eb9a944242689bfe0c46Pavel Březina if (strcmp(conn->set.user_attrs, def->user_attrs) != 0)
b69cb1787209e85cc246eb9a944242689bfe0c46Pavel Březina else if (strcmp(conn->set.user_filter, def->user_filter) != 0)
b69cb1787209e85cc246eb9a944242689bfe0c46Pavel Březina else if (strcmp(conn->set.iterate_attrs, def->iterate_attrs) != 0)
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina else if (strcmp(conn->set.iterate_filter, def->iterate_filter) != 0)
654757bcead49427baaeb1b368c0e3433b67c51aJan Engelhardt i_warning("ldap: Ignoring changed %s in %s, "
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina "because userdb ldap not used. "
f8c829e72968b574e1c9bda96f4d5f206622358fPavel Březina "(If this is intentional, set userdb_warning_disable=yes)",
558998ce664055a75595371118f818084d8f2b23Jan Cholasta/* Building a plugin */
558998ce664055a75595371118f818084d8f2b23Jan Cholastaextern struct passdb_module_interface passdb_ldap_plugin;
654757bcead49427baaeb1b368c0e3433b67c51aJan Engelhardtextern struct userdb_module_interface userdb_ldap_plugin;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher passdb_unregister_module(&passdb_ldap_plugin);