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