sasl-server.c revision e59faf65ce864fe95dc00f5d52b8323cdbd0608a
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "login-common.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "base64.h"
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainen#include "buffer.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "hex-binary.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "istream.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "write-full.h"
a2fdfd2efdbb2d912aad23900a466cf74114920bTimo Sirainen#include "strescape.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "str-sanitize.h"
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi#include "anvil-client.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "auth-client.h"
c6be98b5270900746f35ebe28bd636019976e29eTimo Sirainen#include "ssl-proxy.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "master-service.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "master-interface.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include "master-auth.h"
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi#include "client-common.h"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include <stdlib.h>
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#include <unistd.h>
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen#define ERR_TOO_MANY_USERIP_CONNECTIONS \
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen "Maximum number of connections from user+IP exceeded " \
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen "(mail_max_userip_connections)"
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainenstruct anvil_request {
5fbccc935e3f7b916aa7c6e302a212821072e83aTimo Sirainen struct client *client;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen unsigned int auth_pid, auth_id;
f36c4185474823594a78b3f252e79d8923522c36Timo Sirainen unsigned char cookie[MASTER_AUTH_COOKIE_SIZE];
f36c4185474823594a78b3f252e79d8923522c36Timo Sirainen};
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainenconst struct auth_mech_desc *
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainensasl_server_get_advertised_mechs(struct client *client, unsigned int *count_r)
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen{
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen const struct auth_mech_desc *mech;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen struct auth_mech_desc *ret_mech;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen unsigned int i, j, count;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen mech = auth_client_get_available_mechs(auth_client, &count);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (count == 0) {
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen *count_r = 0;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen return NULL;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen }
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi
f300f927771a39549ce6cb7607129508e9041b4aStephan Bosch ret_mech = t_new(struct auth_mech_desc, count);
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi for (i = j = 0; i < count; i++) {
e60c3e17c656c53da60f0ac51aa15e9ef2742d77Stephan Bosch /* a) transport is secured
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi b) auth mechanism isn't plaintext
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi c) we allow insecure authentication
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen */
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen (client->secured || !client->set->disable_plaintext_auth ||
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen (mech[i].flags & MECH_SEC_PLAINTEXT) == 0))
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen ret_mech[j++] = mech[i];
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen }
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen *count_r = j;
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen return ret_mech;
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi}
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomistatic enum auth_request_flags
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomiclient_get_auth_flags(struct client *client)
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi{
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi enum auth_request_flags auth_flags = 0;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (client->ssl_proxy != NULL &&
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen ssl_proxy_has_valid_client_cert(client->ssl_proxy))
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen auth_flags |= AUTH_REQUEST_FLAG_VALID_CLIENT_CERT;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (client->secured)
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen auth_flags |= AUTH_REQUEST_FLAG_SECURED;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen return auth_flags;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen}
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
f36c4185474823594a78b3f252e79d8923522c36Timo Sirainenstatic void
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainencall_client_callback(struct client *client, enum sasl_server_reply reply,
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen const char *data, const char *const *args)
f36c4185474823594a78b3f252e79d8923522c36Timo Sirainen{
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainen sasl_server_callback_t *sasl_callback;
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainen
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainen i_assert(reply != SASL_SERVER_REPLY_CONTINUE);
5e327e031d1591f8bff17b67eba7139afbd36cddTimo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen sasl_callback = client->sasl_callback;
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi client->sasl_callback = NULL;
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen
a13b1245bee0b6524b4aeb3c8fd9e34af648b746Aki Tuomi sasl_callback(client, reply, data, args);
c6be98b5270900746f35ebe28bd636019976e29eTimo Sirainen /* NOTE: client may be destroyed now */
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen}
a2fdfd2efdbb2d912aad23900a466cf74114920bTimo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainenstatic void
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainenmaster_auth_callback(const struct master_auth_reply *reply, void *context)
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen{
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen struct client *client = context;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen enum sasl_server_reply sasl_reply = SASL_SERVER_REPLY_MASTER_FAILED;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen const char *data = NULL;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen client->master_tag = 0;
c6be98b5270900746f35ebe28bd636019976e29eTimo Sirainen client->authenticating = FALSE;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen switch (reply->status) {
a2fdfd2efdbb2d912aad23900a466cf74114920bTimo Sirainen case MASTER_AUTH_STATUS_OK:
237a6211c7fc4d6dbb58dd0467da6dba1b8f21f6Timo Sirainen sasl_reply = SASL_SERVER_REPLY_SUCCESS;
45ead232666a47819e89dc71dec57767340d0b62Timo Sirainen break;
1e11a94ec50fc9b57eb2c859771c6a326ccaf86fAki Tuomi case MASTER_AUTH_STATUS_INTERNAL_ERROR:
f36c4185474823594a78b3f252e79d8923522c36Timo Sirainen break;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen }
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen client->mail_pid = reply->mail_pid;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen call_client_callback(client, sasl_reply, data, NULL);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen}
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainenstatic void master_send_request(struct anvil_request *anvil_request)
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen{
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen struct client *client = anvil_request->client;
2d1892aaeb63b9774237b6e60d6bb04bf6f8259cTimo Sirainen struct master_auth_request req;
2d1892aaeb63b9774237b6e60d6bb04bf6f8259cTimo Sirainen const unsigned char *data;
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen size_t size;
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen buffer_t *buf;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen memset(&req, 0, sizeof(req));
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen req.auth_pid = anvil_request->auth_pid;
2d1892aaeb63b9774237b6e60d6bb04bf6f8259cTimo Sirainen req.auth_id = anvil_request->auth_id;
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen req.local_ip = client->local_ip;
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen req.remote_ip = client->ip;
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen req.client_pid = getpid();
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen memcpy(req.cookie, anvil_request->cookie, sizeof(req.cookie));
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), 256);
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen buffer_append(buf, client->master_data_prefix,
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen client->master_data_prefix_len);
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen data = i_stream_get_data(client->input, &size);
107659c01b2359b0ee426bde020c8d4e29ede30dTimo Sirainen buffer_append(buf, data, size);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen req.data_size = buf->used;
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen master_auth_request(master_auth, client->fd, &req, buf->data,
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen master_auth_callback, client, &client->master_tag);
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen}
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainenstatic void anvil_lookup_callback(const char *reply, void *context)
c1fc5a97a15332f1253ee13a9cab65a7b4b6cd5fTimo Sirainen{
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen struct anvil_request *req = context;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen struct client *client = req->client;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (reply == NULL ||
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen strtoul(reply, NULL, 10) < client->set->mail_max_userip_connections)
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen master_send_request(req);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen else {
d6b3cfd855c0eebed68be50d3111de1b5a6afeb0Timo Sirainen client->authenticating = FALSE;
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen call_client_callback(client, SASL_SERVER_REPLY_MASTER_FAILED,
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen ERR_TOO_MANY_USERIP_CONNECTIONS, NULL);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen }
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen i_free(req);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen}
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen
static void
anvil_check_too_many_connections(struct client *client,
struct auth_client_request *request)
{
struct anvil_request *req;
const char *query, *cookie;
buffer_t buf;
req = i_new(struct anvil_request, 1);
req->client = client;
req->auth_pid = auth_client_request_get_server_pid(request);
req->auth_id = auth_client_request_get_id(request);
buffer_create_data(&buf, req->cookie, sizeof(req->cookie));
cookie = auth_client_request_get_cookie(request);
if (strlen(cookie) == MASTER_AUTH_COOKIE_SIZE*2)
(void)hex_to_binary(cookie, &buf);
if (client->virtual_user == NULL ||
client->set->mail_max_userip_connections == 0) {
anvil_lookup_callback(NULL, req);
return;
}
query = t_strconcat("LOOKUP\t", login_protocol, "/",
net_ip2addr(&client->ip), "/",
str_tabescape(client->virtual_user), NULL);
anvil_client_query(anvil, query, anvil_lookup_callback, req);
}
static void
authenticate_callback(struct auth_client_request *request,
enum auth_request_status status, const char *data_base64,
const char *const *args, void *context)
{
struct client *client = context;
unsigned int i;
bool nologin;
if (!client->authenticating) {
/* client aborted */
i_assert(status < 0);
return;
}
i_assert(client->auth_request == request);
switch (status) {
case AUTH_REQUEST_STATUS_CONTINUE:
/* continue */
client->sasl_callback(client, SASL_SERVER_REPLY_CONTINUE,
data_base64, NULL);
break;
case AUTH_REQUEST_STATUS_OK:
client->auth_request = NULL;
nologin = FALSE;
for (i = 0; args[i] != NULL; i++) {
if (strncmp(args[i], "user=", 5) == 0) {
i_free(client->virtual_user);
client->virtual_user = i_strdup(args[i] + 5);
}
if (strcmp(args[i], "nologin") == 0 ||
strcmp(args[i], "proxy") == 0) {
/* user can't login */
nologin = TRUE;
}
}
if (nologin) {
client->authenticating = FALSE;
call_client_callback(client, SASL_SERVER_REPLY_SUCCESS,
NULL, args);
} else {
anvil_check_too_many_connections(client, request);
}
break;
case AUTH_REQUEST_STATUS_FAIL:
client->auth_request = NULL;
if (args != NULL) {
/* parse our username if it's there */
for (i = 0; args[i] != NULL; i++) {
if (strncmp(args[i], "user=", 5) == 0) {
i_free(client->virtual_user);
client->virtual_user =
i_strdup(args[i] + 5);
}
}
}
client->authenticating = FALSE;
call_client_callback(client, SASL_SERVER_REPLY_AUTH_FAILED,
NULL, args);
break;
}
}
void sasl_server_auth_begin(struct client *client,
const char *service, const char *mech_name,
const char *initial_resp_base64,
sasl_server_callback_t *callback)
{
struct auth_request_info info;
const struct auth_mech_desc *mech;
i_assert(auth_client_is_connected(auth_client));
client->auth_attempts++;
client->authenticating = TRUE;
i_free(client->auth_mech_name);
client->auth_mech_name = str_ucase(i_strdup(mech_name));
client->sasl_callback = callback;
mech = auth_client_find_mech(auth_client, mech_name);
if (mech == NULL) {
client->auth_tried_unsupported_mech = TRUE;
sasl_server_auth_failed(client,
"Unsupported authentication mechanism.");
return;
}
if (!client->secured && client->set->disable_plaintext_auth &&
(mech->flags & MECH_SEC_PLAINTEXT) != 0) {
client->auth_tried_disabled_plaintext = TRUE;
sasl_server_auth_failed(client,
"Plaintext authentication disabled.");
return;
}
memset(&info, 0, sizeof(info));
info.mech = mech->name;
info.service = service;
info.cert_username = client->ssl_proxy == NULL ? NULL :
ssl_proxy_get_peer_name(client->ssl_proxy);
info.flags = client_get_auth_flags(client);
info.local_ip = client->local_ip;
info.remote_ip = client->ip;
info.local_port = client->local_port;
info.remote_port = client->remote_port;
info.initial_resp_base64 = initial_resp_base64;
client->auth_request =
auth_client_request_new(auth_client, &info,
authenticate_callback, client);
}
static void sasl_server_auth_cancel(struct client *client, const char *reason,
enum sasl_server_reply reply)
{
i_assert(client->authenticating);
if (client->set->verbose_auth && reason != NULL) {
const char *auth_name =
str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
client_log(client, t_strdup_printf(
"Authenticate %s failed: %s", auth_name, reason));
}
client->authenticating = FALSE;
if (client->auth_request != NULL)
auth_client_request_abort(&client->auth_request);
call_client_callback(client, reply, reason, NULL);
}
void sasl_server_auth_failed(struct client *client, const char *reason)
{
sasl_server_auth_cancel(client, reason, SASL_SERVER_REPLY_AUTH_FAILED);
}
void sasl_server_auth_abort(struct client *client)
{
client->auth_try_aborted = TRUE;
sasl_server_auth_cancel(client, NULL, SASL_SERVER_REPLY_AUTH_ABORTED);
}