imap-login-client.c revision 06704acb3bfbee17e463e16f98361be706855a04
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
a35cbba04d0a2823da98e693bd09a051addffdb2Timo Sirainen# error LOGIN_MAX_INBUF_SIZE too short to fit all ID command parameters
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen/* maximum length for IMAP command line. */
cb951d3282610a9a0960230865bc5f3e3347b203Timo Sirainen/* Disconnect client when it sends too many bad commands */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic const char *const imap_login_reserved_id_keys[] = {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "x-originating-ip",
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen "x-originating-port",
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen "x-connected-ip",
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen "x-connected-port",
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen "x-proxy-ttl",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "x-session-id",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "x-session-ext-id",
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen/* Skip incoming data until newline is found,
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen returns TRUE if newline was found. */
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainenbool client_skip_line(struct imap_client *client)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen const unsigned char *data;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen data = i_stream_get_data(client->common.input, &data_size);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen for (i = 0; i < data_size; i++) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic bool client_handle_parser_error(struct imap_client *client,
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen const char *msg;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen msg = imap_parser_get_error(parser, &parse_error);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic bool is_login_cmd_disabled(struct client *client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (auth_client_find_mech(auth_client, "PLAIN") == NULL) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen /* no PLAIN authentication, can't use LOGIN command */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (strcmp(client->ssl_set->ssl, "required") == 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic const char *get_capability(struct client *client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (*imap_client->set->imap_capability == '\0')
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append(cap_str, CAPABILITY_BANNER_STRING);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen else if (*imap_client->set->imap_capability != '+') {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append(cap_str, imap_client->set->imap_capability);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_append(cap_str, CAPABILITY_BANNER_STRING);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen str_append(cap_str, imap_client->set->imap_capability + 1);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client_is_tls_enabled(client) && !client->tls)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_authenticate_get_capabilities(client, cap_str);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic int cmd_capability(struct imap_client *imap_client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* Client is required to send CAPABILITY after STARTTLS, so the
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen capability resp-code workaround checks only pre-STARTTLS
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen CAPABILITY commands. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_client->client_ignores_capability_resp_code = TRUE;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen "* CAPABILITY ", get_capability(client), "\r\n", NULL));
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen "Pre-login capabilities listed, post-login capabilities have more.");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int cmd_starttls(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenimap_client_notify_starttls(struct client *client,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen client_send_reply(client, IMAP_CMD_REPLY_OK, text);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(client, IMAP_CMD_REPLY_BAD, text);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen /* SYNC WITH imap_login_reserved_id_keys */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (strcasecmp(key, "x-originating-ip") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strcasecmp(key, "x-originating-port") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (void)net_str2port(value, &client->common.remote_port);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strcasecmp(key, "x-connected-ip") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (void)net_addr2ip(value, &client->common.local_ip);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strcasecmp(key, "x-connected-port") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen (void)net_str2port(value, &client->common.local_port);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strcasecmp(key, "x-proxy-ttl") == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (str_to_uint(value, &client->common.proxy_ttl) < 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* nothing */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strcasecmp(key, "x-session-id") == 0 ||
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen } else if (strncasecmp(key, "x-forward-", 10) == 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* handle extra field */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_add_forward_field(&client->common, key+10, value);
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainenstatic bool client_id_reserved_word(const char *key)
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen return (strncasecmp(key, "x-forward-", 10) == 0 ||
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainen str_array_icase_find(imap_login_reserved_id_keys, key));
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainenstatic void cmd_id_handle_keyvalue(struct imap_client *client,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* length of key + length of value (NIL for NULL) and two set of
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen quotes and space */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (client->common.trusted && !client->id_logged) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* do not try to process NIL values as client-info,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen but store them for non-reserved keys */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_id_str = !client_id_reserved_word(key);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_id_str = !client_update_info(client, key, value);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_assert(client_id_str == !client_id_reserved_word(key));
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_id_str = !client_id_reserved_word(key);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (client->set->imap_id_retain && client_id_str &&
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen str_len(client->common.client_id) + kvlen < LOGIN_MAX_CLIENT_ID_LEN)) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->common.client_id = str_new(client->common.preproxy_pool, 64);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_append_quoted(client->common.client_id, key);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_append_quoted(client->common.client_id, value);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen str_array_icase_find((void *)client->cmd_id->log_keys, key)))
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_id_log_reply_append(client->cmd_id->log_reply, key, value);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int cmd_id_handle_args(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen struct imap_client_cmd_id *id = client->cmd_id;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* no ID logging */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* already logged the ID reply */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (strcmp(client->set->imap_id_log, "*") == 0) {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen /* log all keys */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* log only specified keys */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen id->log_keys = p_strsplit_spaces(default_pool,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (i_strocpy(id->key, key, sizeof(id->key)) < 0)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen cmd_id_handle_keyvalue(client, id->key, value);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void cmd_id_finish(struct imap_client *client)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen /* finished handling the parameters */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen "ID sent: %s", str_c(client->cmd_id->log_reply)));
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen imap_id_reply_generate(client->set->imap_id_send)));
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "ID completed.");
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenstatic void cmd_id_free(struct imap_client *client)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct imap_client_cmd_id *id = client->cmd_id;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen client->cmd_id = id = i_new(struct imap_client_cmd_id, 1);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen id->parser = imap_parser_create(client->common.input,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen while ((ret = imap_parser_read_args(id->parser, 1, parser_flags, &args)) > 0) {
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if ((ret = cmd_id_handle_args(client, args)) < 0) {
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen "Invalid ID parameters");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* NIL parameter */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* finished the line */
e392fcb39a06609af20a9e79017683f194de3ddeTimo Sirainen if (!client_handle_parser_error(client, id->parser))
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int cmd_noop(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "NOOP completed.");
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int cmd_logout(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, "Logging out");
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen "Logout completed.");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_destroy(&client->common, "Aborted login");
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int cmd_enable(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client_send_raw(&client->common, "* ENABLED\r\n");
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen "ENABLE ignored in non-authenticated state.");
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainenstatic int client_command_execute(struct imap_client *client, const char *cmd,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* atom-specials: */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* list-wildcards: */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* quoted-specials: */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic int client_parse_command(struct imap_client *client,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, args_r)) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (!client_handle_parser_error(client, client->parser)) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* client destroyed */
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen /* not enough data */
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen /* we read the entire line - skip over the CRLF */
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainenstatic bool client_handle_input(struct imap_client *client)
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* clear the previous command from memory. don't do this
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen immediately after handling command since we need the
f6f94063c4c3080280b87ab47ac2bb756ba002f9Timo Sirainen cmd_tag to stay some time after authentication commands. */
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen /* remove \r\n */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen /* the tag is invalid, don't allow it and don't
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen send it back. this attempts to prevent any
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen potentially dangerous replies in case someone tries
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen to access us using HTTP protocol. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return client->common.v.input_next_cmd(&client->common);
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainenstatic bool imap_client_input_next_cmd(struct client *_client)
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen struct imap_client *client = (struct imap_client *)_client;
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen if (strcasecmp(client->cmd_name, "AUTHENTICATE") == 0) {
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen /* SASL-IR may need more space than input buffer's size,
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen so we'll handle it as a special case. */
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen } else if (strcasecmp(client->cmd_name, "ID") == 0) {
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen /* ID extensions allows max. 30 parameters,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen each max. 1024 bytes long. that brings us over the input
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen buffer's size, so handle the parameters one at a time */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen ret = 1; /* don't send the error reply again */
return TRUE;
if (ret == 0)
return FALSE;
} else if (ret < 0) {
return FALSE;
T_BEGIN {
if (tagged)
} T_END;
switch (reply) {
case IMAP_CMD_REPLY_OK:
case IMAP_CMD_REPLY_NO:
case IMAP_CMD_REPLY_BAD:
case IMAP_CMD_REPLY_BYE:
const char *text)
if (bad)
const char *text)
static void imap_login_preinit(void)
static void imap_login_init(void)
static void imap_login_deinit(void)
NULL,
NULL,