From: Timo Sirainen Date: Wed, 14 Jun 2017 07:34:57 +0000 (+0300) Subject: imap-login: Move ID command handling to its own file. X-Git-Tag: 2.3.2.rc1~73 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=994202f3204025e553bd2d4ca6d5f6b010aee229;p=thirdparty%2Fdovecot%2Fcore.git imap-login: Move ID command handling to its own file. --- diff --git a/src/imap-login/Makefile.am b/src/imap-login/Makefile.am index 9a6afb817a..904f530c29 100644 --- a/src/imap-login/Makefile.am +++ b/src/imap-login/Makefile.am @@ -25,6 +25,7 @@ imap_login_DEPENDENCIES = \ imap_login_SOURCES = \ imap-login-client.c \ client-authenticate.c \ + imap-login-cmd-id.c \ imap-login-commands.c \ imap-login-settings.c \ imap-proxy.c diff --git a/src/imap-login/imap-login-client.c b/src/imap-login/imap-login-client.c index f529e38830..9c37245ef7 100644 --- a/src/imap-login/imap-login-client.c +++ b/src/imap-login/imap-login-client.c @@ -25,23 +25,9 @@ # error LOGIN_MAX_INBUF_SIZE too short to fit all ID command parameters #endif -/* maximum length for IMAP command line. */ -#define MAX_IMAP_LINE 8192 - /* Disconnect client when it sends too many bad commands */ #define CLIENT_MAX_BAD_COMMANDS 3 -static const char *const imap_login_reserved_id_keys[] = { - "x-originating-ip", - "x-originating-port", - "x-connected-ip", - "x-connected-port", - "x-proxy-ttl", - "x-session-id", - "x-session-ext-id", - NULL -}; - /* Skip incoming data until newline is found, returns TRUE if newline was found. */ bool client_skip_line(struct imap_client *client) @@ -61,8 +47,8 @@ bool client_skip_line(struct imap_client *client) return FALSE; } -static bool client_handle_parser_error(struct imap_client *client, - struct imap_parser *parser) +bool client_handle_parser_error(struct imap_client *client, + struct imap_parser *parser) { const char *msg; enum imap_parser_error parse_error; @@ -170,226 +156,6 @@ imap_client_notify_starttls(struct client *client, client_send_reply(client, IMAP_CMD_REPLY_BAD, text); } -static bool -client_update_info(struct imap_client *client, - const char *key, const char *value) -{ - i_assert(value != NULL); - - /* SYNC WITH imap_login_reserved_id_keys */ - - if (strcasecmp(key, "x-originating-ip") == 0) { - (void)net_addr2ip(value, &client->common.ip); - } else if (strcasecmp(key, "x-originating-port") == 0) { - (void)net_str2port(value, &client->common.remote_port); - } else if (strcasecmp(key, "x-connected-ip") == 0) { - (void)net_addr2ip(value, &client->common.local_ip); - } else if (strcasecmp(key, "x-connected-port") == 0) { - (void)net_str2port(value, &client->common.local_port); - } else if (strcasecmp(key, "x-proxy-ttl") == 0) { - if (str_to_uint(value, &client->common.proxy_ttl) < 0) { - /* nothing */ - } - } else if (strcasecmp(key, "x-session-id") == 0 || - strcasecmp(key, "x-session-ext-id") == 0) { - if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) { - client->common.session_id = - p_strdup(client->common.pool, value); - } - } else if (strncasecmp(key, "x-forward-", 10) == 0) { - /* handle extra field */ - client_add_forward_field(&client->common, key+10, value); - } else { - return FALSE; - } - return TRUE; -} - -static bool client_id_reserved_word(const char *key) -{ - i_assert(key != NULL); - return (strncasecmp(key, "x-forward-", 10) == 0 || - str_array_icase_find(imap_login_reserved_id_keys, key)); -} - -static void cmd_id_handle_keyvalue(struct imap_client *client, - const char *key, const char *value) -{ - bool client_id_str; - /* length of key + length of value (NIL for NULL) and two set of - quotes and space */ - size_t kvlen = strlen(key) + 2 + 1 + - (value == NULL ? 3 : strlen(value)) + 2; - - if (client->common.trusted && !client->id_logged) { - if (value == NULL) { - /* do not try to process NIL values as client-info, - but store them for non-reserved keys */ - client_id_str = !client_id_reserved_word(key); - } else { - client_id_str = !client_update_info(client, key, value); - i_assert(client_id_str == !client_id_reserved_word(key)); - } - } else { - client_id_str = !client_id_reserved_word(key); - } - - if (client->set->imap_id_retain && client_id_str && - (client->common.client_id == NULL || - str_len(client->common.client_id) + kvlen < LOGIN_MAX_CLIENT_ID_LEN)) { - if (client->common.client_id == NULL) { - client->common.client_id = str_new(client->common.preproxy_pool, 64); - } else { - str_append_c(client->common.client_id, ' '); - } - imap_append_quoted(client->common.client_id, key); - str_append_c(client->common.client_id, ' '); - if (value == NULL) - str_append(client->common.client_id, "NIL"); - else - imap_append_quoted(client->common.client_id, value); - } - - if (client->cmd_id->log_reply != NULL && - (client->cmd_id->log_keys == NULL || - str_array_icase_find((void *)client->cmd_id->log_keys, key))) - imap_id_log_reply_append(client->cmd_id->log_reply, key, value); -} - -static int cmd_id_handle_args(struct imap_client *client, - const struct imap_arg *arg) -{ - struct imap_client_cmd_id *id = client->cmd_id; - const char *key, *value; - - switch (id->state) { - case IMAP_CLIENT_ID_STATE_LIST: - if (arg->type == IMAP_ARG_NIL) - return 1; - if (arg->type != IMAP_ARG_LIST) - return -1; - if (client->set->imap_id_log[0] == '\0') { - /* no ID logging */ - } else if (client->id_logged) { - /* already logged the ID reply */ - } else { - id->log_reply = str_new(default_pool, 64); - if (strcmp(client->set->imap_id_log, "*") == 0) { - /* log all keys */ - } else { - /* log only specified keys */ - id->log_keys = p_strsplit_spaces(default_pool, - client->set->imap_id_log, " "); - } - } - id->state = IMAP_CLIENT_ID_STATE_KEY; - break; - case IMAP_CLIENT_ID_STATE_KEY: - if (!imap_arg_get_string(arg, &key)) - return -1; - if (i_strocpy(id->key, key, sizeof(id->key)) < 0) - return -1; - id->state = IMAP_CLIENT_ID_STATE_VALUE; - break; - case IMAP_CLIENT_ID_STATE_VALUE: - if (!imap_arg_get_nstring(arg, &value)) - return -1; - cmd_id_handle_keyvalue(client, id->key, value); - id->state = IMAP_CLIENT_ID_STATE_KEY; - break; - } - return 0; -} - -static void cmd_id_finish(struct imap_client *client) -{ - /* finished handling the parameters */ - if (!client->id_logged) { - client->id_logged = TRUE; - - if (client->cmd_id->log_reply != NULL) { - client_log(&client->common, t_strdup_printf( - "ID sent: %s", str_c(client->cmd_id->log_reply))); - } - } - - client_send_raw(&client->common, - t_strdup_printf("* ID %s\r\n", - imap_id_reply_generate(client->set->imap_id_send))); - client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "ID completed."); -} - -static void cmd_id_free(struct imap_client *client) -{ - struct imap_client_cmd_id *id = client->cmd_id; - - if (id->log_reply != NULL) - str_free(&id->log_reply); - if (id->log_keys != NULL) - p_strsplit_free(default_pool, id->log_keys); - imap_parser_unref(&id->parser); - - i_free_and_null(client->cmd_id); - client->skip_line = TRUE; -} - -static int cmd_id(struct imap_client *client) -{ - struct imap_client_cmd_id *id; - enum imap_parser_flags parser_flags; - const struct imap_arg *args; - int ret; - - if (client->common.client_id != NULL) - str_truncate(client->common.client_id, 0); - - if (client->cmd_id == NULL) { - client->cmd_id = id = i_new(struct imap_client_cmd_id, 1); - id->parser = imap_parser_create(client->common.input, - client->common.output, - MAX_IMAP_LINE); - if (client->set->imap_literal_minus) - imap_parser_enable_literal_minus(id->parser); - parser_flags = IMAP_PARSE_FLAG_STOP_AT_LIST; - } else { - id = client->cmd_id; - parser_flags = IMAP_PARSE_FLAG_INSIDE_LIST; - } - - while ((ret = imap_parser_read_args(id->parser, 1, parser_flags, &args)) > 0) { - i_assert(ret == 1); - - if ((ret = cmd_id_handle_args(client, args)) < 0) { - client_send_reply(&client->common, - IMAP_CMD_REPLY_BAD, - "Invalid ID parameters"); - cmd_id_free(client); - return -1; - } - if (ret > 0) { - /* NIL parameter */ - ret = 0; - break; - } - imap_parser_reset(id->parser); - parser_flags = IMAP_PARSE_FLAG_INSIDE_LIST; - } - if (ret == 0) { - /* finished the line */ - cmd_id_finish(client); - cmd_id_free(client); - return 1; - } else if (ret == -1) { - if (!client_handle_parser_error(client, id->parser)) - return 0; - cmd_id_free(client); - return -1; - } else { - i_assert(ret == -2); - return 0; - } -} - static int cmd_noop(struct imap_client *client, const struct imap_arg *args ATTR_UNUSED) { @@ -619,7 +385,8 @@ static void imap_client_create(struct client *client, void **other_sets) imap_client->set = other_sets[0]; imap_client->parser = imap_parser_create(imap_client->common.input, - imap_client->common.output, MAX_IMAP_LINE); + imap_client->common.output, + IMAP_LOGIN_MAX_LINE_LENGTH); if (imap_client->set->imap_literal_minus) imap_parser_enable_literal_minus(imap_client->parser); client->io = io_add_istream(client->input, client_input, client); @@ -655,7 +422,8 @@ static void imap_client_starttls(struct client *client) imap_parser_unref(&imap_client->parser); imap_client->parser = imap_parser_create(imap_client->common.input, - imap_client->common.output, MAX_IMAP_LINE); + imap_client->common.output, + IMAP_LOGIN_MAX_LINE_LENGTH); /* CRLF is lost from buffer when streams are reopened. */ imap_client->skip_line = FALSE; diff --git a/src/imap-login/imap-login-client.h b/src/imap-login/imap-login-client.h index 847b2b7f7d..002829ba3a 100644 --- a/src/imap-login/imap-login-client.h +++ b/src/imap-login/imap-login-client.h @@ -8,6 +8,9 @@ /* Master prefix is: <1|0> */ #define IMAP_TAG_MAX_LEN (LOGIN_MAX_MASTER_PREFIX_LEN-2) +/* maximum length for IMAP command line. */ +#define IMAP_LOGIN_MAX_LINE_LENGTH 8192 + enum imap_client_id_state { IMAP_CLIENT_ID_STATE_LIST = 0, IMAP_CLIENT_ID_STATE_KEY, @@ -83,9 +86,12 @@ enum imap_cmd_reply { void client_send_reply(struct client *client, enum imap_cmd_reply reply, const char *text); - void client_send_reply_code(struct client *client, enum imap_cmd_reply reply, const char *resp_code, const char *text) ATTR_NULL(3); +bool client_handle_parser_error(struct imap_client *client, + struct imap_parser *parser); + +int cmd_id(struct imap_client *client); #endif diff --git a/src/imap-login/imap-login-cmd-id.c b/src/imap-login/imap-login-cmd-id.c new file mode 100644 index 0000000000..7bc994751d --- /dev/null +++ b/src/imap-login/imap-login-cmd-id.c @@ -0,0 +1,239 @@ +/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */ + +#include "login-common.h" +#include "str.h" +#include "imap-parser.h" +#include "imap-quote.h" +#include "imap-login-settings.h" +#include "imap-login-client.h" + +static const char *const imap_login_reserved_id_keys[] = { + "x-originating-ip", + "x-originating-port", + "x-connected-ip", + "x-connected-port", + "x-proxy-ttl", + "x-session-id", + "x-session-ext-id", + NULL +}; + +static bool +client_update_info(struct imap_client *client, + const char *key, const char *value) +{ + i_assert(value != NULL); + + /* SYNC WITH imap_login_reserved_id_keys */ + + if (strcasecmp(key, "x-originating-ip") == 0) { + (void)net_addr2ip(value, &client->common.ip); + } else if (strcasecmp(key, "x-originating-port") == 0) { + (void)net_str2port(value, &client->common.remote_port); + } else if (strcasecmp(key, "x-connected-ip") == 0) { + (void)net_addr2ip(value, &client->common.local_ip); + } else if (strcasecmp(key, "x-connected-port") == 0) { + (void)net_str2port(value, &client->common.local_port); + } else if (strcasecmp(key, "x-proxy-ttl") == 0) { + if (str_to_uint(value, &client->common.proxy_ttl) < 0) { + /* nothing */ + } + } else if (strcasecmp(key, "x-session-id") == 0 || + strcasecmp(key, "x-session-ext-id") == 0) { + if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) { + client->common.session_id = + p_strdup(client->common.pool, value); + } + } else if (strncasecmp(key, "x-forward-", 10) == 0) { + /* handle extra field */ + client_add_forward_field(&client->common, key+10, value); + } else { + return FALSE; + } + return TRUE; +} + +static bool client_id_reserved_word(const char *key) +{ + i_assert(key != NULL); + return (strncasecmp(key, "x-forward-", 10) == 0 || + str_array_icase_find(imap_login_reserved_id_keys, key)); +} + +static void cmd_id_handle_keyvalue(struct imap_client *client, + const char *key, const char *value) +{ + bool client_id_str; + /* length of key + length of value (NIL for NULL) and two set of + quotes and space */ + size_t kvlen = strlen(key) + 2 + 1 + + (value == NULL ? 3 : strlen(value)) + 2; + + if (client->common.trusted && !client->id_logged) { + if (value == NULL) { + /* do not try to process NIL values as client-info, + but store them for non-reserved keys */ + client_id_str = !client_id_reserved_word(key); + } else { + client_id_str = !client_update_info(client, key, value); + i_assert(client_id_str == !client_id_reserved_word(key)); + } + } else { + client_id_str = !client_id_reserved_word(key); + } + + if (client->set->imap_id_retain && client_id_str && + (client->common.client_id == NULL || + str_len(client->common.client_id) + kvlen < LOGIN_MAX_CLIENT_ID_LEN)) { + if (client->common.client_id == NULL) { + client->common.client_id = str_new(client->common.preproxy_pool, 64); + } else { + str_append_c(client->common.client_id, ' '); + } + imap_append_quoted(client->common.client_id, key); + str_append_c(client->common.client_id, ' '); + if (value == NULL) + str_append(client->common.client_id, "NIL"); + else + imap_append_quoted(client->common.client_id, value); + } + + if (client->cmd_id->log_reply != NULL && + (client->cmd_id->log_keys == NULL || + str_array_icase_find((void *)client->cmd_id->log_keys, key))) + imap_id_log_reply_append(client->cmd_id->log_reply, key, value); +} + +static int cmd_id_handle_args(struct imap_client *client, + const struct imap_arg *arg) +{ + struct imap_client_cmd_id *id = client->cmd_id; + const char *key, *value; + + switch (id->state) { + case IMAP_CLIENT_ID_STATE_LIST: + if (arg->type == IMAP_ARG_NIL) + return 1; + if (arg->type != IMAP_ARG_LIST) + return -1; + if (client->set->imap_id_log[0] == '\0') { + /* no ID logging */ + } else if (client->id_logged) { + /* already logged the ID reply */ + } else { + id->log_reply = str_new(default_pool, 64); + if (strcmp(client->set->imap_id_log, "*") == 0) { + /* log all keys */ + } else { + /* log only specified keys */ + id->log_keys = p_strsplit_spaces(default_pool, + client->set->imap_id_log, " "); + } + } + id->state = IMAP_CLIENT_ID_STATE_KEY; + break; + case IMAP_CLIENT_ID_STATE_KEY: + if (!imap_arg_get_string(arg, &key)) + return -1; + if (i_strocpy(id->key, key, sizeof(id->key)) < 0) + return -1; + id->state = IMAP_CLIENT_ID_STATE_VALUE; + break; + case IMAP_CLIENT_ID_STATE_VALUE: + if (!imap_arg_get_nstring(arg, &value)) + return -1; + cmd_id_handle_keyvalue(client, id->key, value); + id->state = IMAP_CLIENT_ID_STATE_KEY; + break; + } + return 0; +} + +static void cmd_id_finish(struct imap_client *client) +{ + /* finished handling the parameters */ + if (!client->id_logged) { + client->id_logged = TRUE; + + if (client->cmd_id->log_reply != NULL) { + client_log(&client->common, t_strdup_printf( + "ID sent: %s", str_c(client->cmd_id->log_reply))); + } + } + + client_send_raw(&client->common, + t_strdup_printf("* ID %s\r\n", + imap_id_reply_generate(client->set->imap_id_send))); + client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "ID completed."); +} + +static void cmd_id_free(struct imap_client *client) +{ + struct imap_client_cmd_id *id = client->cmd_id; + + if (id->log_reply != NULL) + str_free(&id->log_reply); + if (id->log_keys != NULL) + p_strsplit_free(default_pool, id->log_keys); + imap_parser_unref(&id->parser); + + i_free_and_null(client->cmd_id); + client->skip_line = TRUE; +} + +int cmd_id(struct imap_client *client) +{ + struct imap_client_cmd_id *id; + enum imap_parser_flags parser_flags; + const struct imap_arg *args; + int ret; + + if (client->common.client_id != NULL) + str_truncate(client->common.client_id, 0); + + if (client->cmd_id == NULL) { + client->cmd_id = id = i_new(struct imap_client_cmd_id, 1); + id->parser = imap_parser_create(client->common.input, + client->common.output, + IMAP_LOGIN_MAX_LINE_LENGTH); + if (client->set->imap_literal_minus) + imap_parser_enable_literal_minus(id->parser); + parser_flags = IMAP_PARSE_FLAG_STOP_AT_LIST; + } else { + id = client->cmd_id; + parser_flags = IMAP_PARSE_FLAG_INSIDE_LIST; + } + + while ((ret = imap_parser_read_args(id->parser, 1, parser_flags, &args)) > 0) { + i_assert(ret == 1); + + if ((ret = cmd_id_handle_args(client, args)) < 0) { + client_send_reply(&client->common, + IMAP_CMD_REPLY_BAD, + "Invalid ID parameters"); + cmd_id_free(client); + return -1; + } + if (ret > 0) { + /* NIL parameter */ + ret = 0; + break; + } + imap_parser_reset(id->parser); + parser_flags = IMAP_PARSE_FLAG_INSIDE_LIST; + } + if (ret == 0) { + /* finished the line */ + cmd_id_finish(client); + cmd_id_free(client); + return 1; + } else if (ret == -1) { + if (!client_handle_parser_error(client, id->parser)) + return 0; + cmd_id_free(client); + return -1; + } else { + i_assert(ret == -2); + return 0; + } +}