Mercurial > dovecot > original-hg > dovecot-1.2
diff src/imap-login/client-authenticate.c @ 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 | f1e9f3ec8135 |
children | e2ce951fa8e6 |
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; }