1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "ioloop.h"
6 #include "net.h"
7 #include "fdpass.h"
8 #include "istream.h"
9 #include "ostream.h"
10 #include "str.h"
11 #include "str-sanitize.h"
12 #include "strescape.h"
13 #include "llist.h"
14 #include "hostpid.h"
15 #include "var-expand.h"
16 #include "process-title.h"
17 #include "randgen.h"
18 #include "restrict-access.h"
19 #include "settings-parser.h"
20 #include "master-service.h"
21 #include "master-interface.h"
22 #include "mail-storage.h"
23 #include "mail-storage-service.h"
24 #include "mail-namespace.h"
25 #include "imap-url.h"
26 #include "imap-msgpart-url.h"
27 #include "imap-urlauth.h"
28 #include "imap-urlauth-fetch.h"
29 #include "imap-urlauth-worker-settings.h"
30 
31 #include <unistd.h>
32 #include <sysexits.h>
33 
34 #define MAX_CTRL_HANDSHAKE 255
35 
36 /* max. length of input lines (URLs) */
37 #define MAX_INBUF_SIZE 2048
38 
39 /* Disconnect client after idling this many milliseconds */
40 #define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000)
41 
42 #define IS_STANDALONE() \
43         (getenv(MASTER_IS_PARENT_ENV) == NULL)
44 
45 #define IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION 2
46 #define IMAP_URLAUTH_WORKER_PROTOCOL_MINOR_VERSION 0
47 
48 struct client {
49 	struct client *prev, *next;
50 
51 	int fd_in, fd_out, fd_ctrl;
52 
53 	struct io *io, *ctrl_io;
54 	struct istream *input, *ctrl_input;
55 	struct ostream *output, *ctrl_output;
56 	struct timeout *to_idle;
57 
58 	char *access_user, *access_service;
59 	ARRAY_TYPE(string) access_apps;
60 
61 	struct mail_storage_service_user *service_user;
62 	struct mail_user *mail_user;
63 
64 	struct imap_urlauth_context *urlauth_ctx;
65 
66 	struct imap_msgpart_url *url;
67 	struct istream *msg_part_input;
68 	uoff_t msg_part_size;
69 
70 	/* settings: */
71 	const struct imap_urlauth_worker_settings *set;
72 	const struct mail_storage_settings *mail_set;
73 
74 	bool debug:1;
75 	bool finished:1;
76 	bool waiting_input:1;
77 	bool version_received:1;
78 	bool access_received:1;
79 	bool access_anonymous:1;
80 };
81 
82 static bool verbose_proctitle = FALSE;
83 static struct mail_storage_service_ctx *storage_service;
84 
85 struct client *imap_urlauth_worker_clients;
86 unsigned int imap_urlauth_worker_client_count;
87 
88 static void client_destroy(struct client *client);
89 static void client_abort(struct client *client, const char *reason);
90 static int client_run_url(struct client *client);
91 static void client_input(struct client *client);
92 static bool client_handle_input(struct client *client);
93 static int client_output(struct client *client);
94 
95 static void client_ctrl_input(struct client *client);
96 
imap_urlauth_worker_refresh_proctitle(void)97 static void imap_urlauth_worker_refresh_proctitle(void)
98 {
99 	struct client *client = imap_urlauth_worker_clients;
100 	string_t *title;
101 
102 	if (!verbose_proctitle)
103 		return;
104 
105 	title = t_str_new(128);
106 	str_append_c(title, '[');
107 	switch (imap_urlauth_worker_client_count) {
108 	case 0:
109 		str_append(title, "idling");
110 		break;
111 	case 1:
112 		if (client->mail_user == NULL)
113 			str_append(title, client->access_user);
114 		else {
115 			str_append(title, client->access_user);
116 			str_append(title, "->");
117 			str_append(title, client->mail_user->username);
118 		}
119 		break;
120 	default:
121 		str_printfa(title, "%u connections",
122 			    imap_urlauth_worker_client_count);
123 		break;
124 	}
125 	str_append_c(title, ']');
126 	process_title_set(str_c(title));
127 }
128 
client_idle_timeout(struct client * client)129 static void client_idle_timeout(struct client *client)
130 {
131 	if (client->url != NULL) {
132 		client_abort(client,
133 			"Session closed for inactivity in reading our output");
134 	} else {
135 		client_destroy(client);
136 	}
137 }
138 
client_create(int fd)139 static struct client *client_create(int fd)
140 {
141 	struct client *client;
142 
143 	/* always use nonblocking I/O */
144 	net_set_nonblock(fd, TRUE);
145 
146 	client = i_new(struct client, 1);
147 	i_array_init(&client->access_apps, 16);
148 	client->fd_in = -1;
149 	client->fd_out = -1;
150 	client->fd_ctrl = fd;
151 	client->access_anonymous = TRUE; /* default until overridden */
152 
153 	client->ctrl_io = io_add(fd, IO_READ, client_ctrl_input, client);
154 	client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
155 				      client_idle_timeout, client);
156 
157 	imap_urlauth_worker_client_count++;
158 	DLLIST_PREPEND(&imap_urlauth_worker_clients, client);
159 
160 	imap_urlauth_worker_refresh_proctitle();
161 	return client;
162 }
163 
164 static struct client *
client_create_standalone(const char * access_user,const char * const * access_applications,int fd_in,int fd_out,bool debug)165 client_create_standalone(const char *access_user,
166 			 const char *const *access_applications,
167 			 int fd_in, int fd_out, bool debug)
168 {
169 	struct client *client;
170 
171 	/* always use nonblocking I/O */
172 	net_set_nonblock(fd_in, TRUE);
173 	net_set_nonblock(fd_out, TRUE);
174 
175 	client = i_new(struct client, 1);
176 	i_array_init(&client->access_apps, 16);
177 	client->fd_in = fd_in;
178 	client->fd_out = fd_out;
179 	client->fd_ctrl = -1;
180 
181 	if (access_user != NULL && *access_user != '\0')
182 		client->access_user = i_strdup(access_user);
183 	else {
184 		client->access_user = i_strdup("anonymous");
185 		client->access_anonymous = TRUE;
186 	}
187 	if (access_applications != NULL) {
188 		const char *const *apps = access_applications;
189 		for (; *apps != NULL; apps++) {
190 			char *app = i_strdup(*apps);
191 			array_push_back(&client->access_apps, &app);
192 		}
193 	}
194 	client->debug = debug;
195 
196 	client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE);
197 	client->output = o_stream_create_fd(fd_out, SIZE_MAX);
198 	client->io = io_add(fd_in, IO_READ, client_input, client);
199 	client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
200 				      client_idle_timeout, client);
201 	o_stream_set_no_error_handling(client->output, TRUE);
202 	o_stream_set_flush_callback(client->output, client_output, client);
203 
204 	imap_urlauth_worker_client_count++;
205 	DLLIST_PREPEND(&imap_urlauth_worker_clients, client);
206 
207 	i_set_failure_prefix("imap-urlauth[%s](%s): ",
208 			     my_pid, client->access_user);
209 	return client;
210 }
211 
client_abort(struct client * client,const char * reason)212 static void client_abort(struct client *client, const char *reason)
213 {
214 	i_error("%s", reason);
215 	client_destroy(client);
216 }
217 
client_destroy(struct client * client)218 static void client_destroy(struct client *client)
219 {
220 	char *app;
221 
222 	i_set_failure_prefix("imap-urlauth[%s](%s): ",
223 			     my_pid, client->access_user);
224 
225 	if (client->url != NULL) {
226 		/* deinitialize url */
227 		i_stream_close(client->input);
228 		o_stream_close(client->output);
229 		(void)client_run_url(client);
230 		i_assert(client->url == NULL);
231 	}
232 
233 	imap_urlauth_worker_client_count--;
234 	DLLIST_REMOVE(&imap_urlauth_worker_clients, client);
235 
236 	if (client->urlauth_ctx != NULL)
237 		imap_urlauth_deinit(&client->urlauth_ctx);
238 
239 	if (client->mail_user != NULL)
240 		mail_user_deinit(&client->mail_user);
241 
242 	io_remove(&client->io);
243 	io_remove(&client->ctrl_io);
244 	timeout_remove(&client->to_idle);
245 
246 	i_stream_destroy(&client->input);
247 	o_stream_destroy(&client->output);
248 
249 	i_stream_destroy(&client->ctrl_input);
250 	o_stream_destroy(&client->ctrl_output);
251 
252 	fd_close_maybe_stdio(&client->fd_in, &client->fd_out);
253 	if (client->fd_ctrl >= 0)
254 		net_disconnect(client->fd_ctrl);
255 
256 	if (client->service_user != NULL)
257 		mail_storage_service_user_unref(&client->service_user);
258 	i_free(client->access_user);
259 	i_free(client->access_service);
260 	array_foreach_elem(&client->access_apps, app)
261 		i_free(app);
262 	array_free(&client->access_apps);
263 	i_free(client);
264 
265 	imap_urlauth_worker_refresh_proctitle();
266 	master_service_client_connection_destroyed(master_service);
267 }
268 
client_run_url(struct client * client)269 static int client_run_url(struct client *client)
270 {
271 	const unsigned char *data;
272 	size_t size;
273 	ssize_t ret = 0;
274 
275 	while (i_stream_read_more(client->msg_part_input, &data, &size) > 0) {
276 		if ((ret = o_stream_send(client->output, data, size)) < 0)
277 			break;
278 		i_stream_skip(client->msg_part_input, ret);
279 
280 		if (o_stream_get_buffer_used_size(client->output) >= 4096) {
281 			if ((ret = o_stream_flush(client->output)) < 0)
282 				break;
283 			if (ret == 0)
284 				return 0;
285 		}
286 	}
287 
288 	if (client->output->closed || ret < 0) {
289 		imap_msgpart_url_free(&client->url);
290 		return -1;
291 	}
292 
293 	if (client->msg_part_input->eof) {
294 		o_stream_nsend(client->output, "\n", 1);
295 		imap_msgpart_url_free(&client->url);
296 		return 1;
297 	}
298 	return 0;
299 }
300 
clients_destroy_all(void)301 static void clients_destroy_all(void)
302 {
303 	while (imap_urlauth_worker_clients != NULL)
304 		client_destroy(imap_urlauth_worker_clients);
305 }
306 
307 static void ATTR_FORMAT(2, 3)
client_send_line(struct client * client,const char * fmt,...)308 client_send_line(struct client *client, const char *fmt, ...)
309 {
310 	va_list va;
311 
312 	if (client->output->closed)
313 		return;
314 
315 	va_start(va, fmt);
316 
317 	T_BEGIN {
318 		string_t *str;
319 
320 		str = t_str_new(256);
321 		str_vprintfa(str, fmt, va);
322 		str_append(str, "\n");
323 
324 		o_stream_nsend(client->output, str_data(str), str_len(str));
325 	} T_END;
326 
327 	va_end(va);
328 }
329 
330 static int
client_fetch_urlpart(struct client * client,const char * url,enum imap_urlauth_fetch_flags url_flags,const char ** bpstruct_r,bool * binary_with_nuls_r,const char ** errormsg_r)331 client_fetch_urlpart(struct client *client, const char *url,
332 		     enum imap_urlauth_fetch_flags url_flags,
333 		     const char **bpstruct_r, bool *binary_with_nuls_r,
334 		     const char **errormsg_r)
335 {
336 	const char *error;
337 	struct imap_msgpart_open_result mpresult;
338 	enum mail_error error_code;
339 	int ret;
340 
341 	*bpstruct_r = NULL;
342 	*errormsg_r = NULL;
343 	*binary_with_nuls_r = FALSE;
344 
345 	ret = imap_urlauth_fetch(client->urlauth_ctx, url,
346 				 &client->url, &error_code, &error);
347 	if (ret <= 0) {
348 		if (ret < 0)
349 			return -1;
350 		error = t_strdup_printf("Failed to fetch URLAUTH \"%s\": %s",
351 					url, error);
352 		if (client->debug)
353 			i_debug("%s", error);
354 		/* don't leak info about existence/accessibility
355 		   of mailboxes */
356 		if (error_code == MAIL_ERROR_PARAMS)
357 			*errormsg_r = error;
358 		return 0;
359 	}
360 
361 	if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0)
362 		imap_msgpart_url_set_decode_to_binary(client->url);
363 	if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0) {
364 		ret = imap_msgpart_url_get_bodypartstructure(client->url,
365 							     bpstruct_r, &error);
366 		if (ret <= 0) {
367 			*errormsg_r = t_strdup_printf(
368 				"Failed to read URLAUTH \"%s\": %s", url, error);
369 			if (client->debug)
370 				i_debug("%s", *errormsg_r);
371 			return ret;
372 		}
373 	}
374 
375 	/* if requested, read the message part the URL points to */
376 	if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0 ||
377 	    (url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) {
378 		ret = imap_msgpart_url_read_part(client->url, &mpresult, &error);
379 		if (ret <= 0) {
380 			*errormsg_r = t_strdup_printf(
381 				"Failed to read URLAUTH \"%s\": %s", url, error);
382 			if (client->debug)
383 				i_debug("%s", *errormsg_r);
384 			return ret;
385 		}
386 		client->msg_part_size = mpresult.size;
387 		client->msg_part_input = mpresult.input;
388 		*binary_with_nuls_r = mpresult.binary_decoded_input_has_nuls;
389 	}
390 	return 1;
391 }
392 
client_fetch_url(struct client * client,const char * url,enum imap_urlauth_fetch_flags url_flags)393 static int client_fetch_url(struct client *client, const char *url,
394 			    enum imap_urlauth_fetch_flags url_flags)
395 {
396 	string_t *response;
397 	const char *bpstruct, *errormsg;
398 	bool binary_with_nuls;
399 	int ret;
400 
401 	i_assert(client->url == NULL);
402 
403 	client->msg_part_size = 0;
404 	client->msg_part_input = NULL;
405 
406 	if (client->debug)
407 		i_debug("Fetching URLAUTH %s", url);
408 
409 	/* fetch URL */
410 	ret = client_fetch_urlpart(client, url, url_flags, &bpstruct,
411 				   &binary_with_nuls, &errormsg);
412 	if (ret <= 0) {
413 		/* fetch failed */
414 		if (client->url != NULL)
415 			imap_msgpart_url_free(&client->url);
416 		/* don't send error details to anonymous users: just to be sure
417 		   that no information about the target user account is unduly
418 		   leaked. */
419 		if (client->access_anonymous || errormsg == NULL)
420 			client_send_line(client, "NO");
421 		else {
422 			client_send_line(client, "NO\terror=%s",
423 					 str_tabescape(errormsg));
424 		}
425 		if (ret < 0) {
426 			/* fetch failed badly */
427 			client_abort(client, "Session aborted: Fatal failure while fetching URL");
428 		}
429 		return 0;
430 	}
431 
432 	response = t_str_new(256);
433 	str_append(response, "OK");
434 	if (binary_with_nuls)
435 		str_append(response, "\thasnuls");
436 	if (bpstruct != NULL) {
437 		str_append(response, "\tbpstruct=");
438 		str_append(response, str_tabescape(bpstruct));
439 		if (client->debug) {
440 			i_debug("Fetched URLAUTH yielded BODYPARTSTRUCTURE (%s)",
441 				bpstruct);
442 		}
443 	}
444 
445 	/* return content */
446 	o_stream_cork(client->output);
447 	if (client->msg_part_size == 0 || client->msg_part_input == NULL) {
448 		/* empty */
449 		str_append(response, "\t0");
450 		client_send_line(client, "%s", str_c(response));
451 
452 		imap_msgpart_url_free(&client->url);
453 		client->url = NULL;
454 		if (client->debug)
455 			i_debug("Fetched URLAUTH yielded empty result");
456 	} else {
457 
458 		/* actual content */
459 		str_printfa(response, "\t%"PRIuUOFF_T, client->msg_part_size);
460 		client_send_line(client, "%s", str_c(response));
461 
462 		if (client->debug) {
463 			i_debug("Fetched URLAUTH yielded %"PRIuUOFF_T" bytes "
464 				"of %smessage data", client->msg_part_size,
465 				(binary_with_nuls ? "binary " : ""));
466 		}
467 		if (client_run_url(client) < 0) {
468 			client_abort(client,
469 				"Session aborted: Fatal failure while transferring URL");
470 			return 0;
471 		}
472 	}
473 
474 	if (client->url != NULL) {
475 		/* URL not finished */
476 		o_stream_set_flush_pending(client->output, TRUE);
477 		client->waiting_input = TRUE;
478 	}
479 	o_stream_uncork(client->output);
480 	return client->url != NULL ? 0 : 1;
481 }
482 
483 static int
client_handle_command(struct client * client,const char * cmd,const char * const * args,const char ** error_r)484 client_handle_command(struct client *client, const char *cmd,
485 		      const char *const *args, const char **error_r)
486 {
487 	int ret;
488 
489 	*error_r = NULL;
490 
491 	/* "URL"["\tbody"]["\tbinary"]["\tbpstruct"]"\t"<url>:
492 	   fetch URL (meta)data */
493 	if (strcmp(cmd, "URL") == 0) {
494 		enum imap_urlauth_fetch_flags url_flags = 0;
495 		const char *url;
496 
497 		if (*args == NULL) {
498 			*error_r = "URL: Missing URL parameter";
499 			return -1;
500 		}
501 
502 		url = *args;
503 
504 		args++;
505 		while (*args != NULL) {
506 			if (strcasecmp(*args, "body") == 0)
507 				url_flags |= IMAP_URLAUTH_FETCH_FLAG_BODY;
508 			else if (strcasecmp(*args, "binary") == 0)
509 				url_flags |= IMAP_URLAUTH_FETCH_FLAG_BINARY;
510 			else if (strcasecmp(*args, "bpstruct") == 0)
511 				url_flags |= IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE;
512 
513 			args++;
514 		}
515 
516 		if (url_flags == 0)
517 			url_flags = IMAP_URLAUTH_FETCH_FLAG_BODY;
518 
519 		T_BEGIN {
520 			ret = client_fetch_url(client, url, url_flags);
521 		} T_END;
522 		return ret;
523 	}
524 
525 	/* "END": unselect current user (closes worker) */
526 	if (strcmp(cmd, "END") == 0) {
527 		if (args[0] != NULL) {
528 			*error_r = "END: Invalid number of parameters";
529 			return -1;
530 		}
531 
532 		client->finished = TRUE;
533 		if (client->ctrl_output != NULL)
534 			o_stream_nsend_str(client->ctrl_output, "FINISHED\n");
535 		client_destroy(client);
536 		return 0;
537 	}
538 
539 	*error_r = t_strconcat("Unknown or inappropriate command: ", cmd, NULL);
540 	return -1;
541 }
542 
543 static int
client_handle_user_command(struct client * client,const char * cmd,const char * const * args,const char ** error_r)544 client_handle_user_command(struct client *client, const char *cmd,
545 			   const char *const *args, const char **error_r)
546 {
547 	struct mail_storage_service_input input;
548 	struct imap_urlauth_worker_settings *set;
549 	struct mail_storage_service_user *user;
550 	struct imap_urlauth_config config;
551 	struct mail_user *mail_user;
552 	const char *error;
553 	unsigned int count;
554 	int ret;
555 
556 	/* "USER\t"<username> */
557 	*error_r = NULL;
558 
559 	/* check command syntax */
560 	if (strcmp(cmd, "USER") != 0) {
561 		*error_r = t_strconcat("Unknown or inappropriate command: ",
562 				       cmd, NULL);
563 		return -1;
564 	}
565 
566 	if (args[0] == NULL || args[1] != NULL) {
567 		*error_r = "USER: Invalid number of parameters";
568 		return -1;
569 	}
570 
571 	/* lookup user */
572 	i_zero(&input);
573 	input.module = "imap-urlauth-worker";
574 	input.service = "imap-urlauth-worker";
575 	input.username = args[0];
576 
577 	if (client->debug)
578 		i_debug("Looking up user %s", input.username);
579 
580 	ret = mail_storage_service_lookup_next(storage_service, &input,
581 					       &user, &mail_user, &error);
582 	if (ret < 0) {
583 		i_error("Failed to lookup user %s: %s", input.username, error);
584 		client_abort(client, "Session aborted: Failed to lookup user");
585 		return 0;
586 	} else if (ret == 0) {
587 		if (client->debug)
588 			i_debug("User %s doesn't exist", input.username);
589 
590 		client_send_line(client, "NO");
591 		return 1;
592 	}
593 
594 	client->debug = mail_user->mail_debug =
595 		client->debug || mail_user->mail_debug;
596 
597 	/* drop privileges */
598 	restrict_access_allow_coredumps(TRUE);
599 
600 	set = mail_storage_service_user_get_set(user)[1];
601 	if (settings_var_expand(&imap_urlauth_worker_setting_parser_info, set,
602 				mail_user->pool,
603 				mail_user_var_expand_table(mail_user),
604 				&error) <= 0) {
605 		client_send_line(client, "NO");
606 		client_abort(client, t_strdup_printf(
607 			"Session aborted: Failed to expand settings: %s", error));
608 		return 0;
609 	}
610 
611 	if (set->verbose_proctitle) {
612 		verbose_proctitle = TRUE;
613 		imap_urlauth_worker_refresh_proctitle();
614 	}
615 
616 	client->service_user = user;
617 	client->mail_user = mail_user;
618 	client->set = set;
619 
620 	if (client->debug) {
621 		i_debug("Found user account `%s' on behalf of user `%s'",
622 			mail_user->username, client->access_user);
623 	}
624 
625 	/* initialize urlauth context */
626 	if (*set->imap_urlauth_host == '\0') {
627 		i_error("imap_urlauth_host setting is not configured for user %s",
628 			mail_user->username);
629 		client_send_line(client, "NO");
630 		client_abort(client, "Session aborted: URLAUTH not configured");
631 		return 0;
632 	}
633 
634 	i_zero(&config);
635 	config.url_host = set->imap_urlauth_host;
636 	config.url_port = set->imap_urlauth_port;
637 	config.access_user = client->access_user;
638 	config.access_service = client->access_service;
639 	config.access_anonymous = client->access_anonymous;
640 	config.access_applications =
641 		(const void *)array_get(&client->access_apps, &count);
642 
643 	client->urlauth_ctx = imap_urlauth_init(client->mail_user, &config);
644 	if (client->debug) {
645 		i_debug("Providing access to user account `%s' on behalf of user `%s' "
646 			"using service `%s'", mail_user->username, client->access_user,
647 			client->access_service);
648 	}
649 
650 	i_set_failure_prefix("imap-urlauth[%s](%s->%s): ",
651 			     my_pid, client->access_user, mail_user->username);
652 
653 	client_send_line(client, "OK");
654 	return 1;
655 }
656 
client_handle_input(struct client * client)657 static bool client_handle_input(struct client *client)
658 {
659 	const char *line, *cmd, *error;
660 	int ret;
661 
662 	if (client->url != NULL) {
663 		/* we're still processing a URL. wait until it's
664 		   finished. */
665 		io_remove(&client->io);
666 		client->io = NULL;
667 		client->waiting_input = TRUE;
668 		return TRUE;
669 	}
670 
671 	if (client->io == NULL) {
672 		client->io = io_add(client->fd_in, IO_READ,
673 				    client_input, client);
674 	}
675 	client->waiting_input = FALSE;
676 	timeout_reset(client->to_idle);
677 
678 	switch (i_stream_read(client->input)) {
679 	case -1:
680 		/* disconnected */
681 		if (client->ctrl_output != NULL)
682 			o_stream_nsend_str(client->ctrl_output, "DISCONNECTED\n");
683 		client_destroy(client);
684 		return FALSE;
685 	case -2:
686 		/* line too long, kill it */
687 		client_abort(client, "Session aborted: Input line too long");
688 		return FALSE;
689 	}
690 
691 	while ((line = i_stream_next_line(client->input)) != NULL) {
692 		const char *const *args = t_strsplit_tabescaped(line);
693 
694 		if (args[0] == NULL)
695 			continue;
696 		cmd = args[0]; args++;
697 
698 		if (client->mail_user == NULL)
699 			ret = client_handle_user_command(client, cmd, args, &error);
700 		else
701 			ret = client_handle_command(client, cmd, args, &error);
702 
703 		if (ret <= 0) {
704 			if (ret == 0)
705 				break;
706 			i_error("Client input error: %s", error);
707 			client_abort(client, "Session aborted: Unexpected input");
708 			return FALSE;
709 		}
710 	}
711 	return TRUE;
712 }
713 
client_input(struct client * client)714 static void client_input(struct client *client)
715 {
716 	(void)client_handle_input(client);
717 }
718 
client_output(struct client * client)719 static int client_output(struct client *client)
720 {
721 	if (o_stream_flush(client->output) < 0) {
722 		if (client->ctrl_output != NULL)
723 			o_stream_nsend_str(client->ctrl_output, "DISCONNECTED\n");
724 		client_destroy(client);
725 		return 1;
726 	}
727 	timeout_reset(client->to_idle);
728 
729 	if (client->url != NULL) {
730 		if (client_run_url(client) < 0) {
731 			client_destroy(client);
732 			return 1;
733 		}
734 
735 		if (client->url == NULL && client->waiting_input) {
736 			if (!client_handle_input(client)) {
737 				/* client got destroyed */
738 				return 1;
739 			}
740 		}
741 	}
742 
743 	if (client->url != NULL) {
744 		/* url not finished yet */
745 		return 0;
746 	} else if (client->io == NULL) {
747 		/* data still in output buffer, get back here to add IO */
748 		return 0;
749 	} else {
750 		return 1;
751 	}
752 }
753 
754 static int
client_ctrl_read_fds(struct client * client)755 client_ctrl_read_fds(struct client *client)
756 {
757 	unsigned char data = 0;
758 	ssize_t ret = 1;
759 
760 	if (client->fd_in == -1) {
761 		ret = fd_read(client->fd_ctrl, &data,
762 			      sizeof(data), &client->fd_in);
763 		if (ret > 0 && data == '0')
764 			client->fd_out = client->fd_in;
765 	}
766 	if (ret > 0 && client->fd_out == -1) {
767 		ret = fd_read(client->fd_ctrl, &data,
768 			      sizeof(data), &client->fd_out);
769 	}
770 
771 	if (ret == 0) {
772 		/* unexpectedly disconnected */
773 		client_destroy(client);
774 		return 0;
775 	} else if (ret < 0) {
776 		if (errno == EAGAIN)
777 			return 0;
778 		i_error("fd_read() failed: %m");
779 		return -1;
780 	} else if (data != '0') {
781 		i_error("fd_read() returned invalid byte 0x%2x", data);
782 		return -1;
783 	}
784 
785 	if (client->fd_in == -1 || client->fd_out == -1) {
786 		i_error("Handshake is missing a file descriptor");
787 		return -1;
788 	}
789 
790 	client->ctrl_input =
791 		i_stream_create_fd(client->fd_ctrl, MAX_INBUF_SIZE);
792 	client->ctrl_output = o_stream_create_fd(client->fd_ctrl, SIZE_MAX);
793 	o_stream_set_no_error_handling(client->ctrl_output, TRUE);
794 	return 1;
795 }
796 
client_ctrl_input(struct client * client)797 static void client_ctrl_input(struct client *client)
798 {
799 	const char *const *args;
800 	const char *line;
801 	int ret;
802 
803 	timeout_reset(client->to_idle);
804 
805 	if (client->fd_in == -1 || client->fd_out == -1) {
806 		if ((ret = client_ctrl_read_fds(client)) <= 0) {
807 			if (ret < 0)
808 				client_abort(client, "FD Transfer failed");
809 			return;
810 		}
811 	}
812 
813 	switch (i_stream_read(client->ctrl_input)) {
814 	case -1:
815 		/* disconnected */
816 		client_destroy(client);
817 		return;
818 	case -2:
819 		/* line too long, kill it */
820 		client_abort(client,
821 			     "Control session aborted: Input line too long");
822 		return;
823 	}
824 
825 	if (!client->version_received) {
826 		if ((line = i_stream_next_line(client->ctrl_input)) == NULL)
827 			return;
828 
829 		if (!version_string_verify(line, "imap-urlauth-worker",
830 				IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION)) {
831 			i_error("imap-urlauth-worker client not compatible with this server "
832 				"(mixed old and new binaries?) %s", line);
833 			client_abort(client, "Control session aborted: Version mismatch");
834 			return;
835 		}
836 
837 		client->version_received = TRUE;
838 		if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) {
839 			client_destroy(client);
840 			return;
841 		}
842 	}
843 
844 	if (client->access_received) {
845 		client_abort(client, "Control session aborted: Unexpected input");
846 		return;
847 	}
848 
849 	if ((line = i_stream_next_line(client->ctrl_input)) == NULL)
850 		return;
851 
852 	args = t_strsplit_tabescaped(line);
853 	if (*args == NULL || strcmp(*args, "ACCESS") != 0) {
854 		i_error("Invalid control command: %s", str_sanitize(line, 80));
855 		client_abort(client, "Control session aborted: Invalid command");
856 		return;
857 	}
858 	args++;
859 	if (args[0] == NULL || args[1] == NULL) {
860 		i_error("Invalid ACCESS command: %s", str_sanitize(line, 80));
861 		client_abort(client, "Control session aborted: Invalid command");
862 		return;
863 	}
864 
865 	i_assert(client->access_user == NULL);
866 	i_assert(client->access_service == NULL);
867 	if (**args != '\0') {
868 		client->access_user = i_strdup(*args);
869 		client->access_anonymous = FALSE;
870 	} else {
871 		client->access_user = i_strdup("anonymous");
872 		client->access_anonymous = TRUE;
873 	}
874 	args++;
875 	client->access_service = i_strdup(*args);
876 
877 	i_set_failure_prefix("imap-urlauth[%s](%s): ",
878 			     my_pid, client->access_user);
879 
880 	args++;
881 	while (*args != NULL) {
882 		/* debug */
883 		if (strcasecmp(*args, "debug") == 0) {
884 			client->debug = TRUE;
885 		/* apps=<access-application>[,<access-application,...] */
886 		} else if (strncasecmp(*args, "apps=", 5) == 0 &&
887 			   (*args)[5] != '\0') {
888 			const char *const *apps = t_strsplit(*args+5, ",");
889 
890 			while (*apps != NULL) {
891 				char *app = i_strdup(*apps);
892 
893 				array_push_back(&client->access_apps, &app);
894 				if (client->debug) {
895 					i_debug("User %s has URLAUTH %s access",
896 						client->access_user, app);
897 				}
898 				apps++;
899 			}
900 		} else {
901 			i_error("Invalid ACCESS parameter: %s", str_sanitize(*args, 80));
902 			client_abort(client, "Control session aborted: Invalid command");
903 			return;
904 		}
905 		args++;
906 	}
907 
908 	client->access_received = TRUE;
909 
910 	if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) {
911 		client_destroy(client);
912 		return;
913 	}
914 
915 	client->input = i_stream_create_fd(client->fd_in, MAX_INBUF_SIZE);
916 	client->output = o_stream_create_fd(client->fd_out, SIZE_MAX);
917 	client->io = io_add(client->fd_in, IO_READ, client_input, client);
918 	o_stream_set_no_error_handling(client->output, TRUE);
919 	o_stream_set_flush_callback(client->output, client_output, client);
920 
921 	if (client->debug) {
922 		i_debug("Worker activated for access by user `%s' using service `%s'",
923 			client->access_user, client->access_service);
924 	}
925 }
926 
imap_urlauth_worker_die(void)927 static void imap_urlauth_worker_die(void)
928 {
929 	/* do nothing */
930 }
931 
main_stdio_run(const char * access_user,const char * const * access_applications)932 static void main_stdio_run(const char *access_user,
933 			   const char *const *access_applications)
934 {
935 	bool debug;
936 
937 	debug = getenv("DEBUG") != NULL;
938 	access_user = access_user != NULL ? access_user : getenv("USER");
939 	if (access_user == NULL && IS_STANDALONE())
940 		access_user = getlogin();
941 	if (access_user == NULL)
942 		i_fatal("USER environment missing");
943 
944 	(void)client_create_standalone(access_user, access_applications,
945 				       STDIN_FILENO, STDOUT_FILENO, debug);
946 }
947 
client_connected(struct master_service_connection * conn)948 static void client_connected(struct master_service_connection *conn)
949 {
950 	master_service_client_connection_accept(conn);
951 	(void)client_create(conn->fd);
952 }
953 
main(int argc,char * argv[])954 int main(int argc, char *argv[])
955 {
956 	static const struct setting_parser_info *set_roots[] = {
957 		&imap_urlauth_worker_setting_parser_info,
958 		NULL
959 	};
960 	enum master_service_flags service_flags = 0;
961 	enum mail_storage_service_flags storage_service_flags =
962 		MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP |
963 		MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT;
964 	ARRAY_TYPE (const_string) access_apps;
965 	const char *access_user = NULL;
966 	int c;
967 
968 	if (IS_STANDALONE()) {
969 		service_flags |= MASTER_SERVICE_FLAG_STANDALONE |
970 			MASTER_SERVICE_FLAG_STD_CLIENT;
971 	} else {
972 		service_flags |= MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN;
973 	}
974 
975 	master_service = master_service_init("imap-urlauth-worker", service_flags,
976 					     &argc, &argv, "a:");
977 
978 	t_array_init(&access_apps, 4);
979 	while ((c = master_getopt(master_service)) > 0) {
980 		switch (c) {
981 		case 'a': {
982 			const char *app = t_strdup(optarg);
983 
984 			array_push_back(&access_apps, &app);
985 			break;
986 		}
987 		default:
988 			return FATAL_DEFAULT;
989 		}
990 	}
991 
992 	if ( optind < argc ) {
993 		access_user = argv[optind++];
994 	}
995 
996 	if (optind != argc) {
997 		i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]);
998 	}
999 
1000 	master_service_init_log_with_pid(master_service);
1001 	master_service_set_die_callback(master_service, imap_urlauth_worker_die);
1002 
1003 	storage_service =
1004 		mail_storage_service_init(master_service,
1005 					  set_roots, storage_service_flags);
1006 	master_service_init_finish(master_service);
1007 
1008 	/* fake that we're running, so we know if client was destroyed
1009 	   while handling its initial input */
1010 	io_loop_set_running(current_ioloop);
1011 
1012 	if (IS_STANDALONE()) {
1013 		T_BEGIN {
1014 			if (array_count(&access_apps) > 0) {
1015 				(void)array_append_space(&access_apps);
1016 				main_stdio_run(access_user,
1017 					       array_front(&access_apps));
1018 			} else {
1019 				main_stdio_run(access_user, NULL);
1020 			}
1021 		} T_END;
1022 	} else {
1023 		io_loop_set_running(current_ioloop);
1024 	}
1025 
1026 	if (io_loop_is_running(current_ioloop))
1027 		master_service_run(master_service, client_connected);
1028 	clients_destroy_all();
1029 
1030 	mail_storage_service_deinit(&storage_service);
1031 	master_service_deinit(&master_service);
1032 	return 0;
1033 }
1034