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