client-common-auth.c revision de754cb78f75e8b3b994cddafe41c9ed1467c33d
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2002-2013 Dovecot authors, see the included COPYING file */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#define PROXY_FAILURE_MSG "Account is temporarily unavailable."
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen/* If we've been waiting auth server to respond for over this many milliseconds,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen send a "waiting" message. */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen#define AUTH_WAITING_WARNING_TIMEOUT_MSECS (10*1000)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic void client_auth_failed(struct client *client)
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen if (client->auth_initializing || client->destroyed)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic void client_auth_waiting_timeout(struct client *client)
6766440ffdd8d3ff99a1f732eef212813089bb81Timo Sirainen client_log_warn(client, "Auth process not responding, "
713a54f695b8ad63826d22ebbe52f55c347e8c88Timo Sirainen "delayed sending initial response (greeting)");
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen client_notify_status(client, FALSE, client->master_tag == 0 ?
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenvoid client_set_auth_waiting(struct client *client)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic void client_auth_parse_args(struct client *client,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen const char *const *args,
4d981bcd01bd1dd5248095b0e59957949332657eTimo Sirainen reply_r->proxy_timeout_msecs = 1000*atoi(value);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen reply_r->port = login_binary->default_ssl_port;
4d981bcd01bd1dd5248095b0e59957949332657eTimo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
4d981bcd01bd1dd5248095b0e59957949332657eTimo Sirainen /* already handled in login-common */
4d981bcd01bd1dd5248095b0e59957949332657eTimo Sirainen i_debug("Ignoring unknown passdb extra field: %s", key);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic void proxy_free_password(struct client *client)
f79ba7136a322a10309e5dad9a22d568fe89fb59Timo Sirainen safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenvoid client_proxy_finish_destroy_client(struct client *client)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen /* input stream got closed in client_send_raw_data().
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen In most places we don't have to check for this explicitly,
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen but login_proxy_detach() attempts to get and use the
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen istream's fd, which is now -1. */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen str_printfa(str, "proxy(%s): started proxying to %s:%u",
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen /* remote username is different, log it */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenstatic void client_proxy_error(struct client *client, const char *text)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenvoid client_proxy_log_failure(struct client *client, const char *line)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen str_printfa(str, "proxy(%s): Login failed to %s:%u",
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen /* remote username is different, log it */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenvoid client_proxy_failed(struct client *client, bool send_line)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen dsasl_client_free(&client->proxy_sasl_client);
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen /* call this last - it may destroy the client */
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainenstatic const char *get_disconnect_reason(struct istream *input)
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen return errno == 0 || errno == EPIPE ? "Connection closed" :
a601cdf61506674a681195acfe57c9864bd3f7acTimo Sirainen /* we're just freeing the proxy */
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen input = login_proxy_get_istream(client->login_proxy);
01c4df2aded7cb44bbaf69d61bd6d9e496b919a4Timo Sirainen /* we came here from client_destroy() */
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen /* failed for some reason, probably server disconnected */
f86f7ec83f6626701137ad86fc7c7d431367ea83Timo Sirainen client_log_err(client, "proxy: Remote input buffer full");
3d8059535db10bacdb96025533d43e826f6762ebTimo Sirainen "proxy: Remote %s:%u disconnected: %s "
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen "(state=%u, duration=%us)%s",
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen while ((line = i_stream_next_line(input)) != NULL) {
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen if (client->v.proxy_parse_line(client, line) != 0)
01c4df2aded7cb44bbaf69d61bd6d9e496b919a4Timo Sirainen const struct dsasl_client_mech *sasl_mech = NULL;
01c4df2aded7cb44bbaf69d61bd6d9e496b919a4Timo Sirainen client_log_err(client, "proxy: password not given");
01c4df2aded7cb44bbaf69d61bd6d9e496b919a4Timo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
01c4df2aded7cb44bbaf69d61bd6d9e496b919a4Timo Sirainen if (reply->host == NULL || *reply->host == '\0') {
01c4df2aded7cb44bbaf69d61bd6d9e496b919a4Timo Sirainen client_log_err(client, "proxy: host not given");
01c4df2aded7cb44bbaf69d61bd6d9e496b919a4Timo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
01c4df2aded7cb44bbaf69d61bd6d9e496b919a4Timo Sirainen sasl_mech = dsasl_client_mech_find(reply->proxy_mech);
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen "proxy: Unsupported SASL mechanism %s",
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen /* have to use PLAIN authentication with master user logins */
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen /* connection_queue_add() decided that we were the oldest
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen connection and killed us. */
f9ca41ec7a26e8e571e268ee5c8da2e5745a4ab0Timo Sirainen if (login_proxy_is_ourself(client, reply->host, reply->port,
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen client_log_err(client, "Proxying loops to itself");
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
01c4df2aded7cb44bbaf69d61bd6d9e496b919a4Timo Sirainen net_addr2ip(reply->hostip, &proxy_set.ip) < 0)
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen proxy_set.connect_timeout_msecs = reply->proxy_timeout_msecs;
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen proxy_set.connect_timeout_msecs = PROXY_DEFAULT_TIMEOUT_MSECS;
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen proxy_set.notify_refresh_secs = reply->proxy_refresh_secs;
f9ca41ec7a26e8e571e268ee5c8da2e5745a4ab0Timo Sirainen if (login_proxy_new(client, &proxy_set, proxy_input) < 0) {
f9ca41ec7a26e8e571e268ee5c8da2e5745a4ab0Timo Sirainen client_proxy_error(client, PROXY_FAILURE_MSG);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen client->proxy_user = i_strdup(reply->destuser);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen client->proxy_master_user = i_strdup(reply->master_user);
f9ca41ec7a26e8e571e268ee5c8da2e5745a4ab0Timo Sirainen client->proxy_password = i_strdup(reply->password);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen /* disable input until authentication is finished */
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainenclient_auth_result(struct client *client, enum client_auth_result result,
d4b03b1b224603afbc2468f07ffb80cc872b1f9fTimo Sirainen const struct client_auth_reply *reply, const char *text)
d4b03b1b224603afbc2468f07ffb80cc872b1f9fTimo Sirainen client->v.auth_result(client, result, reply, text);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainenclient_auth_handle_reply(struct client *client,
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen const struct client_auth_reply *reply, bool success)
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen /* we want to proxy the connection to another server.
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen don't do this unless authentication succeeded. with
f9ca41ec7a26e8e571e268ee5c8da2e5745a4ab0Timo Sirainen master user proxying we can get FAIL with proxy still set.
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen proxy host=.. [port=..] [destuser=..] pass=.. */
95c13a27352739261c53ecd23fe2f0c3d61aa06fTimo Sirainen reason = "Logged in, but you should use this server instead.";
95c13a27352739261c53ecd23fe2f0c3d61aa06fTimo Sirainen /* Authentication went ok, but for some reason user isn't
95c13a27352739261c53ecd23fe2f0c3d61aa06fTimo Sirainen allowed to log in. Shouldn't probably happen. */
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen timestamp = t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_time);
ca096c557fac1cf87dd5f129c202b2c1d990ff59Timo Sirainen msg = t_strdup_printf(AUTH_TEMP_FAILED_MSG" [%s:%s]",
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen client_auth_result(client, CLIENT_AUTH_RESULT_TEMPFAIL,
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen "Authorization failed");
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainenvoid client_auth_respond(struct client *client, const char *response)
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen auth_client_request_continue(client->auth_request, response);
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainenvoid client_auth_fail(struct client *client, const char *text)
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainenint client_auth_read_line(struct client *client)
c58f46762024d6df2c5ef5590be75c8abbe7e5d7Timo Sirainen const unsigned char *data;
244d1dd1a501d1e4b53d8af9a2704640b276b7daTimo Sirainen unsigned int len;
89f9c7bf3cc041de11974af9ac45f543e9761444Timo Sirainen if (i_stream_read_data(client->input, &data, &size, 0) == -1) {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen /* see if we have a full line */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen for (i = 0; i < size; i++) {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen client->auth_response = str_new(default_pool, I_MAX(i+1, 256));
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (str_len(client->auth_response) + i > LOGIN_MAX_AUTH_BUF_SIZE) {
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen client_destroy(client, "Authentication response too large");
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen i_stream_skip(client->input, i == size ? size : i+1);
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen /* drop trailing \r */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (len > 0 && str_c(client->auth_response)[len-1] == '\r')
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainenvoid client_auth_parse_response(struct client *client)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (strcmp(str_c(client->auth_response), "*") == 0) {
5592b04741577d014760097fc40f0f5276241754Aki Tuomi client_auth_respond(client, str_c(client->auth_response));
5592b04741577d014760097fc40f0f5276241754Aki Tuomi memset(str_c_modifiable(client->auth_response), 0,
5592b04741577d014760097fc40f0f5276241754Aki Tuomistatic void client_auth_input(struct client *client)
5592b04741577d014760097fc40f0f5276241754Aki Tuomivoid client_auth_send_challenge(struct client *client, const char *data)
5592b04741577d014760097fc40f0f5276241754Aki Tuomisasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
5592b04741577d014760097fc40f0f5276241754Aki Tuomi if (client_auth_handle_reply(client, &reply, TRUE))
5592b04741577d014760097fc40f0f5276241754Aki Tuomi client_auth_result(client, CLIENT_AUTH_RESULT_SUCCESS,
5592b04741577d014760097fc40f0f5276241754Aki Tuomi if (client_auth_handle_reply(client, &reply, FALSE))
5592b04741577d014760097fc40f0f5276241754Aki Tuomi if (sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED) {
5592b04741577d014760097fc40f0f5276241754Aki Tuomi client_auth_result(client, CLIENT_AUTH_RESULT_ABORTED,
5592b04741577d014760097fc40f0f5276241754Aki Tuomi /* authentication itself succeeded, we just hit some
5592b04741577d014760097fc40f0f5276241754Aki Tuomi internal failure. */
5592b04741577d014760097fc40f0f5276241754Aki Tuomi client_auth_result(client, CLIENT_AUTH_RESULT_TEMPFAIL,
5592b04741577d014760097fc40f0f5276241754Aki Tuomi /* the fd may still be hanging somewhere in kernel or another
5592b04741577d014760097fc40f0f5276241754Aki Tuomi process. make sure the client gets disconnected. */
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen if (shutdown(client->fd, SHUT_RDWR) < 0 && errno != ENOTCONN)
51f750db859e62e2c58a61806b53e0adb13e0775Timo Sirainen i_assert(client->v.auth_send_challenge != NULL);
const char *init_resp)
return TRUE;
if (pass_sent) {
return FALSE;
void clients_notify_auth_connected(void)