1 /* mupdate.c -- cyrus murder database master
2 *
3 * Copyright (c) 1994-2008 Carnegie Mellon University. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. The name "Carnegie Mellon University" must not be used to
18 * endorse or promote products derived from this software without
19 * prior written permission. For permission or any legal
20 * details, please contact
21 * Carnegie Mellon University
22 * Center for Technology Transfer and Enterprise Creation
23 * 4615 Forbes Avenue
24 * Suite 302
25 * Pittsburgh, PA 15213
26 * (412) 268-7393, fax: (412) 268-7395
27 * innovation@andrew.cmu.edu
28 *
29 * 4. Redistributions of any form whatsoever must retain the following
30 * acknowledgment:
31 * "This product includes software developed by Computing Services
32 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33 *
34 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41 */
42
43 #include <config.h>
44
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48
49 #include <stdio.h>
50 #include <string.h>
51 #include <ctype.h>
52 #include <signal.h>
53 #include <stdlib.h>
54 #include <sysexits.h>
55 #include <syslog.h>
56 #include <errno.h>
57
58 #include <netdb.h>
59 #include <sys/socket.h>
60 #include <netinet/in.h>
61 #include <arpa/inet.h>
62 #include <sys/types.h>
63 #include <sys/ioctl.h>
64 #if !defined(SIOCGIFCONF) && defined(HAVE_SYS_SOCKIO_H)
65 # include <sys/sockio.h>
66 #endif
67 #include <net/if.h>
68
69 #include <pthread.h>
70 #include <sasl/sasl.h>
71 #include <sasl/saslutil.h>
72
73 #include "mupdate.h"
74 #include "mupdate-client.h"
75 #include "telemetry.h"
76
77 #include "strarray.h"
78 #include "assert.h"
79 #include "global.h"
80 #include "mailbox.h"
81 #include "mboxlist.h"
82 #include "mpool.h"
83 #include "nonblock.h"
84 #include "prot.h"
85 #include "tls.h"
86 #include "tls_th-lock.h"
87 #include "util.h"
88 #include "version.h"
89 #include "xmalloc.h"
90 #include "xstrlcpy.h"
91
92 /* generated headers are not necessarily in current directory */
93 #include "imap/imap_err.h"
94
95 /* Sent to clients that we can't accept a connection for. */
96 static const char SERVER_UNABLE_STRING[] = "* BYE \"Server Unable\"\r\n";
97
98 static const int NO_NEW_CONNECTION = -1;
99
100 static int masterp = 0;
101
102 typedef enum {
103 DOCMD_OK = 0,
104 DOCMD_CONN_FINISHED = 1
105 } mupdate_docmd_result_t;
106
107 enum {
108 poll_interval = 1,
109 update_wait = 5
110 };
111
112 struct pending {
113 struct pending *next;
114
115 char mailbox[MAX_MAILBOX_BUFFER];
116 };
117
118 struct conn {
119 int fd;
120 int logfd;
121
122 struct protstream *pin;
123 struct protstream *pout;
124 sasl_conn_t *saslconn;
125 char *userid;
126
127 #ifdef HAVE_SSL
128 SSL *tlsconn;
129 #else
130 void *tlsconn;
131 #endif
132 void *tls_comp; /* TLS compression method, if any */
133 int compress_done; /* have we done a successful compress? */
134
135 int idle;
136
137 char clienthost[NI_MAXHOST*2+1];
138
139 struct saslprops_t saslprops;
140
141 /* UPDATE command handling */
142 const char *streaming; /* tag */
143 strarray_t *streaming_hosts; /* partial updates */
144
145 /* pending changes to send, in reverse order */
146 pthread_mutex_t m;
147 struct pending *plist;
148 struct pending *ptail;
149 struct conn *updatelist_next;
150 struct prot_waitevent *ev; /* invoked every 'update_wait' seconds
151 to send out updates */
152
153 /* Prefix for list commands */
154 const char *list_prefix;
155 size_t list_prefix_len;
156
157 /* For parsing */
158 struct buf tag, cmd, arg1, arg2, arg3;
159
160 /* For connection list management */
161 struct conn *next;
162 struct conn *next_idle;
163 };
164
165 static int ready_for_connections = 0;
166 static pthread_cond_t ready_for_connections_cond = PTHREAD_COND_INITIALIZER;
167 static pthread_mutex_t ready_for_connections_mutex = PTHREAD_MUTEX_INITIALIZER;
168
169 static int synced = 0;
170 static pthread_cond_t synced_cond = PTHREAD_COND_INITIALIZER;
171 static pthread_mutex_t synced_mutex = PTHREAD_MUTEX_INITIALIZER;
172
173 static pthread_mutex_t listener_mutex = PTHREAD_MUTEX_INITIALIZER;
174 static pthread_cond_t listener_cond = PTHREAD_COND_INITIALIZER;
175 static int listener_lock = 0;
176
177 /* if you want to lock both listener and either of these two, you
178 * must lock listener first. You must have both listener_mutex and
179 * idle_connlist_mutex locked to remove anything from the idle_connlist */
180 static pthread_mutex_t idle_connlist_mutex = PTHREAD_MUTEX_INITIALIZER;
181 static struct conn *idle_connlist = NULL; /* protected by listener_mutex */
182 static pthread_mutex_t connection_count_mutex = PTHREAD_MUTEX_INITIALIZER;
183 static int connection_count = 0;
184 static pthread_mutex_t idle_worker_mutex = PTHREAD_MUTEX_INITIALIZER;
185 static int idle_worker_count = 0;
186 static pthread_mutex_t worker_count_mutex = PTHREAD_MUTEX_INITIALIZER;
187 static int worker_count = 0;
188
189 static pthread_mutex_t connlist_mutex = PTHREAD_MUTEX_INITIALIZER;
190 static struct conn *connlist = NULL;
191
192 static pthread_mutex_t clienthost_mutex = PTHREAD_MUTEX_INITIALIZER;
193
194 /* ---- connection signaling pipe */
195 static int conn_pipe[2];
196
197 /* ---- database access ---- */
198 static pthread_mutex_t mailboxes_mutex = PTHREAD_MUTEX_INITIALIZER;
199 static struct conn *updatelist = NULL;
200
201 /* --- prototypes --- */
202 static void conn_free(struct conn *C);
203 static mupdate_docmd_result_t docmd(struct conn *c);
204 static void cmd_authenticate(struct conn *C,
205 const char *tag, const char *mech,
206 const char *clientstart);
207 static void cmd_set(struct conn *C,
208 const char *tag, const char *mailbox,
209 const char *location, const char *acl, enum settype t);
210 static void cmd_find(struct conn *C, const char *tag, const char *mailbox,
211 int send_ok, int send_delete);
212 static void cmd_list(struct conn *C, const char *tag, const char *host_prefix);
213 static void cmd_startupdate(struct conn *C, const char *tag,
214 strarray_t *partial);
215 static void cmd_starttls(struct conn *C, const char *tag);
216 #ifdef HAVE_ZLIB
217 static void cmd_compress(struct conn *C, const char *tag, const char *alg);
218 #endif
219 void shut_down(int code);
220 static int reset_saslconn(struct conn *c);
221 static void database_init(void);
222 static void sendupdates(struct conn *C, int flushnow);
223
224 extern int saslserver(sasl_conn_t *conn, const char *mech,
225 const char *init_resp, const char *resp_prefix,
226 const char *continuation, const char *empty_chal,
227 struct protstream *pin, struct protstream *pout,
228 int *sasl_result, char **success_data);
229
230 /* --- prototypes in mupdate-slave.c */
231 void *mupdate_client_start(void *rock);
232 void *mupdate_placebo_kick_start(void *rock);
233
234 /* --- main() for each thread */
235 static void *thread_main(void *rock);
236
237 /* --- for config.c */
238 const int config_need_data = 0;
239
conn_new(int fd)240 static struct conn *conn_new(int fd)
241 {
242 struct conn *C = xzmalloc(sizeof(struct conn));
243 const char *clienthost, *localip, *remoteip;
244 int r;
245
246 C->fd = fd;
247 C->logfd = -1;
248
249 C->pin = prot_new(C->fd, 0);
250 C->pout = prot_new(C->fd, 1);
251
252 prot_setflushonread(C->pin, C->pout);
253 prot_settimeout(C->pin, 180*60);
254
255 C->pin->userdata = C->pout->userdata = C;
256
257 pthread_mutex_lock(&connlist_mutex); /* LOCK */
258 C->next = connlist;
259 connlist = C;
260 pthread_mutex_unlock(&connlist_mutex); /* UNLOCK */
261
262 pthread_mutex_lock(&connection_count_mutex); /* LOCK */
263 connection_count++;
264 pthread_mutex_unlock(&connection_count_mutex); /* UNLOCK */
265
266 /* Find out name of client host
267 *
268 * MUST do this inside a mutex because the values returned
269 * from get_clienthost are all static to that function.
270 */
271 pthread_mutex_lock(&clienthost_mutex); /* LOCK */
272 clienthost = get_clienthost(C->fd, &localip, &remoteip);
273 strlcpy(C->clienthost, clienthost, sizeof(C->clienthost));
274
275 if (localip && remoteip) {
276 buf_setcstr(&C->saslprops.ipremoteport, remoteip);
277 buf_setcstr(&C->saslprops.iplocalport, localip);
278 }
279 pthread_mutex_unlock(&clienthost_mutex); /* UNLOCK */
280
281 /* create sasl connection */
282 r = sasl_server_new("mupdate",
283 config_servername, NULL,
284 buf_cstringnull_ifempty(&C->saslprops.iplocalport),
285 buf_cstringnull_ifempty(&C->saslprops.ipremoteport),
286 NULL, 0,
287 &C->saslconn);
288 if (r != SASL_OK) {
289 syslog(LOG_ERR, "failed to start sasl for connection: %s",
290 sasl_errstring(r, NULL, NULL));
291 prot_printf(C->pout, SERVER_UNABLE_STRING);
292
293 C->idle = 0;
294 conn_free(C);
295 return NULL;
296 }
297
298 /* set my allowable security properties */
299 sasl_setprop(C->saslconn, SASL_SEC_PROPS, mysasl_secprops(SASL_SEC_NOANONYMOUS));
300
301 return C;
302 }
303
conn_free(struct conn * C)304 static void conn_free(struct conn *C)
305 {
306 assert(!C->idle); /* Not allowed to free idle connections */
307
308 if (C->streaming) { /* remove from updatelist */
309 struct conn *upc;
310
311 pthread_mutex_lock(&mailboxes_mutex);
312
313 if (C == updatelist) {
314 /* first thing in updatelist */
315 updatelist = C->updatelist_next;
316 } else {
317 /* find in update list */
318 for (upc = updatelist; upc->updatelist_next != NULL;
319 upc = upc->updatelist_next) {
320 if (upc->updatelist_next == C) break;
321 }
322 /* must find it ! */
323 assert(upc->updatelist_next == C);
324
325 upc->updatelist_next = C->updatelist_next;
326 }
327
328 pthread_mutex_unlock(&mailboxes_mutex);
329 }
330
331 /* decrease connection counter */
332 pthread_mutex_lock(&connection_count_mutex);
333 connection_count--;
334 pthread_mutex_unlock(&connection_count_mutex);
335
336 /* remove from connlist */
337 pthread_mutex_lock(&connlist_mutex); /* LOCK */
338 if (C == connlist) {
339 connlist = connlist->next;
340 } else {
341 struct conn *t;
342
343 for (t = connlist; t->next != NULL; t = t->next) {
344 if (t->next == C) break;
345 }
346 assert(t != NULL);
347 t->next = C->next;
348 }
349 pthread_mutex_unlock(&connlist_mutex); /* UNLOCK */
350
351 if (C->ev) prot_removewaitevent(C->pin, C->ev);
352
353 prot_flush(C->pout);
354
355 if (C->pin) prot_free(C->pin);
356 if (C->pout) prot_free(C->pout);
357
358 #ifdef HAVE_SSL
359 if (C->tlsconn) tls_reset_servertls(&C->tlsconn);
360 tls_shutdown_serverengine();
361 #endif
362
363 cyrus_close_sock(C->fd);
364 if (C->logfd != -1) close(C->logfd);
365
366 if (C->saslconn) sasl_dispose(&C->saslconn);
367
368 saslprops_free(&C->saslprops);
369
370 /* free struct bufs */
371 buf_free(&(C->tag));
372 buf_free(&(C->cmd));
373 buf_free(&(C->arg1));
374 buf_free(&(C->arg2));
375 buf_free(&(C->arg3));
376
377 if (C->streaming_hosts) strarray_free(C->streaming_hosts);
378
379 free(C);
380 }
381
382 /*
383 * The auth_*.c backends called by mysasl_proxy_policy()
384 * use static variables which we need to protect with a mutex.
385 */
386 static pthread_mutex_t proxy_policy_mutex = PTHREAD_MUTEX_INITIALIZER;
387
mupdate_proxy_policy(sasl_conn_t * conn,void * context,const char * requested_user,unsigned rlen,const char * auth_identity,unsigned alen,const char * def_realm,unsigned urlen,struct propctx * propctx)388 static int mupdate_proxy_policy(sasl_conn_t *conn,
389 void *context,
390 const char *requested_user, unsigned rlen,
391 const char *auth_identity, unsigned alen,
392 const char *def_realm,
393 unsigned urlen,
394 struct propctx *propctx)
395 {
396 int r;
397
398 pthread_mutex_lock(&proxy_policy_mutex); /* LOCK */
399
400 r = mysasl_proxy_policy(conn, context, requested_user, rlen,
401 auth_identity, alen, def_realm, urlen, propctx);
402
403 pthread_mutex_unlock(&proxy_policy_mutex); /* UNLOCK */
404
405 return r;
406 }
407
408 static struct sasl_callback mysasl_cb[] = {
409 { SASL_CB_GETOPT, (mysasl_cb_ft *) &mysasl_config, NULL },
410 { SASL_CB_PROXY_POLICY, (mysasl_cb_ft *) &mupdate_proxy_policy, NULL },
411 { SASL_CB_LIST_END, NULL, NULL }
412 };
413
414 /*
415 * Is the IP address of the given hostname local?
416 * Returns 1 if local, 0 otherwise.
417 */
islocalip(const char * hostname)418 static int islocalip(const char *hostname)
419 {
420 struct hostent *hp;
421 struct in_addr *haddr, *iaddr;
422 struct ifconf ifc;
423 struct ifreq *ifr;
424 char buf[8192]; /* XXX this limits us to 256 interfaces */
425 int sock, islocal = 0;
426
427 if ((hp = gethostbyname(hostname)) == NULL) {
428 fprintf(stderr, "unknown host: %s\n", hostname);
429 return 0;
430 }
431
432 haddr = (struct in_addr *) hp->h_addr;
433
434 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
435 fprintf(stderr, "socket() failed\n");
436 return 0;
437 }
438
439 ifc.ifc_buf = buf;
440 ifc.ifc_len = sizeof(buf);
441
442 if (ioctl(sock, SIOCGIFCONF, &ifc) != 0) {
443 fprintf(stderr, "ioctl(SIOCGIFCONF) failed: %d\n", errno);
444 close(sock);
445 return 0;
446 }
447
448 for (ifr = ifc.ifc_req; ifr - ifc.ifc_req < ifc.ifc_len; ifr++) {
449 if (ioctl(sock, SIOCGIFADDR, ifr) != 0) continue;
450 if (ioctl(sock, SIOCGIFFLAGS, ifr) != 0) continue;
451
452 /* skip any inactive or loopback interfaces */
453 if (!(ifr->ifr_flags & IFF_UP) || (ifr->ifr_flags & IFF_LOOPBACK))
454 continue;
455
456 iaddr = &(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr);
457
458 /* compare the host address to the interface address */
459 if (!memcmp(haddr, iaddr, sizeof(struct in_addr))) {
460 islocal = 1;
461 break;
462 }
463 }
464
465 close(sock);
466
467 return islocal;
468 }
469
470 /*
471 * run once when process is forked;
472 * MUST NOT exit directly; must return with non-zero error code
473 */
service_init(int argc,char ** argv,char ** envp)474 int service_init(int argc, char **argv,
475 char **envp __attribute__((unused)))
476 {
477 int i, r, workers_to_start;
478 int opt, autoselect = 0;
479 pthread_t t;
480
481 if (geteuid() == 0) fatal("must run as the Cyrus user", EX_USAGE);
482
483 /* Do minor configuration checking */
484 workers_to_start = config_getint(IMAPOPT_MUPDATE_WORKERS_START);
485
486 if (config_getint(IMAPOPT_MUPDATE_WORKERS_MAX) < config_getint(IMAPOPT_MUPDATE_WORKERS_MINSPARE)) {
487 syslog(LOG_CRIT, "Maximum total worker threads is less than minimum spare worker threads");
488 return EX_SOFTWARE;
489 }
490
491 if (workers_to_start < config_getint(IMAPOPT_MUPDATE_WORKERS_MINSPARE)) {
492 syslog(LOG_CRIT, "Starting worker threads is less than minimum spare worker threads");
493 return EX_SOFTWARE;
494 }
495
496 if (config_getint(IMAPOPT_MUPDATE_WORKERS_MAXSPARE) < workers_to_start) {
497 syslog(LOG_CRIT, "Maximum spare worker threads is less than starting worker threads");
498 return EX_SOFTWARE;
499 }
500
501 if (config_getint(IMAPOPT_MUPDATE_WORKERS_MINSPARE) > workers_to_start) {
502 syslog(LOG_CRIT, "Minimum spare worker threads is greater than starting worker threads");
503 return EX_SOFTWARE;
504 }
505
506 if (config_getint(IMAPOPT_MUPDATE_WORKERS_MAX) < workers_to_start) {
507 syslog(LOG_CRIT, "Maximum total worker threads is less than starting worker threads");
508 return EX_SOFTWARE;
509 }
510
511 /* set signal handlers */
512 signals_set_shutdown(&shut_down);
513 signal(SIGPIPE, SIG_IGN);
514
515 global_sasl_init(1, 1, mysasl_cb);
516
517 /* see if we're the master or a slave */
518 while ((opt = getopt(argc, argv, "ma")) != EOF) {
519 switch (opt) {
520 case 'm':
521 masterp = 1;
522 break;
523 case 'a':
524 autoselect = 1;
525 break;
526 default:
527 break;
528 }
529 }
530
531 if (!masterp && autoselect) masterp = islocalip(config_mupdate_server);
532
533 if (masterp &&
534 config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_UNIFIED) {
535 /* XXX We currently prohibit this because mailboxes created
536 * on the master will cause local mailbox entries to be propagated
537 * to the slave. We can probably fix this by prepending
538 * config_servername onto the entries before updating the slaves.
539 */
540 fatal("cannot run mupdate master on a unified server", EX_USAGE);
541 }
542
543 if (pipe(conn_pipe) == -1) {
544 syslog(LOG_ERR, "could not setup connection signaling pipe %m");
545 return EX_OSERR;
546 }
547
548 database_init();
549
550 #ifdef HAVE_SSL
551 #if OPENSSL_VERSION_NUMBER < 0x10100000L
552 CRYPTO_thread_setup();
553 #endif
554 #endif
555
556 if (!masterp) {
557 r = pthread_create(&t, NULL, &mupdate_client_start, NULL);
558 if (r == 0) {
559 pthread_detach(t);
560 } else {
561 syslog(LOG_ERR, "could not start client thread");
562 return EX_SOFTWARE;
563 }
564
565 /* Wait until they sync the database */
566 pthread_mutex_lock(&synced_mutex);
567 if (!synced)
568 pthread_cond_wait(&synced_cond, &synced_mutex);
569 pthread_mutex_unlock(&synced_mutex);
570 } else {
571 pthread_t t;
572
573 r = pthread_create(&t, NULL, &mupdate_placebo_kick_start, NULL);
574 if (r == 0) {
575 pthread_detach(t);
576 } else {
577 syslog(LOG_ERR, "could not start placebo kick thread");
578 return EX_SOFTWARE;
579 }
580
581 mupdate_ready();
582 }
583
584 /* Now create the worker thread pool */
585 for(i=0; i < workers_to_start; i++) {
586 r = pthread_create(&t, NULL, &thread_main, NULL);
587 if (r == 0) {
588 pthread_detach(t);
589 } else {
590 syslog(LOG_ERR, "could not start client thread");
591 return EX_SOFTWARE;
592 }
593 }
594
595 return 0;
596 }
597
598 /* Called by service API to shut down the service */
service_abort(int error)599 void service_abort(int error)
600 {
601 #ifdef HAVE_SSL
602 #if OPENSSL_VERSION_NUMBER < 0x10100000L
603 CRYPTO_thread_cleanup();
604 #endif
605 #endif
606 shut_down(error);
607 }
608
fatal(const char * s,int code)609 EXPORTED void fatal(const char *s, int code)
610 {
611 static int recurse_code = 0;
612
613 if (recurse_code) exit(code);
614 else recurse_code = code;
615
616 syslog(LOG_ERR, "%s", s);
617 shut_down(code);
618
619 /* NOTREACHED */
620 exit(code); /* shut up GCC */
621 }
622
623 #define CHECKNEWLINE(c, ch) do { if ((ch) == '\r') (ch)=prot_getc((c)->pin); \
624 if ((ch) != '\n') goto extraargs; } while (0)
625
docmd(struct conn * c)626 static mupdate_docmd_result_t docmd(struct conn *c)
627 {
628 mupdate_docmd_result_t ret = DOCMD_OK;
629 int ch;
630 int was_blocking = prot_IS_BLOCKING(c->pin);
631 char *p;
632
633 /* We know we have input, so skip the check below.
634 * Note that we MUST skip this nonblocking check in order to properly
635 * catch connections that have timed out.
636 */
637 goto cmd;
638
639 nextcmd:
640 /* First we do a check for input */
641 prot_NONBLOCK(c->pin);
642 ch = prot_getc(c->pin);
643
644 if (ch == EOF && errno == EAGAIN) {
645 /* no input from client */
646 goto done;
647 } else if (ch == EOF) {
648 goto lost_conn;
649 } else {
650 /* there's input waiting, put back our character */
651 prot_ungetc(ch, c->pin);
652 }
653
654 /* Set it back to blocking so we don't get half a word */
655 prot_BLOCK(c->pin);
656
657 cmd:
658 ch = getword(c->pin, &(c->tag));
659 if (ch == EOF) goto lost_conn;
660
661 if (ch != ' ') {
662 prot_printf(c->pout, "* BAD \"Need command\"\r\n");
663 eatline(c->pin, ch);
664 goto nextcmd;
665 }
666
667 /* parse command name */
668 ch = getword(c->pin, &(c->cmd));
669 if (ch == EOF) {
670 goto lost_conn;
671 } else if (!c->cmd.s[0]) {
672 prot_printf(c->pout, "%s BAD \"Null command\"\r\n", c->tag.s);
673 eatline(c->pin, ch);
674 goto nextcmd;
675 }
676
677 if (Uislower(c->cmd.s[0])) {
678 c->cmd.s[0] = toupper((unsigned char) c->cmd.s[0]);
679 }
680 for (p = &(c->cmd.s[1]); *p; p++) {
681 if (Uisupper(*p)) *p = tolower((unsigned char) *p);
682 }
683
684 switch (c->cmd.s[0]) {
685 case 'A':
686 if (!strcmp(c->cmd.s, "Authenticate")) {
687 int opt = 0;
688
689 if (ch != ' ') goto missingargs;
690 ch = getstring(c->pin, c->pout, &(c->arg1));
691 if (ch == ' ') {
692 ch = getstring(c->pin, c->pout, &(c->arg2));
693 opt = 1;
694 }
695 CHECKNEWLINE(c, ch);
696
697 if (c->userid) {
698 prot_printf(c->pout,
699 "%s BAD \"already authenticated\"\r\n",
700 c->tag.s);
701 goto nextcmd;
702 }
703
704 cmd_authenticate(c, c->tag.s, c->arg1.s,
705 opt ? c->arg2.s : NULL);
706 }
707 else if (!c->userid) goto nologin;
708 else if (!strcmp(c->cmd.s, "Activate")) {
709 if (ch != ' ') goto missingargs;
710 ch = getstring(c->pin, c->pout, &(c->arg1));
711 if (ch != ' ') goto missingargs;
712 ch = getstring(c->pin, c->pout, &(c->arg2));
713 if (ch != ' ') goto missingargs;
714 ch = getstring(c->pin, c->pout, &(c->arg3));
715 CHECKNEWLINE(c, ch);
716
717 if (c->streaming) goto notwhenstreaming;
718 if (!masterp) goto masteronly;
719
720 cmd_set(c, c->tag.s, c->arg1.s, c->arg2.s,
721 c->arg3.s, SET_ACTIVE);
722 }
723 else goto badcmd;
724 break;
725
726 #ifdef HAVE_ZLIB
727 case 'C':
728 if (!strcmp(c->cmd.s, "Compress")) {
729 if (ch != ' ') goto missingargs;
730 ch = getstring(c->pin, c->pout, &(c->arg1));
731 CHECKNEWLINE(c, ch);
732
733 cmd_compress(c, c->tag.s, c->arg1.s);
734 }
735 else goto badcmd;
736 break;
737 #endif
738
739 case 'D':
740 if (!c->userid) goto nologin;
741 else if (!strcmp(c->cmd.s, "Deactivate")) {
742 if (ch != ' ') goto missingargs;
743 ch = getstring(c->pin, c->pout, &(c->arg1));
744 if (ch != ' ') goto missingargs;
745 ch = getstring(c->pin, c->pout, &(c->arg2));
746 CHECKNEWLINE(c, ch);
747
748 if (c->streaming) goto notwhenstreaming;
749 if (!masterp) goto masteronly;
750
751 cmd_set(c, c->tag.s, c->arg1.s, c->arg2.s,
752 NULL, SET_DEACTIVATE);
753 }
754 else if (!strcmp(c->cmd.s, "Delete")) {
755 if (ch != ' ') goto missingargs;
756 ch = getstring(c->pin, c->pout, &(c->arg1));
757 CHECKNEWLINE(c, ch);
758
759 if (c->streaming) goto notwhenstreaming;
760 if (!masterp) goto masteronly;
761
762 cmd_set(c, c->tag.s, c->arg1.s, NULL, NULL, SET_DELETE);
763 }
764 else goto badcmd;
765 break;
766
767 case 'F':
768 if (!c->userid) goto nologin;
769 else if (!strcmp(c->cmd.s, "Find")) {
770 if (ch != ' ') goto missingargs;
771 ch = getstring(c->pin, c->pout, &(c->arg1));
772 CHECKNEWLINE(c, ch);
773
774 if (c->streaming) goto notwhenstreaming;
775
776 cmd_find(c, c->tag.s, c->arg1.s, 1, 0);
777 }
778 else goto badcmd;
779 break;
780
781 case 'L':
782 if (!strcmp(c->cmd.s, "Logout")) {
783 CHECKNEWLINE(c, ch);
784
785 prot_printf(c->pout, "%s OK \"bye-bye\"\r\n", c->tag.s);
786 ret = DOCMD_CONN_FINISHED;
787 goto done;
788 }
789 else if (!c->userid) goto nologin;
790 else if (!strcmp(c->cmd.s, "List")) {
791 int opt = 0;
792
793 if (ch == ' ') {
794 /* Optional partition/host prefix parameter */
795 ch = getstring(c->pin, c->pout, &(c->arg1));
796 opt = 1;
797 }
798 CHECKNEWLINE(c, ch);
799
800 if (c->streaming) goto notwhenstreaming;
801
802 cmd_list(c, c->tag.s, opt ? c->arg1.s : NULL);
803
804 prot_printf(c->pout, "%s OK \"list complete\"\r\n", c->tag.s);
805 }
806 else goto badcmd;
807 break;
808
809 case 'N':
810 if (!c->userid) goto nologin;
811 else if (!strcmp(c->cmd.s, "Noop")) {
812 CHECKNEWLINE(c, ch);
813
814 if (c->streaming) {
815 /* Make *very* sure we are up-to-date */
816 kick_mupdate();
817 sendupdates(c, 0); /* don't flush pout though */
818 }
819
820 prot_printf(c->pout, "%s OK \"Noop done\"\r\n", c->tag.s);
821 }
822 else goto badcmd;
823 break;
824
825 case 'R':
826 if (!c->userid) goto nologin;
827 else if (!strcmp(c->cmd.s, "Reserve")) {
828 if (ch != ' ') goto missingargs;
829 ch = getstring(c->pin, c->pout, &(c->arg1));
830 if (ch != ' ') goto missingargs;
831 ch = getstring(c->pin, c->pout, &(c->arg2));
832 CHECKNEWLINE(c, ch);
833
834 if (c->streaming) goto notwhenstreaming;
835 if (!masterp) goto masteronly;
836
837 cmd_set(c, c->tag.s, c->arg1.s, c->arg2.s, NULL, SET_RESERVE);
838 }
839 else goto badcmd;
840 break;
841
842 case 'S':
843 if (!strcmp(c->cmd.s, "Starttls")) {
844 CHECKNEWLINE(c, ch);
845
846 /* XXX discard any input pipelined after STARTTLS */
847 prot_flush(c->pin);
848
849 if (!tls_enabled()) {
850 /* we don't support starttls */
851 goto badcmd;
852 }
853
854 /* if we've already done SASL fail */
855 if (c->userid) {
856 prot_printf(c->pout,
857 "%s BAD Can't Starttls after authentication\r\n",
858 c->tag.s);
859 goto nextcmd;
860 }
861
862 /* if we've already done COMPRESS fail */
863 if (c->compress_done) {
864 prot_printf(c->pout,
865 "%s BAD Can't Starttls after Compress\r\n",
866 c->tag.s);
867 goto nextcmd;
868 }
869
870 /* check if already did a successful tls */
871 if (c->tlsconn) {
872 prot_printf(c->pout,
873 "%s BAD Already did a successful Starttls\r\n",
874 c->tag.s);
875 goto nextcmd;
876 }
877 cmd_starttls(c, c->tag.s);
878 }
879 else goto badcmd;
880 break;
881
882 case 'U':
883 if (!c->userid) goto nologin;
884 else if (!strcmp(c->cmd.s, "Update")) {
885 strarray_t *arg = NULL;
886 int counter = 30; /* limit on number of processed hosts */
887
888 while(ch == ' ') {
889 /* Hey, look, more bits of a PARTIAL-UPDATE command */
890 ch = getstring(c->pin, c->pout, &(c->arg1));
891 if (c->arg1.s[0] == '\0') {
892 strarray_free(arg);
893 goto badargs;
894 }
895 if (counter-- == 0) {
896 strarray_free(arg);
897 goto extraargs;
898 }
899 if (!arg) arg = strarray_new();
900 strarray_append(arg, c->arg1.s);
901 }
902
903 CHECKNEWLINE(c, ch);
904 if (c->streaming) goto notwhenstreaming;
905
906 cmd_startupdate(c, c->tag.s, arg);
907 }
908 else goto badcmd;
909 break;
910
911 default:
912 badcmd:
913 prot_printf(c->pout, "%s BAD \"Unrecognized command\"\r\n",
914 c->tag.s);
915 eatline(c->pin, ch);
916 break;
917
918 extraargs:
919 prot_printf(c->pout, "%s BAD \"Extra arguments\"\r\n",
920 c->tag.s);
921 eatline(c->pin, ch);
922 break;
923
924 badargs:
925 prot_printf(c->pout, "%s BAD \"Badly formed arguments\"\r\n",
926 c->tag.s);
927 eatline(c->pin, ch);
928 break;
929
930 missingargs:
931 prot_printf(c->pout, "%s BAD \"Missing arguments\"\r\n",
932 c->tag.s);
933 eatline(c->pin, ch);
934 break;
935
936 notwhenstreaming:
937 prot_printf(c->pout, "%s BAD \"not legal when streaming\"\r\n",
938 c->tag.s);
939 break;
940
941 masteronly:
942 prot_printf(c->pout,
943 "%s BAD \"read-only session\"\r\n",
944 c->tag.s);
945 break;
946
947 nologin:
948 prot_printf(c->pout, "%s BAD Please login first\r\n", c->tag.s);
949 eatline(c->pin, ch);
950 break;
951 }
952
953 /* Check for more input */
954 goto nextcmd;
955
956 lost_conn:
957 {
958 const char *err;
959
960 if ((err = prot_error(c->pin)) != NULL
961 && strcmp(err, PROT_EOF_STRING)) {
962 syslog(LOG_WARNING, "%s, closing connection", err);
963 prot_printf(c->pout, "* BYE \"%s\"\r\n", err);
964 }
965
966 ret = DOCMD_CONN_FINISHED;
967 }
968
969 done:
970 /* Restore the state of the input stream */
971 if (was_blocking)
972 prot_BLOCK(c->pin);
973 else
974 prot_NONBLOCK(c->pin);
975
976 /* Necessary since we don't ever do a prot_read on an idle connection
977 * in mupdate */
978 prot_flush(c->pout);
979
980 return ret;
981 }
982
983 /*
984 * run for each accepted connection
985 */
service_main_fd(int fd,int argc,char ** argv,char ** envp)986 int service_main_fd(int fd,
987 int argc __attribute__((unused)),
988 char **argv __attribute__((unused)),
989 char **envp __attribute__((unused)))
990 {
991 int flag;
992 int r;
993
994 /* First check that we can handle the new connection. */
995 pthread_mutex_lock(&connection_count_mutex); /* LOCK */
996 flag =
997 (connection_count >= config_getint(IMAPOPT_MUPDATE_CONNECTIONS_MAX));
998 pthread_mutex_unlock(&connection_count_mutex); /* UNLOCK */
999
1000 if (flag) {
1001 /* Do the nonblocking write, if it fails, too bad for them. */
1002 nonblock(fd, 1);
1003 r = write(fd,SERVER_UNABLE_STRING,sizeof(SERVER_UNABLE_STRING));
1004 close(fd);
1005
1006 syslog(LOG_ERR,
1007 "Server too busy, dropping connection.");
1008 if (r) return 0; /* filthy hack to avoid warning on 'r' */
1009 } else if (write(conn_pipe[1], &fd, sizeof(fd)) == -1) {
1010 /* signal that a new file descriptor is available.
1011 * If it fails... */
1012
1013 syslog(LOG_CRIT,
1014 "write to conn_pipe to signal new connection failed: %m");
1015 return EX_TEMPFAIL;
1016 }
1017 return 0;
1018 }
1019
1020 /*
1021 * Issue the capability banner
1022 */
dobanner(struct conn * c)1023 static void dobanner(struct conn *c)
1024 {
1025 char slavebuf[4096];
1026 const char *mechs;
1027 int mechcount;
1028 int ret;
1029
1030 /* send initial the banner + flush pout */
1031 ret = sasl_listmech(c->saslconn, NULL,
1032 "* AUTH \"", "\" \"", "\"",
1033 &mechs, NULL, &mechcount);
1034
1035 /* Add mupdate:// tag if necessary */
1036 if (!masterp) {
1037 if (!config_mupdate_server)
1038 fatal("mupdate server was not specified for slave",
1039 EX_TEMPFAIL);
1040
1041 snprintf(slavebuf, sizeof(slavebuf), "mupdate://%s",
1042 config_mupdate_server);
1043 }
1044
1045 prot_printf(c->pout, "%s\r\n",
1046 (ret == SASL_OK && mechcount > 0) ? mechs : "* AUTH");
1047
1048 if (tls_enabled() && !c->tlsconn) {
1049 prot_printf(c->pout, "* STARTTLS\r\n");
1050 }
1051
1052 #ifdef HAVE_ZLIB
1053 if (!c->compress_done && !c->tls_comp) {
1054 prot_printf(c->pout, "* COMPRESS \"DEFLATE\"\r\n");
1055 }
1056 #endif
1057
1058 prot_printf(c->pout, "* PARTIAL-UPDATE\r\n");
1059
1060 prot_printf(c->pout,
1061 "* OK MUPDATE \"%s\" \"Cyrus IMAP\" \"%s\" \"%s\"\r\n",
1062 config_servername,
1063 CYRUS_VERSION, masterp ? "(master)" : slavebuf);
1064
1065 prot_flush(c->pout);
1066 }
1067
1068 /*
1069 * The main thread loop
1070 */
1071 /* Note that You Must Lock Listen mutex before idle worker mutex,
1072 * though you can lock them individually too */
thread_main(void * rock)1073 static void *thread_main(void *rock __attribute__((unused)))
1074 {
1075 struct conn *C; /* used for loops */
1076 struct conn *currConn = NULL; /* the connection we care about currently */
1077 struct protgroup *protin = protgroup_new(PROTGROUP_SIZE_DEFAULT);
1078 struct protgroup *protout = NULL;
1079 struct timeval now;
1080 struct timespec timeout;
1081 int need_workers, too_many;
1082 int max_worker_flag;
1083 int do_a_command;
1084 int send_a_banner;
1085 int connflag;
1086 int new_fd;
1087 int ret = 0;
1088 struct conn *ni;
1089
1090 /* Lock Worker Count Mutex */
1091 pthread_mutex_lock(&worker_count_mutex); /* LOCK */
1092 /* Change total number of workers */
1093 worker_count++;
1094 syslog(LOG_DEBUG,
1095 "New worker thread started, for a total of %d", worker_count);
1096 /* Unlock Worker Count Mutex */
1097 pthread_mutex_unlock(&worker_count_mutex); /* UNLOCK */
1098
1099 /* This is a big infinite loop */
1100 while (1) {
1101 send_a_banner = do_a_command = 0;
1102
1103 pthread_mutex_lock(&idle_worker_mutex);
1104 /* If we are over the limit on idle threads, die. */
1105 max_worker_flag = (idle_worker_count >=
1106 config_getint(IMAPOPT_MUPDATE_WORKERS_MAXSPARE));
1107 /* Increment Idle Workers */
1108 if (!max_worker_flag) idle_worker_count++;
1109 pthread_mutex_unlock(&idle_worker_mutex);
1110
1111 if (max_worker_flag) goto worker_thread_done;
1112
1113 retry_lock:
1114
1115 /* Lock Listen Mutex - If locking takes more than 60 seconds,
1116 * kill off this thread. Ideally this is a FILO queue */
1117 pthread_mutex_lock(&listener_mutex); /* LOCK */
1118 ret = 0;
1119 while (listener_lock && ret != ETIMEDOUT) {
1120 gettimeofday(&now, NULL);
1121 timeout.tv_sec = now.tv_sec + 60;
1122 timeout.tv_nsec = now.tv_usec * 1000;
1123 ret = pthread_cond_timedwait(&listener_cond,
1124 &listener_mutex,
1125 &timeout);
1126 }
1127 if (!ret) {
1128 /* Set listener lock until we decide what to do */
1129 listener_lock = 1;
1130 }
1131 pthread_mutex_unlock(&listener_mutex); /* UNLOCK */
1132
1133 if (ret == ETIMEDOUT) {
1134 pthread_mutex_lock(&idle_worker_mutex); /* LOCK */
1135 if (idle_worker_count <= config_getint(IMAPOPT_MUPDATE_WORKERS_MINSPARE)) {
1136 pthread_mutex_unlock(&idle_worker_mutex); /* UNLOCK */
1137 /* below number of spare workers, try to get the lock again */
1138 goto retry_lock;
1139 } else {
1140 /* Decrement Idle Worker Count */
1141 idle_worker_count--;
1142 pthread_mutex_unlock(&idle_worker_mutex); /* UNLOCK */
1143
1144 syslog(LOG_DEBUG,
1145 "Thread timed out waiting for listener_lock");
1146 goto worker_thread_done;
1147 }
1148 }
1149
1150 signals_poll();
1151
1152 /* Check if we are ready for connections, if not, wait */
1153 pthread_mutex_lock(&ready_for_connections_mutex); /* LOCK */
1154 /* are we ready to take connections? */
1155 while (!ready_for_connections) {
1156 pthread_cond_wait(&ready_for_connections_cond,
1157 &ready_for_connections_mutex);
1158 }
1159 pthread_mutex_unlock(&ready_for_connections_mutex); /* UNLOCK */
1160
1161 connflag = 0;
1162
1163 /* Reset protin to all zeros (to preserve memory allocation) */
1164 protgroup_reset(protin);
1165
1166 /* Clear protout if needed */
1167 protgroup_free(protout);
1168 protout = NULL;
1169
1170 /* Build list of idle protstreams */
1171 pthread_mutex_lock(&idle_connlist_mutex); /* LOCK */
1172 for (C=idle_connlist; C; C=C->next_idle) {
1173 assert(C->idle);
1174
1175 protgroup_insert(protin, C->pin);
1176 }
1177 pthread_mutex_unlock(&idle_connlist_mutex); /* UNLOCK */
1178
1179 /* Select on Idle Conns + conn_pipe */
1180 if (prot_select(protin, conn_pipe[0],
1181 &protout, &connflag, NULL) == -1) {
1182 syslog(LOG_ERR, "prot_select() failed in thread_main: %m");
1183 fatal("prot_select() failed in thread_main", EX_TEMPFAIL);
1184 }
1185
1186 /* we've got work to do */
1187 pthread_mutex_lock(&idle_worker_mutex); /* LOCK */
1188 idle_worker_count--;
1189 pthread_mutex_unlock(&idle_worker_mutex); /* UNLOCK */
1190
1191 /* If we've been signaled to be unready, drop all current connections
1192 * in the idle list */
1193 pthread_mutex_lock(&ready_for_connections_mutex); /* LOCK */
1194 if (!ready_for_connections) {
1195 pthread_mutex_unlock(&ready_for_connections_mutex); /* UNLOCK */
1196 /* Free all connections on idle_connlist. Note that
1197 * any connection not currently on the idle_connlist will
1198 * instead be freed when they drop out of their docmd() below */
1199
1200 pthread_mutex_lock(&idle_connlist_mutex); /* LOCK */
1201 for (C=idle_connlist; C; C = ni) {
1202 ni = C->next_idle;
1203
1204 prot_printf(C->pout,
1205 "* BYE \"no longer ready for connections\"\r\n");
1206
1207 C->idle = 0;
1208 conn_free(C);
1209 }
1210 idle_connlist = NULL;
1211 pthread_mutex_unlock(&idle_connlist_mutex); /* UNLOCK */
1212
1213 goto nextlistener;
1214 }
1215 pthread_mutex_unlock(&ready_for_connections_mutex); /* UNLOCK */
1216
1217 if (connflag) {
1218 /* read the fd from the pipe, if needed */
1219 if (read(conn_pipe[0], &new_fd, sizeof(new_fd)) == -1) {
1220 syslog(LOG_CRIT,
1221 "read from conn_pipe for new connection failed: %m");
1222 fatal("conn_pipe read failed", EX_TEMPFAIL);
1223 }
1224 } else {
1225 new_fd = NO_NEW_CONNECTION;
1226 }
1227
1228 if (new_fd != NO_NEW_CONNECTION) {
1229 /* new_fd indicates a new connection */
1230 currConn = conn_new(new_fd);
1231 if (currConn)
1232 send_a_banner = 1;
1233 } else if (protout) {
1234 /* Handle existing connection, we'll need to pull it off
1235 * the idle_connlist */
1236 struct protstream *ptmp;
1237 struct conn **prev;
1238
1239 pthread_mutex_lock(&idle_connlist_mutex); /* LOCK */
1240
1241 /* Grab the first connection out of the ready set, and use it */
1242 ptmp = protgroup_getelement(protout, 0);
1243 assert(ptmp);
1244 currConn = ptmp->userdata;
1245 assert(currConn);
1246 assert(currConn->idle);
1247
1248 currConn->idle = 0;
1249 for (C=idle_connlist, prev = &(idle_connlist); C;
1250 prev = &(C->next_idle), C=C->next_idle) {
1251 if (C == currConn) {
1252 *prev = C->next_idle;
1253 C->next_idle = NULL;
1254 break;
1255 }
1256 }
1257 pthread_mutex_unlock(&idle_connlist_mutex); /* UNLOCK */
1258
1259 do_a_command = 1;
1260 }
1261
1262 /*
1263 * If this worker will do any real work, we'll want to make sure
1264 * there are sufficient additional workers while we're busy.
1265 */
1266 if (send_a_banner || do_a_command) {
1267 pthread_mutex_lock(&idle_worker_mutex); /* LOCK */
1268 need_workers = config_getint(IMAPOPT_MUPDATE_WORKERS_MINSPARE)
1269 - idle_worker_count;
1270 pthread_mutex_unlock(&idle_worker_mutex); /* UNLOCK */
1271
1272 pthread_mutex_lock(&worker_count_mutex); /* LOCK */
1273 if (need_workers > 0) {
1274 too_many = (need_workers + worker_count) -
1275 config_getint(IMAPOPT_MUPDATE_WORKERS_MAX);
1276 if (too_many > 0) need_workers -= too_many;
1277 }
1278 pthread_mutex_unlock(&worker_count_mutex); /* UNLOCK */
1279
1280 /* Do we need a new worker (or two, or three...)?
1281 * (are we allowed to create one?) */
1282 while (need_workers > 0) {
1283 pthread_t t;
1284 int r = pthread_create(&t, NULL, &thread_main, NULL);
1285 if (r == 0) {
1286 pthread_detach(t);
1287 } else {
1288 syslog(LOG_ERR,
1289 "could not start a new worker thread (not fatal)");
1290 }
1291 /* Even if we fail to create the new thread, keep going */
1292 need_workers--;
1293 }
1294 }
1295
1296 nextlistener:
1297 /* Let another listener in */
1298 pthread_mutex_lock(&listener_mutex);
1299 assert(listener_lock);
1300 listener_lock = 0;
1301 pthread_cond_signal(&listener_cond);
1302 pthread_mutex_unlock(&listener_mutex);
1303
1304 /* Do work in this thread, if needed */
1305 if (send_a_banner) {
1306 dobanner(currConn);
1307 } else if (do_a_command) {
1308 assert(currConn);
1309
1310 if (docmd(currConn) == DOCMD_CONN_FINISHED) {
1311 conn_free(currConn);
1312 /* continue to top of loop here since we won't be adding
1313 * this back to the idle list */
1314 continue;
1315 }
1316
1317 /* Are we allowed to continue serving data? */
1318 pthread_mutex_lock(&ready_for_connections_mutex); /* LOCK */
1319 if (!ready_for_connections) {
1320 pthread_mutex_unlock(&ready_for_connections_mutex); /* UNLOCK */
1321 prot_printf(C->pout,
1322 "* BYE \"no longer ready for connections\"\r\n");
1323 conn_free(currConn);
1324 /* continue to top of loop here since we won't be adding
1325 * this back to the idle list */
1326 continue;
1327 }
1328 pthread_mutex_unlock(&ready_for_connections_mutex); /* UNLOCK */
1329 } /* done handling command */
1330
1331 if (send_a_banner || do_a_command) {
1332 /* We did work in this thread, so we need to [re-]add the
1333 * connection to the idle list and signal the current listener */
1334
1335 pthread_mutex_lock(&idle_connlist_mutex); /* LOCK */
1336 currConn->idle = 1;
1337 currConn->next_idle = idle_connlist;
1338 idle_connlist = currConn;
1339 pthread_mutex_unlock(&idle_connlist_mutex); /* UNLOCK */
1340
1341 /* Signal to our caller that we should add something
1342 * to select() on, since this connection is ready again */
1343 if (write(conn_pipe[1], &NO_NEW_CONNECTION,
1344 sizeof(NO_NEW_CONNECTION)) == -1) {
1345 fatal("write to conn_pipe to signal docmd done failed",
1346 EX_TEMPFAIL);
1347 }
1348 }
1349
1350 } /* while(1) */
1351
1352 worker_thread_done:
1353 /* Remove this worker from the pool */
1354 /* Note that workers exiting the loop above should NOT be counted
1355 * in the idle_worker_count */
1356 pthread_mutex_lock(&worker_count_mutex); /* LOCK */
1357 worker_count--;
1358 pthread_mutex_lock(&idle_worker_mutex); /* LOCK */
1359 syslog(LOG_DEBUG,
1360 "Worker thread finished, for a total of %d (%d spare)",
1361 worker_count, idle_worker_count);
1362 pthread_mutex_unlock(&idle_worker_mutex); /* UNLOCK */
1363 pthread_mutex_unlock(&worker_count_mutex); /* UNLOCK */
1364
1365 protgroup_free(protin);
1366 protgroup_free(protout);
1367
1368 return NULL;
1369 }
1370
1371 /* read from disk database must be unlocked. */
database_init(void)1372 static void database_init(void)
1373 {
1374 pthread_mutex_lock(&mailboxes_mutex); /* LOCK */
1375 pthread_mutex_unlock(&mailboxes_mutex); /* UNLOCK */
1376 }
1377
1378 /* log change to database. database must be locked. */
database_log(const struct mbent * mb,struct txn ** mytid)1379 static void database_log(const struct mbent *mb, struct txn **mytid)
1380 {
1381 char *c;
1382 mbentry_t *mbentry = NULL;
1383
1384 mbentry = mboxlist_entry_create();
1385 mbentry->name = xstrdupnull(mb->mailbox);
1386
1387 mbentry->server = xstrdupnull(mb->location);
1388
1389 c = strchr(mbentry->server, '!');
1390 if (c) {
1391 *c++ = '\0';
1392 mbentry->partition = xstrdupnull(c);
1393 }
1394
1395 mbentry->acl = xstrdupnull(mb->acl);
1396
1397 switch (mb->t) {
1398 case SET_ACTIVE:
1399 mbentry->mbtype = 0;
1400 mboxlist_insertremote(mbentry, mytid);
1401 break;
1402
1403 case SET_RESERVE:
1404 mbentry->mbtype = MBTYPE_RESERVE;
1405 mboxlist_insertremote(mbentry, mytid);
1406 break;
1407
1408 case SET_DELETE:
1409 mboxlist_deleteremote(mb->mailbox, mytid);
1410 break;
1411
1412 case SET_DEACTIVATE:
1413 /* SET_DEACTIVATE is not a real value that an actual
1414 mailbox can have! */
1415 abort();
1416 }
1417
1418 mboxlist_entry_free(&mbentry);
1419 }
1420
1421 /* lookup in database. database must be locked */
1422 /* This could probably be more efficient and avoid some copies */
1423 /* passing in a NULL pool implies that we should use regular xmalloc,
1424 * a non-null pool implies we should use the mpool functionality */
database_lookup(const char * name,const mbentry_t * mbentry,struct mpool * pool)1425 static struct mbent *database_lookup(const char *name, const mbentry_t *mbentry,
1426 struct mpool *pool)
1427 {
1428 mbentry_t *my_mbentry = NULL;
1429 struct mbent *out;
1430 char *location = NULL;
1431 int r;
1432
1433 if (!name) return NULL;
1434
1435 if (!mbentry) {
1436 r = mboxlist_lookup_allow_all(name, &my_mbentry, NULL);
1437 if (r) return NULL;
1438 mbentry = my_mbentry;
1439 }
1440
1441 /* XXX - if mbtype & MBTYPE_DELETED, maybe set a delete */
1442
1443 if (mbentry->mbtype & MBTYPE_RESERVE) {
1444 if (!pool) out = xmalloc(sizeof(struct mbent) + 1);
1445 else out = mpool_malloc(pool, sizeof(struct mbent) + 1);
1446 out->t = SET_RESERVE;
1447 out->acl[0] = '\0';
1448 }
1449 else {
1450 if (!pool) out = xmalloc(sizeof(struct mbent) + strlen(mbentry->acl));
1451 else out = mpool_malloc(pool, sizeof(struct mbent) + strlen(mbentry->acl));
1452 out->t = SET_ACTIVE;
1453 strcpy(out->acl, mbentry->acl);
1454 }
1455
1456 if (mbentry->server && mbentry->partition)
1457 location = strconcat(mbentry->server, "!", mbentry->partition, NULL);
1458 else if (mbentry->server)
1459 location = xstrdup(mbentry->server);
1460 else if (mbentry->partition)
1461 location = xstrdup(mbentry->partition);
1462 else
1463 location = xstrdup("");
1464
1465 out->mailbox = (pool) ? mpool_strdup(pool, name) : xstrdup(name);
1466 out->location = (pool) ? mpool_strdup(pool, location)
1467 : xstrdup(location);
1468
1469 free(location);
1470 if (my_mbentry) mboxlist_entry_free(&my_mbentry);
1471
1472 return out;
1473 }
1474
cmd_authenticate(struct conn * C,const char * tag,const char * mech,const char * clientstart)1475 static void cmd_authenticate(struct conn *C,
1476 const char *tag, const char *mech,
1477 const char *clientstart)
1478 {
1479 int r, sasl_result;
1480 const void *val;
1481 int failedloginpause;
1482
1483 r = saslserver(C->saslconn, mech, clientstart, "", "", "",
1484 C->pin, C->pout, &sasl_result, NULL);
1485
1486 if (r) {
1487 const char *errorstring = NULL;
1488 const char *userid = "-notset-";
1489
1490 switch (r) {
1491 case IMAP_SASL_CANCEL:
1492 prot_printf(C->pout,
1493 "%s NO Client canceled authentication\r\n", tag);
1494 break;
1495 case IMAP_SASL_PROTERR:
1496 errorstring = prot_error(C->pin);
1497
1498 prot_printf(C->pout,
1499 "%s NO Error reading client response: %s\r\n",
1500 tag, errorstring ? errorstring : "");
1501 break;
1502 default:
1503 failedloginpause = config_getduration(IMAPOPT_FAILEDLOGINPAUSE, 's');
1504 if (failedloginpause != 0) {
1505 sleep(failedloginpause);
1506 }
1507
1508 if (r != SASL_NOUSER)
1509 sasl_getprop(C->saslconn, SASL_USERNAME, (const void **) &userid);
1510
1511 syslog(LOG_ERR, "badlogin: %s %s (%s) [%s]",
1512 C->clienthost,
1513 mech, userid, sasl_errdetail(C->saslconn));
1514
1515 prot_printf(C->pout, "%s NO \"%s\"\r\n", tag,
1516 sasl_errstring((r == SASL_NOUSER ? SASL_BADAUTH : r),
1517 NULL, NULL));
1518 }
1519
1520 reset_saslconn(C);
1521 return;
1522 }
1523
1524 /* Successful Authentication */
1525 r = sasl_getprop(C->saslconn, SASL_USERNAME, &val);
1526 if (r != SASL_OK) {
1527 prot_printf(C->pout, "%s NO \"SASL Error\"\r\n", tag);
1528 reset_saslconn(C);
1529 return;
1530 }
1531
1532 C->userid = (char *) val;
1533 syslog(LOG_NOTICE, "login: %s %s %s%s %s", C->clienthost, C->userid,
1534 mech, C->tlsconn ? "+TLS" : "", "User logged in");
1535
1536 prot_printf(C->pout, "%s OK \"Authenticated\"\r\n", tag);
1537
1538 prot_setsasl(C->pin, C->saslconn);
1539 prot_setsasl(C->pout, C->saslconn);
1540
1541 C->logfd = telemetry_log(C->userid, C->pin, C->pout, 1);
1542
1543 return;
1544 }
1545
1546 /* Log the update out to anyone who is in our updatelist */
1547 /* INVARIANT: caller MUST hold mailboxes_mutex */
1548 /* oldlocation is the previous value of the location in this update,
1549 thislocation is the current value of the mailbox's location */
log_update(const char * mailbox,const char * oldlocation,const char * thislocation)1550 static void log_update(const char *mailbox,
1551 const char *oldlocation,
1552 const char *thislocation)
1553 {
1554 struct conn *upc;
1555
1556 for (upc = updatelist; upc != NULL; upc = upc->updatelist_next) {
1557 /* for each connection, add to pending list */
1558 struct pending *p = (struct pending *) xmalloc(sizeof(struct pending));
1559 p->next = NULL;
1560 strlcpy(p->mailbox, mailbox, sizeof(p->mailbox));
1561
1562 /* this might need to be inside the mutex, but I doubt it */
1563 if (upc->streaming_hosts
1564 && (!oldlocation || strarray_find(upc->streaming_hosts,
1565 oldlocation, 0) < 0)
1566 && (!thislocation || strarray_find(upc->streaming_hosts,
1567 thislocation, 0) < 0)) {
1568 /* No Match! Continue! */
1569 continue;
1570 }
1571
1572 pthread_mutex_lock(&upc->m);
1573
1574 if ( upc->plist == NULL ) {
1575 upc->plist = upc->ptail = p;
1576 } else {
1577 upc->ptail->next = p;
1578 upc->ptail = p;
1579 }
1580
1581 pthread_mutex_unlock(&upc->m);
1582 }
1583 }
1584
cmd_set(struct conn * C,const char * tag,const char * mailbox,const char * location,const char * acl,enum settype t)1585 static void cmd_set(struct conn *C,
1586 const char *tag, const char *mailbox,
1587 const char *location, const char *acl, enum settype t)
1588 {
1589 struct mbent *m;
1590 char *oldlocation = NULL;
1591 char *thislocation = NULL;
1592 char *tmp;
1593
1594 /* Hold any output that we need to do */
1595 enum {
1596 EXISTS, NOTACTIVE, DOESNTEXIST, ISOK, NOOUTPUT
1597 } msg = NOOUTPUT;
1598
1599 syslog(LOG_DEBUG, "cmd_set(fd:%d, %s)", C->fd, mailbox);
1600
1601 pthread_mutex_lock(&mailboxes_mutex); /* LOCK */
1602
1603 m = database_lookup(mailbox, NULL, NULL);
1604 if (m && t == SET_RESERVE) {
1605 /* Check if we run in a discrete murder topology */
1606 if (config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_STANDARD) {
1607 /* Replicated backends with the same server name issue
1608 * reservations twice. Suppress bailing out on the second one
1609 * (the replica).
1610 */
1611 if (strcmp(m->location, location)) {
1612 /* failed; mailbox already exists */
1613 msg = EXISTS;
1614 goto done;
1615 }
1616 }
1617 /* otherwise do nothing (local create on master) */
1618 }
1619
1620 if ((!m || m->t != SET_ACTIVE) && t == SET_DEACTIVATE) {
1621 /* Check if we run in a discrete murder topology */
1622 if (config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_STANDARD) {
1623 /* Replicated backends with the same server name issue
1624 * deactivation twice. Suppress bailing out on the second one
1625 * (the replica).
1626 */
1627 if (strcmp(m->location, location)) {
1628 /* failed; mailbox not currently active */
1629 msg = NOTACTIVE;
1630 goto done;
1631 }
1632 }
1633 } else if (t == SET_DEACTIVATE) {
1634 t = SET_RESERVE;
1635 }
1636
1637 if (t == SET_DELETE) {
1638 if (!m) {
1639 /* Check if we run in a discrete murder topology */
1640 if (config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_STANDARD) {
1641 /* Replicated backends with the same server name issue
1642 * deletion twice. Suppress bailing out on the second one
1643 * (the replica).
1644 */
1645 if (strcmp(m->location, location)) {
1646 /* failed; mailbox doesn't exist */
1647 msg = DOESNTEXIST;
1648 goto done;
1649 }
1650 }
1651 /* otherwise do nothing (local delete on master) */
1652 } else {
1653 oldlocation = xstrdup(m->location);
1654
1655 /* do the deletion */
1656 m->t = SET_DELETE;
1657 }
1658 } else {
1659 if (m)
1660 oldlocation = m->location;
1661
1662 if (m && (!acl || strlen(acl) < strlen(m->acl))) {
1663 /* change what's already there -- the acl is smaller */
1664 m->location = xstrdup(location);
1665 if (acl) strcpy(m->acl, acl);
1666 else m->acl[0] = '\0';
1667
1668 m->t = t;
1669 } else {
1670 struct mbent *newm;
1671
1672 /* allocate new mailbox */
1673 if (acl) {
1674 newm = xrealloc(m, sizeof(struct mbent) + strlen(acl));
1675 } else {
1676 newm = xrealloc(m, sizeof(struct mbent) + 1);
1677 }
1678 newm->mailbox = xstrdup(mailbox);
1679 newm->location = xstrdup(location);
1680
1681 if (acl) {
1682 strcpy(newm->acl, acl);
1683 } else {
1684 newm->acl[0] = '\0';
1685 }
1686
1687 newm->t = t;
1688
1689 /* re-scope */
1690 m = newm;
1691 }
1692 }
1693
1694 /* write to disk */
1695 if (m) database_log(m, NULL);
1696
1697 if (oldlocation) {
1698 tmp = strchr(oldlocation, '!');
1699 if (tmp) *tmp = '\0';
1700 }
1701
1702 if (location) {
1703 thislocation = xstrdup(location);
1704 tmp = strchr(thislocation, '!');
1705 if (tmp) *tmp = '\0';
1706 }
1707
1708 /* post pending changes */
1709 log_update(mailbox, oldlocation, thislocation);
1710
1711 msg = ISOK;
1712 done:
1713 if (thislocation) free(thislocation);
1714 if (oldlocation) free(oldlocation);
1715 free_mbent(m);
1716 pthread_mutex_unlock(&mailboxes_mutex); /* UNLOCK */
1717
1718 /* Delay output until here to avoid blocking while holding
1719 * mailboxes_mutex */
1720 switch(msg) {
1721 case EXISTS:
1722 prot_printf(C->pout, "%s NO \"mailbox already exists\"\r\n", tag);
1723 break;
1724 case NOTACTIVE:
1725 prot_printf(C->pout, "%s NO \"mailbox not currently active\"\r\n",
1726 tag);
1727 break;
1728 case DOESNTEXIST:
1729 prot_printf(C->pout, "%s NO \"mailbox doesn't exist\"\r\n", tag);
1730 break;
1731 case ISOK:
1732 prot_printf(C->pout, "%s OK \"done\"\r\n", tag);
1733 break;
1734 default:
1735 break;
1736 }
1737 }
1738
cmd_find(struct conn * C,const char * tag,const char * mailbox,int send_ok,int send_delete)1739 static void cmd_find(struct conn *C, const char *tag, const char *mailbox,
1740 int send_ok, int send_delete)
1741 {
1742 struct mbent *m;
1743
1744 syslog(LOG_DEBUG, "cmd_find(fd:%d, %s)", C->fd, mailbox);
1745
1746 /* Only hold the mutex around database_lookup,
1747 * since the mbent stays valid even if the database changes,
1748 * and we don't want to block on network I/O */
1749 pthread_mutex_lock(&mailboxes_mutex); /* LOCK */
1750 m = database_lookup(mailbox, NULL, NULL);
1751 pthread_mutex_unlock(&mailboxes_mutex); /* UNLOCK */
1752
1753 if (m && m->t == SET_ACTIVE) {
1754 prot_printf(C->pout,
1755 "%s MAILBOX "
1756 "{" SIZE_T_FMT "+}\r\n%s "
1757 "{" SIZE_T_FMT "+}\r\n%s "
1758 "{" SIZE_T_FMT "+}\r\n%s\r\n",
1759 tag,
1760 strlen(m->mailbox), m->mailbox,
1761 strlen(m->location), m->location,
1762 strlen(m->acl), m->acl
1763 );
1764
1765 } else if (m && m->t == SET_RESERVE) {
1766 prot_printf(C->pout,
1767 "%s RESERVE "
1768 "{" SIZE_T_FMT "+}\r\n%s "
1769 "{" SIZE_T_FMT "+}\r\n%s\r\n",
1770 tag,
1771 strlen(m->mailbox), m->mailbox,
1772 strlen(m->location), m->location
1773 );
1774 } else if (send_delete) {
1775 /* not found, if needed, send a delete */
1776 prot_printf(C->pout,
1777 "%s DELETE "
1778 "{" SIZE_T_FMT "+}\r\n%s\r\n",
1779 tag,
1780 strlen(mailbox), mailbox
1781 );
1782 }
1783
1784 free_mbent(m);
1785
1786 if (send_ok) {
1787 prot_printf(C->pout, "%s OK \"Search completed\"\r\n", tag);
1788 }
1789 }
1790
1791 /* Callback for cmd_startupdate to be passed to mboxlist_allmbox. */
1792 /* Requires that C->streaming be set to the tag to respond with */
sendupdate(const mbentry_t * mbentry,void * rock)1793 static int sendupdate(const mbentry_t *mbentry, void *rock)
1794 {
1795 struct conn *C = (struct conn *)rock;
1796 struct mbent *m;
1797
1798 if (!C) return -1;
1799
1800 m = database_lookup(mbentry->name, mbentry, NULL);
1801 if (!m) return -1;
1802
1803 if (!C->list_prefix ||
1804 !strncmp(m->location, C->list_prefix, C->list_prefix_len)) {
1805 /* Either there is not a prefix to test, or we matched it */
1806
1807 if (!C->streaming_hosts ||
1808 strarray_find(C->streaming_hosts, mbentry->server, 0) >= 0) {
1809 switch (m->t) {
1810 case SET_ACTIVE:
1811 prot_printf(C->pout,
1812 "%s MAILBOX "
1813 "{" SIZE_T_FMT "+}\r\n%s "
1814 "{" SIZE_T_FMT "+}\r\n%s "
1815 "{" SIZE_T_FMT "+}\r\n%s\r\n",
1816 C->streaming,
1817 strlen(m->mailbox), m->mailbox,
1818 strlen(m->location), m->location,
1819 strlen(m->acl), m->acl
1820 );
1821
1822 break;
1823 case SET_RESERVE:
1824 prot_printf(C->pout,
1825 "%s RESERVE "
1826 "{" SIZE_T_FMT "+}\r\n%s "
1827 "{" SIZE_T_FMT "+}\r\n%s\r\n",
1828 C->streaming,
1829 strlen(m->mailbox), m->mailbox,
1830 strlen(m->location), m->location
1831 );
1832
1833 break;
1834
1835 case SET_DELETE:
1836 /* deleted item in the list !?! */
1837 case SET_DEACTIVATE:
1838 /* SET_DEACTIVATE is not a real value! */
1839 abort();
1840 }
1841 }
1842 }
1843
1844 free_mbent(m);
1845 return 0;
1846 }
1847
cmd_list(struct conn * C,const char * tag,const char * host_prefix)1848 static void cmd_list(struct conn *C, const char *tag, const char *host_prefix)
1849 {
1850 /* List operations can result in a lot of output, let's do this
1851 * with the prot layer nonblocking so we don't hold the mutex forever*/
1852 prot_NONBLOCK(C->pout);
1853
1854 /* indicate interest in updates */
1855 pthread_mutex_lock(&mailboxes_mutex); /* LOCK */
1856
1857 /* since this isn't valid when streaming, just use the same callback */
1858 C->streaming = tag;
1859 C->list_prefix = host_prefix;
1860 if (C->list_prefix) C->list_prefix_len = strlen(C->list_prefix);
1861 else C->list_prefix_len = 0;
1862
1863 mboxlist_allmbox("", sendupdate, (void*)C, /*flags*/0);
1864
1865 C->streaming = NULL;
1866 C->list_prefix = NULL;
1867 C->list_prefix_len = 0;
1868
1869 pthread_mutex_unlock(&mailboxes_mutex); /* UNLOCK */
1870
1871 prot_BLOCK(C->pout);
1872 prot_flush(C->pout);
1873 }
1874
1875
1876 /*
1877 * we've registered this connection for streaming, and every X seconds
1878 * this will be invoked. note that we always send out updates as soon
1879 * as we get a noop: that resets this counter back */
sendupdates_evt(struct protstream * s,struct prot_waitevent * ev,void * rock)1880 static struct prot_waitevent *sendupdates_evt(struct protstream *s __attribute__((unused)),
1881 struct prot_waitevent *ev,
1882 void *rock)
1883 {
1884 struct conn *C = (struct conn *) rock;
1885
1886 sendupdates(C, 1);
1887
1888 /* 'sendupdates()' will update when we next trigger */
1889 return ev;
1890 }
1891
cmd_startupdate(struct conn * C,const char * tag,strarray_t * partial)1892 static void cmd_startupdate(struct conn *C, const char *tag,
1893 strarray_t *partial)
1894 {
1895 /* initialize my condition variable */
1896
1897 /* The inital dump of the database can result in a lot of data,
1898 * let's do this nonblocking */
1899 prot_NONBLOCK(C->pout);
1900
1901 /* indicate interest in updates */
1902 pthread_mutex_lock(&mailboxes_mutex); /* LOCK */
1903
1904 C->updatelist_next = updatelist;
1905 updatelist = C;
1906 C->streaming = xstrdup(tag);
1907 C->streaming_hosts = partial;
1908
1909 /* dump initial list */
1910 mboxlist_allmbox("", sendupdate, (void*)C, /*flags*/0);
1911
1912 pthread_mutex_unlock(&mailboxes_mutex); /* UNLOCK */
1913
1914 prot_printf(C->pout, "%s OK \"streaming starts\"\r\n", tag);
1915
1916 prot_BLOCK(C->pout);
1917 prot_flush(C->pout);
1918
1919 /* schedule our first update */
1920 C->ev = prot_addwaitevent(C->pin, time(NULL) + update_wait,
1921 sendupdates_evt, C);
1922 }
1923
1924 /* send out any pending updates.
1925 if 'flushnow' is set, flush the output buffer */
sendupdates(struct conn * C,int flushnow)1926 static void sendupdates(struct conn *C, int flushnow)
1927 {
1928 struct pending *p, *q;
1929
1930 pthread_mutex_lock(&C->m);
1931
1932 /* just grab the update list and release the lock */
1933 p = C->plist;
1934 C->plist = NULL;
1935 C->ptail = NULL;
1936 pthread_mutex_unlock(&C->m);
1937
1938 while (p != NULL) {
1939 /* send update */
1940 q = p;
1941 p = p->next;
1942
1943 /* notify just like a FIND - except enable sending of DELETE
1944 * notifications */
1945 cmd_find(C, C->streaming, q->mailbox, 0, 1);
1946
1947 free(q);
1948 }
1949
1950 /* reschedule event for 'update_wait' seconds */
1951 C->ev->mark = time(NULL) + update_wait;
1952
1953 if (flushnow) {
1954 prot_flush(C->pout);
1955 }
1956 }
1957
1958 #ifdef HAVE_SSL
cmd_starttls(struct conn * C,const char * tag)1959 static void cmd_starttls(struct conn *C, const char *tag)
1960 {
1961 int result;
1962
1963 result=tls_init_serverengine("mupdate",
1964 5, /* depth to verify */
1965 1, /* can client auth? */
1966 NULL);
1967
1968 if (result == -1) {
1969
1970 syslog(LOG_ERR, "error initializing TLS");
1971
1972 prot_printf(C->pout, "%s NO Error initializing TLS\r\n", tag);
1973
1974 return;
1975 }
1976
1977 prot_printf(C->pout, "%s OK Begin TLS negotiation now\r\n", tag);
1978 /* must flush our buffers before starting tls */
1979 prot_flush(C->pout);
1980
1981 result=tls_start_servertls(C->pin->fd, /* read */
1982 C->pout->fd, /* write */
1983 180, /* 3 minutes */
1984 &C->saslprops,
1985 &C->tlsconn);
1986
1987 /* if error */
1988 if (result==-1) {
1989 prot_printf(C->pout, "%s NO Starttls negotiation failed\r\n",
1990 tag);
1991 syslog(LOG_NOTICE, "STARTTLS negotiation failed: %s",
1992 C->clienthost);
1993 return;
1994 }
1995
1996 /* tell SASL about the negotiated layer */
1997 result = saslprops_set_tls(&C->saslprops, C->saslconn);
1998 if (result != SASL_OK) {
1999 fatal("saslprops_set_tls() failed: cmd_starttls()", EX_TEMPFAIL);
2000 }
2001
2002 /* tell the prot layer about our new layers */
2003 prot_settls(C->pin, C->tlsconn);
2004 prot_settls(C->pout, C->tlsconn);
2005
2006 #if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
2007 C->tls_comp = (void *) SSL_get_current_compression(C->tlsconn);
2008 #endif
2009
2010 /* Reissue capability banner */
2011 dobanner(C);
2012 }
2013 #else
cmd_starttls(struct conn * C,const char * tag)2014 void cmd_starttls(struct conn *C __attribute__((unused)),
2015 const char *tag __attribute__((unused)))
2016 {
2017 fatal("cmd_starttls() executed, but starttls isn't implemented!",
2018 EX_SOFTWARE);
2019 }
2020 #endif /* HAVE_SSL */
2021
2022 #ifdef HAVE_ZLIB
cmd_compress(struct conn * C,const char * tag,const char * alg)2023 static void cmd_compress(struct conn *C, const char *tag, const char *alg)
2024 {
2025 if (C->compress_done) {
2026 prot_printf(C->pout,
2027 "%s BAD DEFLATE active via COMPRESS\r\n", tag);
2028 }
2029 #if defined(HAVE_SSL) && (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
2030 else if (C->tls_comp) {
2031 prot_printf(C->pout,
2032 "%s NO %s active via TLS\r\n",
2033 tag, SSL_COMP_get_name(C->tls_comp));
2034 }
2035 #endif
2036 else if (strcasecmp(alg, "DEFLATE")) {
2037 prot_printf(C->pout,
2038 "%s NO Unknown COMPRESS algorithm: %s\r\n", tag, alg);
2039 }
2040 else if (ZLIB_VERSION[0] != zlibVersion()[0]) {
2041 prot_printf(C->pout,
2042 "%s NO Error initializing %s (incompatible zlib version)\r\n",
2043 tag, alg);
2044 }
2045 else {
2046 prot_printf(C->pout,
2047 "%s OK %s active\r\n", tag, alg);
2048
2049 /* enable (de)compression for the prot layer */
2050 prot_setcompress(C->pin);
2051 prot_setcompress(C->pout);
2052
2053 C->compress_done = 1;
2054 }
2055 }
2056 #else
cmd_compress(struct conn * C,const char * tag,const char * alg)2057 void cmd_compress(struct conn *C __attribute__((unused)),
2058 const char *tag __attribute__((unused)),
2059 const char *alg __attribute__((unused)))
2060 {
2061 fatal("cmd_compress() executed, but COMPRESS isn't implemented!",
2062 EX_SOFTWARE);
2063 }
2064 #endif /* HAVE_ZLIB */
2065
2066 void shut_down(int code) __attribute__((noreturn));
shut_down(int code)2067 void shut_down(int code)
2068 {
2069 in_shutdown = 1;
2070
2071 cyrus_done();
2072
2073 exit(code);
2074 }
2075
2076 /* Reset the given sasl_conn_t to a sane state */
reset_saslconn(struct conn * c)2077 static int reset_saslconn(struct conn *c)
2078 {
2079 int ret;
2080 sasl_security_properties_t *secprops = NULL;
2081
2082 sasl_dispose(&c->saslconn);
2083 /* do initialization typical of service_main */
2084 ret = sasl_server_new("mupdate", config_servername, NULL,
2085 buf_cstringnull_ifempty(&c->saslprops.iplocalport),
2086 buf_cstringnull_ifempty(&c->saslprops.ipremoteport),
2087 NULL, 0, &c->saslconn);
2088 if (ret != SASL_OK) return ret;
2089
2090 secprops = mysasl_secprops(SASL_SEC_NOANONYMOUS);
2091 ret = sasl_setprop(c->saslconn, SASL_SEC_PROPS, secprops);
2092 if (ret != SASL_OK) return ret;
2093 /* end of service_main initialization excepting SSF */
2094
2095 /* If we have TLS/SSL info, set it */
2096 if (c->saslprops.ssf) {
2097 ret = saslprops_set_tls(&c->saslprops, c->saslconn);
2098 }
2099 if (ret != SASL_OK) return ret;
2100 /* End TLS/SSL Info */
2101
2102 return SASL_OK;
2103 }
2104
cmd_change(struct mupdate_mailboxdata * mdata,const char * rock,void * context)2105 int cmd_change(struct mupdate_mailboxdata *mdata,
2106 const char *rock, void *context __attribute__((unused)))
2107 {
2108 struct mbent *m = NULL;
2109 char *oldlocation = NULL;
2110 char *thislocation = NULL;
2111 char *tmp;
2112 enum settype t = -1;
2113 int ret = 0;
2114
2115 if (!mdata || !rock || !mdata->mailbox) return 1;
2116
2117 pthread_mutex_lock(&mailboxes_mutex); /* LOCK */
2118
2119 if (!strncmp(rock, "DELETE", 6)) {
2120 m = database_lookup(mdata->mailbox, NULL, NULL);
2121
2122 if (!m) {
2123 syslog(LOG_DEBUG, "attempt to delete unknown mailbox %s",
2124 mdata->mailbox);
2125 /* Mailbox doesn't exist - this isn't as fatal as you might
2126 * think. */
2127 /* ret = -1; */
2128 goto done;
2129 }
2130 m->t = t = SET_DELETE;
2131
2132 oldlocation = xstrdup(m->location);
2133 } else {
2134 m = database_lookup(mdata->mailbox, NULL, NULL);
2135
2136 if (m)
2137 oldlocation = m->location;
2138
2139 if (m && (!mdata->acl || strlen(mdata->acl) < strlen(m->acl))) {
2140 /* change what's already there */
2141 /* old m->location freed when oldlocation is freed */
2142 m->location = xstrdup(mdata->location);
2143
2144 if (mdata->acl) strcpy(m->acl, mdata->acl);
2145 else m->acl[0] = '\0';
2146
2147 if (!strncmp(rock, "MAILBOX", 6)) {
2148 m->t = t = SET_ACTIVE;
2149 } else if (!strncmp(rock, "RESERVE", 7)) {
2150 m->t = t = SET_RESERVE;
2151 } else {
2152 syslog(LOG_DEBUG,
2153 "bad mupdate command in cmd_change: %s", rock);
2154 ret = 1;
2155 goto done;
2156 }
2157 } else {
2158 struct mbent *newm;
2159
2160 if (m) {
2161 free(m->mailbox);
2162 /* m->location freed when oldlocation freed */
2163 }
2164
2165 /* allocate new mailbox */
2166 if (mdata->acl) {
2167 newm = xrealloc(m, sizeof(struct mbent) + strlen(mdata->acl));
2168 } else {
2169 newm = xrealloc(m, sizeof(struct mbent) + 1);
2170 }
2171
2172 newm->mailbox = xstrdup(mdata->mailbox);
2173 newm->location = xstrdup(mdata->location);
2174
2175 if (mdata->acl) {
2176 strcpy(newm->acl, mdata->acl);
2177 } else {
2178 newm->acl[0] = '\0';
2179 }
2180
2181 if (!strncmp(rock, "MAILBOX", 6)) {
2182 newm->t = t = SET_ACTIVE;
2183 } else if (!strncmp(rock, "RESERVE", 7)) {
2184 newm->t = t = SET_RESERVE;
2185 } else {
2186 syslog(LOG_DEBUG,
2187 "bad mupdate command in cmd_change: %s", rock);
2188 ret = 1;
2189 goto done;
2190 }
2191
2192 /* Bring it back into scope */
2193 m = newm;
2194 }
2195 }
2196
2197 /* write to disk */
2198 database_log(m, NULL);
2199
2200 if (oldlocation) {
2201 tmp = strchr(oldlocation, '!');
2202 if (tmp) *tmp = '\0';
2203 }
2204
2205 if (mdata->location) {
2206 thislocation = xstrdup(mdata->location);
2207 tmp = strchr(thislocation, '!');
2208 if (tmp) *tmp = '\0';
2209 }
2210
2211 /* post pending changes to anyone we are talking to */
2212 log_update(mdata->mailbox, oldlocation, thislocation);
2213
2214 done:
2215 if (oldlocation) free(oldlocation);
2216 if (thislocation) free(thislocation);
2217
2218 free_mbent(m);
2219 pthread_mutex_unlock(&mailboxes_mutex); /* UNLOCK */
2220
2221 return ret;
2222 }
2223
2224 struct sync_rock
2225 {
2226 struct mpool *pool;
2227 struct mbent_queue *boxes;
2228 };
2229
2230 /* Read a series of MAILBOX and RESERVE commands and tack them onto a
2231 * queue */
cmd_resync(struct mupdate_mailboxdata * mdata,const char * rock,void * context)2232 static int cmd_resync(struct mupdate_mailboxdata *mdata,
2233 const char *rock, void *context)
2234 {
2235 struct sync_rock *r = (struct sync_rock *)context;
2236 struct mbent_queue *remote_boxes = r->boxes;
2237 struct mbent *newm = NULL;
2238
2239 if (!mdata || !rock || !mdata->mailbox || !remote_boxes) return 1;
2240
2241 /* allocate new mailbox */
2242 if (mdata->acl) {
2243 newm = mpool_malloc(r->pool,sizeof(struct mbent) + strlen(mdata->acl));
2244 } else {
2245 newm = mpool_malloc(r->pool,sizeof(struct mbent) + 1);
2246 }
2247
2248 newm->mailbox = mpool_strdup(r->pool, mdata->mailbox);
2249 newm->location = mpool_strdup(r->pool, mdata->location);
2250
2251 if (mdata->acl) {
2252 strcpy(newm->acl, mdata->acl);
2253 } else {
2254 newm->acl[0] = '\0';
2255 }
2256
2257 if (!strncmp(rock, "MAILBOX", 6)) {
2258 newm->t = SET_ACTIVE;
2259 } else if (!strncmp(rock, "RESERVE", 7)) {
2260 newm->t = SET_RESERVE;
2261 } else {
2262 syslog(LOG_NOTICE,
2263 "bad mupdate command in cmd_resync: %s", rock);
2264 return 1;
2265 }
2266
2267 /* Insert onto queue */
2268 newm->next = NULL;
2269 *(remote_boxes->tail) = newm;
2270 remote_boxes->tail = &(newm->next);
2271
2272 return 0;
2273 }
2274
2275 /* Callback for mupdate_synchronize to be passed to mboxlist_allmbox. */
sync_findall_cb(const mbentry_t * mbentry,void * rock)2276 static int sync_findall_cb(const mbentry_t *mbentry, void *rock)
2277 {
2278 struct sync_rock *r = (struct sync_rock *)rock;
2279 struct mbent_queue *local_boxes = (struct mbent_queue *)r->boxes;
2280 struct mbent *m;
2281
2282 if (!local_boxes) return 1;
2283
2284 m = database_lookup(mbentry->name, mbentry, r->pool);
2285 /* If it doesn't exist, fine... */
2286 if (!m) return 0;
2287
2288 m->next = NULL;
2289 *(local_boxes->tail) = m;
2290 local_boxes->tail = &(m->next);
2291
2292 return 0;
2293 }
2294
mupdate_synchronize_remote(mupdate_handle * handle,struct mbent_queue * remote_boxes,struct mpool * pool)2295 int mupdate_synchronize_remote(mupdate_handle *handle,
2296 struct mbent_queue *remote_boxes,
2297 struct mpool *pool)
2298 {
2299 struct sync_rock rock;
2300
2301 if (!handle || !handle->saslcompleted) return 1;
2302
2303 rock.pool = pool;
2304
2305 /* ask mupdate master for updates and set nonblocking */
2306 prot_printf(handle->conn->out, "U01 UPDATE\r\n");
2307
2308 syslog(LOG_NOTICE,
2309 "scarfing mailbox list from master mupdate server");
2310
2311 remote_boxes->head = NULL;
2312 remote_boxes->tail = &(remote_boxes->head);
2313
2314 rock.boxes = remote_boxes;
2315
2316 /* If there is a fatal error, return, other errors ignore */
2317 if (mupdate_scarf(handle, cmd_resync, &rock, 1, NULL) != 0) {
2318 struct mbent *p=remote_boxes->head, *p_next=NULL;
2319 while(p) {
2320 p_next = p->next;
2321 p = p_next;
2322 }
2323 return 1;
2324 }
2325
2326 /* Make socket nonblocking now */
2327 prot_NONBLOCK(handle->conn->in);
2328
2329 return 0;
2330 }
2331
mupdate_synchronize(struct mbent_queue * remote_boxes,struct mpool * pool)2332 int mupdate_synchronize(struct mbent_queue *remote_boxes, struct mpool *pool)
2333 {
2334 struct mbent_queue local_boxes;
2335 struct mbent *l,*r;
2336 struct sync_rock rock;
2337 struct txn *tid = NULL;
2338 int ret = 0;
2339 int err = 0;
2340 char *c;
2341
2342 rock.pool = pool;
2343
2344 /* Note that this prevents other people from running an UPDATE against
2345 * us for the duration. this is a GOOD THING */
2346 pthread_mutex_lock(&mailboxes_mutex); /* LOCK */
2347
2348 syslog(LOG_NOTICE,
2349 "synchronizing mailbox list with master mupdate server");
2350
2351 local_boxes.head = NULL;
2352 local_boxes.tail = &(local_boxes.head);
2353
2354 rock.boxes = &local_boxes;
2355
2356 mboxlist_allmbox("", sync_findall_cb, (void*)&rock, /*flags*/0);
2357
2358 /* Traverse both lists, compare the names */
2359 /* If they match, ensure that location and acl are correct, if so,
2360 move on, if not, fix them */
2361 /* If the local is before the next remote, delete it */
2362 /* If the next remote is before the local, insert it and try again */
2363 for(l = local_boxes.head, r = remote_boxes->head; l && r;
2364 l = local_boxes.head, r = remote_boxes->head)
2365 {
2366 int ret = strcmp(l->mailbox, r->mailbox);
2367 if (!ret) {
2368 /* Match */
2369 if (l->t != r->t ||
2370 strcmp(l->location, r->location) ||
2371 strcmp(l->acl,r->acl)) {
2372 /* Something didn't match, replace it */
2373 /*
2374 * If this is a locally hosted mailbox, don't make a
2375 * change, just warn.
2376 */
2377 if ((config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_UNIFIED) &&
2378 (strchr( l->location, '!' ) == NULL )) {
2379 syslog(LOG_ERR, "local mailbox %s wrong in mailbox list",
2380 l->mailbox );
2381 err++;
2382 } else {
2383 mbentry_t *mbentry = mboxlist_entry_create();
2384 mbentry->name = xstrdupnull(r->mailbox);
2385 mbentry->mbtype = (r->t == SET_RESERVE ? MBTYPE_RESERVE : 0);
2386 mbentry->server = xstrdupnull(r->location);
2387
2388 c = strchr(mbentry->server, '!');
2389 if (c) {
2390 *c++ = '\0';
2391 mbentry->partition = xstrdupnull(c);
2392 }
2393
2394 mbentry->acl = xstrdupnull(r->acl);
2395 mboxlist_insertremote(mbentry, &tid);
2396 mboxlist_entry_free(&mbentry);
2397 }
2398 }
2399 /* Okay, dump these two */
2400 local_boxes.head = l->next;
2401 remote_boxes->head = r->next;
2402 } else if (ret < 0) {
2403 /* Local without corresponding remote, delete it */
2404 /*
2405 * In a unified murder, we don't want to delete locally
2406 * hosted mailboxes during mupdate's resync process.
2407 * If that sort of operation appears necessary, it
2408 * probably requires an operator to review it --
2409 * ctl_mboxlist is the right place to fix the kind
2410 * of configuration error implied.
2411 *
2412 * A similar problem exists when the location thinks
2413 * it is locally hosting a mailbox, but mupdate master
2414 * thinks it's somewhere else.
2415 */
2416 if ((config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_UNIFIED) &&
2417 (strchr( l->location, '!' ) == NULL )) {
2418 syslog(LOG_ERR, "local mailbox %s not in mailbox list",
2419 l->mailbox );
2420 err++;
2421 } else {
2422 mboxlist_deleteremote(l->mailbox, &tid);
2423 }
2424 local_boxes.head = l->next;
2425 } else /* (ret > 0) */ {
2426 /* Remote without corresponding local, insert it */
2427 mbentry_t *mbentry = mboxlist_entry_create();
2428 mbentry->name = xstrdupnull(r->mailbox);
2429 mbentry->mbtype = (r->t == SET_RESERVE ? MBTYPE_RESERVE : 0);
2430 mbentry->server = xstrdupnull(r->location);
2431
2432 c = strchr(mbentry->server, '!');
2433 if (c) {
2434 *c++ = '\0';
2435 mbentry->partition = xstrdupnull(c);
2436 }
2437
2438 mbentry->acl = xstrdupnull(r->acl);
2439 mboxlist_insertremote(mbentry, &tid);
2440 mboxlist_entry_free(&mbentry);
2441 remote_boxes->head = r->next;
2442 }
2443 }
2444
2445 if (l && !r) {
2446 /* we have more deletes to do */
2447 while(l) {
2448 if ((config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_UNIFIED) &&
2449 (strchr( l->location, '!' ) == NULL )) {
2450 syslog(LOG_ERR, "local mailbox %s not in mailbox list",
2451 l->mailbox );
2452 err++;
2453 } else {
2454 mboxlist_deleteremote(l->mailbox, &tid);
2455 }
2456 local_boxes.head = l->next;
2457 l = local_boxes.head;
2458 }
2459 } else if (r && !l) {
2460 /* we have more inserts to do */
2461 while (r) {
2462 mbentry_t *mbentry = mboxlist_entry_create();
2463 mbentry->name = xstrdupnull(r->mailbox);
2464 mbentry->mbtype = (r->t == SET_RESERVE ? MBTYPE_RESERVE : 0);
2465 mbentry->server = xstrdupnull(r->location);
2466
2467 c = strchr(mbentry->server, '!');
2468 if (c) {
2469 *c++ = '\0';
2470 mbentry->partition = xstrdupnull(c);
2471 }
2472
2473 mbentry->acl = xstrdupnull(r->acl);
2474 mboxlist_insertremote(mbentry, &tid);
2475 mboxlist_entry_free(&mbentry);
2476 remote_boxes->head = r->next;
2477 r = remote_boxes->head;
2478 }
2479 }
2480
2481 if (tid) mboxlist_commit(tid);
2482
2483 /* All up to date! */
2484 if ( err ) {
2485 syslog(LOG_ERR, "mailbox list synchronization NOT complete (%d) errors",
2486 err);
2487 } else {
2488 syslog(LOG_NOTICE, "mailbox list synchronization complete");
2489 }
2490
2491 pthread_mutex_unlock(&mailboxes_mutex); /* UNLOCK */
2492 return ret;
2493 }
2494
mupdate_signal_db_synced(void)2495 void mupdate_signal_db_synced(void)
2496 {
2497 pthread_mutex_lock(&synced_mutex);
2498 synced = 1;
2499 pthread_cond_broadcast(&synced_cond);
2500 pthread_mutex_unlock(&synced_mutex);
2501 }
2502
mupdate_ready(void)2503 void mupdate_ready(void)
2504 {
2505 pthread_mutex_lock(&ready_for_connections_mutex);
2506
2507 if (ready_for_connections) {
2508 syslog(LOG_CRIT, "mupdate_ready called when already ready");
2509 fatal("mupdate_ready called when already ready", EX_TEMPFAIL);
2510 }
2511
2512 ready_for_connections = 1;
2513 pthread_cond_broadcast(&ready_for_connections_cond);
2514 pthread_mutex_unlock(&ready_for_connections_mutex);
2515 }
2516
2517 /* Signal unreadyness. Next active worker will kill off all idle connections.
2518 * any non-idle connection will die off when it leaves docmd() */
mupdate_unready(void)2519 void mupdate_unready(void)
2520 {
2521 pthread_mutex_lock(&ready_for_connections_mutex);
2522
2523 syslog(LOG_NOTICE, "unready for connections");
2524
2525 ready_for_connections = 0;
2526
2527 pthread_mutex_unlock(&ready_for_connections_mutex);
2528 }
2529
2530 /* Used to free malloc'd mbent's (not for mpool'd mbents) */
free_mbent(struct mbent * p)2531 void free_mbent(struct mbent *p)
2532 {
2533 if (!p) return;
2534 free(p->location);
2535 free(p->mailbox);
2536 free(p);
2537 }
2538