client-common-auth.c revision f32d0295c90ed810889504cdfa5e1a25a415f65f
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen#define PROXY_FAILURE_MSG "Account is temporarily unavailable."
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen/* If we've been waiting auth server to respond for over this many milliseconds,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen send a "waiting" message. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define AUTH_WAITING_WARNING_TIMEOUT_MSECS (10*1000)
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen const char *id;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenstatic const struct client_auth_fail_code_id client_auth_fail_codes[] = {
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainenclient_auth_fail_code_lookup(const char *fail_code)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen const struct client_auth_fail_code_id *fail = client_auth_fail_codes;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenstatic void client_auth_failed(struct client *client)
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen if (client->auth_initializing || client->destroyed)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void client_auth_waiting_timeout(struct client *client)
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen client_log_warn(client, "Auth process not responding, "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "delayed sending initial response (greeting)");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_notify_status(client, FALSE, client->master_tag == 0 ?
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainenvoid client_set_auth_waiting(struct client *client)
993e6c2caaae971dd3c34913a42d854e3b623261Timo Sirainenstatic void alt_username_set(ARRAY_TYPE(const_string) *alt_usernames, pool_t pool,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen char *const *fields;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen unsigned int i, count;
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen fields = array_get(&global_alt_usernames, &count);
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen for (i = 0; i < count; i++) {
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen array_append(&global_alt_usernames, &new_key, 1);
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen /* array is NULL-terminated, so if there are unused fields in
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen the middle set them as "" */
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainenstatic void client_auth_parse_args(struct client *client, bool success,
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen const char *const *args,
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen reply_r->fail_code = CLIENT_AUTH_FAIL_CODE_LOGIN_DISABLED;
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen if (net_str2port(value, &reply_r->port) < 0) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (str_to_uint(value, &reply_r->proxy_timeout_msecs) < 0) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen } else if (strcmp(key, "proxy_refresh") == 0) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (str_to_uint(value, &reply_r->proxy_refresh_secs) < 0) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen else if (strcmp(key, "proxy_nopipelining") == 0)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen else if (strcmp(key, "proxy_not_trusted") == 0)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen reply_r->port = login_binary->default_ssl_port;
c21c33a8c98972c45349066fc76ac9e2c05013c1Timo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (reply_r->fail_code != CLIENT_AUTH_FAIL_CODE_NONE) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* code already assigned */
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen reply_r->fail_code = client_auth_fail_code_lookup(value);
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen /* already handled in sasl-server.c */
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen alt_username_set(&alt_usernames, client->pool,
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen i_debug("Ignoring unknown passdb extra field: %s", key);
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen const char **alt;
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainenstatic void proxy_free_password(struct client *client)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainenvoid client_proxy_finish_destroy_client(struct client *client)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* input stream got closed in client_send_raw_data().
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen In most places we don't have to check for this explicitly,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen but login_proxy_detach() attempts to get and use the
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen istream's fd, which is now -1. */
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen str_printfa(str, "proxy(%s): started proxying to %s:%u",
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
527ed64bc924b4a13b570a8450f8be3efdf71879Timo Sirainen /* remote username is different, log it */
688f0fdfb7ca946e38ae34459b0ca30b71c8457cTimo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void client_proxy_error(struct client *client, const char *text)
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainenvoid client_proxy_log_failure(struct client *client, const char *line)
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen str_printfa(str, "proxy(%s): Login failed to %s:%u",
c680a6b35b459045e92814778908da5a93922107Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
c680a6b35b459045e92814778908da5a93922107Timo Sirainen /* remote username is different, log it */
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
c680a6b35b459045e92814778908da5a93922107Timo Sirainenvoid client_proxy_failed(struct client *client, bool send_line)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen dsasl_client_free(&client->proxy_sasl_client);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* call this last - it may destroy the client */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we're just freeing the proxy */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen input = login_proxy_get_istream(client->login_proxy);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* we came here from client_destroy() */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* failed for some reason, probably server disconnected */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen client_log_err(client, "proxy: Remote input buffer full");
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen "proxy: Remote %s:%u disconnected: %s "
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen "(state=%u, duration=%us)%s",
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen while ((line = i_stream_next_line(input)) != NULL) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (client->v.proxy_parse_line(client, line) != 0)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen const struct dsasl_client_mech *sasl_mech = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_log_err(client, "proxy: password not given");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (reply->host == NULL || *reply->host == '\0') {
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen client_log_err(client, "proxy: host not given");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sasl_mech = dsasl_client_mech_find(reply->proxy_mech);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "proxy: Unsupported SASL mechanism %s",
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen /* have to use PLAIN authentication with master user logins */
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen /* connection_queue_add() decided that we were the oldest
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen connection and killed us. */
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen if (login_proxy_is_ourself(client, reply->host, reply->port,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen client_log_err(client, "Proxying loops to itself");
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen net_addr2ip(reply->hostip, &proxy_set.ip) < 0)
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen if (net_addr2ip(reply->source_ip, &proxy_set.source_ip) < 0)
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen } else if (login_source_ips_count > 0) {
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainen /* select the next source IP with round robin. */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen proxy_set.source_ip = login_source_ips[login_source_ips_idx];
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen (login_source_ips_idx + 1) % login_source_ips_count;
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen proxy_set.connect_timeout_msecs = reply->proxy_timeout_msecs;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen proxy_set.connect_timeout_msecs = PROXY_DEFAULT_TIMEOUT_MSECS;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen proxy_set.notify_refresh_secs = reply->proxy_refresh_secs;
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen if (login_proxy_new(client, &proxy_set, proxy_input) < 0) {
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen client->proxy_user = i_strdup(reply->destuser);
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen client->proxy_master_user = i_strdup(reply->master_user);
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen client->proxy_password = i_strdup(reply->password);
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen client->proxy_nopipelining = reply->proxy_nopipelining;
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen client->proxy_not_trusted = reply->proxy_not_trusted;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* disable input until authentication is finished */
0c909e3461607eadcd66f4eac69b7f34e37fccf1Timo Sirainenclient_auth_result(struct client *client, enum client_auth_result result,
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen const struct client_auth_reply *reply, const char *text)
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen client->v.auth_result(client, result, reply, text);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainenclient_auth_handle_reply(struct client *client,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen const struct client_auth_reply *reply, bool success)
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* we want to proxy the connection to another server.
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen don't do this unless authentication succeeded. with
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen master user proxying we can get FAIL with proxy still set.
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen proxy host=.. [port=..] [destuser=..] pass=.. */
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen /* this for plugins being able th hook into auth reply
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen when proxying is used */
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen client_auth_result(client, CLIENT_AUTH_RESULT_SUCCESS,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen reason = "Logged in, but you should use this server instead.";
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen enum client_auth_result result = CLIENT_AUTH_RESULT_AUTHFAILED;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen const char *timestamp, *reason = reply->reason;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* Either failed or user login is disabled */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen timestamp = t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_time);
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen reason = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen result = CLIENT_AUTH_RESULT_AUTHFAILED_REASON;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_auth_result(client, result, reply, reason);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainenvoid client_auth_respond(struct client *client, const char *response)
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen auth_client_request_continue(client->auth_request, response);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid client_auth_fail(struct client *client, const char *text)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint client_auth_read_line(struct client *client)
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen const unsigned char *data;
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen unsigned int len;
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen if (i_stream_read_more(client->input, &data, &size) == -1) {
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen /* see if we have a full line */
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen for (i = 0; i < size; i++) {
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen client->auth_response = str_new(default_pool, I_MAX(i+1, 256));
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen if (str_len(client->auth_response) + i > LOGIN_MAX_AUTH_BUF_SIZE) {
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen client_destroy(client, "Authentication response too large");
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen i_stream_skip(client->input, i == size ? size : i+1);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* drop trailing \r */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (len > 0 && str_c(client->auth_response)[len-1] == '\r')
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainenvoid client_auth_parse_response(struct client *client)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (strcmp(str_c(client->auth_response), "*") == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_auth_respond(client, str_c(client->auth_response));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memset(str_c_modifiable(client->auth_response), 0,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void client_auth_input(struct client *client)
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen i_assert(client->v.auth_parse_response != NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid client_auth_send_challenge(struct client *client, const char *data)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainensasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED ||
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen sasl_reply == SASL_SERVER_REPLY_MASTER_FAILED);
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen client->last_auth_fail = CLIENT_AUTH_FAIL_CODE_NONE;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen client_auth_parse_args(client, TRUE, args, &reply);
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen if (client_auth_handle_reply(client, &reply, TRUE))
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen client_auth_result(client, CLIENT_AUTH_RESULT_SUCCESS,
data);
const char *init_resp)
return TRUE;
if (pass_sent) {
return FALSE;
void clients_notify_auth_connected(void)