client-common-auth.c revision 2ef0e8ee48c9683f7bd6698798efa3328e4322d1
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "login-common.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "istream.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "ostream.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "str.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "safe-memset.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "login-proxy.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "auth-client.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "client-common.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include <stdlib.h>
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#define PROXY_FAILURE_MSG "Account is temporarily unavailable."
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#define LOGIN_DNS_CLIENT_SOCKET_PATH "dns-client"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
0db42260be85e797aa9909a29b20296996f52e75Timo Sirainen/* If we've been waiting auth server to respond for over this many milliseconds,
0db42260be85e797aa9909a29b20296996f52e75Timo Sirainen send a "waiting" message. */
0db42260be85e797aa9909a29b20296996f52e75Timo Sirainen#define AUTH_WAITING_TIMEOUT_MSECS (30*1000)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#define CLIENT_AUTH_BUF_MAX_SIZE 8192
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid client_auth_failed(struct client *client)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_free_and_null(client->master_data_prefix);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (client->auth_initializing)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (client->io != NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen io_remove(&client->io);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_input(client);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic void client_auth_waiting_timeout(struct client *client)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_STATUS,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client->master_tag == 0 ?
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen timeout_remove(&client->to_auth_waiting);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid client_set_auth_waiting(struct client *client)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(client->to_auth_waiting == NULL);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client->to_auth_waiting =
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen timeout_add(AUTH_WAITING_TIMEOUT_MSECS,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_auth_waiting_timeout, client);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainenstatic void client_auth_parse_args(struct client *client,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const char *const *args,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct client_auth_reply *reply_r)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const char *key, *value, *p;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen memset(reply_r, 0, sizeof(*reply_r));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->port = login_default_port;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen for (; *args != NULL; args++) {
0ca3b9cb0f2a322a25ce7f229dc3d3a0b46be17bTimo Sirainen p = strchr(*args, '=');
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (p == NULL) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen key = *args;
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen value = "";
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen key = t_strdup_until(*args, p);
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen value = p + 1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (strcmp(key, "nologin") == 0)
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen reply_r->nologin = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(key, "proxy") == 0)
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen reply_r->proxy = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(key, "temp") == 0)
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen reply_r->temp = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(key, "authz") == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->authz_failure = TRUE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(key, "reason") == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->reason = value;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(key, "host") == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->host = value;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(key, "port") == 0)
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen reply_r->port = atoi(value);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(key, "destuser") == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->destuser = value;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(key, "pass") == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->password = value;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(key, "proxy_timeout") == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->proxy_timeout_msecs = 1000*atoi(value);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(key, "master") == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->master_user = value;
56aa97d74071f3a2987140c2ff1cfd5a59cb35aaTimo Sirainen else if (strcmp(key, "ssl") == 0) {
56aa97d74071f3a2987140c2ff1cfd5a59cb35aaTimo Sirainen if (strcmp(value, "yes") == 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_YES;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else if (strcmp(value, "any-cert") == 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_YES |
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen PROXY_SSL_FLAG_ANY_CERT;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else if (strcmp(key, "starttls") == 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->ssl_flags |= PROXY_SSL_FLAG_STARTTLS;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else if (strcmp(key, "user") == 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* already handled in login-common */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen } else if (client->set->auth_debug)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_debug("Ignoring unknown passdb extra field: %s", key);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (reply_r->destuser == NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply_r->destuser = client->virtual_user;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic void proxy_free_password(struct client *client)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (client->proxy_password == NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_free_and_null(client->proxy_password);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
8c909e451d14075c05d90382cf8eebc4e354f569Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid client_proxy_finish_destroy_client(struct client *client)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen string_t *str = t_str_new(128);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_printfa(str, "proxy(%s): started proxying to %s:%u",
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client->virtual_user,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen login_proxy_get_host(client->login_proxy),
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen login_proxy_get_port(client->login_proxy));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* remote username is different, log it */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_append_c(str, '/');
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_append(str, client->proxy_user);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (client->proxy_master_user != NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen login_proxy_detach(client->login_proxy);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_destroy_success(client, str_c(str));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid client_proxy_log_failure(struct client *client, const char *line)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen string_t *str = t_str_new(128);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_printfa(str, "proxy(%s): Login failed to %s:%u",
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client->virtual_user,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen login_proxy_get_host(client->login_proxy),
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen login_proxy_get_port(client->login_proxy));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (strcmp(client->virtual_user, client->proxy_user) != 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* remote username is different, log it */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_append_c(str, '/');
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_append(str, client->proxy_user);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (client->proxy_master_user != NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_printfa(str, " (master %s)", client->proxy_master_user);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_append(str, ": ");
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_append(str, line);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_info("%s", str_c(str));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid client_proxy_failed(struct client *client, bool send_line)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (send_line) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
9a1f68e5ab08eabd352d533315cba1c69006e2c1Timo Sirainen PROXY_FAILURE_MSG);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen login_proxy_free(&client->login_proxy);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen proxy_free_password(client);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_free_and_null(client->proxy_user);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_free_and_null(client->proxy_master_user);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* call this last - it may destroy the client */
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen client_auth_failed(client);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic void proxy_input(struct client *client)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct istream *input;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const char *line;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (client->login_proxy == NULL) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* we're just freeing the proxy */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen input = login_proxy_get_istream(client->login_proxy);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (input == NULL) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (client->destroyed) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* we came here from client_destroy() */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* failed for some reason, probably server disconnected */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_proxy_failed(client, TRUE);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(!client->destroyed);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen switch (i_stream_read(input)) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen case -2:
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_log_err(client, "proxy: Remote input buffer full");
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_proxy_failed(client, TRUE);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen case -1:
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_log_err(client, "proxy: Remote disconnected");
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_proxy_failed(client, TRUE);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen while ((line = i_stream_next_line(input)) != NULL) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (client->v.proxy_parse_line(client, line) != 0)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen break;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic int proxy_start(struct client *client,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const struct client_auth_reply *reply)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen struct login_proxy_settings proxy_set;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(reply->destuser != NULL);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(!client->destroyed);
56aa97d74071f3a2987140c2ff1cfd5a59cb35aaTimo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client->v.proxy_reset(client);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (reply->password == NULL) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_log_err(client, "proxy: password not given");
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
c6afd726060aae56b6622c6c52aec10231c4bf1cTimo Sirainen PROXY_FAILURE_MSG);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (reply->host == NULL || *reply->host == '\0') {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_log_err(client, "proxy: host not given");
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen PROXY_FAILURE_MSG);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(client->refcount > 1);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (client->destroyed) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* connection_queue_add() decided that we were the oldest
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen connection and killed us. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (login_proxy_is_ourself(client, reply->host, reply->port,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen reply->destuser)) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_log_err(client, "Proxying loops to itself");
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen PROXY_FAILURE_MSG);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen memset(&proxy_set, 0, sizeof(proxy_set));
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen proxy_set.host = reply->host;
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen proxy_set.port = reply->port;
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen proxy_set.dns_client_socket_path = LOGIN_DNS_CLIENT_SOCKET_PATH;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen proxy_set.connect_timeout_msecs = reply->proxy_timeout_msecs;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen proxy_set.ssl_flags = reply->ssl_flags;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (login_proxy_new(client, &proxy_set, proxy_input) < 0) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen PROXY_FAILURE_MSG);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return -1;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client->proxy_user = i_strdup(reply->destuser);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client->proxy_master_user = i_strdup(reply->master_user);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen client->proxy_password = i_strdup(reply->password);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* disable input until authentication is finished */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (client->io != NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen io_remove(&client->io);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic bool
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenclient_auth_handle_reply(struct client *client,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const struct client_auth_reply *reply, bool success)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (reply->proxy) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* we want to proxy the connection to another server.
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen don't do this unless authentication succeeded. with
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen master user proxying we can get FAIL with proxy still set.
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
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)
{
int ret;
if ((ret = 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)
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:
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_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);
}
}
}