client-common-auth.c revision 2e37d45867d081db150ab78dad303b9077aea24f
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "login-common.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "istream.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "ostream.h"
3343a61404603b21c246783a7963b77833095f31Timo Sirainen#include "str.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "safe-memset.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "login-proxy.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "auth-client.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "client-common.h"
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include <stdlib.h>
3ed2d0f6b5e67e2663d44489d9da3176823789a8Timo Sirainen
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen#define PROXY_FAILURE_MSG "Account is temporarily unavailable."
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen#define LOGIN_DNS_CLIENT_SOCKET_PATH "dns-client"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen/* If we've been waiting auth server to respond for over this many milliseconds,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen send a "waiting" message. */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#define AUTH_WAITING_TIMEOUT_MSECS (30*1000)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#define CLIENT_AUTH_BUF_MAX_SIZE 8192
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainenvoid client_auth_failed(struct client *client)
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen{
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen i_free_and_null(client->master_data_prefix);
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen if (client->auth_initializing || client->destroyed)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen return;
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen if (client->io != NULL)
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen io_remove(&client->io);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
3f190f4cbb9233a3a6830956cb5c7ae56a577b79Timo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
3f190f4cbb9233a3a6830956cb5c7ae56a577b79Timo Sirainen client_input(client);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenstatic void client_auth_waiting_timeout(struct client *client)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen{
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_STATUS,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen client->master_tag == 0 ?
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen timeout_remove(&client->to_auth_waiting);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenvoid client_set_auth_waiting(struct client *client)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen{
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen i_assert(client->to_auth_waiting == NULL);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen client->to_auth_waiting =
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen timeout_add(AUTH_WAITING_TIMEOUT_MSECS,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen client_auth_waiting_timeout, client);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenstatic void client_auth_parse_args(struct client *client,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen const char *const *args,
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen struct client_auth_reply *reply_r)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen{
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen const char *key, *value, *p;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainen memset(reply_r, 0, sizeof(*reply_r));
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen for (; *args != NULL; args++) {
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen p = strchr(*args, '=');
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (p == NULL) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen key = *args;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen value = "";
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen } else {
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen key = t_strdup_until(*args, p);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen value = p + 1;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen }
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen if (strcmp(key, "nologin") == 0)
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen reply_r->nologin = TRUE;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen else if (strcmp(key, "proxy") == 0)
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen reply_r->proxy = TRUE;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen else if (strcmp(key, "temp") == 0)
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen reply_r->temp = TRUE;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen else if (strcmp(key, "authz") == 0)
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen reply_r->authz_failure = TRUE;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen else if (strcmp(key, "reason") == 0)
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen reply_r->reason = value;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen else if (strcmp(key, "host") == 0)
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen reply_r->host = value;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen else if (strcmp(key, "port") == 0)
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen reply_r->port = atoi(value);
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen else if (strcmp(key, "destuser") == 0)
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen reply_r->destuser = value;
d42eb03b3a4e79a2da22a1be2de59b95660af2beTimo Sirainen else if (strcmp(key, "pass") == 0)
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen reply_r->password = value;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen else if (strcmp(key, "proxy_timeout") == 0)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen reply_r->proxy_timeout_msecs = 1000*atoi(value);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen else if (strcmp(key, "proxy_refresh") == 0)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen reply_r->proxy_refresh_secs = atoi(value);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen else if (strcmp(key, "master") == 0)
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen reply_r->master_user = value;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen else if (strcmp(key, "ssl") == 0) {
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_YES;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (strcmp(value, "any-cert") == 0)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (reply_r->port == 0)
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen reply_r->port = login_binary.default_ssl_port;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen } else if (strcmp(key, "starttls") == 0) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_YES |
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen PROXY_SSL_FLAG_STARTTLS;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen if (strcmp(value, "any-cert") == 0)
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_ANY_CERT;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen } else if (strcmp(key, "user") == 0) {
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen /* already handled in login-common */
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen } else if (client->set->auth_debug)
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen i_debug("Ignoring unknown passdb extra field: %s", key);
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen }
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen if (reply_r->port == 0)
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen reply_r->port = login_binary.default_port;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen if (reply_r->destuser == NULL)
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen reply_r->destuser = client->virtual_user;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen}
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainenstatic void proxy_free_password(struct client *client)
3d4c24127f4f83259c0f81851184abc34793dbe0Timo Sirainen{
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (client->proxy_password == NULL)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen return;
3d4c24127f4f83259c0f81851184abc34793dbe0Timo Sirainen
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen i_free_and_null(client->proxy_password);
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen}
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenvoid client_proxy_finish_destroy_client(struct client *client)
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen{
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen string_t *str = t_str_new(128);
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen str_printfa(str, "proxy(%s): started proxying to %s:%u",
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen client->virtual_user,
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen login_proxy_get_host(client->login_proxy),
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen login_proxy_get_port(client->login_proxy));
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen /* remote username is different, log it */
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen str_append_c(str, '/');
424236b2b88a5a7bbde5cf6a6b32189ca3437629Timo Sirainen str_append(str, client->proxy_user);
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen }
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen if (client->proxy_master_user != NULL)
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen login_proxy_detach(client->login_proxy);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen client_destroy_success(client, str_c(str));
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen}
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainenvoid client_proxy_log_failure(struct client *client, const char *line)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen{
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen string_t *str = t_str_new(128);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen str_printfa(str, "proxy(%s): Login failed to %s:%u",
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen client->virtual_user,
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen login_proxy_get_host(client->login_proxy),
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen login_proxy_get_port(client->login_proxy));
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen /* remote username is different, log it */
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen str_append_c(str, '/');
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen str_append(str, client->proxy_user);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen }
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen if (client->proxy_master_user != NULL)
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen str_append(str, ": ");
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen str_append(str, line);
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen i_info("%s", str_c(str));
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen}
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainenvoid client_proxy_failed(struct client *client, bool send_line)
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen{
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen if (send_line) {
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen PROXY_FAILURE_MSG);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen }
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen login_proxy_free(&client->login_proxy);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen proxy_free_password(client);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_free_and_null(client->proxy_user);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_free_and_null(client->proxy_master_user);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen /* call this last - it may destroy the client */
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen client_auth_failed(client);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen}
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenstatic const char *get_disconnect_reason(struct istream *input)
e5afebd2df1d4990f7bec2a839260ff2e6d78168Timo Sirainen{
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen errno = input->stream_errno;
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen return errno == 0 || errno == EPIPE ? "Connection closed" :
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen t_strdup_printf("Connection closed: %m");
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenstatic void proxy_input(struct client *client)
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen{
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen struct istream *input;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen const char *line;
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (client->login_proxy == NULL) {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen /* we're just freeing the proxy */
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen return;
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen }
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen input = login_proxy_get_istream(client->login_proxy);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (input == NULL) {
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen if (client->destroyed) {
3343a61404603b21c246783a7963b77833095f31Timo Sirainen /* we came here from client_destroy() */
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen }
3343a61404603b21c246783a7963b77833095f31Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* failed for some reason, probably server disconnected */
3343a61404603b21c246783a7963b77833095f31Timo Sirainen client_proxy_failed(client, TRUE);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen i_assert(!client->destroyed);
switch (i_stream_read(input)) {
case -2:
client_log_err(client, "proxy: Remote input buffer full");
client_proxy_failed(client, TRUE);
return;
case -1:
client_log_err(client, t_strdup_printf(
"proxy: Remote %s:%u disconnected: %s (state=%u)",
login_proxy_get_host(client->login_proxy),
login_proxy_get_port(client->login_proxy),
get_disconnect_reason(input), client->proxy_state));
client_proxy_failed(client, TRUE);
return;
}
while ((line = i_stream_next_line(input)) != NULL) {
if (client->v.proxy_parse_line(client, line) != 0)
break;
}
}
static int proxy_start(struct client *client,
const struct client_auth_reply *reply)
{
struct login_proxy_settings proxy_set;
i_assert(reply->destuser != NULL);
i_assert(!client->destroyed);
client->v.proxy_reset(client);
if (reply->password == NULL) {
client_log_err(client, "proxy: password not given");
client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
PROXY_FAILURE_MSG);
return -1;
}
if (reply->host == NULL || *reply->host == '\0') {
client_log_err(client, "proxy: host not given");
client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
PROXY_FAILURE_MSG);
return -1;
}
i_assert(client->refcount > 1);
if (client->destroyed) {
/* connection_queue_add() decided that we were the oldest
connection and killed us. */
return -1;
}
if (login_proxy_is_ourself(client, reply->host, reply->port,
reply->destuser)) {
client_log_err(client, "Proxying loops to itself");
client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
PROXY_FAILURE_MSG);
return -1;
}
memset(&proxy_set, 0, sizeof(proxy_set));
proxy_set.host = reply->host;
proxy_set.port = reply->port;
proxy_set.dns_client_socket_path = LOGIN_DNS_CLIENT_SOCKET_PATH;
proxy_set.connect_timeout_msecs = reply->proxy_timeout_msecs;
proxy_set.notify_refresh_secs = reply->proxy_refresh_secs;
proxy_set.ssl_flags = reply->ssl_flags;
if (login_proxy_new(client, &proxy_set, proxy_input) < 0) {
client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
PROXY_FAILURE_MSG);
return -1;
}
client->proxy_user = i_strdup(reply->destuser);
client->proxy_master_user = i_strdup(reply->master_user);
client->proxy_password = i_strdup(reply->password);
/* disable input until authentication is finished */
if (client->io != NULL)
io_remove(&client->io);
return 0;
}
static bool
client_auth_handle_reply(struct client *client,
const struct client_auth_reply *reply, bool success)
{
if (reply->proxy) {
/* we want to proxy the connection to another server.
don't do this unless authentication succeeded. with
master user proxying we can get FAIL with proxy still set.
proxy host=.. [port=..] [destuser=..] pass=.. */
if (!success)
return FALSE;
if (proxy_start(client, reply) < 0)
client_auth_failed(client);
return TRUE;
}
return client->v.auth_handle_reply(client, reply);
}
static int client_auth_read_line(struct client *client)
{
const unsigned char *data;
size_t i, size;
unsigned int len;
if (i_stream_read_data(client->input, &data, &size, 0) == -1) {
client_destroy(client, "Disconnected");
return -1;
}
/* see if we have a full line */
for (i = 0; i < size; i++) {
if (data[i] == '\n')
break;
}
if (str_len(client->auth_response) + i > CLIENT_AUTH_BUF_MAX_SIZE) {
client_destroy(client, "Authentication response too large");
return -1;
}
str_append_n(client->auth_response, data, i);
i_stream_skip(client->input, i == size ? size : i+1);
/* drop trailing \r */
len = str_len(client->auth_response);
if (len > 0 && str_c(client->auth_response)[len-1] == '\r')
str_truncate(client->auth_response, len-1);
return i < size;
}
int client_auth_parse_response(struct client *client)
{
int ret;
if ((ret = client_auth_read_line(client)) <= 0)
return ret;
if (strcmp(str_c(client->auth_response), "*") == 0) {
sasl_server_auth_abort(client);
return -1;
}
return 1;
}
static void client_auth_input(struct client *client)
{
if (client->v.auth_parse_response(client) <= 0)
return;
client_set_auth_waiting(client);
auth_client_request_continue(client->auth_request,
str_c(client->auth_response));
io_remove(&client->io);
memset(str_c_modifiable(client->auth_response), 0,
str_len(client->auth_response));
}
void client_auth_send_challenge(struct client *client, const char *data)
{
struct const_iovec iov[3];
iov[0].iov_base = "+ ";
iov[0].iov_len = 2;
iov[1].iov_base = data;
iov[1].iov_len = strlen(data);
iov[2].iov_base = "\r\n";
iov[2].iov_len = 2;
(void)o_stream_sendv(client->output, iov, 3);
}
static void
sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
const char *data, const char *const *args)
{
struct client_auth_reply reply;
i_assert(!client->destroyed ||
sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED ||
sasl_reply == SASL_SERVER_REPLY_MASTER_FAILED);
switch (sasl_reply) {
case SASL_SERVER_REPLY_SUCCESS:
if (client->to_auth_waiting != NULL)
timeout_remove(&client->to_auth_waiting);
if (args != NULL) {
client_auth_parse_args(client, args, &reply);
if (client_auth_handle_reply(client, &reply, TRUE))
break;
}
client_destroy_success(client, "Login");
break;
case SASL_SERVER_REPLY_AUTH_FAILED:
case SASL_SERVER_REPLY_AUTH_ABORTED:
if (client->to_auth_waiting != NULL)
timeout_remove(&client->to_auth_waiting);
if (args != NULL) {
client_auth_parse_args(client, args, &reply);
reply.nologin = TRUE;
if (client_auth_handle_reply(client, &reply, FALSE))
break;
}
if (sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED) {
client_send_line(client, CLIENT_CMD_REPLY_BAD,
"Authentication aborted by client.");
} else if (data == NULL) {
client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAILED,
AUTH_FAILED_MSG);
} else {
client_send_line(client,
CLIENT_CMD_REPLY_AUTH_FAIL_REASON,
data);
}
if (!client->destroyed)
client_auth_failed(client);
break;
case SASL_SERVER_REPLY_MASTER_FAILED:
if (data != NULL) {
/* authentication itself succeeded, we just hit some
internal failure. */
client_send_line(client,
CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data);
}
/* the fd may still be hanging somewhere in kernel or another
process. make sure the client gets disconnected. */
if (shutdown(client->fd, SHUT_RDWR) < 0 && errno != ENOTCONN)
i_error("shutdown() failed: %m");
if (data == NULL)
client_destroy_internal_failure(client);
else
client_destroy_success(client, data);
break;
case SASL_SERVER_REPLY_CONTINUE:
client->v.auth_send_challenge(client, data);
if (client->to_auth_waiting != NULL)
timeout_remove(&client->to_auth_waiting);
str_truncate(client->auth_response, 0);
i_assert(client->io == NULL);
client->io = io_add(client->fd, IO_READ,
client_auth_input, client);
client_auth_input(client);
return;
}
client_unref(&client);
}
int client_auth_begin(struct client *client, const char *mech_name,
const char *init_resp)
{
if (!client->secured && strcmp(client->set->ssl, "required") == 0) {
if (client->set->verbose_auth) {
client_log(client, "Login failed: "
"SSL required for authentication");
}
client->auth_attempts++;
client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
"Authentication not allowed until SSL/TLS is enabled.");
return 1;
}
if (client->auth_response == NULL)
client->auth_response = str_new(default_pool, 256);
client_ref(client);
client->auth_initializing = TRUE;
sasl_server_auth_begin(client, login_binary.protocol, mech_name,
init_resp, sasl_callback);
client->auth_initializing = FALSE;
if (!client->authenticating)
return 1;
/* don't handle input until we get the initial auth reply */
if (client->io != NULL)
io_remove(&client->io);
client_set_auth_waiting(client);
return 0;
}
bool client_check_plaintext_auth(struct client *client, bool pass_sent)
{
if (client->secured || !client->set->disable_plaintext_auth)
return TRUE;
if (client->set->verbose_auth) {
client_log(client, "Login failed: "
"Plaintext authentication disabled");
}
if (pass_sent) {
client_send_line(client, CLIENT_CMD_REPLY_STATUS_BAD,
"Plaintext authentication not allowed "
"without SSL/TLS, but your client did it anyway. "
"If anyone was listening, the password was exposed.");
}
client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_NOSSL,
AUTH_PLAINTEXT_DISABLED_MSG);
client->auth_tried_disabled_plaintext = TRUE;
client->auth_attempts++;
return FALSE;
}
void clients_notify_auth_connected(void)
{
struct client *client, *next;
for (client = clients; client != NULL; client = next) {
next = client->next;
if (client->to_auth_waiting != NULL)
timeout_remove(&client->to_auth_waiting);
if (!client->greeting_sent)
client->v.send_greeting(client);
if (client->input_blocked) {
client->input_blocked = FALSE;
client_input(client);
}
}
}