Mercurial > dovecot > original-hg > dovecot-1.2
changeset 2733:9b9d9c164a31 HEAD
Login process cleanups. Share more authentication code between pop3/imap.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 11 Oct 2004 20:14:26 +0300 |
parents | 69c521ecf570 |
children | e2ce951fa8e6 |
files | src/imap-login/client-authenticate.c src/imap-login/client.c src/imap-login/client.h src/login-common/Makefile.am src/login-common/auth-common.c src/login-common/auth-common.h src/login-common/client-common.h src/login-common/sasl-server.c src/login-common/sasl-server.h src/pop3-login/client-authenticate.c src/pop3-login/client.c src/pop3-login/client.h |
diffstat | 12 files changed, 518 insertions(+), 751 deletions(-) [+] |
line wrap: on
line diff
--- a/src/imap-login/client-authenticate.c Mon Oct 11 17:29:51 2004 +0300 +++ b/src/imap-login/client-authenticate.c Mon Oct 11 20:14:26 2004 +0300 @@ -11,14 +11,8 @@ #include "str-sanitize.h" #include "imap-parser.h" #include "auth-client.h" -#include "ssl-proxy.h" #include "client.h" #include "client-authenticate.h" -#include "auth-common.h" -#include "master.h" - -/* Used only for string sanitization while verbose_auth is set. */ -#define MAX_MECH_NAME 64 const char *client_authenticate_get_capabilities(int secured) { @@ -45,214 +39,6 @@ return str_c(str); } -static void client_auth_abort(struct imap_client *client, const char *msg) -{ - client->authenticating = FALSE; - - if (client->common.auth_request != NULL) { - auth_client_request_abort(client->common.auth_request); - client->common.auth_request = NULL; - } - - if (msg != NULL && verbose_auth) - client_syslog(client, "Authentication failed: %s", msg); - - client_send_tagline(client, msg != NULL ? - t_strconcat("NO ", msg, NULL) : - "NO Authentication failed."); - - /* get back to normal client input */ - if (client->common.io != NULL) - io_remove(client->common.io); - client->common.io = client->common.fd == -1 ? NULL : - io_add(client->common.fd, IO_READ, client_input, client); - - client_unref(client); -} - -static void master_callback(struct client *_client, int success) -{ - struct imap_client *client = (struct imap_client *) _client; - const char *reason = NULL; - - if (success) { - reason = t_strconcat("Login: ", client->common.virtual_user, - NULL); - } else { - reason = t_strconcat("Internal login failure: ", - client->common.virtual_user, NULL); - client_send_line(client, "* BYE Internal login failure. " - "Error report written to server log."); - } - - client_destroy(client, reason); -} - -static void client_send_auth_data(struct imap_client *client, - const unsigned char *data, size_t size) -{ - buffer_t *buf; - const void *buf_data; - size_t buf_size; - ssize_t ret; - - t_push(); - - buf = buffer_create_dynamic(pool_datastack_create(), size*2); - buffer_append(buf, "+ ", 2); - base64_encode(data, size, buf); - buffer_append(buf, "\r\n", 2); - - buf_data = buffer_get_data(buf, &buf_size); - if ((ret = o_stream_send(client->output, buf_data, buf_size)) < 0) - client_destroy(client, "Disconnected"); - else if ((size_t)ret != buf_size) - client_destroy(client, "Transmit buffer full"); - - t_pop(); -} - -static void login_callback(struct auth_request *request, - struct auth_client_request_reply *reply, - const unsigned char *data, void *context) -{ - struct imap_client *client = context; - const char *error; - - switch (auth_callback(request, reply, data, &client->common, - master_callback, &error)) { - case -1: - case 0: - /* login failed */ - client_auth_abort(client, error); - break; - - default: - /* success, we should be able to log in. if we fail, just - disconnect the client. */ - client->authenticating = FALSE; - client_send_tagline(client, "OK Logged in."); - client_unref(client); - } -} - -static enum auth_client_request_new_flags -client_get_auth_flags(struct imap_client *client) -{ - enum auth_client_request_new_flags auth_flags = 0; - - if (client->common.proxy != NULL && - ssl_proxy_has_valid_client_cert(client->common.proxy)) - auth_flags |= AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT; - if (client->tls) - auth_flags |= AUTH_CLIENT_FLAG_SSL_ENABLED; - return auth_flags; -} - -int cmd_login(struct imap_client *client, struct imap_arg *args) -{ - const char *user, *pass, *error; - struct auth_request_info info; - string_t *plain_login; - - /* two arguments: username and password */ - if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING) - return FALSE; - if (args[1].type != IMAP_ARG_ATOM && args[1].type != IMAP_ARG_STRING) - return FALSE; - if (args[2].type != IMAP_ARG_EOL) - return FALSE; - - user = IMAP_ARG_STR(&args[0]); - pass = IMAP_ARG_STR(&args[1]); - - if (!client->secured && disable_plaintext_auth) { - if (verbose_auth) { - client_syslog(client, "Login failed: " - "Plaintext authentication disabled"); - } - client_send_line(client, - "* BAD [ALERT] Plaintext authentication is disabled, " - "but your client sent password in plaintext anyway. " - "If anyone was listening, the password was exposed."); - client_send_tagline(client, - "NO Plaintext authentication disabled."); - return TRUE; - } - - /* authorization ID \0 authentication ID \0 pass */ - plain_login = t_str_new(64); - str_append_c(plain_login, '\0'); - str_append(plain_login, user); - str_append_c(plain_login, '\0'); - str_append(plain_login, pass); - - memset(&info, 0, sizeof(info)); - info.mech = "PLAIN"; - info.protocol = "IMAP"; - info.flags = client_get_auth_flags(client); - info.local_ip = client->common.local_ip; - info.remote_ip = client->common.ip; - info.initial_resp_data = str_data(plain_login); - info.initial_resp_size = str_len(plain_login); - - client_ref(client); - - client->common.auth_request = - auth_client_request_new(auth_client, NULL, &info, - login_callback, client, &error); - if (client->common.auth_request == NULL) { - if (verbose_auth) - client_syslog(client, "Login failed: %s", error); - client_send_tagline(client, t_strconcat( - "NO Login failed: ", error, NULL)); - client_unref(client); - return TRUE; - } - - /* don't read any input from client until login is finished */ - if (client->common.io != NULL) { - io_remove(client->common.io); - client->common.io = NULL; - } - - client->authenticating = TRUE; - return TRUE; -} - -static void authenticate_callback(struct auth_request *request, - struct auth_client_request_reply *reply, - const unsigned char *data, void *context) -{ - struct imap_client *client = context; - const char *error; - - if (!client->authenticating) { - /* client aborted */ - i_assert(reply == NULL); - return; - } - - switch (auth_callback(request, reply, data, &client->common, - master_callback, &error)) { - case -1: - /* login failed */ - client_auth_abort(client, error); - break; - - case 0: - /* continue */ - client_send_auth_data(client, data, reply->data_size); - break; - default: - /* success, we should be able to log in. if we fail, just - disconnect the client. */ - client->authenticating = FALSE; - client_send_tagline(client, "OK Logged in."); - client_unref(client); - } -} - static void client_auth_input(void *context) { struct imap_client *client = context; @@ -276,7 +62,8 @@ return; if (strcmp(line, "*") == 0) { - client_auth_abort(client, "Authentication aborted"); + sasl_server_auth_cancel(&client->common, + "Authentication aborted"); return; } @@ -285,13 +72,13 @@ if (base64_decode(line, linelen, NULL, buf) < 0) { /* failed */ - client_auth_abort(client, "Invalid base64 data"); + sasl_server_auth_cancel(&client->common, "Invalid base64 data"); } else if (client->common.auth_request == NULL) { - client_auth_abort(client, "Don't send unrequested data"); + sasl_server_auth_cancel(&client->common, + "Don't send unrequested data"); } else { auth_client_request_continue(client->common.auth_request, - buffer_get_data(buf, NULL), - buffer_get_used_size(buf)); + buf->data, buf->used); } /* clear sensitive data */ @@ -301,75 +88,142 @@ safe_memset(buffer_free_without_data(buf), 0, bufsize); } +static void sasl_callback(struct client *_client, enum sasl_server_reply reply, + const char *data) +{ + struct imap_client *client = (struct imap_client *)_client; + struct const_iovec iov[3]; + size_t data_len; + ssize_t ret; + + switch (reply) { + case SASL_SERVER_REPLY_SUCCESS: + client_send_tagline(client, "OK Logged in."); + client_destroy(client, t_strconcat( + "Login: ", client->common.virtual_user, NULL)); + break; + case SASL_SERVER_REPLY_AUTH_FAILED: + if (data == NULL) + client_send_tagline(client, "Authentication failed"); + else { + client_send_tagline(client, t_strconcat( + "NO Authentication failed: ", data, NULL)); + } + + /* get back to normal client input. */ + if (client->io != NULL) + io_remove(client->io); + client->io = io_add(client->common.fd, IO_READ, + client_input, client); + break; + case SASL_SERVER_REPLY_MASTER_FAILED: + client_send_line(client, "* BYE Internal login failure. " + "Error report written to server log."); + client_destroy(client, t_strconcat("Internal login failure: ", + client->common.virtual_user, + NULL)); + 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; + + ret = o_stream_sendv(client->output, iov, 3); + if (ret < 0) + client_destroy(client, "Disconnected"); + else if ((size_t)ret != 2 + data_len + 2) + client_destroy(client, "Transmit buffer full"); + else { + /* continue */ + return; + } + break; + } + + client_unref(client); +} + int cmd_authenticate(struct imap_client *client, struct imap_arg *args) { - const struct auth_mech_desc *mech; - const char *mech_name, *error; - struct auth_request_info info; + const char *mech_name; /* we want only one argument: authentication mechanism name */ if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING) - return FALSE; + return -1; if (args[1].type != IMAP_ARG_EOL) - return FALSE; + return -1; mech_name = IMAP_ARG_STR(&args[0]); if (*mech_name == '\0') return FALSE; - mech = auth_client_find_mech(auth_client, mech_name); - if (mech == NULL) { + client_ref(client); + sasl_server_auth_begin(&client->common, "IMAP", mech_name, NULL, 0, + sasl_callback); + if (!client->common.authenticating) + return 1; + + /* following input data will go to authentication */ + if (client->io != NULL) + io_remove(client->io); + client->io = io_add(client->common.fd, IO_READ, + client_auth_input, client); + return 0; +} + +int cmd_login(struct imap_client *client, struct imap_arg *args) +{ + const char *user, *pass; + string_t *plain_login; + + /* two arguments: username and password */ + if (args[0].type != IMAP_ARG_ATOM && args[0].type != IMAP_ARG_STRING) + return -1; + if (args[1].type != IMAP_ARG_ATOM && args[1].type != IMAP_ARG_STRING) + return -1; + if (args[2].type != IMAP_ARG_EOL) + return -1; + + user = IMAP_ARG_STR(&args[0]); + pass = IMAP_ARG_STR(&args[1]); + + if (!client->common.secured && disable_plaintext_auth) { if (verbose_auth) { - client_syslog(client, "Authenticate %s failed: " - "Unsupported mechanism", - str_sanitize(mech_name, MAX_MECH_NAME)); + client_syslog(&client->common, "Login failed: " + "Plaintext authentication disabled"); } - client_send_tagline(client, - "NO Unsupported authentication mechanism."); - return TRUE; - } - - if (!client->secured && mech->plaintext && disable_plaintext_auth) { - if (verbose_auth) { - client_syslog(client, "Authenticate %s failed: " - "Plaintext authentication disabled", - str_sanitize(mech_name, MAX_MECH_NAME)); - } + client_send_line(client, + "* BAD [ALERT] Plaintext authentication is disabled, " + "but your client sent password in plaintext anyway. " + "If anyone was listening, the password was exposed."); client_send_tagline(client, "NO Plaintext authentication disabled."); - return TRUE; + return 1; } - memset(&info, 0, sizeof(info)); - info.mech = mech->name; - info.protocol = "IMAP"; - info.flags = client_get_auth_flags(client); - info.local_ip = client->common.local_ip; - info.remote_ip = client->common.ip; + /* authorization ID \0 authentication ID \0 pass */ + plain_login = buffer_create_dynamic(pool_datastack_create(), 64); + buffer_append_c(plain_login, '\0'); + buffer_append(plain_login, user, strlen(user)); + buffer_append_c(plain_login, '\0'); + buffer_append(plain_login, pass, strlen(pass)); client_ref(client); - o_stream_uncork(client->output); + sasl_server_auth_begin(&client->common, "IMAP", "PLAIN", + plain_login->data, plain_login->used, + sasl_callback); + if (!client->common.authenticating) + return 1; - client->common.auth_request = - auth_client_request_new(auth_client, NULL, &info, - authenticate_callback, client, &error); - if (client->common.auth_request != NULL) { - /* following input data will go to authentication */ - if (client->common.io != NULL) - io_remove(client->common.io); - client->common.io = io_add(client->common.fd, IO_READ, - client_auth_input, client); - client->authenticating = TRUE; - } else { - if (verbose_auth) { - client_syslog(client, "Authenticate %s failed: %s", - str_sanitize(mech_name, MAX_MECH_NAME), - error); - } - client_send_tagline(client, t_strconcat( - "NO Authentication failed: ", error, NULL)); - client_unref(client); + /* don't read any input from client until login is finished */ + if (client->io != NULL) { + io_remove(client->io); + client->io = NULL; } - return TRUE; + return 0; }
--- a/src/imap-login/client.c Mon Oct 11 17:29:51 2004 +0300 +++ b/src/imap-login/client.c Mon Oct 11 20:14:26 2004 +0300 @@ -56,8 +56,8 @@ if (addr == NULL) addr = "??"; - process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]", - addr)); + process_title_set(t_strdup_printf(client->common.tls ? + "[%s TLS]" : "[%s]", addr)); } static void client_open_streams(struct imap_client *client, int fd) @@ -93,10 +93,11 @@ { const char *auths; - auths = client_authenticate_get_capabilities(client->secured); + auths = client_authenticate_get_capabilities(client->common.secured); return t_strconcat(CAPABILITY_STRING, - (ssl_initialized && !client->tls) ? " STARTTLS" : "", - disable_plaintext_auth && !client->secured ? + (ssl_initialized && !client->common.tls) ? + " STARTTLS" : "", + disable_plaintext_auth && !client->common.secured ? " LOGINDISABLED" : "", auths, NULL); } @@ -120,8 +121,8 @@ return; } - client->tls = TRUE; - client->secured = TRUE; + client->common.tls = TRUE; + client->common.secured = TRUE; client_set_title(client); client->common.fd = fd_ssl; @@ -133,8 +134,7 @@ client->skip_line = FALSE; client_open_streams(client, fd_ssl); - client->common.io = io_add(client->common.fd, IO_READ, - client_input, client); + client->io = io_add(client->common.fd, IO_READ, client_input, client); } static void client_output_starttls(void *context) @@ -153,7 +153,7 @@ static int cmd_starttls(struct imap_client *client) { - if (client->tls) { + if (client->common.tls) { client_send_tagline(client, "BAD TLS is already active."); return TRUE; } @@ -165,9 +165,9 @@ /* remove input handler, SSL proxy gives us a new fd. we also have to remove it in case we have to wait for buffer to be flushed */ - if (client->common.io != NULL) { - io_remove(client->common.io); - client->common.io = NULL; + if (client->io != NULL) { + io_remove(client->io); + client->io = NULL; } client_send_tagline(client, "OK Begin TLS negotiation now."); @@ -212,17 +212,16 @@ if (strcmp(cmd, "LOGOUT") == 0) return cmd_logout(client); - return FALSE; + return -1; } static int client_handle_input(struct imap_client *client) { struct imap_arg *args; const char *msg; - int fatal; + int ret, fatal; - if (client->authenticating) - return FALSE; /* wait until authentication is finished */ + i_assert(!client->common.authenticating); if (client->cmd_finished) { /* clear the previous command from memory. don't do this @@ -276,8 +275,13 @@ } client->skip_line = TRUE; - if (*client->cmd_tag == '\0' || - !client_command_execute(client, client->cmd_name, args)) { + if (*client->cmd_tag == '\0') + ret = -1; + else + ret = client_command_execute(client, client->cmd_name, args); + + client->cmd_finished = TRUE; + if (ret < 0) { if (*client->cmd_tag == '\0') client->cmd_tag = "*"; if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) { @@ -286,13 +290,12 @@ client_destroy(client, "Disconnected: " "Too many invalid commands"); return FALSE; - } + } client_send_tagline(client, "BAD Error in IMAP command received by server."); } - client->cmd_finished = TRUE; - return TRUE; + return ret != 0; } int client_read(struct imap_client *client) @@ -397,10 +400,10 @@ client = i_new(struct imap_client, 1); client->created = ioloop_time; client->refcount = 1; - client->tls = ssl; + client->common.tls = ssl; addr = net_ip2addr(ip); - client->secured = ssl || + client->common.secured = ssl || (IPADDR_IS_V4(ip) && strncmp(addr, "127.", 4) == 0) || (IPADDR_IS_V6(ip) && strcmp(addr, "::1") == 0); @@ -409,7 +412,7 @@ client->common.fd = fd; client_open_streams(client, fd); - client->common.io = io_add(fd, IO_READ, client_input, client); + client->io = io_add(fd, IO_READ, client_input, client); client->last_input = ioloop_time; hash_insert(clients, client, client); @@ -434,7 +437,7 @@ client->destroyed = TRUE; if (reason != NULL) - client_syslog(client, "%s", reason); + client_syslog(&client->common, "%s", reason); hash_remove(clients, client); @@ -449,9 +452,9 @@ if (client->common.master_tag != 0) master_request_abort(&client->common); - if (client->common.io != NULL) { - io_remove(client->common.io); - client->common.io = NULL; + if (client->io != NULL) { + io_remove(client->io); + client->io = NULL; } if (client->common.fd != -1) { @@ -507,22 +510,6 @@ client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL)); } -void client_syslog(struct imap_client *client, const char *format, ...) -{ - const char *addr; - va_list args; - - addr = net_ip2addr(&client->common.ip); - if (addr == NULL) - addr = "??"; - - t_push(); - va_start(args, format); - i_info("%s [%s]", t_strdup_vprintf(format, args), addr); - va_end(args); - t_pop(); -} - static void client_check_idle(struct imap_client *client) { if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
--- a/src/imap-login/client.h Mon Oct 11 17:29:51 2004 +0300 +++ b/src/imap-login/client.h Mon Oct 11 20:14:26 2004 +0300 @@ -11,6 +11,7 @@ time_t created; int refcount; + struct io *io; struct istream *input; struct ostream *output; struct imap_parser *parser; @@ -20,12 +21,9 @@ const char *cmd_tag, *cmd_name; - unsigned int tls:1; - unsigned int secured:1; unsigned int cmd_finished:1; unsigned int skip_line:1; unsigned int input_blocked:1; - unsigned int authenticating:1; unsigned int destroyed:1; }; @@ -33,8 +31,6 @@ void client_send_line(struct imap_client *client, const char *line); void client_send_tagline(struct imap_client *client, const char *line); -void client_syslog(struct imap_client *client, const char *format, ...) - __attr_format__(2, 3); int client_read(struct imap_client *client); void client_input(void *context);
--- a/src/login-common/Makefile.am Mon Oct 11 17:29:51 2004 +0300 +++ b/src/login-common/Makefile.am Mon Oct 11 20:14:26 2004 +0300 @@ -7,15 +7,15 @@ -DSBINDIR=\""$(sbindir)"\" liblogin_common_a_SOURCES = \ - auth-common.c \ + client-common.c \ main.c \ master.c \ + sasl-server.c \ ssl-proxy.c \ ssl-proxy-gnutls.c \ ssl-proxy-openssl.c noinst_HEADERS = \ - auth-common.h \ client-common.h \ common.h \ master.h \
--- a/src/login-common/auth-common.c Mon Oct 11 17:29:51 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/* Copyright (C) 2002 Timo Sirainen */ - -#include "common.h" -#include "ioloop.h" -#include "client-common.h" -#include "auth-client.h" -#include "auth-common.h" - -static const char *auth_client_get_str(struct auth_client_request_reply *reply, - const unsigned char *data, size_t idx) -{ - size_t stop; - - if (idx >= reply->data_size || idx >= reply->reply_idx) - return NULL; - - stop = reply->reply_idx < reply->data_size ? - reply->reply_idx-1 : reply->data_size; - - return t_strndup(data + idx, stop); -} - -int auth_callback(struct auth_request *request, - struct auth_client_request_reply *reply, - const unsigned char *data, struct client *client, - master_callback_t *master_callback, const char **error_r) -{ - const char *user; - - *error_r = NULL; - - if (reply == NULL) { - /* failed */ - client->auth_request = NULL; - *error_r = "Authentication process died."; - return -1; - } - - switch (reply->result) { - case AUTH_CLIENT_RESULT_CONTINUE: - if (client->auth_request != NULL) { - i_assert(client->auth_request == request); - } else { - i_assert(client->auth_request == NULL); - - client->auth_request = request; - } - return 0; - - case AUTH_CLIENT_RESULT_SUCCESS: - client->auth_request = NULL; - - user = auth_client_get_str(reply, data, reply->username_idx); - - i_free(client->virtual_user); - client->virtual_user = i_strdup(user); - - master_request_login(client, master_callback, - auth_client_request_get_server_pid(request), - auth_client_request_get_id(request)); - - /* disable IO until we're back from master */ - if (client->io != NULL) { - io_remove(client->io); - client->io = NULL; - } - return 1; - - case AUTH_CLIENT_RESULT_FAILURE: - /* see if we have error message */ - client->auth_request = NULL; - - if (reply->data_size > 0 && data[reply->data_size-1] == '\0') { - *error_r = t_strconcat("Authentication failed: ", - (const char *) data, NULL); - } - return -1; - } - - i_unreached(); -}
--- a/src/login-common/auth-common.h Mon Oct 11 17:29:51 2004 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -#ifndef __AUTH_COMMON_H -#define __AUTH_COMMON_H - -int auth_callback(struct auth_request *request, - struct auth_client_request_reply *reply, - const unsigned char *data, struct client *client, - master_callback_t *master_callback, const char **error_r); - -#endif -
--- a/src/login-common/client-common.h Mon Oct 11 17:29:51 2004 +0300 +++ b/src/login-common/client-common.h Mon Oct 11 20:14:26 2004 +0300 @@ -3,6 +3,7 @@ #include "network.h" #include "master.h" +#include "sasl-server.h" struct client { struct ip_addr local_ip; @@ -10,19 +11,27 @@ struct ssl_proxy *proxy; int fd; - struct io *io; + char *auth_mech_name; struct auth_request *auth_request; + unsigned int master_tag; master_callback_t *master_callback; + sasl_server_callback_t *sasl_callback; char *virtual_user; + unsigned int tls:1; + unsigned int secured:1; + unsigned int authenticating:1; /* ... */ }; struct client *client_create(int fd, int ssl, const struct ip_addr *local_ip, const struct ip_addr *ip); +void client_syslog(struct client *client, const char *format, ...) + __attr_format__(2, 3); + unsigned int clients_get_count(void); void clients_notify_auth_connected(void); void clients_destroy_all(void);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/login-common/sasl-server.c Mon Oct 11 20:14:26 2004 +0300 @@ -0,0 +1,182 @@ +/* Copyright (C) 2002-2004 Timo Sirainen */ + +#include "common.h" +#include "base64.h" +#include "buffer.h" +#include "str-sanitize.h" +#include "auth-client.h" +#include "ssl-proxy.h" +#include "client-common.h" +#include "master.h" + +/* Used only for string sanitization while verbose_auth is set. */ +#define MAX_MECH_NAME 64 + +static enum auth_client_request_new_flags +client_get_auth_flags(struct client *client) +{ + enum auth_client_request_new_flags auth_flags = 0; + + if (client->proxy != NULL && + ssl_proxy_has_valid_client_cert(client->proxy)) + auth_flags |= AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT; + if (client->tls) + auth_flags |= AUTH_CLIENT_FLAG_SSL_ENABLED; + return auth_flags; +} + +static void master_callback(struct client *client, int success) +{ + client->authenticating = FALSE; + i_free(client->auth_mech_name); + client->auth_mech_name = NULL; + + client->sasl_callback(client, success ? SASL_SERVER_REPLY_SUCCESS : + SASL_SERVER_REPLY_MASTER_FAILED, NULL); +} + +static const char *auth_client_get_str(struct auth_client_request_reply *reply, + const unsigned char *data, size_t idx) +{ + size_t stop; + + if (idx >= reply->data_size || idx >= reply->reply_idx) + return NULL; + + stop = reply->reply_idx < reply->data_size ? + reply->reply_idx-1 : reply->data_size; + + return t_strndup(data + idx, stop); +} + +static void authenticate_callback(struct auth_request *request, + struct auth_client_request_reply *reply, + const unsigned char *data, void *context) +{ + struct client *client = context; + buffer_t *buf; + const char *user, *error; + + if (!client->authenticating) { + /* client aborted */ + i_assert(reply == NULL); + return; + } + + if (reply == NULL) { + /* failed */ + client->auth_request = NULL; + sasl_server_auth_cancel(client, "Authentication process died."); + return; + } + + switch (reply->result) { + case AUTH_CLIENT_RESULT_CONTINUE: + if (client->auth_request != NULL) { + i_assert(client->auth_request == request); + } else { + i_assert(client->auth_request == NULL); + + client->auth_request = request; + } + + t_push(); + buf = buffer_create_dynamic(pool_datastack_create(), + MAX_BASE64_ENCODED_SIZE(reply->data_size)); + base64_encode(data, reply->data_size, buf); + buffer_append_c(buf, '\0'); + + client->sasl_callback(client, SASL_SERVER_REPLY_CONTINUE, + buf->data); + t_pop(); + break; + case AUTH_CLIENT_RESULT_SUCCESS: + client->auth_request = NULL; + + user = auth_client_get_str(reply, data, reply->username_idx); + i_free(client->virtual_user); + client->virtual_user = i_strdup(user); + + master_request_login(client, master_callback, + auth_client_request_get_server_pid(request), + auth_client_request_get_id(request)); + break; + case AUTH_CLIENT_RESULT_FAILURE: + client->auth_request = NULL; + + /* see if we have error message */ + if (reply->data_size > 0 && data[reply->data_size-1] == '\0') { + error = t_strconcat("Authentication failed: ", + (const char *)data, NULL); + } else { + error = NULL; + } + sasl_server_auth_cancel(client, error); + break; + } +} + +void sasl_server_auth_begin(struct client *client, + const char *protocol, const char *mech_name, + const unsigned char *initial_resp, + size_t initial_resp_size, + sasl_server_callback_t *callback) +{ + struct auth_request_info info; + const struct auth_mech_desc *mech; + const char *error; + + client->authenticating = TRUE; + client->auth_mech_name = i_strdup(mech_name); + client->sasl_callback = callback; + + mech = auth_client_find_mech(auth_client, mech_name); + if (mech == NULL) { + sasl_server_auth_cancel(client, + "Unsupported authentication mechanism."); + return; + } + + if (!client->secured && mech->plaintext && disable_plaintext_auth) { + sasl_server_auth_cancel(client, + "Plaintext authentication disabled."); + return; + } + + memset(&info, 0, sizeof(info)); + info.mech = mech->name; + info.protocol = protocol; + info.flags = client_get_auth_flags(client); + info.local_ip = client->local_ip; + info.remote_ip = client->ip; + info.initial_resp_data = initial_resp; + info.initial_resp_size = initial_resp_size; + + client->auth_request = + auth_client_request_new(auth_client, NULL, &info, + authenticate_callback, client, &error); + if (client->auth_request == NULL) { + sasl_server_auth_cancel(client, + t_strconcat("Authentication failed: ", error, NULL)); + } +} + +void sasl_server_auth_cancel(struct client *client, const char *reason) +{ + if (verbose_auth && reason != NULL) { + client_syslog(client, "Authenticate %s failed: %s", + str_sanitize(client->auth_mech_name, + MAX_MECH_NAME), reason); + } + + client->authenticating = FALSE; + i_free(client->auth_mech_name); + client->auth_mech_name = NULL; + + if (client->auth_request != NULL) { + auth_client_request_abort(client->auth_request); + client->auth_request = NULL; + } + + client->sasl_callback(client, SASL_SERVER_REPLY_AUTH_FAILED, reason); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/login-common/sasl-server.h Mon Oct 11 20:14:26 2004 +0300 @@ -0,0 +1,22 @@ +#ifndef __SASL_SERVER_H +#define __SASL_SERVER_H + +enum sasl_server_reply { + SASL_SERVER_REPLY_SUCCESS, + SASL_SERVER_REPLY_AUTH_FAILED, + SASL_SERVER_REPLY_MASTER_FAILED, + SASL_SERVER_REPLY_CONTINUE +}; + +typedef void sasl_server_callback_t(struct client *client, + enum sasl_server_reply reply, + const char *data); + +void sasl_server_auth_begin(struct client *client, + const char *protocol, const char *mech_name, + const unsigned char *initial_resp, + size_t initial_resp_size, + sasl_server_callback_t *callback); +void sasl_server_auth_cancel(struct client *client, const char *reason); + +#endif
--- a/src/pop3-login/client-authenticate.c Mon Oct 11 17:29:51 2004 +0300 +++ b/src/pop3-login/client-authenticate.c Mon Oct 11 20:14:26 2004 +0300 @@ -13,14 +13,8 @@ #include "auth-client.h" #include "../pop3/capability.h" #include "ssl-proxy.h" -#include "master.h" -#include "auth-common.h" #include "client.h" #include "client-authenticate.h" -#include "ssl-proxy.h" - -/* Used only for string sanitization while verbose_auth is set. */ -#define MAX_MECH_NAME 64 int cmd_capa(struct pop3_client *client, const char *args __attr_unused__) { @@ -53,212 +47,6 @@ return TRUE; } -static void client_auth_abort(struct pop3_client *client, const char *msg) -{ - client->authenticating = FALSE; - - if (client->common.auth_request != NULL) { - auth_client_request_abort(client->common.auth_request); - client->common.auth_request = NULL; - } - - if (msg != NULL && verbose_auth) - client_syslog(client, "Authentication failed: %s", msg); - - client_send_line(client, msg != NULL ? t_strconcat("-ERR ", msg, NULL) : - "-ERR Authentication failed."); - - /* get back to normal client input */ - if (client->common.io != NULL) - io_remove(client->common.io); - client->common.io = client->common.fd == -1 ? NULL : - io_add(client->common.fd, IO_READ, client_input, client); - - client_unref(client); -} - -static void master_callback(struct client *_client, int success) -{ - struct pop3_client *client = (struct pop3_client *) _client; - const char *reason = NULL; - - if (success) { - reason = t_strconcat("Login: ", client->common.virtual_user, - NULL); - } else { - reason = t_strconcat("Internal login failure: ", - client->common.virtual_user, NULL); - client_send_line(client, "* BYE Internal login failure. " - "Error report written to server log."); - } - - client_destroy(client, reason); -} - -static void client_send_auth_data(struct pop3_client *client, - const unsigned char *data, size_t size) -{ - buffer_t *buf; - const void *buf_data; - size_t buf_size; - ssize_t ret; - - t_push(); - - buf = buffer_create_dynamic(pool_datastack_create(), size*2); - buffer_append(buf, "+ ", 2); - base64_encode(data, size, buf); - buffer_append(buf, "\r\n", 2); - - buf_data = buffer_get_data(buf, &buf_size); - if ((ret = o_stream_send(client->output, buf_data, buf_size)) < 0) - client_destroy(client, "Disconnected"); - else if ((size_t)ret != buf_size) - client_destroy(client, "Transmit buffer full"); - - t_pop(); -} - -static void login_callback(struct auth_request *request, - struct auth_client_request_reply *reply, - const unsigned char *data, void *context) -{ - struct pop3_client *client = context; - const char *error; - - switch (auth_callback(request, reply, data, &client->common, - master_callback, &error)) { - case -1: - case 0: - /* login failed */ - client_auth_abort(client, error); - break; - - default: - /* success, we should be able to log in. if we fail, just - disconnect the client. */ - client->authenticating = FALSE; - client_send_line(client, "+OK Logged in."); - client_unref(client); - } -} - -static enum auth_client_request_new_flags -client_get_auth_flags(struct pop3_client *client) -{ - enum auth_client_request_new_flags auth_flags = 0; - - if (client->common.proxy != NULL && - ssl_proxy_has_valid_client_cert(client->common.proxy)) - auth_flags |= AUTH_CLIENT_FLAG_SSL_VALID_CLIENT_CERT; - if (client->tls) - auth_flags |= AUTH_CLIENT_FLAG_SSL_ENABLED; - return auth_flags; -} - -int cmd_user(struct pop3_client *client, const char *args) -{ - if (!client->secured && disable_plaintext_auth) { - if (verbose_auth) { - client_syslog(client, "Login failed: " - "Plaintext authentication disabled"); - } - client_send_line(client, - "-ERR Plaintext authentication disabled."); - return TRUE; - } - - i_free(client->last_user); - client->last_user = i_strdup(args); - - client_send_line(client, "+OK"); - return TRUE; -} - -int cmd_pass(struct pop3_client *client, const char *args) -{ - const char *error; - struct auth_request_info info; - string_t *plain_login; - - if (client->last_user == NULL) { - client_send_line(client, "-ERR No username given."); - return TRUE; - } - - /* authorization ID \0 authentication ID \0 pass */ - plain_login = t_str_new(128); - str_append_c(plain_login, '\0'); - str_append(plain_login, client->last_user); - str_append_c(plain_login, '\0'); - str_append(plain_login, args); - - memset(&info, 0, sizeof(info)); - info.mech = "PLAIN"; - info.protocol = "POP3"; - info.flags = client_get_auth_flags(client); - info.local_ip = client->common.local_ip; - info.remote_ip = client->common.ip; - info.initial_resp_data = str_data(plain_login); - info.initial_resp_size = str_len(plain_login); - - client_ref(client); - - client->common.auth_request = - auth_client_request_new(auth_client, NULL, &info, - login_callback, client, &error); - if (client->common.auth_request == NULL) { - if (verbose_auth) - client_syslog(client, "Login failed: %s", error); - client_send_line(client, - t_strconcat("-ERR Login failed: ", error, NULL)); - client_unref(client); - return TRUE; - } - - /* don't read any input from client until login is finished */ - if (client->common.io != NULL) { - io_remove(client->common.io); - client->common.io = NULL; - } - - client->authenticating = TRUE; - return TRUE; -} - -static void authenticate_callback(struct auth_request *request, - struct auth_client_request_reply *reply, - const unsigned char *data, void *context) -{ - struct pop3_client *client = context; - const char *error; - - if (!client->authenticating) { - /* client aborted */ - i_assert(reply == NULL); - return; - } - - switch (auth_callback(request, reply, data, &client->common, - master_callback, &error)) { - case -1: - /* login failed */ - client_auth_abort(client, error); - break; - - case 0: - client_send_auth_data(client, data, reply->data_size); - break; - - default: - /* success, we should be able to log in. if we fail, just - disconnect the client. */ - client->authenticating = FALSE; - client_send_line(client, "+OK Logged in."); - client_unref(client); - } -} - static void client_auth_input(void *context) { struct pop3_client *client = context; @@ -275,7 +63,8 @@ return; if (strcmp(line, "*") == 0) { - client_auth_abort(client, "Authentication aborted"); + sasl_server_auth_cancel(&client->common, + "Authentication aborted"); return; } @@ -284,13 +73,13 @@ if (base64_decode(line, linelen, NULL, buf) < 0) { /* failed */ - client_auth_abort(client, "Invalid base64 data"); + sasl_server_auth_cancel(&client->common, "Invalid base64 data"); } else if (client->common.auth_request == NULL) { - client_auth_abort(client, "Don't send unrequested data"); + sasl_server_auth_cancel(&client->common, + "Don't send unrequested data"); } else { auth_client_request_continue(client->common.auth_request, - buffer_get_data(buf, NULL), - buffer_get_used_size(buf)); + buf->data, buf->used); } /* clear sensitive data */ @@ -300,11 +89,66 @@ safe_memset(buffer_free_without_data(buf), 0, bufsize); } +static void sasl_callback(struct client *_client, enum sasl_server_reply reply, + const char *data) +{ + struct pop3_client *client = (struct pop3_client *)_client; + struct const_iovec iov[3]; + size_t data_len; + ssize_t ret; + + switch (reply) { + case SASL_SERVER_REPLY_SUCCESS: + client_send_line(client, "+OK Logged in."); + client_destroy(client, t_strconcat( + "Login: ", client->common.virtual_user, NULL)); + break; + case SASL_SERVER_REPLY_AUTH_FAILED: + if (data == NULL) + client_send_line(client, "-ERR Authentication failed"); + else { + client_send_line(client, t_strconcat( + "-ERR Authentication failed: ", data, NULL)); + } + + /* get back to normal client input. */ + io_remove(client->io); + client->io = io_add(client->common.fd, IO_READ, + client_input, client); + break; + case SASL_SERVER_REPLY_MASTER_FAILED: + client_destroy(client, t_strconcat("Internal login failure: ", + client->common.virtual_user, + NULL)); + 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; + + ret = o_stream_sendv(client->output, iov, 3); + if (ret < 0) + client_destroy(client, "Disconnected"); + else if ((size_t)ret != 2 + data_len + 2) + client_destroy(client, "Transmit buffer full"); + else { + /* continue */ + return; + } + break; + } + + client_unref(client); +} + int cmd_auth(struct pop3_client *client, const char *args) { - struct auth_request_info info; const struct auth_mech_desc *mech; - const char *mech_name, *error, *p; + const char *mech_name, *p; string_t *buf; size_t argslen; @@ -332,29 +176,6 @@ args = p+1; } - mech = auth_client_find_mech(auth_client, mech_name); - if (mech == NULL) { - if (verbose_auth) { - client_syslog(client, "Authenticate %s failed: " - "Unsupported mechanism", - str_sanitize(mech_name, MAX_MECH_NAME)); - } - client_send_line(client, - "-ERR Unsupported authentication mechanism."); - return TRUE; - } - - if (!client->secured && mech->plaintext && disable_plaintext_auth) { - if (verbose_auth) { - client_syslog(client, "Authenticate %s failed: " - "Plaintext authentication disabled", - str_sanitize(mech_name, MAX_MECH_NAME)); - } - client_send_line(client, - "-ERR Plaintext authentication disabled."); - return TRUE; - } - argslen = strlen(args); buf = buffer_create_static_hard(pool_datastack_create(), argslen); @@ -364,49 +185,80 @@ return TRUE; } - memset(&info, 0, sizeof(info)); - info.mech = mech->name; - info.protocol = "POP3"; - info.flags = client_get_auth_flags(client); - info.local_ip = client->common.local_ip; - info.remote_ip = client->common.ip; - info.initial_resp_data = str_data(buf); - info.initial_resp_size = str_len(buf); + client_ref(client); + sasl_server_auth_begin(&client->common, "POP3", mech_name, + buf->data, buf->used, sasl_callback); + if (!client->common.authenticating) + return TRUE; + + /* following input data will go to authentication */ + if (client->io != NULL) + io_remove(client->io); + client->io = io_add(client->common.fd, IO_READ, + client_auth_input, client); + return TRUE; +} + +int cmd_user(struct pop3_client *client, const char *args) +{ + if (!client->secured && disable_plaintext_auth) { + if (verbose_auth) { + client_syslog(&client->common, "Login failed: " + "Plaintext authentication disabled"); + } + client_send_line(client, + "-ERR Plaintext authentication disabled."); + return TRUE; + } + + i_free(client->last_user); + client->last_user = i_strdup(args); + + client_send_line(client, "+OK"); + return TRUE; +} + +int cmd_pass(struct pop3_client *client, const char *args) +{ + string_t *plain_login; + + if (client->last_user == NULL) { + client_send_line(client, "-ERR No username given."); + return TRUE; + } + + /* authorization ID \0 authentication ID \0 pass */ + plain_login = t_str_new(128); + str_append_c(plain_login, '\0'); + str_append(plain_login, client->last_user); + str_append_c(plain_login, '\0'); + str_append(plain_login, args); client_ref(client); - client->common.auth_request = - auth_client_request_new(auth_client, NULL, &info, - authenticate_callback, client, &error); - if (client->common.auth_request != NULL) { - /* following input data will go to authentication */ - if (client->common.io != NULL) - io_remove(client->common.io); - client->common.io = io_add(client->common.fd, IO_READ, - client_auth_input, client); - client->authenticating = TRUE; - } else { - if (verbose_auth) { - client_syslog(client, "Authenticate %s failed: %s", - str_sanitize(mech_name, MAX_MECH_NAME), - error); - } - client_send_line(client, t_strconcat( - "-ERR Authentication failed: ", error, NULL)); - client_unref(client); + sasl_server_auth_begin(&client->common, "POP3", "PLAIN", + plain_login->data, plain_login->used, + sasl_callback); + if (!client->common.authenticating) + return TRUE; + + /* don't read any input from client until login is finished */ + if (client->io != NULL) { + io_remove(client->io); + client->io = NULL; } - return TRUE; } int cmd_apop(struct pop3_client *client, const char *args) { - struct auth_request_info info; - const char *error, *p; buffer_t *apop_data; + const char *p; if (client->apop_challenge == NULL) { - if (verbose_auth) - client_syslog(client, "APOP failed: APOP not enabled"); + if (verbose_auth) { + client_syslog(&client->common, + "APOP failed: APOP not enabled"); + } client_send_line(client, "-ERR APOP not enabled."); return TRUE; } @@ -415,8 +267,8 @@ p = strchr(args, ' '); if (p == NULL || strlen(p+1) != 32) { if (verbose_auth) { - client_syslog(client, "APOP failed: " - "Invalid parameters"); + client_syslog(&client->common, + "APOP failed: Invalid parameters"); } client_send_line(client, "-ERR Invalid parameters."); return TRUE; @@ -431,7 +283,7 @@ if (hex_to_binary(p+1, apop_data) < 0) { if (verbose_auth) { - client_syslog(client, "APOP failed: " + client_syslog(&client->common, "APOP failed: " "Invalid characters in MD5 response"); } client_send_line(client, @@ -439,41 +291,16 @@ return TRUE; } - memset(&info, 0, sizeof(info)); - info.mech = "APOP"; - info.protocol = "POP3"; - info.flags = client_get_auth_flags(client); - info.local_ip = client->common.local_ip; - info.remote_ip = client->common.ip; - info.initial_resp_data = - buffer_get_data(apop_data, &info.initial_resp_size); - client_ref(client); - o_stream_uncork(client->output); - - client->common.auth_request = - auth_client_request_new(auth_client, &client->auth_id, &info, - login_callback, client, &error); + sasl_server_auth_begin(&client->common, "POP3", "APOP", + apop_data->data, apop_data->used, sasl_callback); + if (!client->common.authenticating) + return TRUE; - if (client->common.auth_request != NULL) { - /* don't read any input from client until login is finished */ - if (client->common.io != NULL) { - io_remove(client->common.io); - client->common.io = NULL; - } - client->authenticating = TRUE; - } else if (error == NULL) { - /* the auth connection was lost. we have no choice - but to fail the APOP logins completely since the - challenge is auth connection-specific. disconnect. */ - client_destroy(client, "APOP auth connection lost"); - client_unref(client); - } else { - if (verbose_auth) - client_syslog(client, "APOP failed: %s", error); - client_send_line(client, - t_strconcat("-ERR Login failed: ", error, NULL)); - client_unref(client); + /* don't read any input from client until login is finished */ + if (client->io != NULL) { + io_remove(client->io); + client->io = NULL; } return TRUE; }
--- a/src/pop3-login/client.c Mon Oct 11 17:29:51 2004 +0300 +++ b/src/pop3-login/client.c Mon Oct 11 20:14:26 2004 +0300 @@ -88,8 +88,7 @@ o_stream_unref(client->output); client_open_streams(client, fd_ssl); - client->common.io = io_add(client->common.fd, IO_READ, - client_input, client); + client->io = io_add(client->common.fd, IO_READ, client_input, client); } static void client_output_starttls(void *context) @@ -120,9 +119,9 @@ /* remove input handler, SSL proxy gives us a new fd. we also have to remove it in case we have to wait for buffer to be flushed */ - if (client->common.io != NULL) { - io_remove(client->common.io); - client->common.io = NULL; + if (client->io != NULL) { + io_remove(client->io); + client->io = NULL; } client_send_line(client, "+OK Begin TLS negotiation now."); @@ -282,8 +281,7 @@ static void client_auth_ready(struct pop3_client *client) { - client->common.io = - io_add(client->common.fd, IO_READ, client_input, client); + client->io = io_add(client->common.fd, IO_READ, client_input, client); client->apop_challenge = get_apop_challenge(client); client_send_line(client, t_strconcat("+OK ", greeting, @@ -340,7 +338,7 @@ client->destroyed = TRUE; if (reason != NULL) - client_syslog(client, "%s", reason); + client_syslog(&client->common, "%s", reason); hash_remove(clients, client); @@ -355,9 +353,9 @@ if (client->common.master_tag != 0) master_request_abort(&client->common); - if (client->common.io != NULL) { - io_remove(client->common.io); - client->common.io = NULL; + if (client->io != NULL) { + io_remove(client->io); + client->io = NULL; } net_disconnect(client->common.fd); @@ -405,22 +403,6 @@ client_destroy(client, "Transmit buffer full"); } -void client_syslog(struct pop3_client *client, const char *format, ...) -{ - const char *addr; - va_list args; - - addr = net_ip2addr(&client->common.ip); - if (addr == NULL) - addr = "??"; - - t_push(); - va_start(args, format); - i_info("%s [%s]", t_strdup_vprintf(format, args), addr); - va_end(args); - t_pop(); -} - static void client_check_idle(struct pop3_client *client) { if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT)
--- a/src/pop3-login/client.h Mon Oct 11 17:29:51 2004 +0300 +++ b/src/pop3-login/client.h Mon Oct 11 20:14:26 2004 +0300 @@ -12,6 +12,7 @@ time_t created; int refcount; + struct io *io; struct istream *input; struct ostream *output; @@ -33,8 +34,6 @@ void client_destroy(struct pop3_client *client, const char *reason); void client_send_line(struct pop3_client *client, const char *line); -void client_syslog(struct pop3_client *client, const char *format, ...) - __attr_format__(2, 3); int client_read(struct pop3_client *client); void client_input(void *context);