client.c revision 7cb128dc4cae2a03a742f63ba7afee23c78e3af0
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2015 Dovecot authors, see the included COPYING file */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen# error LOGIN_MAX_INBUF_SIZE too short to fit all ID command parameters
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen/* maximum length for IMAP command line. */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen/* Disconnect client when it sends too many bad commands */
2dd39e478269d6fb0bb26d12b394aa30ee965e38Timo Sirainen/* Skip incoming data until newline is found,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen returns TRUE if newline was found. */
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainenbool client_skip_line(struct imap_client *client)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const unsigned char *data;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen data = i_stream_get_data(client->common.input, &data_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (i = 0; i < data_size; i++) {
857c471c13ca215f4be9dd4b336b742b8d434e31Timo Sirainenstatic bool client_handle_parser_error(struct imap_client *client,
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *msg;
4ba9a1d3facc515b3feb5238a16bcf91f76fac61Timo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg);
8b31f966d9688e07672ef1958dcbdb7686523c04Timo Sirainenstatic bool is_login_cmd_disabled(struct client *client)
9847ec56efa15fa063eea9988eee2d4ed9ec7d58Timo Sirainen if (auth_client_find_mech(auth_client, "PLAIN") == NULL) {
d46a1e3f999dda802dc5137e883adcd7a6629cd3Timo Sirainen /* no PLAIN authentication, can't use LOGIN command */
291ce16fffca75e8598a8c9dceb08613413dcb07Timo Sirainen if (strcmp(client->ssl_set->ssl, "required") == 0)
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainenstatic const char *get_capability(struct client *client)
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen if (*imap_client->set->imap_capability == '\0')
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen str_append(cap_str, CAPABILITY_BANNER_STRING);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen else if (*imap_client->set->imap_capability != '+')
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen str_append(cap_str, imap_client->set->imap_capability);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen str_append(cap_str, CAPABILITY_BANNER_STRING);
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen str_append(cap_str, imap_client->set->imap_capability + 1);
97ae33602db7d5bc8eede82512a965d49ab8853bTimo Sirainen if (client_is_tls_enabled(client) && !client->tls)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen client_authenticate_get_capabilities(client, cap_str);
afe1da042382720393eca6497253106e4eec75e0Timo Sirainenstatic int cmd_capability(struct imap_client *imap_client)
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* Client is required to send CAPABILITY after STARTTLS, so the
539977f9257bd8985be5a8093658da266ae9cd19Timo Sirainen capability resp-code workaround checks only pre-STARTTLS
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen CAPABILITY commands. */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen imap_client->client_ignores_capability_resp_code = TRUE;
420040a5930a2b497e79ff0b5f59ba4b764a5b39Timo Sirainen "* CAPABILITY ", get_capability(client), "\r\n", NULL));
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen "Pre-login capabilities listed, post-login capabilities have more.");
eecb235c14b49c01774134ea593c266f2d2c2be1Timo Sirainenstatic int cmd_starttls(struct imap_client *client)
98c217499d578495e982ea6010ebff831e9669aeMartti Rannanjärviimap_client_notify_starttls(struct client *client,
97ae33602db7d5bc8eede82512a965d49ab8853bTimo Sirainen client_send_reply(client, IMAP_CMD_REPLY_OK, text);
f1743785713e7632459d623d5df2108f4b93accbTimo Sirainen client_send_reply(client, IMAP_CMD_REPLY_BAD, text);
c5ab90cfad9cc3e33bcb1baeb30ffc82a7b7053aTimo Sirainen else if (strcasecmp(key, "x-originating-port") == 0)
9fd2181788a61500641c66aec0f8c746b19bf830Timo Sirainen else if (strcasecmp(key, "x-connected-ip") == 0)
9fd2181788a61500641c66aec0f8c746b19bf830Timo Sirainen (void)net_addr2ip(value, &client->common.local_ip);
9fd2181788a61500641c66aec0f8c746b19bf830Timo Sirainen else if (strcasecmp(key, "x-connected-port") == 0)
c6afd726060aae56b6622c6c52aec10231c4bf1cTimo Sirainen else if (strcasecmp(key, "x-session-id") == 0 ||
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (strlen(value) <= LOGIN_MAX_SESSION_ID_LEN) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic void cmd_id_handle_keyvalue(struct imap_client *client,
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen if (client->common.trusted && !client->id_logged)
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen str_array_icase_find((void *)client->cmd_id->log_keys, key)))
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen imap_id_log_reply_append(client->cmd_id->log_reply, key, value);
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainenstatic int cmd_id_handle_args(struct imap_client *client,
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen struct imap_client_cmd_id *id = client->cmd_id;
cff1f182205e674285cf3ff446a0dcf7afea277dTimo Sirainen /* no ID logging */
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen /* already logged the ID reply */
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen if (strcmp(client->set->imap_id_log, "*") == 0) {
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen /* log all keys */
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen /* log only specified keys */
f318b3dbe2acc177b8ee1c160e4b5b14e7f2cd41Timo Sirainen id->log_keys = p_strsplit_spaces(default_pool,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (i_strocpy(id->key, key, sizeof(id->key)) < 0)
62041dfb7d6ac6e9c633a557075999cdfcff7bd5Timo Sirainen cmd_id_handle_keyvalue(client, id->key, value);
d3442384ca53d4b18a493db7dd0b000f470419cfTimo Sirainenstatic void cmd_id_finish(struct imap_client *client)
aa47c9bd1d1fc70cd699c49fd1ca92dbc7615953Timo Sirainen /* finished handling the parameters */
c0d069950af1dbc6a4e5c3de3bf2e437796e3ae0Timo Sirainen "ID sent: %s", str_c(client->cmd_id->log_reply)));
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen imap_id_reply_generate(client->set->imap_id_send)));
c979eeda1f46483d9c963e265786b701d7683d77Timo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK, "ID completed.");
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainenstatic void cmd_id_free(struct imap_client *client)
4b41116563110d00330896a568eff1078c382827Timo Sirainen struct imap_client_cmd_id *id = client->cmd_id;
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen client->cmd_id = id = i_new(struct imap_client_cmd_id, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen id->parser = imap_parser_create(client->common.input,
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainen while ((ret = imap_parser_read_args(id->parser, 1, parser_flags, &args)) > 0) {
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen if ((ret = cmd_id_handle_args(client, args)) < 0) {
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainen "Invalid ID parameters");
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen /* NIL parameter */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* finished the line */
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (!client_handle_parser_error(client, id->parser))
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainenstatic int cmd_noop(struct imap_client *client)
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
2c70086138fe7ac9abf52cd4223c224fe0bbb488Timo Sirainen "NOOP completed.");
2b9dbb270ad82e58d5f3581436e6f143176d5819Timo Sirainenstatic int cmd_logout(struct imap_client *client)
1ae5d61ec366fdb2f3c5b150ca378d6141b0f4bdTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, "Logging out");
1ae5d61ec366fdb2f3c5b150ca378d6141b0f4bdTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
1ae5d61ec366fdb2f3c5b150ca378d6141b0f4bdTimo Sirainen "Logout completed.");
1ae5d61ec366fdb2f3c5b150ca378d6141b0f4bdTimo Sirainen client_destroy(&client->common, "Aborted login");
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainenstatic int cmd_enable(struct imap_client *client)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen client_send_raw(&client->common, "* ENABLED\r\n");
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
aa41b2e17912d6cad3151babea6a85dd88539d28Timo Sirainen "ENABLE ignored in non-authenticated state.");
d938e9e4ec4c0f326dffd5ebe42c1ad893ce7e52Timo Sirainenstatic int client_command_execute(struct imap_client *client, const char *cmd,
ca98d6a1bbe73499da758a36bfab2963375c8d06Timo Sirainen /* atom-specials: */
bd4e36a8cd7257cca7d1434c49a1e343ed7c5100Timo Sirainen /* list-wildcards: */
df00412606a00714a6e85383fa87fbdc7cc1fb5bTimo Sirainen /* quoted-specials: */
817d027593510c3ba70ad542ce0011f5f6916d1eTimo Sirainenstatic int client_parse_command(struct imap_client *client,
817d027593510c3ba70ad542ce0011f5f6916d1eTimo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, args_r)) {
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen if (!client_handle_parser_error(client, client->parser)) {
f81f4bc282cd1944cec187bae89c0701a416ed2aTimo Sirainen /* client destroyed */
fcfe85637e1ee14a9fc39c41fd6ceca106301542Timo Sirainen /* not enough data */
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen /* we read the entire line - skip over the CRLF */
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainenstatic bool client_handle_input(struct imap_client *client)
3857e2945a3b6744d603f0f5a656849ed8436ba3Timo Sirainen /* clear the previous command from memory. don't do this
f90cbe597c41d5cc91debd371f8648bd8e6ffbc2Timo Sirainen immediately after handling command since we need the
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen cmd_tag to stay some time after authentication commands. */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen /* remove \r\n */
b3484b5b1f47e4cf112f0e371478a2d7794b31bbTimo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen /* the tag is invalid, don't allow it and don't
4e3bcf7fdaeef92dd07a2acb1ded58422a907e87Timo Sirainen send it back. this attempts to prevent any
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen potentially dangerous replies in case someone tries
19e161dd9e2c3a2ffc96ee8852bec0720cb30d1cTimo Sirainen to access us using HTTP protocol. */
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (strcasecmp(client->cmd_name, "AUTHENTICATE") == 0) {
aa247243412a49f9bdebf7255e131dc6ece4ed46Timo Sirainen /* SASL-IR may need more space than input buffer's size,
e15b305e90c9834734ccf35ed78f0ad29d570ee9Timo Sirainen so we'll handle it as a special case. */
ecd69c4e8371853667e01b0c16d436ef7f7393e2Timo Sirainen } else if (strcasecmp(client->cmd_name, "ID") == 0) {
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen /* ID extensions allows max. 30 parameters,
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen each max. 1024 bytes long. that brings us over the input
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainen buffer's size, so handle the parameters one at a time */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen ret = 1; /* don't send the error reply again */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen client_command_execute(client, client->cmd_name, args);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (ret == -2 && strcasecmp(client->cmd_tag, "LOGIN") == 0) {
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen "First parameter in line is IMAP's command tag, "
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen "not the command name. Add that before the command, "
6df0ab0c1ab91f06b6418cb30eff44405a1b8f02Timo Sirainen "like: a login user pass");
6df0ab0c1ab91f06b6418cb30eff44405a1b8f02Timo Sirainen } else if (ret < 0) {
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_BYE,
306b3f41b05da642d87e7ca7a1496efce9f5902fTimo Sirainen "Too many invalid IMAP commands.");
e53ab6c7081246c865917f9aa0eff031a08ad1e7Timo Sirainen "Disconnected: Too many invalid commands");
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
21aaa6affb9f134112b75b5db737309fc35ef1cfMartti Rannanjärvi "Error in IMAP command received by server.");
ad48319996942463675b53877092ab7e13a7a75aTimo Sirainenstatic void imap_client_input(struct client *client)
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen /* we're not currently connected to auth process -
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen don't allow any commands */
310767ca33e7636d40ec45dee68a2c319a5fa3c0Timo Sirainenstatic struct client *imap_client_alloc(pool_t pool)
2c8ca7e88ec881c473fb90e5f647c1f563877164Timo Sirainen imap_client = p_new(pool, struct imap_client, 1);
fdc557286bc9f92c5f3bb49096ff6e2bcec0ea79Timo Sirainenstatic void imap_client_create(struct client *client, void **other_sets)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic void imap_client_destroy(struct client *client)
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainen i_free_and_null(imap_client->proxy_backend_capability);
2d01cc1880cf2afd4fb1c8ad7fa6ce78e562e71eTimo Sirainenstatic void imap_client_notify_auth_ready(struct client *client)
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen str_append(greet, client->set->login_greeting);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void imap_client_starttls(struct client *client)
d1fbd2c264368d8b4c7139b96e3b21f45930b857Timo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
50c617761ee9653bd44646a95178773a3686d62eTimo Sirainen /* CRLF is lost from buffer when streams are reopened. */
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
1eaaa2c9003cf3fbf672d597473e3f84e70d2ee6Timo Sirainen client_send_raw_data(client, str_data(line), str_len(line));
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenvoid client_send_reply_code(struct client *client, enum imap_cmd_reply reply,
3dd0679b6f24be0287cc42d7a60bbf59cdf8b637Timo Sirainen client_send_reply_raw(client, prefix, resp_code, text, tagged);
a3ee5ce6ecc8e228ee69300fdd562d7ac8be89a7Timo Sirainenvoid client_send_reply(struct client *client, enum imap_cmd_reply reply,
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen client_send_reply_code(client, reply, NULL, text);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenimap_client_notify_status(struct client *client, bool bad, const char *text)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen client_send_reply_raw(client, "BAD", "ALERT", text, FALSE);
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen client_send_reply_raw(client, "OK", NULL, text, FALSE);
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainenimap_client_notify_disconnect(struct client *client,
9617ac7078a17bd346fed69526861c3e7fd9d809Timo Sirainen if (reason == CLIENT_DISCONNECT_INTERNAL_ERROR) {
4d527c363482be2b65dd0573d878ecda86cbb0bbTimo Sirainen client_send_reply_code(client, IMAP_CMD_REPLY_BYE,
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen client_send_reply_code(client, IMAP_CMD_REPLY_BYE, NULL, text);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainenstatic void imap_login_preinit(void)
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainenstatic void imap_login_init(void)
b62140c5849297a800fee942026d9c3cb8c60206Timo Sirainenstatic void imap_login_deinit(void)
b09eaeb9a81e5b58c6e605eb762573a2b4a69e0eTimo Sirainenstatic struct client_vfuncs imap_client_vfuncs = {
a423d985ba7261661475811c22b21b80ec765a71Timo Sirainenstatic const struct login_binary imap_login_binary = {