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