client-common-auth.c revision d3a7d023b47d2a137f01109e7b38702dca3f11d3
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "common.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "istream.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "ostream.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "str.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "safe-memset.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "login-proxy.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "auth-client.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "client-common.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include <stdlib.h>
f6c1297c26b355c4aec2a08978f51ec3efecb351Timo Sirainen
f6c1297c26b355c4aec2a08978f51ec3efecb351Timo Sirainen/* If we've been waiting auth server to respond for over this many milliseconds,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen send a "waiting" message. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#define AUTH_WAITING_TIMEOUT_MSECS (30*1000)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#define AUTH_FAILURE_DELAY_INCREASE_MSECS 5000
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS < AUTH_REQUEST_TIMEOUT*1000
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen# error client idle timeout must be larger than authentication timeout
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#endif
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic void client_authfail_delay_timeout(struct client *client)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout_remove(&client->to_authfail_delay);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* get back to normal client input. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(client->io == NULL);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
4ea6c43a08b37f270bd54b5809142246fd118263Timo Sirainen client_input(client);
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenvoid client_auth_failed(struct client *client, bool nodelay)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unsigned int delay_msecs;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_free_and_null(client->master_data_prefix);
4ea6c43a08b37f270bd54b5809142246fd118263Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->auth_initializing)
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen return;
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->io != NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen io_remove(&client->io);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (nodelay) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_input(client);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* increase the timeout after each unsuccessful attempt, but don't
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen increase it so high that the idle timeout would be triggered */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen delay_msecs = client->auth_attempts * AUTH_FAILURE_DELAY_INCREASE_MSECS;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (delay_msecs > CLIENT_LOGIN_IDLE_TIMEOUT_MSECS)
2769eecf814bd243033dcbf5bdc38f4162d3202dTimo Sirainen delay_msecs = CLIENT_LOGIN_IDLE_TIMEOUT_MSECS - 1000;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(client->to_authfail_delay == NULL);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->to_authfail_delay =
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout_add(delay_msecs, client_authfail_delay_timeout, client);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
2769eecf814bd243033dcbf5bdc38f4162d3202dTimo Sirainenstatic void client_auth_waiting_timeout(struct client *client)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_STATUS,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->master_tag == 0 ?
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout_remove(&client->to_auth_waiting);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenvoid client_set_auth_waiting(struct client *client)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(client->to_auth_waiting == NULL);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->to_auth_waiting =
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen timeout_add(AUTH_WAITING_TIMEOUT_MSECS,
bda9a6d9b021c122a01a85cb3cee2f996263d8f0Timo Sirainen client_auth_waiting_timeout, client);
e17d463c0d18233d1184f5b9237ff3f514f031ceTimo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic void client_auth_parse_args(struct client *client,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const char *const *args,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct client_auth_reply *reply_r)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const char *key, *value, *p;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
2769eecf814bd243033dcbf5bdc38f4162d3202dTimo Sirainen memset(reply_r, 0, sizeof(*reply_r));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->port = login_default_port;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen for (; *args != NULL; args++) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen p = strchr(*args, '=');
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (p == NULL) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen key = *args;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen value = "";
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen key = t_strdup_until(*args, p);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen value = p + 1;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (strcmp(key, "nologin") == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->nologin = TRUE;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen else if (strcmp(key, "nodelay") == 0)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen reply_r->nodelay = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (strcmp(key, "proxy") == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->proxy = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (strcmp(key, "temp") == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->temp = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (strcmp(key, "authz") == 0)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen reply_r->authz_failure = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (strcmp(key, "reason") == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->reason = value;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (strcmp(key, "host") == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->host = value;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (strcmp(key, "port") == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->port = atoi(value);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (strcmp(key, "destuser") == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->destuser = value;
bda9a6d9b021c122a01a85cb3cee2f996263d8f0Timo Sirainen else if (strcmp(key, "pass") == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->password = value;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (strcmp(key, "master") == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->master_user = value;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (strcmp(key, "ssl") == 0) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (strcmp(value, "yes") == 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_YES;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else if (strcmp(value, "any-cert") == 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_YES |
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen PROXY_SSL_FLAG_ANY_CERT;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else if (strcmp(key, "starttls") == 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_STARTTLS;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else if (strcmp(key, "user") == 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* already handled in login-common */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else if (client->set->auth_debug)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen i_info("Ignoring unknown passdb extra field: %s", key);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
f1e9611e93dcb3b745c1904029084fa81644e1b3Timo Sirainen if (reply_r->destuser == NULL)
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen reply_r->destuser = client->virtual_user;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic void proxy_free_password(struct client *client)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->proxy_password == NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_free_and_null(client->proxy_password);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenvoid client_proxy_finish_destroy_client(struct client *client)
bda9a6d9b021c122a01a85cb3cee2f996263d8f0Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen string_t *str = t_str_new(128);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_printfa(str, "proxy(%s): started proxying to %s:%u",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->virtual_user,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen login_proxy_get_host(client->login_proxy),
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen login_proxy_get_port(client->login_proxy));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* remote username is different, log it */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append_c(str, '/');
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append(str, client->proxy_user);
7c7a364a72d4edd1701df72fee835c09db19d933Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->proxy_master_user != NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen login_proxy_detach(client->login_proxy, client->input, client->output);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->login_proxy = NULL;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->input = NULL;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->output = NULL;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->fd = -1;
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen client->proxying = TRUE;
bf9ea5404a0094a8fb8199b677d81f803512c44eTimo Sirainen client_destroy_success(client, str_c(str));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenvoid client_proxy_log_failure(struct client *client, const char *line)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen string_t *str = t_str_new(128);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_printfa(str, "proxy(%s): Login failed to %s:%u",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client->virtual_user,
f8da06de93e28b5d3e039a427cdde7e1e15daec8Timo Sirainen login_proxy_get_host(client->login_proxy),
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen login_proxy_get_port(client->login_proxy));
66a872b4569c048e804f0731680d89c6042d8890Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
66a872b4569c048e804f0731680d89c6042d8890Timo Sirainen /* remote username is different, log it */
66a872b4569c048e804f0731680d89c6042d8890Timo Sirainen str_append_c(str, '/');
378d924da5853145a6df9a299074f04be69986c7Timo Sirainen str_append(str, client->proxy_user);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->proxy_master_user != NULL)
f8da06de93e28b5d3e039a427cdde7e1e15daec8Timo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append(str, ": ");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen str_append(str, line);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_info("%s", str_c(str));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
void client_proxy_failed(struct client *client, bool send_line)
{
if (send_line) {
client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
AUTH_TEMP_FAILED_MSG);
}
login_proxy_free(&client->login_proxy);
proxy_free_password(client);
i_free_and_null(client->proxy_user);
i_free_and_null(client->proxy_master_user);
/* call this last - it may destroy the client */
client_auth_failed(client, TRUE);
}
static void proxy_input(struct client *client)
{
struct istream *input;
const char *line;
if (client->login_proxy == NULL) {
/* we're just freeing the proxy */
return;
}
input = login_proxy_get_istream(client->login_proxy);
if (input == NULL) {
if (client->destroyed) {
/* we came here from client_destroy() */
return;
}
/* failed for some reason, probably server disconnected */
client_proxy_failed(client, TRUE);
return;
}
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, "proxy: Remote disconnected");
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)
{
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,
AUTH_TEMP_FAILED_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,
AUTH_TEMP_FAILED_MSG);
return -1;
}
client->login_proxy =
login_proxy_new(client, reply->host, reply->port,
reply->ssl_flags, proxy_input, client);
if (client->login_proxy == NULL) {
client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
AUTH_TEMP_FAILED_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, TRUE);
return TRUE;
}
return client->v.auth_handle_reply(client, reply);
}
static void client_auth_input(struct client *client)
{
char *line;
if (!client_read(client))
return;
/* @UNSAFE */
line = i_stream_next_line(client->input);
if (line == NULL)
return;
if (strcmp(line, "*") == 0)
sasl_server_auth_abort(client);
else {
client_set_auth_waiting(client);
auth_client_request_continue(client->auth_request, line);
io_remove(&client->io);
/* clear sensitive data */
safe_memset(line, 0, strlen(line));
}
}
static void
sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
const char *data, const char *const *args)
{
struct const_iovec iov[3];
struct client_auth_reply reply;
size_t data_len;
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, reply.nodelay);
break;
case SASL_SERVER_REPLY_MASTER_FAILED:
if (data == NULL)
client_destroy_internal_failure(client);
else {
client_send_line(client,
CLIENT_CMD_REPLY_AUTH_FAIL_TEMP, data);
/* authentication itself succeeded, we just hit some
internal failure. */
client_destroy_success(client, data);
}
break;
case SASL_SERVER_REPLY_CONTINUE:
data_len = strlen(data);
iov[0].iov_base = "+ ";
iov[0].iov_len = 2;
iov[1].iov_base = data;
iov[1].iov_len = data_len;
iov[2].iov_base = "\r\n";
iov[2].iov_len = 2;
/* don't check return value here. it gets tricky if we try
to call client_destroy() in here. */
(void)o_stream_sendv(client->output, iov, 3);
if (client->to_auth_waiting != NULL)
timeout_remove(&client->to_auth_waiting);
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;
}
client_ref(client);
client->auth_initializing = TRUE;
sasl_server_auth_begin(client, login_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;
for (client = clients; client != NULL; client = 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);
}
}
}