summaryrefslogtreecommitdiffstats
path: root/src/imap-login/imap-login-cmd-id.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/imap-login/imap-login-cmd-id.c')
-rw-r--r--src/imap-login/imap-login-cmd-id.c281
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;
+ }
+}