client-common-auth.c revision b9c76fe9d9ca194816606342da1ddbd9be6bc8ab
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#define PROXY_FAILURE_MSG "Account is temporarily unavailable."
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#define LOGIN_DNS_CLIENT_SOCKET_PATH "dns-client"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen/* If we've been waiting auth server to respond for over this many milliseconds,
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen send a "waiting" message. */
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#define GREETING_WARNING_TIMEOUT_MSECS (10*1000)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if (client->auth_initializing || client->destroyed)
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainenstatic void client_auth_waiting_timeout(struct client *client)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen client_log_warn(client, "Auth process not responding, "
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen "delayed sending greeting");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_STATUS,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenvoid client_set_auth_waiting(struct client *client)
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainenstatic void client_auth_parse_args(struct client *client,
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen const char *const *args,
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen reply_r->proxy_timeout_msecs = 1000*atoi(value);
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen reply_r->port = login_binary->default_ssl_port;
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen /* already handled in login-common */
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen i_debug("Ignoring unknown passdb extra field: %s", key);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void proxy_free_password(struct client *client)
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainenvoid client_proxy_finish_destroy_client(struct client *client)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* input stream got closed in client_send_raw_data().
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen In most places we don't have to check for this explicitly,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen but login_proxy_detach() attempts to get and use the
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen istream's fd, which is now -1. */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen str_printfa(str, "proxy(%s): started proxying to %s:%u",
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen /* remote username is different, log it */
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainenvoid client_proxy_log_failure(struct client *client, const char *line)
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen str_printfa(str, "proxy(%s): Login failed to %s:%u",
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen /* remote username is different, log it */
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainenvoid client_proxy_failed(struct client *client, bool send_line)
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* call this last - it may destroy the client */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainenstatic const char *get_disconnect_reason(struct istream *input)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return errno == 0 || errno == EPIPE ? "Connection closed" :
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* we're just freeing the proxy */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen input = login_proxy_get_istream(client->login_proxy);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* we came here from client_destroy() */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* failed for some reason, probably server disconnected */
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen client_log_err(client, "proxy: Remote input buffer full");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "proxy: Remote %s:%u disconnected: %s "
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "(state=%u, duration=%us)%s",
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen while ((line = i_stream_next_line(input)) != NULL) {
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen if (client->v.proxy_parse_line(client, line) != 0)
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen client_log_err(client, "proxy: password not given");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if (reply->host == NULL || *reply->host == '\0') {
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen client_log_err(client, "proxy: host not given");
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen /* connection_queue_add() decided that we were the oldest
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen connection and killed us. */
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (login_proxy_is_ourself(client, reply->host, reply->port,
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen client_log_err(client, "Proxying loops to itself");
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen proxy_set.dns_client_socket_path = LOGIN_DNS_CLIENT_SOCKET_PATH;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen proxy_set.connect_timeout_msecs = reply->proxy_timeout_msecs;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen proxy_set.notify_refresh_secs = reply->proxy_refresh_secs;
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen if (login_proxy_new(client, &proxy_set, proxy_input) < 0) {
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen client->proxy_user = i_strdup(reply->destuser);
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen client->proxy_master_user = i_strdup(reply->master_user);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen client->proxy_password = i_strdup(reply->password);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* disable input until authentication is finished */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainenclient_auth_handle_reply(struct client *client,
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen const struct client_auth_reply *reply, bool success)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* we want to proxy the connection to another server.
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen don't do this unless authentication succeeded. with
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen master user proxying we can get FAIL with proxy still set.
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen proxy host=.. [port=..] [destuser=..] pass=.. */
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen return client->v.auth_handle_reply(client, reply);
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainenstatic int client_auth_read_line(struct client *client)
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen const unsigned char *data;
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen unsigned int len;
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen if (i_stream_read_data(client->input, &data, &size, 0) == -1) {
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen /* see if we have a full line */
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen for (i = 0; i < size; i++) {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (str_len(client->auth_response) + i > CLIENT_AUTH_BUF_MAX_SIZE) {
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen client_destroy(client, "Authentication response too large");
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen i_stream_skip(client->input, i == size ? size : i+1);
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen /* drop trailing \r */
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (len > 0 && str_c(client->auth_response)[len-1] == '\r')
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenint client_auth_parse_response(struct client *client)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if ((ret = client_auth_read_line(client)) <= 0)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (strcmp(str_c(client->auth_response), "*") == 0) {
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenstatic void client_auth_input(struct client *client)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (client->v.auth_parse_response(client) <= 0)
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen auth_client_request_continue(client->auth_request,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen memset(str_c_modifiable(client->auth_response), 0,
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenvoid client_auth_send_challenge(struct client *client, const char *data)
7b42d6cbee8186195d8c5e66078043a0fa1f25c1Timo Sirainensasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED ||
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen sasl_reply == SASL_SERVER_REPLY_MASTER_FAILED);
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen if (client_auth_handle_reply(client, &reply, TRUE))
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen if (client_auth_handle_reply(client, &reply, FALSE))
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen if (sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED) {
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BAD,
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen "Authentication aborted by client.");
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED,
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen /* authentication itself succeeded, we just hit some
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen internal failure. */
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen /* the fd may still be hanging somewhere in kernel or another
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen process. make sure the client gets disconnected. */
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen if (shutdown(client->fd, SHUT_RDWR) < 0 && errno != ENOTCONN)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenint client_auth_begin(struct client *client, const char *mech_name,
0b47e9f5e0181053b4d9ca7b426b0e5c185e820eTimo Sirainen if (!client->secured && strcmp(client->set->ssl, "required") == 0) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen "SSL required for authentication");
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen "Authentication not allowed until SSL/TLS is enabled.");
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen client->auth_response = str_new(default_pool, 256);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen sasl_server_auth_begin(client, login_binary->protocol, mech_name,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen /* don't handle input until we get the initial auth reply */
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainenbool client_check_plaintext_auth(struct client *client, bool pass_sent)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (client->secured || !client->set->disable_plaintext_auth)
97e62b2b36dda0acb3215667042f5c80cdee8155Timo Sirainen "Plaintext authentication disabled");
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_STATUS_BAD,
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen "Plaintext authentication not allowed "
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen "without SSL/TLS, but your client did it anyway. "
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen "If anyone was listening, the password was exposed.");
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen for (client = clients; client != NULL; client = next) {