diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:51:24 +0000 |
commit | f7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch) | |
tree | a3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/pop3-login/client-authenticate.c | |
parent | Initial commit. (diff) | |
download | dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.tar.xz dovecot-f7548d6d28c313cf80e6f3ef89aed16a19815df1.zip |
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/pop3-login/client-authenticate.c')
-rw-r--r-- | src/pop3-login/client-authenticate.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/pop3-login/client-authenticate.c b/src/pop3-login/client-authenticate.c new file mode 100644 index 0000000..e215833 --- /dev/null +++ b/src/pop3-login/client-authenticate.c @@ -0,0 +1,247 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "login-common.h" +#include "base64.h" +#include "buffer.h" +#include "hex-binary.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "safe-memset.h" +#include "str.h" +#include "str-sanitize.h" +#include "auth-client.h" +#include "../pop3/pop3-capability.h" +#include "client.h" +#include "client-authenticate.h" +#include "pop3-proxy.h" + + +static const char *capability_string = POP3_CAPABILITY_REPLY; + +bool cmd_capa(struct pop3_client *client, const char *args ATTR_UNUSED) +{ + const struct auth_mech_desc *mech; + unsigned int i, count; + string_t *str; + + str = t_str_new(128); + str_append(str, "+OK\r\n"); + str_append(str, capability_string); + + if (client_is_tls_enabled(&client->common) && !client->common.tls) + str_append(str, "STLS\r\n"); + if (!client->common.set->disable_plaintext_auth || + client->common.secured) + str_append(str, "USER\r\n"); + + str_append(str, "SASL"); + mech = sasl_server_get_advertised_mechs(&client->common, &count); + for (i = 0; i < count; i++) { + str_append_c(str, ' '); + str_append(str, mech[i].name); + } + str_append(str, "\r\n.\r\n"); + + client_send_raw(&client->common, str_c(str)); + return TRUE; +} + +void pop3_client_auth_result(struct client *client, + enum client_auth_result result, + const struct client_auth_reply *reply ATTR_UNUSED, + const char *text) +{ + switch (result) { + case CLIENT_AUTH_RESULT_SUCCESS: + /* nothing to be done for POP3 */ + break; + case CLIENT_AUTH_RESULT_TEMPFAIL: + client_send_reply(client, POP3_CMD_REPLY_TEMPFAIL, text); + break; + case CLIENT_AUTH_RESULT_AUTHFAILED: + case CLIENT_AUTH_RESULT_AUTHFAILED_REASON: + case CLIENT_AUTH_RESULT_AUTHZFAILED: + case CLIENT_AUTH_RESULT_PASS_EXPIRED: + case CLIENT_AUTH_RESULT_SSL_REQUIRED: + case CLIENT_AUTH_RESULT_LOGIN_DISABLED: + case CLIENT_AUTH_RESULT_MECH_INVALID: + case CLIENT_AUTH_RESULT_MECH_SSL_REQUIRED: + case CLIENT_AUTH_RESULT_INVALID_BASE64: + client_send_reply(client, POP3_CMD_REPLY_AUTH_ERROR, text); + break; + default: + client_send_reply(client, POP3_CMD_REPLY_ERROR, text); + break; + } +} + +int cmd_auth(struct pop3_client *pop3_client) +{ + /* NOTE: This command's input is handled specially because the + SASL-IR can be large. */ + struct client *client = &pop3_client->common; + const unsigned char *data; + size_t i, size; + int ret; + + /* <auth mechanism name> [<initial SASL response>] */ + if (!pop3_client->auth_mech_name_parsed) { + data = i_stream_get_data(client->input, &size); + for (i = 0; i < size; i++) { + if (data[i] == ' ' || + data[i] == '\r' || data[i] == '\n') + break; + } + if (i == size) + return 0; + if (i == 0) { + /* Old-style SASL discovery, used by MS Outlook */ + unsigned int i, count; + const struct auth_mech_desc *mech; + + client_send_raw(client, "+OK\r\n"); + mech = sasl_server_get_advertised_mechs(client, &count); + for (i = 0; i < count; i++) { + client_send_raw(client, mech[i].name); + client_send_raw(client, "\r\n"); + } + client_send_raw(client, ".\r\n"); + (void)i_stream_read_next_line(client->input); + return 1; + } + i_free(client->auth_mech_name); + client->auth_mech_name = i_strndup(data, i); + pop3_client->auth_mech_name_parsed = TRUE; + if (data[i] == ' ') + i++; + i_stream_skip(client->input, i); + } + + /* get SASL-IR, if any */ + if ((ret = client_auth_read_line(client)) <= 0) + return ret; + + const char *ir = NULL; + if (client->auth_response->used > 0) + ir = t_strdup(str_c(client->auth_response)); + + pop3_client->auth_mech_name_parsed = FALSE; + /* The whole AUTH line command is parsed now. The rest of the SASL + protocol exchange happens in login-common code. We can free the + current command here already, because no pop3-login code is called + until the authentication is finished. Also, there's currently no + single location that is called in pop3-login code after the + authentication is finished. For example it could be an auth failure + or it could be a successful authentication with a proxying + failure. */ + i_free(pop3_client->current_cmd); + return client_auth_begin(client, t_strdup(client->auth_mech_name), ir); +} + +bool cmd_user(struct pop3_client *pop3_client, const char *args) +{ + if (!client_check_plaintext_auth(&pop3_client->common, FALSE)) { + if (pop3_client->common.virtual_user == NULL) + pop3_client->common.virtual_user = i_strdup(args); + return TRUE; + } + + i_free(pop3_client->last_user); + pop3_client->last_user = i_strdup(args); + + client_send_raw(&pop3_client->common, "+OK\r\n"); + return TRUE; +} + +bool cmd_pass(struct pop3_client *pop3_client, const char *args) +{ + struct client *client = &pop3_client->common; + string_t *plain_login, *base64; + + if (pop3_client->last_user == NULL) { + /* client may ignore the USER reply and only display the error + message from PASS */ + if (!client_check_plaintext_auth(client, TRUE)) + return TRUE; + + client_send_reply(client, POP3_CMD_REPLY_ERROR, + "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, pop3_client->last_user); + str_append_c(plain_login, '\0'); + str_append(plain_login, args); + + i_free_and_null(pop3_client->last_user); + + base64 = t_buffer_create(MAX_BASE64_ENCODED_SIZE(plain_login->used)); + base64_encode(plain_login->data, plain_login->used, base64); + + (void)client_auth_begin(client, "PLAIN", str_c(base64)); + return TRUE; +} + +bool cmd_apop(struct pop3_client *pop3_client, const char *args) +{ + struct client *client = &pop3_client->common; + buffer_t *apop_data, *base64; + const char *p; + unsigned int server_pid, connect_uid; + + if (pop3_client->apop_challenge == NULL) { + if (client->set->auth_verbose) + e_info(client->event, "APOP failed: APOP not enabled"); + client_send_reply(client, POP3_CMD_REPLY_ERROR, + "APOP not enabled."); + return TRUE; + } + + /* <username> <md5 sum in hex> */ + p = strchr(args, ' '); + if (p == NULL || strlen(p+1) != 32) { + if (client->set->auth_verbose) + e_info(client->event, "APOP failed: Invalid parameters"); + client_send_reply(client, POP3_CMD_REPLY_ERROR, + "Invalid parameters."); + return TRUE; + } + + /* APOP challenge \0 username \0 APOP response */ + apop_data = t_buffer_create(128); + buffer_append(apop_data, pop3_client->apop_challenge, + strlen(pop3_client->apop_challenge)+1); + buffer_append(apop_data, args, (size_t)(p-args)); + buffer_append_c(apop_data, '\0'); + + if (hex_to_binary(p+1, apop_data) < 0) { + if (client->set->auth_verbose) { + e_info(client->event, "APOP failed: " + "Invalid characters in MD5 response"); + } + client_send_reply(client, POP3_CMD_REPLY_ERROR, + "Invalid characters in MD5 response."); + return TRUE; + } + + base64 = t_buffer_create(MAX_BASE64_ENCODED_SIZE(apop_data->used)); + base64_encode(apop_data->data, apop_data->used, base64); + + auth_client_get_connect_id(auth_client, &server_pid, &connect_uid); + if (pop3_client->apop_server_pid != server_pid || + pop3_client->apop_connect_uid != connect_uid) { + /* we reconnected to auth server and can't authenticate + with APOP in this session anymore. disconnecting the user + is probably the best solution now. */ + client_destroy(client, + "Reconnected to auth server, can't do APOP"); + return TRUE; + } + + (void)client_auth_begin_private(client, "APOP", str_c(base64)); + return TRUE; +} |