db-ldap.c revision 964c86de7158ccafdfe665853579d71232e2634e
183bea41fa640dc8117f3eb45ff935cd81377a84Timo Sirainen/* Copyright (c) 2003-2011 Dovecot authors, see the included COPYING file */
44fc0a34c39f1ddb3a776918630010867a5dd04eTimo Sirainen#if defined(BUILTIN_LDAP) || defined(PLUGIN_BUILD)
594d203bdcbd160688bce5d5a6d65783b919ad49Timo Sirainen# define LDAP_SASL_QUIET 0 /* Doesn't exist in Solaris LDAP */
a8fc29f19ea6e2d472ba779b2dd5ca4e1f3dac79Timo Sirainen/* Older versions may require calling ldap_result() twice */
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen/* Solaris LDAP library doesn't have LDAP_OPT_SUCCESS */
1cb29929a19dea32779606cd54a1e63aefead88dTimo Sirainen const char *const *static_attrs;
a84eb0599fa1d796206eaed65c4e3239f0799276Timo Sirainen#define DEF_STR(name) DEF_STRUCT_STR(name, ldap_settings)
a84eb0599fa1d796206eaed65c4e3239f0799276Timo Sirainen#define DEF_INT(name) DEF_STRUCT_INT(name, ldap_settings)
a84eb0599fa1d796206eaed65c4e3239f0799276Timo Sirainen#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, ldap_settings)
ab0d9eecd85f74acae18fe88529302e0776cc500Timo Sirainenstatic struct ldap_settings default_ldap_settings = {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .user_attrs = "homeDirectory=home,uidNumber=uid,gidNumber=gid",
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .user_filter = "(&(objectClass=posixAccount)(uid=%u))",
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .pass_attrs = "uid=user,userPassword=password",
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .pass_filter = "(&(objectClass=posixAccount)(uid=%u))",
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .iterate_filter = "(objectClass=posixAccount)",
c4457e497e01b57565d24da624968699b166e02aTimo Sirainenstatic struct ldap_connection *ldap_connections = NULL;
16133a719ce8b6a5b8cedd721340cc1607c43433Timo Sirainenstatic int db_ldap_bind(struct ldap_connection *conn);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenstatic void db_ldap_conn_close(struct ldap_connection *conn);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_fatal("LDAP: Unknown deref option '%s'", str);
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen i_fatal("LDAP: Unknown scope option '%s'", str);
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainenstatic int tls_require_cert2str(const char *str)
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen i_fatal("LDAP: Unknown tls_require_cert value '%s'", str);
78361883c67c58e339697c167ca285731f50287bTimo Sirainenstatic int ldap_get_errno(struct ldap_connection *conn)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen ret = ldap_get_option(conn->ld, LDAP_OPT_ERROR_NUMBER, (void *) &err);
78361883c67c58e339697c167ca285731f50287bTimo Sirainenconst char *ldap_get_error(struct ldap_connection *conn)
c184857e1fc86878761f6e47896c9cc1fad2d666Timo Sirainen const char *ret;
c184857e1fc86878761f6e47896c9cc1fad2d666Timo Sirainen ldap_get_option(conn->ld, LDAP_OPT_ERROR_STRING, (void *)&str);
c184857e1fc86878761f6e47896c9cc1fad2d666Timo Sirainen ldap_set_option(conn->ld, LDAP_OPT_ERROR_STRING, NULL);
613daa324c2b61ec69291519a57186be7cc23286Timo Sirainenstatic void ldap_conn_reconnect(struct ldap_connection *conn)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenstatic int ldap_handle_error(struct ldap_connection *conn)
613daa324c2b61ec69291519a57186be7cc23286Timo Sirainen /* invalid input */
613daa324c2b61ec69291519a57186be7cc23286Timo Sirainen /* connection problems */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenstatic int db_ldap_request_bind(struct ldap_connection *conn,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen i_assert(request->type == LDAP_REQUEST_TYPE_BIND);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_AUTH ||
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen request->msgid = ldap_bind(conn->ld, brequest->dn,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen auth_request_log_error(request->auth_request, "ldap",
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen "ldap_bind(%s) failed: %s",
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* broken request, remove it */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenstatic int db_ldap_request_search(struct ldap_connection *conn,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen i_assert(conn->conn_state == LDAP_CONN_STATE_BOUND_DEFAULT);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen ldap_search(conn->ld, srequest->base, conn->set.ldap_scope,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen auth_request_log_error(request->auth_request, "ldap",
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen "ldap_search(%s) parsing failed: %s",
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* broken request, remove it */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenstatic bool db_ldap_request_queue_next(struct ldap_connection *conn)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen struct ldap_request *const *requestp, *request;
89676692402d8a58415b2c11256652322091ebabTimo Sirainen /* connecting may call db_ldap_connect_finish(), which gets us back
89676692402d8a58415b2c11256652322091ebabTimo Sirainen here. so do the connection before checking the request queue. */
42ec694fb0f2e1fb1d8afcfb441382daea487bd9Timo Sirainen if (conn->pending_count == aqueue_count(conn->request_queue)) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* no non-pending requests */
42ec694fb0f2e1fb1d8afcfb441382daea487bd9Timo Sirainen if (conn->pending_count > DB_LDAP_MAX_PENDING_REQUESTS) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* wait until server has replied to some requests */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* we can't do binds until all existing requests are finished */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* wait until we're in bound state */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* bind to default dn first */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* we can do anything in this state */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* success */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen } else if (ret < 0) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* disconnected */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* broken request, remove from queue */
584a5375b70caa8bf7b202248aea84092bcb9c22Timo Sirainendb_ldap_check_limits(struct ldap_connection *conn, struct ldap_request *request)
584a5375b70caa8bf7b202248aea84092bcb9c22Timo Sirainen unsigned int count;
584a5375b70caa8bf7b202248aea84092bcb9c22Timo Sirainen first_requestp = array_idx(&conn->request_array,
584a5375b70caa8bf7b202248aea84092bcb9c22Timo Sirainen secs_diff = ioloop_time - (*first_requestp)->create_time;
584a5375b70caa8bf7b202248aea84092bcb9c22Timo Sirainen if (secs_diff > DB_LDAP_REQUEST_LOST_TIMEOUT_SECS) {
584a5375b70caa8bf7b202248aea84092bcb9c22Timo Sirainen auth_request_log_error(request->auth_request, "ldap",
584a5375b70caa8bf7b202248aea84092bcb9c22Timo Sirainen "Connection appears to be hanging, reconnecting");
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenvoid db_ldap_request(struct ldap_connection *conn,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenstatic int db_ldap_connect_finish(struct ldap_connection *conn, int ret)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen conn->set.dn == NULL ? "(none)" : conn->set.dn,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenstatic void db_ldap_default_bind_finished(struct ldap_connection *conn,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen ret = ldap_result2error(conn->ld, res, FALSE);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* lost connection, close it */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenstatic void db_ldap_abort_requests(struct ldap_connection *conn,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen struct ldap_request *const *requestp, *request;
63cde222abaaa2a9bdaa9a143698dbc8b23bd742Timo Sirainen while (aqueue_count(conn->request_queue) > 0 && max_count > 0) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* timed out, abort */
3cff7935d606a75357472a3e4269e0b06ac1bef2Timo Sirainen auth_request_log_error(request->auth_request, "ldap",
3cff7935d606a75357472a3e4269e0b06ac1bef2Timo Sirainen auth_request_log_info(request->auth_request, "ldap",
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainendb_ldap_find_request(struct ldap_connection *conn, int msgid,
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen unsigned int *idx_r)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen struct ldap_request *const *requests, *request = NULL;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen unsigned int i, count;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen requests = array_idx(&conn->request_array, 0);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen for (i = 0; i < count; i++) {
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen request = requests[aqueue_idx(conn->request_queue, i)];
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainendb_ldap_handle_result(struct ldap_connection *conn, LDAPMessage *res)
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen unsigned int idx;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen request = db_ldap_find_request(conn, msgid, &idx);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen i_error("LDAP: Reply with unknown msgid %d", msgid);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen if (request->type == LDAP_REQUEST_TYPE_BIND) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen conn->conn_state = LDAP_CONN_STATE_BOUND_AUTH;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen /* we're going to ignore this */
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen i_error("LDAP: Reply with unexpected type %d",
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen if (ldap_msgtype(res) == LDAP_RES_SEARCH_ENTRY)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen if (ret != LDAP_SUCCESS && request->type == LDAP_REQUEST_TYPE_SEARCH) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* handle search failures here */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen auth_request_log_error(request->auth_request, "ldap",
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen "ldap_search(%s) failed: %s",
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* see if there are timed out requests */
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainenstatic void ldap_input(struct ldap_connection *conn)
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen ret = ldap_result(conn->ld, LDAP_RES_ANY, 0, &timeout, &res);
a8fc29f19ea6e2d472ba779b2dd5ca4e1f3dac79Timo Sirainen /* try again, there may be another in buffer */
c51fe409810cbb2432b72d6819bd183469fcaebcTimo Sirainen prev_reply_diff = ioloop_time - conn->last_reply_stamp;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen /* input disabled, continue once it's enabled */
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen } else if (ret == 0) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* send more requests */
d39e77e1f7f58e1e21042a673b718541fa3f63c7Timo Sirainen } else if (ldap_get_errno(conn) != LDAP_SERVER_DOWN) {
613daa324c2b61ec69291519a57186be7cc23286Timo Sirainen i_error("LDAP: ldap_result() failed: %s", ldap_get_error(conn));
63cde222abaaa2a9bdaa9a143698dbc8b23bd742Timo Sirainen } else if (aqueue_count(conn->request_queue) > 0 ||
c51fe409810cbb2432b72d6819bd183469fcaebcTimo Sirainen prev_reply_diff < DB_LDAP_IDLE_RECONNECT_SECS) {
d39e77e1f7f58e1e21042a673b718541fa3f63c7Timo Sirainen i_error("LDAP: Connection lost to LDAP server, reconnecting");
d39e77e1f7f58e1e21042a673b718541fa3f63c7Timo Sirainen /* server probably disconnected an idle connection. don't
d39e77e1f7f58e1e21042a673b718541fa3f63c7Timo Sirainen reconnect until the next request comes. */
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainensasl_interact(LDAP *ld ATTR_UNUSED, unsigned flags ATTR_UNUSED,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen struct db_ldap_sasl_bind_context *context = defaults;
23f8c5356cacdbd1cc09a39a08ef37a39125bb74Timo Sirainen const char *str;
23f8c5356cacdbd1cc09a39a08ef37a39125bb74Timo Sirainen for (in = interact; in->id != SASL_CB_LIST_END; in++) {
2d0a002723dac5c58c250f6566efb1f5e474c169Timo Sirainenstatic void ldap_connection_timeout(struct ldap_connection *conn)
2d0a002723dac5c58c250f6566efb1f5e474c169Timo Sirainen i_assert(conn->conn_state == LDAP_CONN_STATE_BINDING);
2d0a002723dac5c58c250f6566efb1f5e474c169Timo Sirainen i_error("LDAP: Initial binding to LDAP server timed out");
16133a719ce8b6a5b8cedd721340cc1607c43433Timo Sirainenstatic int db_ldap_bind(struct ldap_connection *conn)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen i_assert(conn->conn_state != LDAP_CONN_STATE_BINDING);
763fd2ac217023c0940415379abcd5eb7a0f7ba7Timo Sirainen msgid = ldap_bind(conn->ld, conn->set.dn, conn->set.dnpass,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen i_assert(ldap_get_errno(conn) != LDAP_SUCCESS);
7cf0a6613fca9983b8a3443f9f6ef15df5a22162Timo Sirainen if (db_ldap_connect_finish(conn, ldap_get_errno(conn)) < 0) {
7cf0a6613fca9983b8a3443f9f6ef15df5a22162Timo Sirainen /* lost connection, close it */
2d0a002723dac5c58c250f6566efb1f5e474c169Timo Sirainen conn->to = timeout_add(DB_LDAP_REQUEST_LOST_TIMEOUT_SECS*1000,
b270b29d458f3cbd6e63320bb17e23f809da0045Timo Sirainenstatic void db_ldap_get_fd(struct ldap_connection *conn)
b270b29d458f3cbd6e63320bb17e23f809da0045Timo Sirainen /* get the connection's fd */
b270b29d458f3cbd6e63320bb17e23f809da0045Timo Sirainen ret = ldap_get_option(conn->ld, LDAP_OPT_DESC, (void *)&conn->fd);
d2ded6e1da2d07ac070888873ddc10999a6d87baTimo Sirainen /* Solaris LDAP library seems to be broken */
d2ded6e1da2d07ac070888873ddc10999a6d87baTimo Sirainen i_fatal("LDAP: Buggy LDAP library returned wrong fd: %d",
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainendb_ldap_set_opt(struct ldap_connection *conn, int opt, const void *value,
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen ret = ldap_set_option(conn == NULL ? NULL : conn->ld, opt, value);
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen i_fatal("LDAP: Can't set option %s to %s: %s",
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainendb_ldap_set_opt_str(struct ldap_connection *conn, int opt, const char *value,
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen db_ldap_set_opt(conn, opt, value, optname, value);
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainenstatic void db_ldap_set_tls_options(struct ldap_connection *conn)
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CACERTFILE,
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen conn->set.tls_ca_cert_file, "tls_ca_cert_file");
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CACERTDIR,
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen conn->set.tls_ca_cert_dir, "tls_ca_cert_dir");
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CERTFILE,
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_KEYFILE,
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen db_ldap_set_opt_str(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen conn->set.tls_cipher_suite, "tls_cipher_suite");
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen int value = tls_require_cert2str(conn->set.tls_require_cert);
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen db_ldap_set_opt(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &value,
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen "tls_require_cert", conn->set.tls_require_cert);
f023d5a1665cc388131159914898ceecc526eda5Timo Sirainen "your LDAP library doesn't seem to support them");
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainenstatic void db_ldap_set_options(struct ldap_connection *conn)
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen db_ldap_set_opt(conn, LDAP_OPT_DEREF, &conn->set.ldap_deref,
f4a19b0cf11cdff437571708d9d788d02a906a00Timo Sirainen db_ldap_set_opt(NULL, LDAP_OPT_DEBUG_LEVEL, &value,
4756ae94c4db02e694c3425d9d949678117b66a3Timo Sirainen i_fatal("LDAP: sasl_bind=yes requires ldap_version=3");
4756ae94c4db02e694c3425d9d949678117b66a3Timo Sirainen i_fatal("LDAP: tls=yes requires ldap_version=3");
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen db_ldap_set_opt(conn, LDAP_OPT_PROTOCOL_VERSION, &ldap_version,
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainenint db_ldap_connect(struct ldap_connection *conn)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen if (conn->conn_state != LDAP_CONN_STATE_DISCONNECTED)
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen if (ldap_initialize(&conn->ld, conn->set.uris) != LDAP_SUCCESS)
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen i_fatal("LDAP: Your LDAP library doesn't support "
48559742084e98049335c21c53dfd1ff95f11cd8Timo Sirainen "'uris' setting, use 'hosts' instead.");
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen conn->ld = ldap_init(conn->set.hosts, LDAP_PORT);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen i_fatal("LDAP: ldap_init() failed with hosts: %s",
8f0a5540a21d235f4f830517c6211f6f92948f2cTimo Sirainen "and ldaps URI");
40992309053d51192ae1b36d1dd6c057f2d37257Timo Sirainen i_error("LDAP: ldap_start_tls_s() failed: %s",
40992309053d51192ae1b36d1dd6c057f2d37257Timo Sirainen i_error("LDAP: Your LDAP library doesn't support TLS");
efb7a523ea2f7670ca07acaaa5aeb30692ad6cd3Timo Sirainen /* There doesn't seem to be a way to do SASL binding
efb7a523ea2f7670ca07acaaa5aeb30692ad6cd3Timo Sirainen asynchronously.. */
23f8c5356cacdbd1cc09a39a08ef37a39125bb74Timo Sirainen ret = ldap_sasl_interactive_bind_s(conn->ld, NULL,
efb7a523ea2f7670ca07acaaa5aeb30692ad6cd3Timo Sirainen i_fatal("LDAP: sasl_bind=yes but no SASL support compiled in");
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen conn->conn_state = LDAP_CONN_STATE_BOUND_DEFAULT;
16133a719ce8b6a5b8cedd721340cc1607c43433Timo Sirainen conn->io = io_add(conn->fd, IO_READ, ldap_input, conn);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainenvoid db_ldap_enable_input(struct ldap_connection *conn, bool enable)
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen conn->io = io_add(conn->fd, IO_READ, ldap_input, conn);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenstatic void db_ldap_disconnect_timeout(struct ldap_connection *conn)
1512eb3e1f8d7366122089a03e3c8688986a5a26Timo Sirainen DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS, FALSE,
1512eb3e1f8d7366122089a03e3c8688986a5a26Timo Sirainen "Aborting (timeout), we're not connected to LDAP server");
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* no requests left, remove this timeout handler */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenstatic void db_ldap_conn_close(struct ldap_connection *conn)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen struct ldap_request *const *requests, *request;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen unsigned int i;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen conn->conn_state = LDAP_CONN_STATE_DISCONNECTED;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen requests = array_idx(&conn->request_array, 0);
63cde222abaaa2a9bdaa9a143698dbc8b23bd742Timo Sirainen request = requests[aqueue_idx(conn->request_queue, i)];
baf992882696ce085fa9122a559dbba8e627e19fTimo Sirainen /* the fd may have already been closed before ldap_unbind(),
baf992882696ce085fa9122a559dbba8e627e19fTimo Sirainen so we'll have to use io_remove_closed(). */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen conn->to = timeout_add(DB_LDAP_REQUEST_DISCONNECT_TIMEOUT_SECS *
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainenvoid db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist,
0d7d27765267594a5870892268ab345148306d49Timo Sirainen char ***attr_names_r, struct hash_table *attr_map,
16133a719ce8b6a5b8cedd721340cc1607c43433Timo Sirainen unsigned int i, j, size;
0e90e1b11b699166a4a4c5e01d132a28c3e26affTimo Sirainen /* @UNSAFE */
0d7d27765267594a5870892268ab345148306d49Timo Sirainen *attr_names_r = p_new(conn->pool, char *, size + 1);
16133a719ce8b6a5b8cedd721340cc1607c43433Timo Sirainen for (i = j = 0; i < size; i++) {
99ee694f4099ea3dcc3b6b8331b093a8f1a2c604Timo Sirainen /* allow spaces here so "foo=1, bar=2" works */
99ee694f4099ea3dcc3b6b8331b093a8f1a2c604Timo Sirainen name = value = p_strdup(conn->pool, attr_data);
99ee694f4099ea3dcc3b6b8331b093a8f1a2c604Timo Sirainen else if (p != attr_data) {
99ee694f4099ea3dcc3b6b8331b093a8f1a2c604Timo Sirainen name = p_strdup_until(conn->pool, attr_data, p);
1cb29929a19dea32779606cd54a1e63aefead88dTimo Sirainen /* =<static key>=<static value> */
73583cff4f0ca9ee87204256ca1994adf17cb94cTimo Sirainen (skip_attr == NULL || strcmp(skip_attr, value) != 0)) {
e434abb86a137bbe710320b5f5431804f05c6e26Timo Sirainen if (hash_table_lookup(attr_map, name) != NULL) {
e434abb86a137bbe710320b5f5431804f05c6e26Timo Sirainen i_fatal("ldap: LDAP attribute '%s' used multiple times. This is currently unsupported.",
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainendb_ldap_value_get_var_expand_table(struct auth_request *auth_request)
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen const struct var_expand_table *auth_table = NULL;
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen unsigned int count;
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen auth_table = auth_request_get_var_expand_table(auth_request, NULL);
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen for (count = 0; auth_table[count].key != '\0'; count++) ;
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen table = t_new(struct var_expand_table, count + 1);
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen memcpy(table + 1, auth_table, sizeof(*table) * count);
1c38a95332f1945c9806d7d83175a0d948f51291Timo Sirainen ((c) == '*' || (c) == '(' || (c) == ')' || (c) == '\\')
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen const struct auth_request *auth_request ATTR_UNUSED)
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen const char *p;
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen if (*p == '\0')
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen for (; *p != '\0'; p++) {
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainendb_ldap_result_iterate_init(struct ldap_connection *conn, LDAPMessage *entry,
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen ctx = t_new(struct db_ldap_result_iterate_context, 1);
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen static_data = hash_table_lookup(attr_map, "");
db966fce2a30ac996d90ad31daffe47734ef4ad9Timo Sirainen table = auth_request_get_var_expand_table(auth_request, NULL);
db966fce2a30ac996d90ad31daffe47734ef4ad9Timo Sirainen ctx->static_attrs = t_strsplit(str_c(str), ",");
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen ctx->attr = ldap_first_attribute(conn->ld, entry, &ctx->ber);
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainendb_ldap_result_iterate_finish(struct db_ldap_result_iterate_context *ctx)
337db2d47544b384b06cda46931560f9f54530b2Timo Sirainen auth_request_log_debug(ctx->auth_request, "ldap",
337db2d47544b384b06cda46931560f9f54530b2Timo Sirainen auth_request_log_debug(ctx->auth_request, "ldap",
337db2d47544b384b06cda46931560f9f54530b2Timo Sirainen "no fields returned by the server");
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainendb_ldap_result_change_attr(struct db_ldap_result_iterate_context *ctx)
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen ctx->name = hash_table_lookup(ctx->attr_map, ctx->attr);
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen str_printfa(ctx->debug, " %s(%s)=", ctx->attr,
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen (ctx->template = strchr(ctx->name, '=')) != NULL) {
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen /* we want to use variables */
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen ctx->name = t_strdup_until(ctx->name, ctx->template);
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen ctx->var_table = db_ldap_value_get_var_expand_table(
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen ctx->vals = ldap_get_values(ctx->conn->ld, ctx->entry,
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainendb_ldap_result_return_value(struct db_ldap_result_iterate_context *ctx)
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen unsigned int i;
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen auth_request_log_warning(ctx->auth_request, "ldap",
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen "Multiple values found for '%s', "
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen var_expand(ctx->var, ctx->template, ctx->var_table);
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen /* no debugging */
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen } else if (ctx->auth_request->set->debug_passwords ||
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen strcmp(ctx->name, "password_noscheme") != 0)) {
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen str_printfa(ctx->debug, ", %s", ctx->vals[i]);
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainenstatic bool db_ldap_result_int_next(struct db_ldap_result_iterate_context *ctx)
1cb29929a19dea32779606cd54a1e63aefead88dTimo Sirainen const char *p;
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen ctx->attr = ldap_next_attribute(ctx->conn->ld, ctx->entry,
1cb29929a19dea32779606cd54a1e63aefead88dTimo Sirainen if (ctx->static_attrs != NULL && *ctx->static_attrs != NULL) {
1cb29929a19dea32779606cd54a1e63aefead88dTimo Sirainen ctx->name = t_strdup_until(*ctx->static_attrs, p);
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen /* make _next() return correct values */
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainenbool db_ldap_result_iterate_next(struct db_ldap_result_iterate_context *ctx,
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen const char **name_r,
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen const char *const **values_r)
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen /* we can use only one value with templates */
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstatic const char *parse_setting(const char *key, const char *value,
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return parse_setting_from_defs(conn->pool, setting_defs,
c4457e497e01b57565d24da624968699b166e02aTimo Sirainenstatic struct ldap_connection *ldap_conn_find(const char *config_path)
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen for (conn = ldap_connections; conn != NULL; conn = conn->next) {
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen if (strcmp(conn->config_path, config_path) == 0)
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainenstruct ldap_connection *db_ldap_init(const char *config_path, bool userdb)
f8464772990b52cb8de4553bc1135adcf72813b8Timo Sirainen const char *str;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen /* see if it already exists */
2e1e493b248dec0127b1eabeea5a8bc330378fcdTimo Sirainen i_fatal("LDAP: Configuration file path not given");
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen pool = pool_alloconly_create("ldap_connection", 1024);
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn = p_new(pool, struct ldap_connection, 1);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen conn->conn_state = LDAP_CONN_STATE_DISCONNECTED;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen conn->config_path = p_strdup(pool, config_path);
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen if (!settings_read(config_path, NULL, parse_setting,
b1504ccfa93e77d906e39e6aa31a592d69f6b5b4Timo Sirainen if (conn->set.uris == NULL && conn->set.hosts == NULL)
b1504ccfa93e77d906e39e6aa31a592d69f6b5b4Timo Sirainen i_fatal("LDAP: Dovecot compiled without support for LDAP uris "
b1504ccfa93e77d906e39e6aa31a592d69f6b5b4Timo Sirainen "(ldap_initialize not found)");
f8464772990b52cb8de4553bc1135adcf72813b8Timo Sirainen if (str != NULL && strcmp(str, conn->set.ldaprc_path) != 0) {
f8464772990b52cb8de4553bc1135adcf72813b8Timo Sirainen i_fatal("LDAP: Multiple different ldaprc_path "
f8464772990b52cb8de4553bc1135adcf72813b8Timo Sirainen "settings not allowed (%s and %s)",
f8464772990b52cb8de4553bc1135adcf72813b8Timo Sirainen env_put(t_strconcat("LDAPRC=", conn->set.ldaprc_path, NULL));
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen conn->set.ldap_deref = deref2str(conn->set.deref);
e714eed72515794c46c6712a611e5ab924d903daTimo Sirainen conn->set.ldap_scope = scope2str(conn->set.scope);
63cde222abaaa2a9bdaa9a143698dbc8b23bd742Timo Sirainen conn->request_queue = aqueue_init(&conn->request_array.arr);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid db_ldap_unref(struct ldap_connection **_conn)
b321df9603081896b70ec44635af96d674a9839aTimo Sirainen for (p = &ldap_connections; *p != NULL; p = &(*p)->next) {
3cff7935d606a75357472a3e4269e0b06ac1bef2Timo Sirainen db_ldap_abort_requests(conn, -1U, 0, FALSE, "Shutting down");
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainenvoid db_ldap_check_userdb_warning(struct ldap_connection *conn)
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen const struct ldap_settings *def = &default_ldap_settings;
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen if (worker || conn->userdb_used || conn->set.userdb_warning_disable)
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen if (strcmp(conn->set.user_attrs, def->user_attrs) != 0)
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen else if (strcmp(conn->set.user_filter, def->user_filter) != 0)
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen else if (strcmp(conn->set.iterate_attrs, def->iterate_attrs) != 0)
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen else if (strcmp(conn->set.iterate_filter, def->iterate_filter) != 0)
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen "because userdb ldap not used. "
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen "(If this is intentional, set userdb_warning_disable=yes)",
be20a7ddf87cb56ee63016dd0029f0c523be09b6Timo Sirainen/* Building a plugin */
cc03958ccda8258252c512412f8d5600ce383b14Timo Sirainenextern struct passdb_module_interface passdb_ldap_plugin;
cc03958ccda8258252c512412f8d5600ce383b14Timo Sirainenextern struct userdb_module_interface userdb_ldap_plugin;
cc03958ccda8258252c512412f8d5600ce383b14Timo Sirainen passdb_unregister_module(&passdb_ldap_plugin);