1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3 #include "login-common.h"
4 #include "buffer.h"
5 #include "ioloop.h"
6 #include "istream.h"
7 #include "ostream.h"
8 #include "safe-memset.h"
9 #include "str.h"
10 #include "imap-parser.h"
11 #include "imap-id.h"
12 #include "imap-resp-code.h"
13 #include "master-service.h"
14 #include "master-service-ssl-settings.h"
15 #include "master-auth.h"
16 #include "imap-login-client.h"
17 #include "client-authenticate.h"
18 #include "auth-client.h"
19 #include "imap-proxy.h"
20 #include "imap-quote.h"
21 #include "imap-login-commands.h"
22 #include "imap-login-settings.h"
23
24 #if LOGIN_MAX_INBUF_SIZE < 1024+2
25 # error LOGIN_MAX_INBUF_SIZE too short to fit all ID command parameters
26 #endif
27
28 /* Disconnect client when it sends too many bad commands */
29 #define CLIENT_MAX_BAD_COMMANDS 3
30
31 /* Skip incoming data until newline is found,
32 returns TRUE if newline was found. */
client_skip_line(struct imap_client * client)33 bool client_skip_line(struct imap_client *client)
34 {
35 const unsigned char *data;
36 size_t i, data_size;
37
38 data = i_stream_get_data(client->common.input, &data_size);
39
40 for (i = 0; i < data_size; i++) {
41 if (data[i] == '\n') {
42 i_stream_skip(client->common.input, i+1);
43 return TRUE;
44 }
45 }
46
47 return FALSE;
48 }
49
client_handle_parser_error(struct imap_client * client,struct imap_parser * parser)50 bool client_handle_parser_error(struct imap_client *client,
51 struct imap_parser *parser)
52 {
53 const char *msg;
54 enum imap_parser_error parse_error;
55
56 msg = imap_parser_get_error(parser, &parse_error);
57 switch (parse_error) {
58 case IMAP_PARSE_ERROR_NONE:
59 i_unreached();
60 case IMAP_PARSE_ERROR_LITERAL_TOO_BIG:
61 client_send_reply(&client->common,
62 IMAP_CMD_REPLY_BYE, msg);
63 client_destroy(&client->common, msg);
64 return FALSE;
65 default:
66 break;
67 }
68
69 client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg);
70 client->cmd_finished = TRUE;
71 client->skip_line = TRUE;
72 return TRUE;
73 }
74
is_login_cmd_disabled(struct client * client)75 static bool is_login_cmd_disabled(struct client *client)
76 {
77 if (client->secured) {
78 if (sasl_server_find_available_mech(client, "PLAIN") == NULL) {
79 /* no PLAIN authentication, can't use LOGIN command */
80 return TRUE;
81 }
82 return FALSE;
83 }
84 if (client->set->disable_plaintext_auth)
85 return TRUE;
86 if (strcmp(client->ssl_set->ssl, "required") == 0)
87 return TRUE;
88 return FALSE;
89 }
90
get_capability(struct client * client)91 static const char *get_capability(struct client *client)
92 {
93 struct imap_client *imap_client = (struct imap_client *)client;
94 string_t *cap_str = t_str_new(256);
95 bool explicit_capability = FALSE;
96
97 if (*imap_client->set->imap_capability == '\0')
98 str_append(cap_str, CAPABILITY_BANNER_STRING);
99 else if (*imap_client->set->imap_capability != '+') {
100 explicit_capability = TRUE;
101 str_append(cap_str, imap_client->set->imap_capability);
102 } else {
103 str_append(cap_str, CAPABILITY_BANNER_STRING);
104 str_append_c(cap_str, ' ');
105 str_append(cap_str, imap_client->set->imap_capability + 1);
106 }
107
108 if (!explicit_capability) {
109 if (imap_client->set->imap_literal_minus)
110 str_append(cap_str, " LITERAL-");
111 else
112 str_append(cap_str, " LITERAL+");
113 }
114
115 if (client_is_tls_enabled(client) && !client->tls)
116 str_append(cap_str, " STARTTLS");
117 if (is_login_cmd_disabled(client))
118 str_append(cap_str, " LOGINDISABLED");
119
120 client_authenticate_get_capabilities(client, cap_str);
121 return str_c(cap_str);
122 }
123
cmd_capability(struct imap_client * imap_client,const struct imap_arg * args ATTR_UNUSED)124 static int cmd_capability(struct imap_client *imap_client,
125 const struct imap_arg *args ATTR_UNUSED)
126 {
127 struct client *client = &imap_client->common;
128
129 /* Client is required to send CAPABILITY after STARTTLS, so the
130 capability resp-code workaround checks only pre-STARTTLS
131 CAPABILITY commands. */
132 if (!client->starttls)
133 imap_client->client_ignores_capability_resp_code = TRUE;
134 client_send_raw(client, t_strconcat(
135 "* CAPABILITY ", get_capability(client), "\r\n", NULL));
136 client_send_reply(client, IMAP_CMD_REPLY_OK,
137 "Pre-login capabilities listed, post-login capabilities have more.");
138 return 1;
139 }
140
cmd_starttls(struct imap_client * client,const struct imap_arg * args ATTR_UNUSED)141 static int cmd_starttls(struct imap_client *client,
142 const struct imap_arg *args ATTR_UNUSED)
143 {
144 client_cmd_starttls(&client->common);
145 return 1;
146 }
147
148 static void
imap_client_notify_starttls(struct client * client,bool success,const char * text)149 imap_client_notify_starttls(struct client *client,
150 bool success, const char *text)
151 {
152 if (success)
153 client_send_reply(client, IMAP_CMD_REPLY_OK, text);
154 else
155 client_send_reply(client, IMAP_CMD_REPLY_BAD, text);
156 }
157
cmd_noop(struct imap_client * client,const struct imap_arg * args ATTR_UNUSED)158 static int cmd_noop(struct imap_client *client,
159 const struct imap_arg *args ATTR_UNUSED)
160 {
161 client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
162 "NOOP completed.");
163 return 1;
164 }
165
cmd_logout(struct imap_client * client,const struct imap_arg * args ATTR_UNUSED)166 static int cmd_logout(struct imap_client *client,
167 const struct imap_arg *args ATTR_UNUSED)
168 {
169 client_send_reply(&client->common, IMAP_CMD_REPLY_BYE, "Logging out");
170 client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
171 "Logout completed.");
172 client_destroy(&client->common, CLIENT_UNAUTHENTICATED_LOGOUT_MSG);
173 return 1;
174 }
175
cmd_enable(struct imap_client * client,const struct imap_arg * args ATTR_UNUSED)176 static int cmd_enable(struct imap_client *client,
177 const struct imap_arg *args ATTR_UNUSED)
178 {
179 client_send_raw(&client->common, "* ENABLED\r\n");
180 client_send_reply(&client->common, IMAP_CMD_REPLY_OK,
181 "ENABLE ignored in non-authenticated state.");
182 return 1;
183 }
184
client_command_execute(struct imap_client * client,const char * cmd,const struct imap_arg * args)185 static int client_command_execute(struct imap_client *client, const char *cmd,
186 const struct imap_arg *args)
187 {
188 struct imap_login_command *login_cmd;
189
190 login_cmd = imap_login_command_lookup(cmd);
191 if (login_cmd == NULL)
192 return -2;
193 return login_cmd->func(client, args);
194 }
195
client_invalid_command(struct imap_client * client)196 static bool client_invalid_command(struct imap_client *client)
197 {
198 if (client->cmd_tag == NULL || *client->cmd_tag == '\0')
199 client->cmd_tag = "*";
200 if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
201 client_send_reply(&client->common, IMAP_CMD_REPLY_BYE,
202 "Too many invalid IMAP commands.");
203 client_destroy(&client->common, "Too many invalid commands");
204 return FALSE;
205 }
206 client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
207 "Error in IMAP command received by server.");
208 return TRUE;
209 }
210
client_parse_command(struct imap_client * client,const struct imap_arg ** args_r)211 static int client_parse_command(struct imap_client *client,
212 const struct imap_arg **args_r)
213 {
214 switch (imap_parser_read_args(client->parser, 0, 0, args_r)) {
215 case -1:
216 /* error */
217 if (!client_handle_parser_error(client, client->parser)) {
218 /* client destroyed */
219 return 0;
220 }
221 return -1;
222 case -2:
223 /* not enough data */
224 return 0;
225 default:
226 /* we read the entire line - skip over the CRLF */
227 if (!client_skip_line(client))
228 i_unreached();
229 return 1;
230 }
231 }
232
client_handle_input(struct imap_client * client)233 static bool client_handle_input(struct imap_client *client)
234 {
235 const char *tag, *name;
236 int ret;
237
238 i_assert(!client->common.authenticating);
239
240 if (client->cmd_finished) {
241 /* clear the previous command from memory. don't do this
242 immediately after handling command since we need the
243 cmd_tag to stay some time after authentication commands. */
244 client->cmd_tag = NULL;
245 client->cmd_name = NULL;
246 imap_parser_reset(client->parser);
247
248 /* remove \r\n */
249 if (client->skip_line) {
250 if (!client_skip_line(client))
251 return FALSE;
252 client->skip_line = FALSE;
253 }
254
255 client->cmd_finished = FALSE;
256 }
257
258 if (client->cmd_tag == NULL) {
259 ret = imap_parser_read_tag(client->parser, &tag);
260 if (ret == 0)
261 return FALSE; /* need more data */
262 if (ret < 0 || strlen(tag) > IMAP_TAG_MAX_LEN) {
263 /* the tag is invalid, don't allow it and don't
264 send it back. this attempts to prevent any
265 potentially dangerous replies in case someone tries
266 to access us using HTTP protocol. */
267 client->skip_line = TRUE;
268 client->cmd_finished = TRUE;
269 if (!client_invalid_command(client))
270 return FALSE;
271 return client_handle_input(client);
272 }
273 client->cmd_tag = tag;
274 }
275
276 if (client->cmd_name == NULL) {
277 ret = imap_parser_read_command_name(client->parser, &name);
278 if (ret == 0)
279 return FALSE; /* need more data */
280 if (ret < 0) {
281 client->skip_line = TRUE;
282 client->cmd_finished = TRUE;
283 if (!client_invalid_command(client))
284 return FALSE;
285 return client_handle_input(client);
286 }
287 client->cmd_name = name;
288 }
289 return client->common.v.input_next_cmd(&client->common);
290 }
291
imap_client_input_next_cmd(struct client * _client)292 static bool imap_client_input_next_cmd(struct client *_client)
293 {
294 struct imap_client *client = (struct imap_client *)_client;
295 const struct imap_arg *args;
296 bool parsed;
297 int ret;
298
299 if (strcasecmp(client->cmd_name, "AUTHENTICATE") == 0) {
300 /* SASL-IR may need more space than input buffer's size,
301 so we'll handle it as a special case. */
302 ret = cmd_authenticate(client, &parsed);
303 if (ret == 0 && !parsed)
304 return FALSE;
305 } else if (strcasecmp(client->cmd_name, "ID") == 0) {
306 /* ID extensions allows max. 30 parameters,
307 each max. 1024 bytes long. that brings us over the input
308 buffer's size, so handle the parameters one at a time */
309 ret = cmd_id(client);
310 if (ret == 0)
311 return FALSE;
312 if (ret < 0)
313 ret = 1; /* don't send the error reply again */
314 } else {
315 ret = client_parse_command(client, &args);
316 if (ret < 0)
317 return TRUE;
318 if (ret == 0)
319 return FALSE;
320 ret = *client->cmd_tag == '\0' ? -1 :
321 client_command_execute(client, client->cmd_name, args);
322 }
323
324 client->cmd_finished = TRUE;
325 if (ret == -2 && strcasecmp(client->cmd_tag, "LOGIN") == 0) {
326 client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
327 "First parameter in line is IMAP's command tag, "
328 "not the command name. Add that before the command, "
329 "like: a login user pass");
330 } else if (ret < 0) {
331 if (!client_invalid_command(client))
332 return FALSE;
333 }
334
335 return ret != 0 && !client->common.destroyed;
336 }
337
imap_client_input(struct client * client)338 static void imap_client_input(struct client *client)
339 {
340 struct imap_client *imap_client = (struct imap_client *)client;
341
342 if (!client_read(client))
343 return;
344
345 client_ref(client);
346 o_stream_cork(imap_client->common.output);
347 for (;;) {
348 if (!auth_client_is_connected(auth_client)) {
349 /* we're not currently connected to auth process -
350 don't allow any commands */
351 client_notify_status(client, FALSE,
352 AUTH_SERVER_WAITING_MSG);
353 timeout_remove(&client->to_auth_waiting);
354
355 client->input_blocked = TRUE;
356 break;
357 } else {
358 if (!client_handle_input(imap_client))
359 break;
360 }
361 }
362 o_stream_uncork(imap_client->common.output);
363 client_unref(&client);
364 }
365
imap_client_alloc(pool_t pool)366 static struct client *imap_client_alloc(pool_t pool)
367 {
368 struct imap_client *imap_client;
369
370 imap_client = p_new(pool, struct imap_client, 1);
371 return &imap_client->common;
372 }
373
imap_client_create(struct client * client,void ** other_sets)374 static void imap_client_create(struct client *client, void **other_sets)
375 {
376 struct imap_client *imap_client = (struct imap_client *)client;
377
378 imap_client->set = other_sets[0];
379 imap_client->parser =
380 imap_parser_create(imap_client->common.input,
381 imap_client->common.output,
382 IMAP_LOGIN_MAX_LINE_LENGTH);
383 if (imap_client->set->imap_literal_minus)
384 imap_parser_enable_literal_minus(imap_client->parser);
385 client->io = io_add_istream(client->input, client_input, client);
386 }
387
imap_client_destroy(struct client * client)388 static void imap_client_destroy(struct client *client)
389 {
390 struct imap_client *imap_client = (struct imap_client *)client;
391
392 i_free_and_null(imap_client->proxy_backend_capability);
393 imap_parser_unref(&imap_client->parser);
394 }
395
imap_client_notify_auth_ready(struct client * client)396 static void imap_client_notify_auth_ready(struct client *client)
397 {
398 string_t *greet;
399
400 greet = t_str_new(128);
401 str_append(greet, "* OK ");
402 str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
403 str_append(greet, client->set->login_greeting);
404 str_append(greet, "\r\n");
405
406 client_send_raw(client, str_c(greet));
407
408 client->banner_sent = TRUE;
409 }
410
imap_client_starttls(struct client * client)411 static void imap_client_starttls(struct client *client)
412 {
413 struct imap_client *imap_client = (struct imap_client *)client;
414
415 imap_parser_unref(&imap_client->parser);
416 imap_client->parser =
417 imap_parser_create(imap_client->common.input,
418 imap_client->common.output,
419 IMAP_LOGIN_MAX_LINE_LENGTH);
420
421 /* CRLF is lost from buffer when streams are reopened. */
422 imap_client->skip_line = FALSE;
423 }
424
425 static void ATTR_NULL(3)
client_send_reply_raw(struct client * client,const char * prefix,const char * resp_code,const char * text,bool tagged)426 client_send_reply_raw(struct client *client,
427 const char *prefix, const char *resp_code,
428 const char *text, bool tagged)
429 {
430 struct imap_client *imap_client = (struct imap_client *)client;
431
432 T_BEGIN {
433 string_t *line = t_str_new(256);
434
435 if (tagged)
436 str_append(line, imap_client->cmd_tag);
437 else
438 str_append_c(line, '*');
439 str_append_c(line, ' ');
440 str_append(line, prefix);
441 str_append_c(line, ' ');
442 if (resp_code != NULL)
443 str_printfa(line, "[%s] ", resp_code);
444 str_append(line, text);
445 str_append(line, "\r\n");
446
447 client_send_raw_data(client, str_data(line), str_len(line));
448 } T_END;
449 }
450
client_send_reply_code(struct client * client,enum imap_cmd_reply reply,const char * resp_code,const char * text)451 void client_send_reply_code(struct client *client, enum imap_cmd_reply reply,
452 const char *resp_code, const char *text)
453 {
454 const char *prefix = "NO";
455 bool tagged = TRUE;
456
457 switch (reply) {
458 case IMAP_CMD_REPLY_OK:
459 prefix = "OK";
460 break;
461 case IMAP_CMD_REPLY_NO:
462 break;
463 case IMAP_CMD_REPLY_BAD:
464 prefix = "BAD";
465 break;
466 case IMAP_CMD_REPLY_BYE:
467 prefix = "BYE";
468 tagged = FALSE;
469 break;
470 }
471 client_send_reply_raw(client, prefix, resp_code, text, tagged);
472 }
473
client_send_reply(struct client * client,enum imap_cmd_reply reply,const char * text)474 void client_send_reply(struct client *client, enum imap_cmd_reply reply,
475 const char *text)
476 {
477 client_send_reply_code(client, reply, NULL, text);
478 }
479
480 static void
imap_client_notify_status(struct client * client,bool bad,const char * text)481 imap_client_notify_status(struct client *client, bool bad, const char *text)
482 {
483 if (bad)
484 client_send_reply_raw(client, "BAD", "ALERT", text, FALSE);
485 else
486 client_send_reply_raw(client, "OK", NULL, text, FALSE);
487 }
488
489 static void
imap_client_notify_disconnect(struct client * client,enum client_disconnect_reason reason,const char * text)490 imap_client_notify_disconnect(struct client *client,
491 enum client_disconnect_reason reason,
492 const char *text)
493 {
494 if (reason == CLIENT_DISCONNECT_INTERNAL_ERROR) {
495 client_send_reply_code(client, IMAP_CMD_REPLY_BYE,
496 IMAP_RESP_CODE_UNAVAILABLE, text);
497 } else {
498 client_send_reply_code(client, IMAP_CMD_REPLY_BYE, NULL, text);
499 }
500 }
501
imap_login_preinit(void)502 static void imap_login_preinit(void)
503 {
504 login_set_roots = imap_login_setting_roots;
505 }
506
507 static const struct imap_login_command imap_login_commands[] = {
508 { "LOGIN", cmd_login },
509 { "CAPABILITY", cmd_capability },
510 { "STARTTLS", cmd_starttls },
511 { "NOOP", cmd_noop },
512 { "LOGOUT", cmd_logout },
513 { "ENABLE", cmd_enable }
514 };
515
imap_login_init(void)516 static void imap_login_init(void)
517 {
518 imap_login_commands_init();
519 imap_login_commands_register(imap_login_commands,
520 N_ELEMENTS(imap_login_commands));
521 }
522
imap_login_deinit(void)523 static void imap_login_deinit(void)
524 {
525 clients_destroy_all();
526 imap_login_commands_deinit();
527 }
528
529 static struct client_vfuncs imap_client_vfuncs = {
530 .alloc = imap_client_alloc,
531 .create = imap_client_create,
532 .destroy = imap_client_destroy,
533 .notify_auth_ready = imap_client_notify_auth_ready,
534 .notify_disconnect = imap_client_notify_disconnect,
535 .notify_status = imap_client_notify_status,
536 .notify_starttls = imap_client_notify_starttls,
537 .starttls = imap_client_starttls,
538 .input = imap_client_input,
539 .auth_result = imap_client_auth_result,
540 .proxy_reset = imap_proxy_reset,
541 .proxy_parse_line = imap_proxy_parse_line,
542 .proxy_failed = imap_proxy_failed,
543 .proxy_get_state = imap_proxy_get_state,
544 .send_raw_data = client_common_send_raw_data,
545 .input_next_cmd = imap_client_input_next_cmd,
546 .free = client_common_default_free,
547 };
548
549 static struct login_binary imap_login_binary = {
550 .protocol = "imap",
551 .process_name = "imap-login",
552 .default_port = 143,
553 .default_ssl_port = 993,
554
555 .event_category = {
556 .name = "imap",
557 },
558
559 .client_vfuncs = &imap_client_vfuncs,
560 .preinit = imap_login_preinit,
561 .init = imap_login_init,
562 .deinit = imap_login_deinit,
563
564 .sasl_support_final_reply = FALSE,
565 .anonymous_login_acceptable = TRUE,
566 };
567
main(int argc,char * argv[])568 int main(int argc, char *argv[])
569 {
570 return login_binary_run(&imap_login_binary, argc, argv);
571 }
572