1 /* lmtpengine.c: LMTP protocol engine
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 #include <signal.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <fcntl.h>
53 #include <sys/stat.h>
54 #include <sysexits.h>
55 #include <syslog.h>
56 #include <errno.h>
57 #include <sys/types.h>
58 #include <limits.h>
59 #include <sys/wait.h>
60 #include <netdb.h>
61 #include <sys/socket.h>
62 #include <sys/un.h>
63 #include <netinet/in.h>
64 #include <arpa/inet.h>
65 
66 #include <sasl/sasl.h>
67 #include <sasl/saslutil.h>
68 
69 #include "assert.h"
70 #include "util.h"
71 #include "auth.h"
72 #include "prot.h"
73 #include "times.h"
74 #include "global.h"
75 #include "prometheus.h"
76 #include "xmalloc.h"
77 #include "xstrlcpy.h"
78 #include "version.h"
79 
80 /* generated headers are not necessarily in current directory */
81 #include "imap/imap_err.h"
82 #include "imap/lmtp_err.h"
83 #include "imap/lmtpstats.h"
84 #include "imap/mupdate_err.h"
85 
86 #include "lmtpengine.h"
87 #include "tls.h"
88 #include "telemetry.h"
89 
90 #define RCPT_GROW 30
91 
92 /* data per message */
93 struct address_data {
94     mbname_t *mbname;
95     int ignorequota;
96     int status;
97     strarray_t *resp;
98 };
99 
100 struct clientdata {
101     struct protstream *pin;
102     struct protstream *pout;
103     int fd;
104 
105     const char *clienthost;
106     char lhlo_param[250];
107 
108     sasl_conn_t *conn;
109 
110     enum {
111         TLSCERT_AUTHED = -2,  /* -2: TLS cert auth'd, but no AUTH issued */
112         EXTERNAL_AUTHED = -1, /* -1: external auth'd, but no AUTH issued */
113         NOAUTH = 0,
114         DIDAUTH = 1
115     } authenticated;
116 
117 #ifdef HAVE_SSL
118     SSL *tls_conn;
119 #endif /* HAVE_SSL */
120     int starttls_done;
121 };
122 
123 /* defined in lmtpd.c */
124 extern int deliver_logfd;
125 
126 extern int saslserver(sasl_conn_t *conn, const char *mech,
127                       const char *init_resp, const char *resp_prefix,
128                       const char *continuation, const char *empty_chal,
129                       struct protstream *pin, struct protstream *pout,
130                       int *sasl_result, char **success_data);
131 
132 static struct saslprops_t saslprops = SASLPROPS_INITIALIZER;
133 
134 
135 #ifdef USING_SNMPGEN
136 /* round to nearest 1024 bytes and return number of Kbytes.
137  used for SNMP updates. */
roundToK(int x)138 static int roundToK(int x)
139 {
140     double rd = (x*1.0)/1024.0;
141     int ri = x/1024;
142 
143     if (rd-ri < 0.5)
144         return ri;
145     else
146         return ri+1;
147 }
148 #else
149 #define roundToK(x)
150 #endif /* USING_SNMPGEN */
151 
send_lmtp_error(struct protstream * pout,int r,strarray_t * resp)152 static void send_lmtp_error(struct protstream *pout, int r, strarray_t *resp)
153 {
154     int code;
155 
156     if (resp) {
157         int i;
158 
159         for (i = 0; i < strarray_size(resp); i++) {
160             prot_puts(pout, strarray_nth(resp, i));
161         }
162         return;
163     }
164 
165     switch (r) {
166     case 0:
167         code = LMTP_OK;
168         break;
169 
170     case IMAP_SERVER_UNAVAILABLE:
171     case MUPDATE_NOCONN:
172     case MUPDATE_NOAUTH:
173     case MUPDATE_TIMEOUT:
174     case MUPDATE_PROTOCOL_ERROR:
175         code = LMTP_SERVER_FAILURE;
176         break;
177 
178     case IMAP_NOSPACE:
179         code = LMTP_SERVER_FULL;
180         break;
181 
182     case IMAP_PERMISSION_DENIED:
183         if (LMTP_LONG_ERROR_MSGS) {
184             prot_printf(pout, error_message(LMTP_NOT_AUTHORIZED_LONG),
185                         config_getstring(IMAPOPT_POSTMASTER));
186         }
187         code = LMTP_NOT_AUTHORIZED;
188         break;
189 
190     case IMAP_QUOTA_EXCEEDED:
191         if(config_getswitch(IMAPOPT_LMTP_OVER_QUOTA_PERM_FAILURE)) {
192             /* Not Default - Perm Failure */
193             code = LMTP_MAILBOX_FULL_PERM;
194         } else {
195             /* Default - Temp Failure */
196             code = LMTP_MAILBOX_FULL;
197         }
198         break;
199 
200     case IMAP_MAILBOX_BADFORMAT:
201     case IMAP_MAILBOX_NOTSUPPORTED:
202         code = LMTP_MAILBOX_ERROR;
203         break;
204 
205     case IMAP_MAILBOX_MOVED:
206     case IMAP_MAILBOX_RESERVED:
207     case IMAP_MAILBOX_DISABLED:
208         code = LMTP_MAILBOX_DISABLED;
209         break;
210 
211     case IMAP_MESSAGE_CONTAINSNULL:
212     case IMAP_MESSAGE_CONTAINSNL:
213     case IMAP_MESSAGE_CONTAINS8BIT:
214     case IMAP_MESSAGE_BADHEADER:
215     case IMAP_MESSAGE_NOBLANKLINE:
216         code = LMTP_MESSAGE_INVALID;
217         break;
218 
219     case IMAP_MAILBOX_NONEXISTENT:
220         /* XXX Might have been moved to other server */
221         if (LMTP_LONG_ERROR_MSGS) {
222             prot_puts(pout, error_message(LMTP_USER_UNKNOWN_LONG));
223         }
224         code = LMTP_USER_UNKNOWN;
225         break;
226 
227     case IMAP_PROTOCOL_BAD_PARAMETERS:
228         code = LMTP_PROTOCOL_ERROR;
229         break;
230 
231     case IMAP_IOERROR:
232     case IMAP_AGAIN:
233     case MUPDATE_BADPARAM:
234     default:
235         /* Some error we're not expecting. */
236         code = LMTP_SYSTEM_ERROR;
237         break;
238     }
239 
240     prot_printf(pout, error_message(code), error_message(r), session_id());
241     prot_puts(pout, "\r\n");
242 }
243 
244 /* ----- this section defines functions on message_data_t.
245    ----- access functions and the like, etc. */
246 
247 /* returns non-zero on failure */
msg_new(message_data_t ** m,const struct namespace * ns)248 static int msg_new(message_data_t **m, const struct namespace *ns)
249 {
250     message_data_t *ret = (message_data_t *) xmalloc(sizeof(message_data_t));
251 
252     ret->data = NULL;
253     ret->f = NULL;
254     ret->id = NULL;
255     ret->size = 0;
256     ret->return_path = NULL;
257     ret->rcpt = NULL;
258     ret->rcpt_num = 0;
259     ret->date = NULL;
260 
261     ret->authuser = NULL;
262     ret->authstate = NULL;
263 
264     ret->rock = NULL;
265 
266     ret->hdrcache = spool_new_hdrcache();
267 
268     ret->ns = ns;
269 
270     *m = ret;
271     return 0;
272 }
273 
msg_free(message_data_t * m)274 static void msg_free(message_data_t *m)
275 {
276     int i;
277 
278     if (m->data) {
279         prot_free(m->data);
280     }
281     if (m->f) {
282         fclose(m->f);
283     }
284     if (m->id) {
285         free(m->id);
286     }
287 
288     if (m->return_path) {
289         free(m->return_path);
290     }
291     if (m->rcpt) {
292         for (i = 0; i < m->rcpt_num; i++) {
293             mbname_free(&m->rcpt[i]->mbname);
294             strarray_free(m->rcpt[i]->resp);
295             free(m->rcpt[i]);
296         }
297         free(m->rcpt);
298     }
299     if (m->date) {
300       free(m->date);
301      }
302 
303     if (m->authuser) {
304         free(m->authuser);
305         if (m->authstate) auth_freestate(m->authstate);
306     }
307 
308     spool_free_hdrcache(m->hdrcache);
309 
310     free(m);
311 }
312 
msg_getheader(message_data_t * m,const char * phead)313 const char **msg_getheader(message_data_t *m, const char *phead)
314 {
315     assert(m && phead);
316 
317     return spool_getheader(m->hdrcache, phead);
318 }
319 
msg_getsize(message_data_t * m)320 int msg_getsize(message_data_t *m)
321 {
322     return m->size;
323 }
324 
msg_getnumrcpt(message_data_t * m)325 int msg_getnumrcpt(message_data_t *m)
326 {
327     return m->rcpt_num;
328 }
329 
msg_getrcpt(message_data_t * m,int rcpt_num)330 const mbname_t *msg_getrcpt(message_data_t *m, int rcpt_num)
331 {
332     assert(0 <= rcpt_num && rcpt_num < m->rcpt_num);
333     return m->rcpt[rcpt_num]->mbname;
334 }
335 
msg_getrcptall(message_data_t * m,int rcpt_num)336 const char *msg_getrcptall(message_data_t *m, int rcpt_num)
337 {
338     assert(0 <= rcpt_num && rcpt_num < m->rcpt_num);
339     return mbname_recipient(m->rcpt[rcpt_num]->mbname, m->ns);
340 }
341 
msg_getrcpt_ignorequota(message_data_t * m,int rcpt_num)342 int msg_getrcpt_ignorequota(message_data_t *m, int rcpt_num)
343 {
344     assert(0 <= rcpt_num && rcpt_num < m->rcpt_num);
345     return m->rcpt[rcpt_num]->ignorequota;
346 }
347 
348 /* set a recipient status; 'r' should be an IMAP error code that will be
349    translated into an LMTP status code */
msg_setrcpt_status(message_data_t * m,int rcpt_num,int r,strarray_t * resp)350 void msg_setrcpt_status(message_data_t *m, int rcpt_num, int r, strarray_t *resp)
351 {
352     assert(0 <= rcpt_num && rcpt_num < m->rcpt_num);
353     if (!m->rcpt[rcpt_num]->status) {
354         m->rcpt[rcpt_num]->status = r;
355         m->rcpt[rcpt_num]->resp = resp;
356     }
357 }
358 
msg_getrock(message_data_t * m)359 void *msg_getrock(message_data_t *m)
360 {
361     return m->rock;
362 }
363 
msg_setrock(message_data_t * m,void * rock)364 void msg_setrock(message_data_t *m, void *rock)
365 {
366     m->rock = rock;
367 }
368 
369 /* return a malloc'd string representing the authorized user.
370  advance 'strp' over the parameter */
parseautheq(char ** strp)371 static char *parseautheq(char **strp)
372 {
373     char *ret;
374     char *str;
375     char *s = *strp;
376 
377     if (!strcmp(s, "<>")) {
378         *strp = s + 2;
379         return NULL;
380     }
381 
382     ret = (char *) xmalloc(strlen(s)+1);
383     ret[0]='\0';
384     str = ret;
385 
386     if (*s == '<') s++;         /* we'll be liberal and accept "<foo>" */
387     while (1)
388     {
389         /* hexchar */
390         if (*s == '+')
391         {
392             int lup;
393             *str = '\0';
394             s++;
395 
396             for (lup=0;lup<2;lup++)
397             {
398                 if ((*s>='0') && (*s<='9'))
399                     (*str) = (*str) & (*s - '0');
400                 else if ((*s>='A') && (*s<='F'))
401                     (*str) = (*str) & (*s - 'A' + 10);
402                 else {
403                     free(ret);
404                     *strp = s;
405                     return NULL;
406                 }
407                 if (lup==0)
408                 {
409                     (*str) = (*str) << 4;
410                     s++;
411                 }
412             }
413             str++;
414 
415         } else if ((*s >= '!') && (*s <='~') && (*s!='+') && (*s!='=')) {
416             /* ascii char */
417             *str = *s;
418             str++;
419         } else {
420             /* bad char or end-of-line */
421             break;
422         }
423         s++;
424     }
425 
426     *strp = s;
427     if (*s && (*s!=' ')) { free(ret); return NULL; }
428 
429     *str = '\0';
430 
431     /* take off trailing '>' */
432     if ((str!=ret) && ( *(str-1)=='>'))
433     {
434         *(str-1) = '\0';
435     }
436 
437     return ret;
438 }
439 
440 /* return malloc'd string containing the address */
parseaddr(char * s)441 static char *parseaddr(char *s)
442 {
443     char *p;
444     int lmtp_strict_rfc2821 = config_getswitch(IMAPOPT_LMTP_STRICT_RFC2821);
445 
446     p = s;
447 
448     if (*p++ != '<') return 0;
449 
450     /* at-domain-list */
451     while (*p == '@') {
452         p++;
453         if (*p == '[') {
454             p++;
455             while (Uisdigit(*p) || *p == '.') p++;
456             if (*p++ != ']') return 0;
457         }
458         else {
459             while (Uisalnum(*p) || *p == '.' || *p == '-') p++;
460         }
461         if (*p == ',' && p[1] == '@') p++;
462         else if (*p == ':' && p[1] != '@') p++;
463         else return 0;
464     }
465 
466     /* local-part */
467     if (*p == '\"') {
468         p++;
469         while (*p && *p != '\"') {
470             if (*p == '\\') {
471                 if (!*++p) return 0;
472             }
473             p++;
474         }
475         if (!*p++) return 0;
476     }
477     else {
478         while (*p && *p != '@' && *p != '>') {
479             if (*p == '\\') {
480                 if (!*++p) return 0;
481             }
482             else {
483                 if (*p & 128 && !lmtp_strict_rfc2821) {
484                     /* this prevents us from becoming a backscatter
485                        source if our MTA allows 8bit in local-part
486                        of addresses. */
487                     *p = 'X';
488                 }
489                 if (*p <= ' ' || (*p & 128) ||
490                     strchr("<>()[]\\,;:\"", *p)) return 0;
491             }
492             p++;
493         }
494     }
495 
496     /* @domain */
497     if (*p == '@') {
498         p++;
499         if (*p == '[') {
500             p++;
501             while (Uisdigit(*p) || *p == '.') p++;
502             if (*p++ != ']') return 0;
503         }
504         else {
505             while (Uisalnum(*p) || *p == '.' || *p == '-') p++;
506         }
507     }
508 
509     if (*p++ != '>') return 0;
510     if (*p && *p != ' ') return 0;
511 
512     return xstrndup(s, p - s);
513 }
514 
515 /* clean off the <> from the return path */
clean_retpath(char * rpath)516 static void clean_retpath(char *rpath)
517 {
518     int sl;
519 
520     /* Remove any angle brackets around return path */
521     if (*rpath == '<') {
522         sl = strlen(rpath);
523         /* use strlen(rpath) so we move the NUL too */
524         memmove(rpath, rpath+1, sl);
525         sl--; /* string is one shorter now */
526         if (rpath[sl-1] == '>') {
527             rpath[sl-1] = '\0';
528         }
529     }
530 }
531 
532 /*
533  * Destructively remove any whitespace and 822 comments
534  * from string pointed to by 'buf'.  Does not handle continuation header
535  * lines.
536  */
clean822space(char * buf)537 static void clean822space(char *buf)
538 {
539     char *from=buf, *to=buf;
540     int c;
541     int commentlevel = 0;
542 
543     while ((c = *from++)!=0) {
544         switch (c) {
545         case '\r':
546         case '\n':
547         case '\0':
548             *to = '\0';
549             return;
550 
551         case ' ':
552         case '\t':
553             continue;
554 
555         case '(':
556             commentlevel++;
557             break;
558 
559         case ')':
560             if (commentlevel) commentlevel--;
561             break;
562 
563         case '\\':
564             if (commentlevel && *from) from++;
565             /* FALL THROUGH */
566 
567         default:
568             if (!commentlevel) *to++ = c;
569             break;
570         }
571     }
572 }
573 
574 /*
575  * file in the message structure 'm' from 'pin', assuming a dot-stuffed
576  * stream a la lmtp.
577  *
578  * returns 0 on success, imap error code on failure
579  */
savemsg(struct clientdata * cd,const struct lmtp_func * func,message_data_t * m)580 static int savemsg(struct clientdata *cd,
581                    const struct lmtp_func *func,
582                    message_data_t *m)
583 {
584     FILE *f;
585     struct stat sbuf;
586     const char **body;
587     int r;
588     int nrcpts = m->rcpt_num;
589     time_t now = time(NULL);
590     static unsigned msgid_count = 0;
591     char datestr[RFC5322_DATETIME_MAX+1], tls_info[250] = "";
592     const char *skipheaders[] = {
593         "Return-Path",  /* need to remove (we add our own) */
594         NULL
595     };
596     char *addbody, *fold[5], *p;
597     int addlen, nfold, i;
598 
599     /* Copy to spool file */
600     f = func->spoolfile(m);
601     if (!f) {
602         prot_printf(cd->pout,
603                     "451 4.3.%c cannot create temporary file: %s\r\n",
604                     (
605 #ifdef EDQUOT
606                         errno == EDQUOT ||
607 #endif
608                         errno == ENOSPC) ? '1' : '2',
609                     error_message(errno));
610         return IMAP_IOERROR;
611     }
612 
613     prot_printf(cd->pout, "354 go ahead\r\n");
614 
615     if (m->return_path && func->addretpath) { /* add the return path */
616         char *rpath = m->return_path;
617         const char *hostname = 0;
618 
619         clean_retpath(rpath);
620         /* Append our hostname if there's no domain in address */
621         hostname = NULL;
622         if (!strchr(rpath, '@') && strlen(rpath) > 0) {
623             hostname = config_servername;
624         }
625 
626         addlen = 2 + strlen(rpath) + (hostname ? 1 + strlen(hostname) : 0);
627         addbody = xmalloc(addlen + 1);
628         sprintf(addbody, "<%s%s%s>",
629                 rpath, hostname ? "@" : "", hostname ? hostname : "");
630         fprintf(f, "Return-Path: %s\r\n", addbody);
631         spool_cache_header(xstrdup("Return-Path"), addbody, m->hdrcache);
632     }
633 
634     /* add a received header */
635     time_to_rfc5322(now, datestr, sizeof(datestr));
636     addlen = 8 + strlen(cd->lhlo_param) + strlen(cd->clienthost);
637     if (m->authuser) addlen += 28 + strlen(m->authuser) + 5; /* +5 for ssf */
638     addlen += 25 + strlen(config_servername) + strlen(CYRUS_VERSION);
639 #ifdef HAVE_SSL
640     if (cd->tls_conn) {
641         addlen += 3 + tls_get_info(cd->tls_conn, tls_info, sizeof(tls_info));
642     }
643 #endif
644     addlen += 2 + strlen(datestr);
645     p = addbody = xmalloc(addlen + 1);
646 
647     nfold = 0;
648     p += sprintf(p, "from %s (%s)", cd->lhlo_param, cd->clienthost);
649     fold[nfold++] = p;
650     if (m->authuser) {
651         const void *ssfp;
652         sasl_ssf_t ssf;
653         sasl_getprop(cd->conn, SASL_SSF, &ssfp);
654         ssf = *((sasl_ssf_t *) ssfp);
655         p += sprintf(p, " (authenticated user=%s bits=%d)", m->authuser, ssf);
656         fold[nfold++] = p;
657     }
658 
659     /* We are always atleast "with LMTPA" -- no unauth delivery */
660     p += sprintf(p, " by %s", config_servername);
661     if (config_serverinfo == IMAP_ENUM_SERVERINFO_ON) {
662         p += sprintf(p, " (Cyrus %s)", CYRUS_VERSION);
663     }
664     p += sprintf(p, " with LMTP%s%s",
665                  cd->starttls_done ? "S" : "",
666                  cd->authenticated != NOAUTH ? "A" : "");
667 
668     if (*tls_info) {
669         fold[nfold++] = p;
670         p += sprintf(p, " (%s)", tls_info);
671     }
672 
673     strcat(p++, ";");
674     fold[nfold++] = p;
675     p += sprintf(p, " %s", datestr);
676 
677     struct buf rbuf = BUF_INITIALIZER;
678     buf_setcstr(&rbuf, "Received: ");
679     for (i = 0, p = addbody; i < nfold; p = fold[i], i++) {
680         buf_printf(&rbuf, "%.*s\r\n\t", (int) (fold[i] - p), p);
681     }
682     buf_printf(&rbuf, "%s\r\n", p);
683     fputs(buf_cstring(&rbuf), f);
684     spool_append_header_raw(xstrdup("Received"), addbody, buf_release(&rbuf), m->hdrcache);
685 
686     char *sid = xstrdup(session_id());
687     fprintf(f, "X-Cyrus-Session-Id: %s\r\n", sid);
688     spool_cache_header(xstrdup("X-Cyrus-Session-Id"), sid, m->hdrcache);
689 
690     /* add any requested headers */
691     if (func->addheaders) {
692         struct addheader *h;
693         for (h = func->addheaders; h && h->name; h++) {
694             fprintf(f, "%s: %s\r\n", h->name, h->body);
695             spool_cache_header(xstrdup(h->name), xstrdup(h->body), m->hdrcache);
696         }
697     }
698 
699     /* fill the cache */
700     r = spool_fill_hdrcache(cd->pin, f, m->hdrcache, skipheaders);
701 
702     /* now, using our header cache, fill in the data that we want */
703 
704     /* first check x-me-message-id, then resent-message-id */
705     if ((body = msg_getheader(m, "x-me-message-id")) && body[0][0]) {
706         m->id = xstrdup(body[0]);
707     } else if ((body = msg_getheader(m, "resent-message-id")) && body[0][0]) {
708         m->id = xstrdup(body[0]);
709     } else if ((body = msg_getheader(m, "message-id")) && body[0][0]) {
710         m->id = xstrdup(body[0]);
711     } else if (body) {
712         r = IMAP_MESSAGE_BADHEADER;  /* empty message-id */
713     } else {
714         /* no message-id, create one */
715         pid_t p = getpid();
716 
717         m->id = xmalloc(40 + strlen(config_servername));
718         sprintf(m->id, "<cmu-lmtpd-%d-%d-%u@%s>", p, (int) now,
719                 msgid_count++, config_servername);
720         fprintf(f, "Message-ID: %s\r\n", m->id);
721         spool_cache_header(xstrdup("Message-ID"), xstrdup(m->id), m->hdrcache);
722     }
723 
724     /* get date */
725     if (!(body = spool_getheader(m->hdrcache, "date"))) {
726         /* no date, create one */
727         addbody = xstrdup(datestr);
728         m->date = xstrdup(datestr);
729         fprintf(f, "Date: %s\r\n", addbody);
730         spool_cache_header(xstrdup("Date"), addbody, m->hdrcache);
731     }
732     else {
733         m->date = xstrdup(body[0]);
734     }
735 
736     if (!m->return_path &&
737         (body = msg_getheader(m, "return-path"))) {
738         /* let's grab return_path */
739         m->return_path = xstrdup(body[0]);
740         clean822space(m->return_path);
741         clean_retpath(m->return_path);
742     }
743 
744     /* get offset of message body */
745     m->body_offset = ftell(f);
746 
747     r |= spool_copy_msg(cd->pin, f);
748     if (r) {
749         fclose(f);
750         if (func->removespool) {
751             /* remove the spool'd message */
752             func->removespool(m);
753         }
754         while (nrcpts--) {
755             send_lmtp_error(cd->pout, r, NULL);
756         }
757         return r;
758     }
759 
760     fflush(f);
761     if (ferror(f)) {
762         while (nrcpts--) {
763             prot_printf(cd->pout,
764                "451 4.3.%c cannot copy message to temporary file: %s\r\n",
765                    (
766 #ifdef EDQUOT
767                     errno == EDQUOT ||
768 #endif
769                     errno == ENOSPC) ? '1' : '2',
770                    error_message(errno));
771         }
772         fclose(f);
773         if (func->removespool) func->removespool(m);
774         return IMAP_IOERROR;
775     }
776 
777     if (fstat(fileno(f), &sbuf) == -1) {
778         while (nrcpts--) {
779             prot_printf(cd->pout,
780                         "451 4.3.2 cannot stat message temporary file: %s\r\n",
781                         error_message(errno));
782         }
783         fclose(f);
784         if (func->removespool) func->removespool(m);
785         return IMAP_IOERROR;
786     }
787     m->size = sbuf.st_size;
788     m->f = f;
789     m->data = prot_new(fileno(f), 0);
790 
791     return 0;
792 }
793 
794 /* see if 'addr' exists. if so, fill in 'ad' appropriately.
795    on success, return NULL.
796    on failure, return the error. */
process_recipient(char * addr,int ignorequota,int (* verify_user)(const mbname_t * mbname,quota_t,quota_t,struct auth_state *),message_data_t * msg)797 static int process_recipient(char *addr,
798                              int ignorequota,
799                              int (*verify_user)(const mbname_t *mbname,
800                                                 quota_t, quota_t,
801                                                 struct auth_state *),
802                              message_data_t *msg)
803 {
804     assert(addr != NULL && msg != NULL);
805 
806     if (*addr == '<') addr++;
807 
808     /* Skip at-domain-list */
809     if (*addr == '@') {
810         addr = strchr(addr, ':');
811         if (!addr)
812             return IMAP_PROTOCOL_BAD_PARAMETERS;
813         addr++;
814     }
815 
816     mbname_t *mbname = NULL;
817     int r = 0;
818 
819     size_t sl = strlen(addr);
820     if (addr[sl-1] == '>') sl--;
821 
822     if (sl) {
823         char *rcpt = xstrndup(addr, sl);
824         mbname = mbname_from_recipient(rcpt, msg->ns);
825         free(rcpt);
826 
827         int forcedowncase = config_getswitch(IMAPOPT_LMTP_DOWNCASE_RCPT);
828         if (forcedowncase) mbname_downcaseuser(mbname);
829 
830         /* strip username if postuser */
831         if (!strcmpsafe(mbname_localpart(mbname), config_getstring(IMAPOPT_POSTUSER))) {
832             mbname_set_localpart(mbname, NULL);
833             if (!config_virtdomains || !strcmpsafe(mbname_domain(mbname), config_defdomain))
834                 mbname_set_domain(mbname, NULL);
835         }
836 
837         if ((r = verify_user(mbname,
838                         (quota_t) (ignorequota ? -1 : msg->size),
839                         ignorequota ? -1 : 1, msg->authstate))) {
840             mbname_free(&mbname);
841         }
842     }
843 
844     if (!mbname) {
845         const char *catchall = config_getstring(IMAPOPT_LMTP_CATCHALL_MAILBOX);
846         if (catchall) {
847             mbname = mbname_from_userid(catchall);
848             if ((r = verify_user(mbname,
849                             ignorequota ? -1 : msg->size,
850                             ignorequota ? -1 : 1, msg->authstate))) {
851                 mbname_free(&mbname);
852             }
853         }
854     }
855 
856     if (!mbname) {
857         /* we lost */
858         if (r) {
859             return r;
860         }
861         return IMAP_MAILBOX_NONEXISTENT;
862     }
863 
864     address_data_t *ret = (address_data_t *) xzmalloc(sizeof(address_data_t));
865     ret->mbname = mbname;
866     ret->ignorequota = ignorequota;
867     msg->rcpt[msg->rcpt_num] = ret;
868 
869     return 0;
870 }
871 
localauth_mechlist_override(void * context,const char * plugin_name,const char * option,const char ** result,unsigned * len)872 static int localauth_mechlist_override(
873     void *context __attribute__((unused)),
874     const char *plugin_name __attribute__((unused)),
875     const char *option,
876     const char **result,
877     unsigned *len)
878 {
879     /* If we are doing local auth, we only support EXTERNAL */
880     if (strcmp(option,"mech_list")==0)
881     {
882         *result = "EXTERNAL";
883         if (len)
884             *len = strlen(*result);
885         return SASL_OK;
886     }
887 
888     /* if we don't find the option,
889        this should percolate to the global getopt */
890     return SASL_FAIL;
891 }
892 
893 static struct sasl_callback localauth_override_cb[] = {
894     { SASL_CB_GETOPT, (mysasl_cb_ft *) &localauth_mechlist_override, NULL },
895     { SASL_CB_LIST_END, NULL, NULL },
896 };
897 
898 /* Reset the given sasl_conn_t to a sane state */
reset_saslconn(sasl_conn_t ** conn)899 static int reset_saslconn(sasl_conn_t **conn)
900 {
901     int ret;
902     sasl_security_properties_t *secprops = NULL;
903 
904     sasl_dispose(conn);
905     /* do initialization typical of service_main */
906     ret = sasl_server_new("lmtp", config_servername, NULL,
907                           buf_cstringnull_ifempty(&saslprops.iplocalport),
908                           buf_cstringnull_ifempty(&saslprops.ipremoteport),
909                           NULL, 0, conn);
910     if(ret != SASL_OK) return ret;
911 
912     secprops = mysasl_secprops(SASL_SEC_NOANONYMOUS);
913     ret = sasl_setprop(*conn, SASL_SEC_PROPS, secprops);
914     if(ret != SASL_OK) return ret;
915     /* end of service_main initialization excepting SSF */
916 
917     /* If we have TLS/SSL info, set it */
918     if(saslprops.ssf) {
919         ret = saslprops_set_tls(&saslprops, *conn);
920     }
921     if(ret != SASL_OK) return ret;
922     /* End TLS/SSL Info */
923 
924     return SASL_OK;
925 }
926 
lmtpmode(struct lmtp_func * func,struct protstream * pin,struct protstream * pout,int fd)927 void lmtpmode(struct lmtp_func *func,
928               struct protstream *pin,
929               struct protstream *pout,
930               int fd)
931 {
932     message_data_t *msg = NULL;
933     int max_msgsize;
934     char buf[4096];
935     char *p;
936     int r;
937     struct clientdata cd;
938 
939     const char *localip, *remoteip;
940 
941     sasl_security_properties_t *secprops = NULL;
942 
943     /* setup the clientdata structure */
944     cd.pin = pin;
945     cd.pout = pout;
946     cd.fd = fd;
947     cd.clienthost = "";
948     cd.lhlo_param[0] = '\0';
949     cd.authenticated =  NOAUTH;
950 #ifdef HAVE_SSL
951     cd.tls_conn = NULL;
952 #endif
953     cd.starttls_done = 0;
954 
955     max_msgsize = config_getint(IMAPOPT_MAXMESSAGESIZE);
956 
957     /* If max_msgsize is 0, allow any size */
958     if(!max_msgsize) max_msgsize = INT_MAX;
959 
960     msg_new(&msg, func->namespace);
961 
962     /* don't leak old connections */
963     saslprops_reset(&saslprops);
964 
965     /* determine who we're talking to */
966     cd.clienthost = get_clienthost(fd, &localip, &remoteip);
967     if (!strcmp(cd.clienthost, UNIX_SOCKET)) {
968         /* we're not connected to a internet socket! */
969         func->preauth = 1;
970     }
971     else if (localip && remoteip) {
972         buf_setcstr(&saslprops.ipremoteport, remoteip);
973         buf_setcstr(&saslprops.iplocalport, localip);
974     }
975 
976     syslog(LOG_DEBUG, "connection from %s%s",
977            cd.clienthost,
978            func->preauth ? " preauth'd as postman" : "");
979 
980     /* Setup SASL to go.  We need to do this *after* we decide if
981      *  we are preauthed or not. */
982     if (sasl_server_new("lmtp", config_servername, NULL,
983                         buf_cstringnull_ifempty(&saslprops.iplocalport),
984                         buf_cstringnull_ifempty(&saslprops.ipremoteport),
985                         (func->preauth ? localauth_override_cb : NULL),
986                         0, &cd.conn) != SASL_OK) {
987         fatal("SASL failed initializing: sasl_server_new()", EX_TEMPFAIL);
988     }
989 
990     /* set my allowable security properties */
991     /* ANONYMOUS is silly because we allow that anyway */
992     secprops = mysasl_secprops(SASL_SEC_NOANONYMOUS);
993     sasl_setprop(cd.conn, SASL_SEC_PROPS, secprops);
994 
995     if (func->preauth) {
996         const char *auth_id = "postman";
997 
998         cd.authenticated = EXTERNAL_AUTHED;     /* we'll allow commands,
999                                                    but we still accept
1000                                                    the AUTH command */
1001         saslprops.ssf = 2;
1002         buf_setcstr(&saslprops.authid, auth_id);
1003         if (saslprops_set_tls(&saslprops, cd.conn) != SASL_OK)
1004             fatal("saslprops_set_tls() failed: preauth",EX_TEMPFAIL);
1005 
1006         deliver_logfd = telemetry_log(auth_id, pin, pout, 0);
1007     }
1008 
1009     prot_printf(pout, "220 %s", config_servername);
1010     if (config_serverinfo == IMAP_ENUM_SERVERINFO_ON) {
1011         prot_printf(pout, " Cyrus LMTP %s", CYRUS_VERSION);
1012     }
1013     prot_printf(pout, " server ready\r\n");
1014 
1015     for (;;) {
1016     nextcmd:
1017       signals_poll();
1018 
1019       if (!prot_fgets(buf, sizeof(buf), pin)) {
1020           const char *err = prot_error(pin);
1021 
1022           if (err != NULL) {
1023               prot_printf(pout, "421 4.4.1 bye %s\r\n", err);
1024               prot_flush(pout);
1025           }
1026           goto cleanup;
1027       }
1028       p = buf + strlen(buf) - 1;
1029       if (p >= buf && *p == '\n') *p-- = '\0';
1030       if (p >= buf && *p == '\r') *p-- = '\0';
1031 
1032       /* Only allow LHLO/NOOP/QUIT when there is a shutdown file */
1033       if (!strchr("LlNnQq", buf[0]) &&
1034           shutdown_file(buf, sizeof(buf))) {
1035 
1036           prot_printf(pout, "421 4.3.2 %s\r\n", buf);
1037           prot_flush(pout);
1038 
1039           func->shutdown(0);
1040       }
1041 
1042       if (config_getswitch(IMAPOPT_CHATTY))
1043         syslog(LOG_NOTICE, "command: %s", buf);
1044 
1045       switch (buf[0]) {
1046       case 'a':
1047       case 'A':
1048           if (!strncasecmp(buf, "auth ", 5)) {
1049               char mech[128];
1050               int sasl_result;
1051               const void *val;
1052               const char *user;
1053 
1054               if (cd.authenticated > 0) {
1055                   prot_printf(pout,
1056                               "503 5.5.0 already authenticated\r\n");
1057                   continue;
1058               }
1059               if (msg->rcpt_num != 0) {
1060                   prot_printf(pout,
1061                               "503 5.5.0 AUTH not permitted now\r\n");
1062                   continue;
1063               }
1064 
1065               /* ok, what mechanism ? */
1066               p = buf + 5;
1067               while ((*p != ' ') && (*p != '\0')) {
1068                   p++;
1069               }
1070               if (*p == ' ') {
1071                   *p = '\0';
1072                   p++;
1073               } else {
1074                   p = NULL;
1075               }
1076               strlcpy(mech, buf + 5, sizeof(mech));
1077 
1078               r = saslserver(cd.conn, mech, p, "", "334 ", "",
1079                              pin, pout, &sasl_result, NULL);
1080 
1081               if (r) {
1082                   const char *errorstring = NULL;
1083                   const char *userid = "-notset-";
1084 
1085                   switch (r) {
1086                   case IMAP_SASL_CANCEL:
1087                       prot_printf(pout,
1088                                   "501 5.5.4 client canceled authentication\r\n");
1089                       break;
1090                   case IMAP_SASL_PROTERR:
1091                       errorstring = prot_error(pin);
1092 
1093                       prot_printf(pout,
1094                                   "501 5.5.4 Error reading client response: %s\r\n",
1095                                   errorstring ? errorstring : "");
1096                       break;
1097                   default:
1098                       if (sasl_result == SASL_NOMECH) {
1099                           prot_printf(pout,
1100                                       "504 Unrecognized authentication type.\r\n");
1101                           continue;
1102                       }
1103                       else {
1104                           if (r != SASL_NOUSER)
1105                               sasl_getprop(cd.conn, SASL_USERNAME, (const void **) &userid);
1106 
1107                           syslog(LOG_ERR, "badlogin: %s %s (%s) [%s]",
1108                                  cd.clienthost, mech, userid, sasl_errdetail(cd.conn));
1109 
1110                           prometheus_increment(CYRUS_IMAP_AUTHENTICATE_TOTAL_RESULT_NO);
1111                           snmp_increment_args(AUTHENTICATION_NO, 1,
1112                                               VARIABLE_AUTH, hash_simple(mech),
1113                                               VARIABLE_LISTEND);
1114 
1115                           prot_printf(pout, "501 5.5.4 %s\r\n",
1116                                       sasl_errstring((r == SASL_NOUSER ?
1117                                                       SASL_BADAUTH : r),
1118                                                      NULL, NULL));
1119                       }
1120                   }
1121 
1122                   reset_saslconn(&cd.conn);
1123                   continue;
1124               }
1125               r = sasl_getprop(cd.conn, SASL_USERNAME, &val);
1126               if (r != SASL_OK) {
1127                   prot_printf(pout, "501 5.5.4 SASL Error\r\n");
1128                   reset_saslconn(&cd.conn);
1129                   goto nextcmd;
1130               }
1131               user = (const char *) val;
1132 
1133               r = sasl_getprop(cd.conn, SASL_SSF, &val);
1134               if (r != SASL_OK) {
1135                   prot_printf(pout, "501 5.5.4 SASL Error\r\n");
1136                   reset_saslconn(&cd.conn);
1137                   goto nextcmd;
1138               }
1139               saslprops.ssf = *((sasl_ssf_t *) val);
1140 
1141               /* Create telemetry log */
1142               deliver_logfd = telemetry_log(user, pin, pout, 0);
1143 
1144               /* authenticated successfully! */
1145 
1146               prometheus_increment(CYRUS_IMAP_AUTHENTICATE_TOTAL_RESULT_YES);
1147               snmp_increment_args(AUTHENTICATION_YES,1,
1148                                   VARIABLE_AUTH, hash_simple(mech),
1149                                   VARIABLE_LISTEND);
1150               syslog(LOG_NOTICE, "login: %s %s %s%s %s",
1151                      cd.clienthost, user, mech,
1152                      cd.starttls_done ? "+TLS" : "", "User logged in");
1153 
1154               cd.authenticated = DIDAUTH;
1155               prot_printf(pout, "235 Authenticated!\r\n");
1156 
1157               /* set protection layers */
1158               prot_setsasl(pin,  cd.conn);
1159               prot_setsasl(pout, cd.conn);
1160               continue;
1161           }
1162           goto syntaxerr;
1163 
1164       case 'd':
1165       case 'D':
1166             if (!strcasecmp(buf, "data")) {
1167                 int delivered = 0;
1168                 int j;
1169 
1170                 if (!msg->rcpt_num) {
1171                     prot_printf(pout, "503 5.5.1 No recipients\r\n");
1172                     continue;
1173                 }
1174                 /* copy message from input to msg structure */
1175                 r = savemsg(&cd, func, msg);
1176                 if (r) {
1177                     goto rset;
1178                 }
1179 
1180                 if (msg->size > max_msgsize) {
1181                     prot_printf(pout,
1182                                 "552 5.2.3 Message size (%d) exceeds fixed "
1183                                 "maximum message size (%d)\r\n",
1184                                 msg->size, max_msgsize);
1185                     continue;
1186                 }
1187 
1188                 prometheus_increment(CYRUS_LMTP_RECEIVED_MESSAGES_TOTAL);
1189                 prometheus_apply_delta(CYRUS_LMTP_RECEIVED_BYTES_TOTAL, msg->size);
1190                 prometheus_apply_delta(CYRUS_LMTP_RECEIVED_RECIPIENTS_TOTAL, msg->rcpt_num);
1191 
1192                 snmp_increment(mtaReceivedMessages, 1);
1193                 snmp_increment(mtaReceivedVolume, roundToK(msg->size));
1194                 snmp_increment(mtaReceivedRecipients, msg->rcpt_num);
1195 
1196                 /* do delivery, report status */
1197                 func->deliver(msg, msg->authuser, msg->authstate, msg->ns);
1198                 for (j = 0; j < msg->rcpt_num; j++) {
1199                     if (!msg->rcpt[j]->status) delivered++;
1200                     send_lmtp_error(pout, msg->rcpt[j]->status,
1201                                     msg->rcpt[j]->resp);
1202                 }
1203 
1204                 prometheus_increment(CYRUS_LMTP_TRANSMITTED_MESSAGES_TOTAL);
1205                 prometheus_apply_delta(CYRUS_LMTP_TRANSMITTED_BYTES_TOTAL, delivered * msg->size);
1206 
1207                 snmp_increment(mtaTransmittedMessages, delivered);
1208                 snmp_increment(mtaTransmittedVolume,
1209                                roundToK(delivered * msg->size));
1210                 goto rset;
1211             }
1212             goto syntaxerr;
1213 
1214       case 'l':
1215       case 'L':
1216           if (!strncasecmp(buf, "lhlo ", 5)) {
1217               int mechcount;
1218               const char *mechs;
1219 
1220               prot_printf(pout, "250-%s\r\n"
1221                           "250-8BITMIME\r\n"
1222                           "250-ENHANCEDSTATUSCODES\r\n"
1223                           "250-PIPELINING\r\n",
1224                           config_servername);
1225               if (max_msgsize < INT_MAX)
1226                   prot_printf(pout, "250-SIZE %d\r\n", max_msgsize);
1227               else
1228                   prot_printf(pout, "250-SIZE\r\n");
1229               if (tls_enabled() && !cd.starttls_done &&
1230                   cd.authenticated == NOAUTH) {
1231                   prot_printf(pout, "250-STARTTLS\r\n");
1232               }
1233               if ((cd.authenticated <= 0) &&
1234                   sasl_listmech(cd.conn, NULL, "AUTH ", " ", "", &mechs,
1235                                 NULL, &mechcount) == SASL_OK &&
1236                   mechcount > 0) {
1237                   prot_printf(pout,"250-%s\r\n", mechs);
1238               }
1239               prot_printf(pout, "250-IGNOREQUOTA\r\n");
1240               prot_printf(pout, "250 Ok SESSIONID=<%s>\r\n", session_id());
1241 
1242               strlcpy(cd.lhlo_param, buf + 5, sizeof(cd.lhlo_param));
1243 
1244               session_new_id();
1245               continue;
1246           }
1247           goto syntaxerr;
1248 
1249       case 'm':
1250       case 'M':
1251             if (!cd.authenticated) {
1252                 if (config_getswitch(IMAPOPT_SOFT_NOAUTH)) {
1253                     prot_printf(pout, "430 Authentication required\r\n");
1254                 } else {
1255                     prot_printf(pout, "530 Authentication required\r\n");
1256                 }
1257                 continue;
1258             }
1259 
1260             if (!strncasecmp(buf, "mail ", 5)) {
1261                 char *tmp;
1262                 if (msg->return_path) {
1263                     prot_printf(pout,
1264                                 "503 5.5.1 Nested MAIL command\r\n");
1265                     continue;
1266                 }
1267                 /* +5 to get past "mail "
1268                  * +10 to get past "mail from:" */
1269                 if (strncasecmp(buf+5, "from:", 5) != 0 ||
1270                     !(msg->return_path = parseaddr(buf+10))) {
1271                     prot_printf(pout,
1272                                 "501 5.5.4 Syntax error in parameters\r\n");
1273                     continue;
1274                 }
1275                 tmp = buf+10+strlen(msg->return_path);
1276 
1277                 /* is any other whitespace allow separating? */
1278                 while (*tmp == ' ') {
1279                     tmp++;
1280                     switch (*tmp) {
1281                     case 'a': case 'A':
1282                         if (strncasecmp(tmp, "auth=", 5) != 0) {
1283                             goto badparam;
1284                         }
1285                         tmp += 5;
1286                         msg->authuser = parseautheq(&tmp);
1287                         if (msg->authuser) {
1288                             msg->authstate = auth_newstate(msg->authuser);
1289                         } else {
1290                             /* do we want to bounce mail because of this? */
1291                             /* i guess not. accept with no auth user */
1292                             msg->authstate = NULL;
1293                         }
1294                         break;
1295 
1296                     case 'b': case 'B':
1297                         if (strncasecmp(tmp, "body=", 5) != 0) {
1298                             goto badparam;
1299                         }
1300                         tmp += 5;
1301                         /* just verify it's one of
1302                            body-value ::= "7BIT" / "8BITMIME" */
1303                         if (!strncasecmp(tmp, "7bit", 4)) {
1304                             tmp += 4;
1305                         } else if (!strncasecmp(tmp, "8bitmime", 8)) {
1306                             tmp += 8;
1307                         } else {
1308                             prot_printf(pout,
1309                               "501 5.5.4 Unrecognized BODY type\r\n");
1310                             goto nextcmd;
1311                         }
1312                         break;
1313 
1314                     case 's': case 'S':
1315                         if (strncasecmp(tmp, "size=", 5) != 0) {
1316                             goto badparam;
1317                         }
1318                         tmp += 5;
1319                         /* make sure we have a value */
1320                         if (!Uisdigit(*tmp)) {
1321                                 prot_printf(pout,
1322                                             "501 5.5.2 SIZE requires a value\r\n");
1323                                 goto nextcmd;
1324                         }
1325                         msg->size = strtoul(tmp, &p, 10);
1326                         tmp = p;
1327                         /* make sure the value is in range */
1328                         if (errno == ERANGE || msg->size < 0 ||
1329                             msg->size > max_msgsize) {
1330                             prot_printf(pout,
1331                                         "552 5.2.3 Message SIZE exceeds fixed "
1332                                         "maximum message size (%d)\r\n",
1333                                         max_msgsize);
1334                             goto nextcmd;
1335                         }
1336                         break;
1337 
1338                     default:
1339                     badparam:
1340                         prot_printf(pout,
1341                                     "501 5.5.4 Unrecognized parameters\r\n");
1342                         goto nextcmd;
1343                     }
1344                 }
1345                 if (*tmp != '\0') {
1346                     prot_printf(pout,
1347                                 "501 5.5.4 Syntax error in parameters\r\n");
1348                     continue;
1349                 }
1350 
1351                 prot_printf(pout, "250 2.1.0 ok\r\n");
1352                 continue;
1353             }
1354             goto syntaxerr;
1355 
1356       case 'n':
1357       case 'N':
1358             if (!strcasecmp(buf, "noop")) {
1359                 prot_printf(pout,"250 2.0.0 ok\r\n");
1360                 continue;
1361             }
1362             goto syntaxerr;
1363 
1364       case 'q':
1365       case 'Q':
1366             if (!strcasecmp(buf, "quit")) {
1367                 prot_printf(pout,"221 2.0.0 bye\r\n");
1368                 prot_flush(pout);
1369                 goto cleanup;
1370             }
1371             goto syntaxerr;
1372 
1373       case 'r':
1374       case 'R':
1375             if (!strncasecmp(buf, "rcpt ", 5)) {
1376                 char *rcpt = NULL;
1377                 int ignorequota = 0;
1378                 char *tmp;
1379 
1380                 if (!msg->return_path) {
1381                     prot_printf(pout, "503 5.5.1 Need MAIL command\r\n");
1382                     continue;
1383                 }
1384                 if (!(msg->rcpt_num % RCPT_GROW)) { /* time to alloc more */
1385                     msg->rcpt = (address_data_t **)
1386                         xrealloc(msg->rcpt, (msg->rcpt_num + RCPT_GROW + 1) *
1387                                  sizeof(address_data_t *));
1388                 }
1389                 /* +5 to get past "rcpt "
1390                  * +8 to get past "rcpt to:" */
1391                 if (strncasecmp(buf+5, "to:", 3) != 0 ||
1392                     !(rcpt = parseaddr(buf+8))) {
1393                     prot_printf(pout,
1394                                 "501 5.5.4 Syntax error in parameters\r\n");
1395                     continue;
1396                 }
1397 
1398                 tmp = buf+8+strlen(rcpt);
1399                 while (*tmp == ' ') {
1400                     tmp++;
1401                     switch (*tmp) {
1402                     case 'i': case 'I':
1403                         if (strncasecmp(tmp, "ignorequota", 12) != 0) {
1404                             goto badrparam;
1405                         }
1406                         tmp += 12;
1407                         ignorequota = 1;
1408                         break;
1409 
1410                     default:
1411                     badrparam:
1412                         prot_printf(pout,
1413                                     "501 5.5.4 Unrecognized parameters\r\n");
1414                         goto nextcmd;
1415                     }
1416                 }
1417                 if (*tmp != '\0') {
1418                     prot_printf(pout,
1419                                 "501 5.5.4 Syntax error in parameters\r\n");
1420                     continue;
1421                 }
1422 
1423                 r = process_recipient(rcpt,
1424                                       ignorequota,
1425                                       func->verify_user,
1426                                       msg);
1427                 if (rcpt) free(rcpt); /* malloc'd in parseaddr() */
1428                 if (r) {
1429                     send_lmtp_error(pout, r, NULL);
1430                     continue;
1431                 }
1432                 msg->rcpt_num++;
1433                 msg->rcpt[msg->rcpt_num] = NULL;
1434                 prot_printf(pout, "250 2.1.5 ok\r\n");
1435                 continue;
1436             }
1437             else if (!strcasecmp(buf, "rset")) {
1438                 session_new_id();
1439                 prot_printf(pout, "250 2.0.0 Ok SESSIONID=<%s>\r\n", session_id());
1440 
1441               rset:
1442                 if (msg) msg_free(msg);
1443                 msg_new(&msg, func->namespace);
1444 
1445                 continue;
1446             }
1447             goto syntaxerr;
1448 
1449       case 's':
1450       case 'S':
1451 #ifdef HAVE_SSL
1452             if (!strcasecmp(buf, "starttls") && tls_enabled() &&
1453                 !func->preauth) { /* don't need TLS for preauth'd connect */
1454 
1455                 /* XXX  discard any input pipelined after STARTTLS */
1456                 prot_flush(pin);
1457 
1458                 if (cd.starttls_done == 1) {
1459                     prot_printf(pout, "454 4.3.3 %s\r\n",
1460                                 "Already successfully executed STARTTLS");
1461                     continue;
1462                 }
1463                 if (msg->rcpt_num != 0) {
1464                     prot_printf(pout,
1465                                 "503 5.5.0 STARTTLS not permitted now\r\n");
1466                     continue;
1467                 }
1468 
1469                 r=tls_init_serverengine("lmtp",
1470                                         5,   /* depth to verify */
1471                                         1,   /* can client auth? */
1472                                         NULL);
1473 
1474                 if (r == -1) {
1475 
1476                     syslog(LOG_ERR, "[lmtpd] error initializing TLS");
1477 
1478                     prot_printf(pout, "454 4.3.3 %s\r\n", "Error initializing TLS");
1479                     continue;
1480                 }
1481 
1482                 prot_printf(pout, "220 %s\r\n", "Begin TLS negotiation now");
1483                 /* must flush our buffers before starting tls */
1484                 prot_flush(pout);
1485 
1486                 r=tls_start_servertls(0, /* read */
1487                                       1, /* write */
1488                                       360, /* 6 minutes */
1489                                       &saslprops,
1490                                       &(cd.tls_conn));
1491 
1492                 /* if error */
1493                 if (r==-1) {
1494                     prot_printf(pout, "454 4.3.3 STARTTLS failed\r\n");
1495                     syslog(LOG_NOTICE, "[lmtpd] STARTTLS failed: %s", cd.clienthost);
1496                     continue;
1497                 }
1498 
1499                 /* tell SASL about the negotiated layer */
1500                 r = saslprops_set_tls(&saslprops, cd.conn);
1501                 if (r != SASL_OK) {
1502                     fatal("saslprops_set_tls() failed: STARTTLS", EX_TEMPFAIL);
1503                 }
1504                 if (buf_len(&saslprops.authid)) {
1505                     cd.authenticated = TLSCERT_AUTHED;
1506                 }
1507 
1508                 /* tell the prot layer about our new layers */
1509                 prot_settls(pin, cd.tls_conn);
1510                 prot_settls(pout, cd.tls_conn);
1511 
1512                 cd.starttls_done = 1;
1513 
1514                 continue;
1515             }
1516 #endif /* HAVE_SSL*/
1517             goto syntaxerr;
1518 
1519       case 'v':
1520       case 'V':
1521             if (!strncasecmp(buf, "vrfy ", 5)) {
1522                 prot_printf(pout,
1523                             "252 2.3.3 try RCPT to attempt delivery\r\n");
1524                 continue;
1525             }
1526             goto syntaxerr;
1527 
1528       default:
1529       syntaxerr:
1530             prot_printf(pout, "500 5.5.2 Syntax error\r\n");
1531             continue;
1532       }
1533     }
1534 
1535  cleanup:
1536     /* free resources and return; this connection has been closed */
1537 
1538     if (msg) msg_free(msg);
1539 
1540     /* security */
1541     if (cd.conn) sasl_dispose(&cd.conn);
1542     saslprops_reset(&saslprops);
1543 
1544     cd.starttls_done = 0;
1545 #ifdef HAVE_SSL
1546     if (cd.tls_conn) {
1547         tls_reset_servertls(&cd.tls_conn);
1548         cd.tls_conn = NULL;
1549     }
1550 #endif
1551 }
1552 
1553 /************** client-side LMTP ****************/
1554 
1555 #define ISGOOD(r) (((r) / 100) == 2)
1556 #define TEMPFAIL(r) (((r) / 100) == 4)
1557 #define PERMFAIL(r) (((r) / 100) == 5)
1558 #define ISCONT(s) (s && (s[3] == '-'))
1559 
revconvert_lmtp(const char * code)1560 static int revconvert_lmtp(const char *code)
1561 {
1562     int c = atoi(code);
1563     switch (c) {
1564     case 250:
1565     case 251:
1566         return 0;
1567     case 451:
1568         if (code[4] == '4' && code[6] == '3') {
1569             if (code[8] == '0') {
1570                 return IMAP_IOERROR;
1571             } else if (code[8] == '1') {
1572                 return IMAP_NOSPACE;
1573             } else {
1574                 return IMAP_IOERROR;
1575             }
1576         }
1577         else if (code[4] == '4' && code [6] == '4') {
1578             return IMAP_SERVER_UNAVAILABLE;
1579         }
1580         else if (code[4] == '4' && code[6] == '2') {
1581             if (code[8] == '1') {
1582                 return IMAP_MAILBOX_MOVED;
1583             } else {
1584                 return IMAP_MAILBOX_BADFORMAT;
1585             }
1586         }
1587         else {
1588             return IMAP_IOERROR;
1589         }
1590     case 452:
1591         return IMAP_QUOTA_EXCEEDED;
1592     case 550:
1593         if (code[4] == '5' && code[6] == '7') {
1594             return IMAP_PERMISSION_DENIED;
1595         } else if (code[4] == '5' && code[6] == '1') {
1596             return IMAP_MAILBOX_NONEXISTENT;
1597         }
1598         return IMAP_PERMISSION_DENIED;
1599     case 552:
1600         if (code[6] == '2') {
1601             return IMAP_QUOTA_EXCEEDED;
1602         } else if (code[6] == '3') {
1603             return IMAP_MESSAGE_TOO_LARGE;
1604         }
1605         return IMAP_QUOTA_EXCEEDED;
1606     case 554:
1607         return IMAP_MESSAGE_BADHEADER; /* sigh, pick one */
1608 
1609     default:
1610         if (ISGOOD(c)) return 0;
1611         else if (TEMPFAIL(c)) return IMAP_AGAIN;
1612         else if (PERMFAIL(c)) return IMAP_PROTOCOL_ERROR;
1613         else return IMAP_AGAIN;
1614     }
1615 }
1616 
ask_code(const char * s)1617 static int ask_code(const char *s)
1618 {
1619     int ret = 0;
1620 
1621     if (s==NULL) return -1;
1622 
1623     if (strlen(s) < 3) return -1;
1624 
1625     /* check to make sure 0-2 are digits */
1626     if ((Uisdigit(s[0])==0) ||
1627         (Uisdigit(s[1])==0) ||
1628         (Uisdigit(s[2])==0))
1629     {
1630         return -1;
1631     }
1632 
1633     ret = ((s[0]-'0')*100)+((s[1]-'0')*10)+(s[2]-'0');
1634 
1635     return ret;
1636 }
1637 
1638 /* getlastresp reads from 'pin' until we get an LMTP that isn't a continuation.
1639    it puts it in 'buf', which must be at least 'len' big.
1640 
1641    '*code' will contain the integer three digit response code.
1642    if a read failed, '*code == 400', a temporary failure.
1643 
1644    returns an IMAP error code. */
getlastresp(char * buf,int len,int * code,struct protstream * pin,strarray_t ** sa)1645 static int getlastresp(char *buf, int len, int *code, struct protstream *pin,
1646                        strarray_t **sa)
1647 {
1648     *code = 0;
1649 
1650     do {
1651         if (!prot_fgets(buf, len, pin)) {
1652             *code = 400;
1653             return IMAP_SERVER_UNAVAILABLE;
1654         }
1655 
1656         if (!*code) *code = ask_code(buf);
1657         if (sa && (*code == 550)) {
1658             if (!*sa) *sa = strarray_new();
1659             strarray_append(*sa, buf);
1660         }
1661     } while (ISCONT(buf));
1662 
1663     return 0;
1664 }
1665 
pushmsg(struct protstream * in,struct protstream * out,int isdotstuffed)1666 static void pushmsg(struct protstream *in, struct protstream *out,
1667                     int isdotstuffed)
1668 {
1669     char buf[8192], *p;
1670     int lastline_hadendline = 1;
1671 
1672     /* -2: Might need room to add a \r\n\0 set */
1673     while (prot_fgets(buf, sizeof(buf)-2, in)) {
1674         /* dot stuff */
1675         if (!isdotstuffed && (lastline_hadendline == 1) && (buf[0]=='.')) {
1676             (void)prot_putc('.', out);
1677         }
1678         p = buf + strlen(buf) - 1;
1679         if (*p == '\n') {
1680             if (p == buf || p[-1] != '\r') {
1681                 p[0] = '\r';
1682                 p[1] = '\n';
1683                 p[2] = '\0';
1684             }
1685             lastline_hadendline = 1;
1686         }
1687         else if (*p == '\r') {
1688             if (buf[0] == '\r' && buf[1] == '\0') {
1689                 /* The message contained \r\0, and fgets is confusing us.
1690                    XXX ignored
1691                  */
1692                 lastline_hadendline = 1;
1693             } else {
1694                 /*
1695                  * We were unlucky enough to get a CR just before we ran
1696                  * out of buffer--put it back.
1697                  */
1698                 prot_ungetc('\r', in);
1699                 *p = '\0';
1700                 lastline_hadendline = 0;
1701             }
1702         } else {
1703             lastline_hadendline = 0;
1704         }
1705 
1706         /* Remove any lone CR characters */
1707         while ((p = strchr(buf, '\r')) && p[1] != '\n') {
1708             /* Src/Target overlap, use memmove */
1709             /* strlen(p) will result in copying the NUL byte as well */
1710             memmove(p, p+1, strlen(p));
1711         }
1712 
1713         prot_write(out, buf, strlen(buf));
1714     }
1715 
1716     if (!isdotstuffed) {
1717         /* signify end of message */
1718         if (!lastline_hadendline) {
1719             prot_printf(out, "\r\n");
1720         }
1721         prot_printf(out, ".\r\n");
1722     }
1723 }
1724 
lmtp_runtxn(struct backend * conn,struct lmtp_txn * txn)1725 int lmtp_runtxn(struct backend *conn, struct lmtp_txn *txn)
1726 {
1727     int j, code, r = 0;
1728     char buf[8192], rsessionid[MAX_SESSIONID_SIZE];
1729     int onegood;
1730 
1731     assert(conn && txn);
1732     /* pipelining v. no pipelining? */
1733 
1734     /* here's the straightforward non-pipelining version */
1735 
1736     /* rset */
1737     prot_printf(conn->out, "RSET\r\n");
1738     r = getlastresp(buf, sizeof(buf)-1, &code, conn->in, NULL);
1739     if (!ISGOOD(code)) {
1740         goto failall;
1741     }
1742 
1743     if (config_auditlog) {
1744         parse_sessionid(buf, rsessionid);
1745         syslog(LOG_NOTICE, "auditlog: proxy sessionid=<%s> remote=<%s>", session_id(), rsessionid);
1746     }
1747 
1748     /* mail from */
1749     if (!txn->from) {
1750         prot_printf(conn->out, "MAIL FROM:<>");
1751     } else if (txn->from[0] == '<') {
1752         prot_printf(conn->out, "MAIL FROM:%s", txn->from);
1753     } else {
1754         prot_printf(conn->out, "MAIL FROM:<%s>", txn->from);
1755     }
1756     if (CAPA(conn, CAPA_AUTH)) {
1757         prot_printf(conn->out, " AUTH=%s",
1758                     txn->auth && txn->auth[0] ? txn->auth : "<>");
1759     }
1760     prot_printf(conn->out, "\r\n");
1761     r = getlastresp(buf, sizeof(buf)-1, &code, conn->in, NULL);
1762     if (!ISGOOD(code)) {
1763         goto failall;
1764     }
1765 
1766     /* rcpt to */
1767     onegood = 0;
1768     for (j = 0; j < txn->rcpt_num; j++) {
1769         prot_printf(conn->out, "RCPT TO:<%s>", txn->rcpt[j].addr);
1770         if (txn->rcpt[j].ignorequota && CAPA(conn, CAPA_IGNOREQUOTA)) {
1771             prot_printf(conn->out, " IGNOREQUOTA");
1772         }
1773         prot_printf(conn->out, "\r\n");
1774         r = getlastresp(buf, sizeof(buf)-1, &code, conn->in, NULL);
1775         if (r) {
1776             goto failall;
1777         }
1778         txn->rcpt[j].r = revconvert_lmtp(buf);
1779         if (ISGOOD(code)) {
1780             onegood = 1;
1781             txn->rcpt[j].result = RCPT_GOOD;
1782         } else if (TEMPFAIL(code)) {
1783             txn->rcpt[j].result = RCPT_TEMPFAIL;
1784         } else if (PERMFAIL(code)) {
1785             if(txn->tempfail_unknown_mailbox &&
1786                txn->rcpt[j].r == IMAP_MAILBOX_NONEXISTENT) {
1787                 /* If there is a nonexistent error, we have been told
1788                  * to mask it (e.g. proxy got out-of-date mupdate data) */
1789                 txn->rcpt[j].result = RCPT_TEMPFAIL;
1790                 txn->rcpt[j].r = IMAP_AGAIN;
1791             } else {
1792                 txn->rcpt[j].result = RCPT_PERMFAIL;
1793             }
1794         } else {
1795             /* yikes?!? */
1796             code = 400;
1797             goto failall;
1798         }
1799     }
1800     if (!onegood) {
1801         /* all recipients failed! */
1802         return 0;
1803     }
1804 
1805     /* data */
1806     prot_printf(conn->out, "DATA\r\n");
1807     r = getlastresp(buf, sizeof(buf)-1, &code, conn->in, NULL);
1808     if (r) {
1809         goto failall;
1810     }
1811     if (code != 354) {
1812         /* erg? */
1813         if (ISGOOD(code)) code = 400;
1814         r = IMAP_PROTOCOL_ERROR;
1815         goto failall;
1816     }
1817 
1818     /* send the data, dot-stuffing as needed */
1819     pushmsg(txn->data, conn->out, txn->isdotstuffed);
1820 
1821     /* read the response codes, one for each accepted RCPT TO */
1822     for (j = 0; j < txn->rcpt_num; j++) {
1823         if (txn->rcpt[j].result == RCPT_GOOD) {
1824             /* expecting a status code for this recipient */
1825             r = getlastresp(buf, sizeof(buf)-1, &code, conn->in,
1826                             &txn->rcpt[j].resp);
1827             if (r) {
1828                 /* technically, some recipients might've succeeded here,
1829                    but we'll be paranoid */
1830                 goto failall;
1831             }
1832             txn->rcpt[j].r = revconvert_lmtp(buf);
1833             if (ISGOOD(code)) {
1834                 onegood = 1;
1835                 txn->rcpt[j].result = RCPT_GOOD;
1836             } else if (TEMPFAIL(code)) {
1837                 txn->rcpt[j].result = RCPT_TEMPFAIL;
1838             } else if (PERMFAIL(code)) {
1839                 txn->rcpt[j].result = RCPT_PERMFAIL;
1840             } else {
1841                 /* yikes?!? */
1842                 txn->rcpt[j].result = RCPT_TEMPFAIL;
1843             }
1844         }
1845     }
1846 
1847     /* done with txn */
1848     return 0;
1849 
1850  failall:
1851     /* something fatal happened during the transaction; we should assign
1852        'code' to all recipients and return */
1853     for (j = 0; j < txn->rcpt_num; j++) {
1854         if (ISGOOD(code)) {
1855             txn->rcpt[j].r = 0;
1856             txn->rcpt[j].result = RCPT_GOOD;
1857         } else if (TEMPFAIL(code)) {
1858             txn->rcpt[j].r = IMAP_AGAIN;
1859             txn->rcpt[j].result = RCPT_TEMPFAIL;
1860         } else if (PERMFAIL(code)) {
1861             txn->rcpt[j].r = IMAP_PROTOCOL_ERROR;
1862             txn->rcpt[j].result = RCPT_PERMFAIL;
1863         } else {
1864             /* code should have been a valid number */
1865             abort();
1866         }
1867     }
1868 
1869     /* return overall error code already set */
1870     return r;
1871 }
1872