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