diff options
Diffstat (limited to 'src/imap-login/imap-login-cmd-id.c')
-rw-r--r-- | src/imap-login/imap-login-cmd-id.c | 281 |
1 files changed, 281 insertions, 0 deletions
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 0000000..a1c6294 --- /dev/null +++ b/src/imap-login/imap-login-cmd-id.c @@ -0,0 +1,281 @@ +/* Copyright (c) 2002-2018 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" + +struct imap_id_param_handler { + const char *key; + bool key_is_prefix; + + void (*callback)(struct imap_client *client, + const char *key, const char *value); +}; + +static void +cmd_id_x_originating_ip(struct imap_client *client, + const char *key ATTR_UNUSED, const char *value) +{ + (void)net_addr2ip(value, &client->common.ip); +} + +static void +cmd_id_x_originating_port(struct imap_client *client, + const char *key ATTR_UNUSED, const char *value) +{ + (void)net_str2port(value, &client->common.remote_port); +} + +static void +cmd_id_x_connected_ip(struct imap_client *client, + const char *key ATTR_UNUSED, const char *value) +{ + (void)net_addr2ip(value, &client->common.local_ip); +} + +static void +cmd_id_x_connected_port(struct imap_client *client, + const char *key ATTR_UNUSED, const char *value) +{ + (void)net_str2port(value, &client->common.local_port); +} + +static void +cmd_id_x_proxy_ttl(struct imap_client *client, + const char *key ATTR_UNUSED, const char *value) +{ + if (str_to_uint(value, &client->common.proxy_ttl) < 0) { + /* nothing */ + } +} + +static void +cmd_id_x_session_id(struct imap_client *client, + const char *key ATTR_UNUSED, const char *value) +{ + if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) { + client->common.session_id = + p_strdup(client->common.pool, value); + } +} + +static void +cmd_id_x_forward_(struct imap_client *client, + const char *key, const char *value) +{ + i_assert(strncasecmp(key, "x-forward-", 10) == 0); + client_add_forward_field(&client->common, key+10, value); +} + +static const struct imap_id_param_handler imap_login_id_params[] = { + { "x-originating-ip", FALSE, cmd_id_x_originating_ip }, + { "x-originating-port", FALSE, cmd_id_x_originating_port }, + { "x-connected-ip", FALSE, cmd_id_x_connected_ip }, + { "x-connected-port", FALSE, cmd_id_x_connected_port }, + { "x-proxy-ttl", FALSE, cmd_id_x_proxy_ttl }, + { "x-session-id", FALSE, cmd_id_x_session_id }, + { "x-session-ext-id", FALSE, cmd_id_x_session_id }, + { "x-forward-", TRUE, cmd_id_x_forward_ }, + + { NULL, FALSE, NULL } +}; + +static const struct imap_id_param_handler * +imap_id_param_handler_find(const char *key) +{ + for (unsigned int i = 0; imap_login_id_params[i].key != NULL; i++) { + unsigned int prefix_len = strlen(imap_login_id_params[i].key); + + if (strncasecmp(imap_login_id_params[i].key, key, prefix_len) == 0 && + (key[prefix_len] == '\0' || + imap_login_id_params[i].key_is_prefix)) + return &imap_login_id_params[i]; + } + return NULL; +} + +static bool +client_try_update_info(struct imap_client *client, + const char *key, const char *value) +{ + const struct imap_id_param_handler *handler; + + handler = imap_id_param_handler_find(key); + if (handler == NULL) + return FALSE; + + /* do not try to process NIL values as client-info, + but store them for non-reserved keys */ + if (client->common.trusted && !client->id_logged && value != NULL) + handler->callback(client, key, value); + return TRUE; +} + +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; + + client_id_str = !client_try_update_info(client, key, value); + + 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) { + e_info(client->common.event, "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; + + 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; + } +} |