1 /*
2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
3 * Copyright (c) 1990, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 *
6 * By using this file, you agree to the terms and conditions set
7 * forth in the LICENSE file which can be found at the top level
8 * of the sendmail distribution.
9 */
10
11 /*
12 * Copyright 1994-2007 Sun Microsystems, Inc. All rights reserved.
13 * Use is subject to license terms.
14 */
15
16 #ifndef lint
17 static char copyright[] =
18 "@(#) Copyright (c) 1990, 1993, 1994\n\
19 The Regents of the University of California. All rights reserved.\n";
20 #endif /* not lint */
21
22 #pragma ident "%Z%%M% %I% %E% SMI"
23
24 #ifndef lint
25 static char sccsid[] = "@(#)mail.local.c 8.83 (Berkeley) 12/17/98";
26 static char sccsi2[] = "%W% (Sun) %G%";
27 #endif /* not lint */
28
29 #include <sys/param.h>
30 #include <sys/stat.h>
31 #include <sys/socket.h>
32 #include <sys/file.h>
33
34 #include <netinet/in.h>
35
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <netdb.h>
39 #include <pwd.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <signal.h>
43 #include <ctype.h>
44 #include <string.h>
45 #include <sysexits.h>
46 #include <time.h>
47 #include <unistd.h>
48 #include <maillock.h>
49 #include <grp.h>
50
51 #ifdef __STDC__
52 #include <stdarg.h>
53 #else
54 #include <varargs.h>
55 #endif
56
57 #include <syslog.h>
58
59 #include <sysexits.h>
60 #include <ctype.h>
61
62 #include <sm/conf.h>
63 #include <sendmail/pathnames.h>
64
65 /*
66 ** If you don't have flock, you could try using lockf instead.
67 */
68
69 #ifdef LDA_USE_LOCKF
70 # define flock(a, b) lockf(a, b, 0)
71 # ifdef LOCK_EX
72 # undef LOCK_EX
73 # endif /* LOCK_EX */
74 # define LOCK_EX F_LOCK
75 #endif /* LDA_USE_LOCKF */
76
77 #ifndef LOCK_EX
78 # include <sys/file.h>
79 #endif /* ! LOCK_EX */
80
81 #ifndef MAILER_DAEMON
82 # define MAILER_DAEMON "MAILER-DAEMON"
83 #endif
84
85 typedef int bool;
86
87 #define FALSE 0
88 #define TRUE 1
89
90 bool EightBitMime = TRUE; /* advertise 8BITMIME in LMTP */
91 static int eval = EX_OK; /* sysexits.h error value. */
92 static int lmtpmode = 0;
93 bool bouncequota = FALSE; /* permanent error when over quota */
94
95 #define _PATH_MAILDIR "/var/mail"
96 #define _PATH_LOCTMP "/tmp/local.XXXXXX"
97 #define _PATH_LOCHTMP "/tmp/lochd.XXXXXX"
98 #define FALSE 0
99 #define TRUE 1
100 #define MAXLINE 2048
101
102 static void deliver(int, int, char *, bool);
103 static void e_to_sys(int);
104 static void err(const char *fmt, ...);
105 static void notifybiff(char *);
106 static void store(char *, int);
107 static void usage(void);
108 static void vwarn();
109 static void warn(const char *fmt, ...);
110 static void mailerr(const char *, const char *, ...);
111 static void sigterm_handler();
112
113 static char unix_from_line[MAXLINE];
114 static int ulen;
115 static int content_length;
116 static int bfd, hfd; /* temp file */
117 static uid_t src_uid, targ_uid, saved_uid;
118 static int sigterm_caught;
119
120 int
main(argc,argv)121 main(argc, argv)
122 int argc;
123 char *argv[];
124 {
125 struct passwd *pw;
126 int ch;
127 uid_t uid;
128 char *from;
129 struct group *grpptr;
130 void dolmtp();
131
132 openlog("mail.local", 0, LOG_MAIL);
133
134 from = NULL;
135 pw = NULL;
136 sigterm_caught = FALSE;
137
138 (void) sigset(SIGTERM, sigterm_handler);
139
140 while ((ch = getopt(argc, argv, "7bdf:r:l")) != EOF)
141 switch (ch) {
142 case '7': /* Do not advertise 8BITMIME */
143 EightBitMime = FALSE;
144 break;
145
146 case 'b': /* bounce mail when over quota. */
147 bouncequota = TRUE;
148 break;
149
150 case 'd': /* Backward compatible. */
151 break;
152 case 'f':
153 case 'r': /* Backward compatible. */
154 if (from != NULL) {
155 warn("multiple -f options");
156 usage();
157 }
158 from = optarg;
159 break;
160 case 'l':
161 lmtpmode++;
162 break;
163 case '?':
164 default:
165 usage();
166 }
167 argc -= optind;
168 argv += optind;
169
170 notifybiff(NULL); /* initialize biff structures */
171
172 /*
173 * We expect sendmail will invoke us with saved id 0
174 * We then do setgid and setuid defore delivery
175 * setgid to mail group
176 */
177 if ((grpptr = getgrnam("mail")) != NULL)
178 (void) setgid(grpptr->gr_gid);
179 saved_uid = geteuid();
180
181 if (lmtpmode) {
182 if (saved_uid != 0) {
183 warn("only super-user can use -l option");
184 exit(EX_CANTCREAT);
185 }
186 dolmtp(bouncequota);
187 }
188
189 if (!*argv)
190 usage();
191
192 /*
193 * If from not specified, use the name from getlogin() if the
194 * uid matches, otherwise, use the name from the password file
195 * corresponding to the uid.
196 */
197 uid = getuid();
198 if (!from && (!(from = getlogin()) ||
199 !(pw = getpwnam(from)) || pw->pw_uid != uid))
200 from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
201 src_uid = pw ? pw->pw_uid : uid;
202
203 /*
204 * There is no way to distinguish the error status of one delivery
205 * from the rest of the deliveries. So, if we failed hard on one
206 * or more deliveries, but had no failures on any of the others, we
207 * return a hard failure. If we failed temporarily on one or more
208 * deliveries, we return a temporary failure regardless of the other
209 * failures. This results in the delivery being reattempted later
210 * at the expense of repeated failures and multiple deliveries.
211 */
212
213 for (store(from, 0); *argv; ++argv)
214 deliver(hfd, bfd, *argv, bouncequota);
215 return (eval);
216 }
217
218 void
sigterm_handler()219 sigterm_handler()
220 {
221 sigterm_caught = TRUE;
222 (void) sigignore(SIGTERM);
223 }
224
225 char *
parseaddr(s)226 parseaddr(s)
227 char *s;
228 {
229 char *p;
230 int len;
231
232 if (*s++ != '<')
233 return NULL;
234
235 p = s;
236
237 /* at-domain-list */
238 while (*p == '@') {
239 p++;
240 if (*p == '[') {
241 p++;
242 while (isascii(*p) &&
243 (isalnum(*p) || *p == '.' ||
244 *p == '-' || *p == ':'))
245 p++;
246 if (*p++ != ']')
247 return NULL;
248 } else {
249 while ((isascii(*p) && isalnum(*p)) ||
250 strchr(".-_", *p))
251 p++;
252 }
253 if (*p == ',' && p[1] == '@')
254 p++;
255 else if (*p == ':' && p[1] != '@')
256 p++;
257 else
258 return NULL;
259 }
260
261 s = p;
262
263 /* local-part */
264 if (*p == '\"') {
265 p++;
266 while (*p && *p != '\"') {
267 if (*p == '\\') {
268 if (!*++p)
269 return NULL;
270 }
271 p++;
272 }
273 if (!*p++)
274 return NULL;
275 } else {
276 while (*p && *p != '@' && *p != '>') {
277 if (*p == '\\') {
278 if (!*++p)
279 return NULL;
280 } else {
281 if (*p <= ' ' || (*p & 128) ||
282 strchr("<>()[]\\,;:\"", *p))
283 return NULL;
284 }
285 p++;
286 }
287 }
288
289 /* @domain */
290 if (*p == '@') {
291 p++;
292 if (*p == '[') {
293 p++;
294 while (isascii(*p) &&
295 (isalnum(*p) || *p == '.' ||
296 *p == '-' || *p == ':'))
297 p++;
298 if (*p++ != ']')
299 return NULL;
300 } else {
301 while ((isascii(*p) && isalnum(*p)) ||
302 strchr(".-_", *p))
303 p++;
304 }
305 }
306
307 if (*p++ != '>')
308 return NULL;
309 if (*p && *p != ' ')
310 return NULL;
311 len = p - s - 1;
312
313 if (*s == '\0' || len <= 0)
314 {
315 s = MAILER_DAEMON;
316 len = strlen(s);
317 }
318
319 p = malloc(len + 1);
320 if (p == NULL) {
321 printf("421 4.3.0 memory exhausted\r\n");
322 exit(EX_TEMPFAIL);
323 }
324
325 strncpy(p, s, len);
326 p[len] = '\0';
327 return p;
328 }
329
330 char *
process_recipient(addr)331 process_recipient(addr)
332 char *addr;
333 {
334 if (getpwnam(addr) == NULL) {
335 return "550 5.1.1 user unknown";
336 }
337
338 return NULL;
339 }
340
341 #define RCPT_GROW 30
342
343 void
dolmtp(bouncequota)344 dolmtp(bouncequota)
345 bool bouncequota;
346 {
347 char *return_path = NULL;
348 char **rcpt_addr = NULL;
349 int rcpt_num = 0;
350 int rcpt_alloc = 0;
351 bool gotlhlo = FALSE;
352 char myhostname[MAXHOSTNAMELEN];
353 char buf[4096];
354 char *err;
355 char *p;
356 int i;
357
358 gethostname(myhostname, sizeof myhostname - 1);
359
360 printf("220 %s LMTP ready\r\n", myhostname);
361 for (;;) {
362 if (sigterm_caught) {
363 for (; rcpt_num > 0; rcpt_num--)
364 printf("451 4.3.0 shutting down\r\n");
365 exit(EX_OK);
366 }
367 fflush(stdout);
368 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
369 exit(EX_OK);
370 }
371 p = buf + strlen(buf) - 1;
372 if (p >= buf && *p == '\n')
373 *p-- = '\0';
374 if (p >= buf && *p == '\r')
375 *p-- = '\0';
376
377 switch (buf[0]) {
378
379 case 'd':
380 case 'D':
381 if (strcasecmp(buf, "data") == 0) {
382 if (rcpt_num == 0) {
383 printf("503 5.5.1 No recipients\r\n");
384 continue;
385 }
386 store(return_path, rcpt_num);
387 if (bfd == -1 || hfd == -1)
388 continue;
389
390 for (i = 0; i < rcpt_num; i++) {
391 p = strchr(rcpt_addr[i], '+');
392 if (p != NULL)
393 *p++ = '\0';
394 deliver(hfd, bfd, rcpt_addr[i],
395 bouncequota);
396 }
397 close(bfd);
398 close(hfd);
399 goto rset;
400 }
401 goto syntaxerr;
402 /* NOTREACHED */
403 break;
404
405 case 'l':
406 case 'L':
407 if (strncasecmp(buf, "lhlo ", 5) == 0)
408 {
409 /* check for duplicate per RFC 1651 4.2 */
410 if (gotlhlo)
411 {
412 printf("503 %s Duplicate LHLO\r\n",
413 myhostname);
414 continue;
415 }
416 gotlhlo = TRUE;
417 printf("250-%s\r\n", myhostname);
418 if (EightBitMime)
419 printf("250-8BITMIME\r\n");
420 printf("250-ENHANCEDSTATUSCODES\r\n");
421 printf("250 PIPELINING\r\n");
422 continue;
423 }
424 goto syntaxerr;
425 /* NOTREACHED */
426 break;
427
428 case 'm':
429 case 'M':
430 if (strncasecmp(buf, "mail ", 5) == 0) {
431 if (return_path != NULL) {
432 printf("503 5.5.1 Nested MAIL command\r\n");
433 continue;
434 }
435 if (strncasecmp(buf+5, "from:", 5) != 0 ||
436 ((return_path = parseaddr(buf+10)) == NULL)) {
437 printf("501 5.5.4 Syntax error in parameters\r\n");
438 continue;
439 }
440 printf("250 2.5.0 ok\r\n");
441 continue;
442 }
443 goto syntaxerr;
444
445 case 'n':
446 case 'N':
447 if (strcasecmp(buf, "noop") == 0) {
448 printf("250 2.0.0 ok\r\n");
449 continue;
450 }
451 goto syntaxerr;
452
453 case 'q':
454 case 'Q':
455 if (strcasecmp(buf, "quit") == 0) {
456 printf("221 2.0.0 bye\r\n");
457 exit(EX_OK);
458 }
459 goto syntaxerr;
460
461 case 'r':
462 case 'R':
463 if (strncasecmp(buf, "rcpt ", 5) == 0) {
464 if (return_path == NULL) {
465 printf("503 5.5.1 Need MAIL command\r\n");
466 continue;
467 }
468 if (rcpt_num >= rcpt_alloc) {
469 rcpt_alloc += RCPT_GROW;
470 rcpt_addr = (char **)
471 realloc((char *)rcpt_addr,
472 rcpt_alloc * sizeof(char **));
473 if (rcpt_addr == NULL) {
474 printf("421 4.3.0 memory exhausted\r\n");
475 exit(EX_TEMPFAIL);
476 }
477 }
478 if (strncasecmp(buf+5, "to:", 3) != 0 ||
479 ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) {
480 printf("501 5.5.4 Syntax error in parameters\r\n");
481 continue;
482 }
483 if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) {
484 printf("%s\r\n", err);
485 continue;
486 }
487 rcpt_num++;
488 printf("250 2.1.5 ok\r\n");
489 continue;
490 }
491 else if (strcasecmp(buf, "rset") == 0) {
492 printf("250 2.0.0 ok\r\n");
493
494 rset:
495 while (rcpt_num > 0) {
496 free(rcpt_addr[--rcpt_num]);
497 }
498 if (return_path != NULL)
499 free(return_path);
500 return_path = NULL;
501 continue;
502 }
503 goto syntaxerr;
504
505 case 'v':
506 case 'V':
507 if (strncasecmp(buf, "vrfy ", 5) == 0) {
508 printf("252 2.3.3 try RCPT to attempt delivery\r\n");
509 continue;
510 }
511 goto syntaxerr;
512
513 default:
514 syntaxerr:
515 printf("500 5.5.2 Syntax error\r\n");
516 continue;
517 }
518 }
519 }
520
521 static void
store(from,lmtprcpts)522 store(from, lmtprcpts)
523 char *from;
524 int lmtprcpts;
525 {
526 FILE *fp = NULL;
527 time_t tval;
528 bool fullline = TRUE; /* current line is terminated */
529 bool prevfl; /* previous line was terminated */
530 char line[MAXLINE];
531 FILE *bfp, *hfp;
532 char *btn, *htn;
533 int in_header_section;
534 int newfd;
535
536 bfd = -1;
537 hfd = -1;
538 btn = strdup(_PATH_LOCTMP);
539 if ((bfd = mkstemp(btn)) == -1 || (bfp = fdopen(bfd, "w+")) == NULL) {
540 if (bfd != -1)
541 (void) close(bfd);
542 if (lmtprcpts) {
543 printf("451 4.3.0 unable to open temporary file\r\n");
544 return;
545 } else {
546 mailerr("451 4.3.0", "unable to open temporary file");
547 exit(eval);
548 }
549 }
550 (void) unlink(btn);
551 free(btn);
552
553 if (lmtpmode) {
554 printf("354 go ahead\r\n");
555 fflush(stdout);
556 }
557
558 htn = strdup(_PATH_LOCHTMP);
559 if ((hfd = mkstemp(htn)) == -1 || (hfp = fdopen(hfd, "w+")) == NULL) {
560 if (hfd != -1)
561 (void) close(hfd);
562 e_to_sys(errno);
563 err("unable to open temporary file");
564 }
565 (void) unlink(htn);
566 free(htn);
567
568 in_header_section = TRUE;
569 content_length = 0;
570 fp = hfp;
571
572 line[0] = '\0';
573 while (fgets(line, sizeof(line), stdin) != (char *)NULL)
574 {
575 size_t line_len = 0;
576 int peek;
577
578 prevfl = fullline; /* preserve state of previous line */
579 while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
580 line_len++;
581 line_len++;
582
583 /* Check for dot-stuffing */
584 if (prevfl && lmtprcpts && line[0] == '.')
585 {
586 if (line[1] == '\n' ||
587 (line[1] == '\r' && line[2] == '\n'))
588 goto lmtpdot;
589 memcpy(line, line + 1, line_len);
590 line_len--;
591 }
592
593 /* Check to see if we have the full line from fgets() */
594 fullline = FALSE;
595 if (line_len > 0)
596 {
597 if (line[line_len - 1] == '\n')
598 {
599 if (line_len >= 2 &&
600 line[line_len - 2] == '\r')
601 {
602 line[line_len - 2] = '\n';
603 line[line_len - 1] = '\0';
604 line_len--;
605 }
606 fullline = TRUE;
607 }
608 else if (line[line_len - 1] == '\r')
609 {
610 /* Did we just miss the CRLF? */
611 peek = fgetc(stdin);
612 if (peek == '\n')
613 {
614 line[line_len - 1] = '\n';
615 fullline = TRUE;
616 }
617 else
618 (void) ungetc(peek, stdin);
619 }
620 }
621 else
622 fullline = TRUE;
623
624 if (prevfl && line[0] == '\n' && in_header_section) {
625 in_header_section = FALSE;
626 if (fflush(fp) == EOF || ferror(fp)) {
627 if (lmtprcpts) {
628 while (lmtprcpts--)
629 printf("451 4.3.0 temporary file write error\r\n");
630 fclose(fp);
631 return;
632 } else {
633 mailerr("451 4.3.0",
634 "temporary file write error");
635 fclose(fp);
636 exit(eval);
637 }
638 }
639 fp = bfp;
640 continue;
641 }
642
643 if (in_header_section) {
644 if (strncasecmp("Content-Length:", line, 15) == 0) {
645 continue; /* skip this header */
646 }
647 } else
648 content_length += strlen(line);
649 (void) fwrite(line, sizeof(char), line_len, fp);
650 if (ferror(fp)) {
651 if (lmtprcpts) {
652 while (lmtprcpts--)
653 printf("451 4.3.0 temporary file write error\r\n");
654 fclose(fp);
655 return;
656 } else {
657 mailerr("451 4.3.0",
658 "temporary file write error");
659 fclose(fp);
660 exit(eval);
661 }
662 }
663 }
664 if (sigterm_caught) {
665 if (lmtprcpts)
666 while (lmtprcpts--)
667 printf("451 4.3.0 shutting down\r\n");
668 else
669 mailerr("451 4.3.0", "shutting down");
670 fclose(fp);
671 exit(eval);
672 }
673
674 if (lmtprcpts) {
675 /* Got a premature EOF -- toss message and exit */
676 exit(EX_OK);
677 }
678
679 /* If message not newline terminated, need an extra. */
680 if (!strchr(line, '\n')) {
681 (void) putc('\n', fp);
682 content_length++;
683 }
684
685 lmtpdot:
686
687 /* Output a newline; note, empty messages are allowed. */
688 (void) putc('\n', fp);
689
690 if (fflush(fp) == EOF || ferror(fp)) {
691 if (lmtprcpts) {
692 while (lmtprcpts--) {
693 printf("451 4.3.0 temporary file write error\r\n");
694 }
695 fclose(fp);
696 return;
697 } else {
698 mailerr("451 4.3.0", "temporary file write error");
699 fclose(fp);
700 exit(eval);
701 }
702 }
703
704 if ((newfd = dup(bfd)) >= 0) {
705 fclose(bfp);
706 bfd = newfd;
707 }
708 if ((newfd = dup(hfd)) >= 0) {
709 fclose(hfp);
710 hfd = newfd;
711 }
712 (void) time(&tval);
713 (void) snprintf(unix_from_line, sizeof (unix_from_line), "From %s %s",
714 from, ctime(&tval));
715 ulen = strlen(unix_from_line);
716 }
717
718 static void
handle_error(err_num,bouncequota,path)719 handle_error(err_num, bouncequota, path)
720 int err_num;
721 bool bouncequota;
722 char *path;
723 {
724 #ifdef EDQUOT
725 if (err_num == EDQUOT && bouncequota) {
726 mailerr("552 5.2.2", "%s: %s", path, sm_errstring(err_num));
727 } else
728 #endif /* EDQUOT */
729 mailerr("450 4.2.0", "%s: %s", path, sm_errstring(err_num));
730 }
731
732 static void
deliver(hfd,bfd,name,bouncequota)733 deliver(hfd, bfd, name, bouncequota)
734 int hfd;
735 int bfd;
736 char *name;
737 bool bouncequota;
738 {
739 struct stat fsb, sb;
740 int mbfd = -1, nr, nw = 0, off;
741 char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
742 off_t curoff, cursize;
743 int len;
744 struct passwd *pw = NULL;
745
746 /*
747 * Disallow delivery to unknown names -- special mailboxes
748 * can be handled in the sendmail aliases file.
749 */
750 if ((pw = getpwnam(name)) == NULL) {
751 eval = EX_TEMPFAIL;
752 mailerr("451 4.3.0", "cannot lookup name: %s", name);
753 return;
754 }
755 endpwent();
756
757 if (sigterm_caught) {
758 mailerr("451 4.3.0", "shutting down");
759 return;
760 }
761
762 /* mailbox may be NFS mounted, seteuid to user */
763 targ_uid = pw->pw_uid;
764 (void) seteuid(targ_uid);
765
766 if ((saved_uid != 0) && (src_uid != targ_uid)) {
767 /*
768 * If saved_uid == 0 (root), anything is OK; this is
769 * as it should be. But to prevent a random user from
770 * calling "mail.local foo" in an attempt to hijack
771 * foo's mail-box, make sure src_uid == targ_uid o/w.
772 */
773 warn("%s: wrong owner (is %d, should be %d)",
774 name, src_uid, targ_uid);
775 eval = EX_CANTCREAT;
776 return;
777 }
778
779 path[0] = '\0';
780 (void) snprintf(path, sizeof (path), "%s/%s", _PATH_MAILDIR, name);
781
782 /*
783 * If the mailbox is linked or a symlink, fail. There's an obvious
784 * race here, that the file was replaced with a symbolic link after
785 * the lstat returned, but before the open. We attempt to detect
786 * this by comparing the original stat information and information
787 * returned by an fstat of the file descriptor returned by the open.
788 *
789 * NB: this is a symptom of a larger problem, that the mail spooling
790 * directory is writeable by the wrong users. If that directory is
791 * writeable, system security is compromised for other reasons, and
792 * it cannot be fixed here.
793 *
794 * If we created the mailbox, set the owner/group. If that fails,
795 * just return. Another process may have already opened it, so we
796 * can't unlink it. Historically, binmail set the owner/group at
797 * each mail delivery. We no longer do this, assuming that if the
798 * ownership or permissions were changed there was a reason.
799 *
800 * XXX
801 * open(2) should support flock'ing the file.
802 */
803 tryagain:
804 /* should check lock status, but... maillock return no value */
805 maillock(name, 10);
806
807 if (sigterm_caught) {
808 mailerr("451 4.3.0", "shutting down");
809 goto err0;
810 }
811
812 if (lstat(path, &sb)) {
813 mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY,
814 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
815 if (mbfd != -1)
816 (void) fchmod(mbfd, 0660);
817
818
819 if (mbfd == -1) {
820 if (errno == EEXIST) {
821 mailunlock();
822 goto tryagain;
823 }
824 }
825 } else if (sb.st_nlink != 1) {
826 mailerr("550 5.2.0", "%s: too many links", path);
827 goto err0;
828 } else if (!S_ISREG(sb.st_mode)) {
829 mailerr("550 5.2.0", "%s: irregular file", path);
830 goto err0;
831 } else {
832 mbfd = open(path, O_APPEND|O_WRONLY, 0);
833 if (mbfd != -1 &&
834 (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
835 S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
836 sb.st_ino != fsb.st_ino)) {
837 eval = EX_TEMPFAIL;
838 mailerr("550 5.2.0",
839 "%s: fstat: file changed after open", path);
840 goto err1;
841 }
842 }
843
844 if (mbfd == -1) {
845 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
846 goto err0;
847 }
848
849 if (sigterm_caught) {
850 mailerr("451 4.3.0", "shutting down");
851 goto err0;
852 }
853
854 /* Get the starting offset of the new message for biff. */
855 curoff = lseek(mbfd, (off_t)0, SEEK_END);
856 (void) snprintf(biffmsg, sizeof (biffmsg), "%s@%ld\n", name, curoff);
857
858 /* Copy the message into the file. */
859 if (lseek(hfd, (off_t)0, SEEK_SET) == (off_t)-1) {
860 mailerr("450 4.2.0", "temporary file: %s", strerror(errno));
861 goto err1;
862 }
863 /* Copy the message into the file. */
864 if (lseek(bfd, (off_t)0, SEEK_SET) == (off_t)-1) {
865 mailerr("450 4.2.0", "temporary file: %s", strerror(errno));
866 goto err1;
867 }
868 if ((write(mbfd, unix_from_line, ulen)) != ulen) {
869 handle_error(errno, bouncequota, path);
870 goto err2;
871 }
872
873 if (sigterm_caught) {
874 mailerr("451 4.3.0", "shutting down");
875 goto err2;
876 }
877
878 while ((nr = read(hfd, buf, sizeof (buf))) > 0)
879 for (off = 0; off < nr; nr -= nw, off += nw)
880 if ((nw = write(mbfd, buf + off, nr)) < 0)
881 {
882 handle_error(errno, bouncequota, path);
883 goto err2;
884 }
885 if (nr < 0) {
886 handle_error(errno, bouncequota, path);
887 goto err2;
888 }
889
890 if (sigterm_caught) {
891 mailerr("451 4.3.0", "shutting down");
892 goto err2;
893 }
894
895 (void) snprintf(buf, sizeof (buf), "Content-Length: %d\n\n",
896 content_length);
897 len = strlen(buf);
898 if (write(mbfd, buf, len) != len) {
899 handle_error(errno, bouncequota, path);
900 goto err2;
901 }
902
903 if (sigterm_caught) {
904 mailerr("451 4.3.0", "shutting down");
905 goto err2;
906 }
907
908 while ((nr = read(bfd, buf, sizeof (buf))) > 0) {
909 for (off = 0; off < nr; nr -= nw, off += nw)
910 if ((nw = write(mbfd, buf + off, nr)) < 0) {
911 handle_error(errno, bouncequota, path);
912 goto err2;
913 }
914 if (sigterm_caught) {
915 mailerr("451 4.3.0", "shutting down");
916 goto err2;
917 }
918 }
919 if (nr < 0) {
920 handle_error(errno, bouncequota, path);
921 goto err2;
922 }
923
924 /* Flush to disk, don't wait for update. */
925 if (fsync(mbfd)) {
926 handle_error(errno, bouncequota, path);
927 err2: if (mbfd >= 0)
928 (void)ftruncate(mbfd, curoff);
929 err1: (void)close(mbfd);
930 err0: mailunlock();
931 (void)seteuid(saved_uid);
932 return;
933 }
934
935 /*
936 ** Save the current size so if the close() fails below
937 ** we can make sure no other process has changed the mailbox
938 ** between the failed close and the re-open()/re-lock().
939 ** If something else has changed the size, we shouldn't
940 ** try to truncate it as we may do more harm then good
941 ** (e.g., truncate a later message delivery).
942 */
943
944 if (fstat(mbfd, &sb) < 0)
945 cursize = 0;
946 else
947 cursize = sb.st_size;
948
949 /* Close and check -- NFS doesn't write until the close. */
950 if (close(mbfd))
951 {
952 handle_error(errno, bouncequota, path);
953 mbfd = open(path, O_WRONLY, 0);
954 if (mbfd < 0 ||
955 cursize == 0
956 || flock(mbfd, LOCK_EX) < 0 ||
957 fstat(mbfd, &sb) < 0 ||
958 sb.st_size != cursize ||
959 sb.st_nlink != 1 ||
960 !S_ISREG(sb.st_mode) ||
961 sb.st_dev != fsb.st_dev ||
962 sb.st_ino != fsb.st_ino ||
963 sb.st_uid != fsb.st_uid)
964 {
965 /* Don't use a bogus file */
966 if (mbfd >= 0)
967 {
968 (void) close(mbfd);
969 mbfd = -1;
970 }
971 }
972
973 /* Attempt to truncate back to pre-write size */
974 goto err2;
975 } else
976 notifybiff(biffmsg);
977
978 mailunlock();
979
980 (void)seteuid(saved_uid);
981
982 if (lmtpmode) {
983 printf("250 2.1.5 %s OK\r\n", name);
984 }
985 }
986
987 static void
notifybiff(msg)988 notifybiff(msg)
989 char *msg;
990 {
991 static struct sockaddr_in addr;
992 static int f = -1;
993 struct hostent *hp;
994 struct servent *sp;
995 int len;
996
997 if (msg == NULL) {
998 /* Be silent if biff service not available. */
999 if ((sp = getservbyname("biff", "udp")) == NULL)
1000 return;
1001 if ((hp = gethostbyname("localhost")) == NULL) {
1002 warn("localhost: %s", strerror(errno));
1003 return;
1004 }
1005 addr.sin_family = hp->h_addrtype;
1006 (void) memmove(&addr.sin_addr, hp->h_addr, hp->h_length);
1007 addr.sin_port = sp->s_port;
1008 return;
1009 }
1010
1011 if (addr.sin_family == 0)
1012 return; /* did not initialize */
1013
1014 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
1015 warn("socket: %s", strerror(errno));
1016 return;
1017 }
1018 len = strlen(msg) + 1;
1019 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof (addr))
1020 != len)
1021 warn("sendto biff: %s", strerror(errno));
1022 }
1023
1024 static void
usage()1025 usage()
1026 {
1027 eval = EX_USAGE;
1028 err("usage: mail.local [-l] [-f from] user ...");
1029 }
1030
1031 static void
1032 /*VARARGS2*/
1033 #ifdef __STDC__
mailerr(const char * hdr,const char * fmt,...)1034 mailerr(const char *hdr, const char *fmt, ...)
1035 #else
1036 mailerr(hdr, fmt, va_alist)
1037 const char *hdr;
1038 const char *fmt;
1039 va_dcl
1040 #endif
1041 {
1042 va_list ap;
1043
1044 #ifdef __STDC__
1045 va_start(ap, fmt);
1046 #else
1047 va_start(ap);
1048 #endif
1049 if (lmtpmode)
1050 {
1051 if (hdr != NULL)
1052 printf("%s ", hdr);
1053 vprintf(fmt, ap);
1054 printf("\r\n");
1055 }
1056 else
1057 {
1058 e_to_sys(errno);
1059 vwarn(fmt, ap);
1060 }
1061 }
1062
1063 static void
1064 /*VARARGS1*/
1065 #ifdef __STDC__
err(const char * fmt,...)1066 err(const char *fmt, ...)
1067 #else
1068 err(fmt, va_alist)
1069 const char *fmt;
1070 va_dcl
1071 #endif
1072 {
1073 va_list ap;
1074
1075 #ifdef __STDC__
1076 va_start(ap, fmt);
1077 #else
1078 va_start(ap);
1079 #endif
1080 vwarn(fmt, ap);
1081 va_end(ap);
1082
1083 exit(eval);
1084 }
1085
1086 static void
1087 /*VARARGS1*/
1088 #ifdef __STDC__
warn(const char * fmt,...)1089 warn(const char *fmt, ...)
1090 #else
1091 warn(fmt, va_alist)
1092 const char *fmt;
1093 va_dcl
1094 #endif
1095 {
1096 va_list ap;
1097
1098 #ifdef __STDC__
1099 va_start(ap, fmt);
1100 #else
1101 va_start(ap);
1102 #endif
1103 vwarn(fmt, ap);
1104 va_end(ap);
1105 }
1106
1107 static void
vwarn(fmt,ap)1108 vwarn(fmt, ap)
1109 const char *fmt;
1110 va_list ap;
1111 {
1112 /*
1113 * Log the message to stderr.
1114 *
1115 * Don't use LOG_PERROR as an openlog() flag to do this,
1116 * it's not portable enough.
1117 */
1118 if (eval != EX_USAGE)
1119 (void) fprintf(stderr, "mail.local: ");
1120 (void) vfprintf(stderr, fmt, ap);
1121 (void) fprintf(stderr, "\n");
1122
1123 /* Log the message to syslog. */
1124 vsyslog(LOG_ERR, fmt, ap);
1125 }
1126
1127 /*
1128 * e_to_sys --
1129 * Guess which errno's are temporary. Gag me.
1130 */
1131 static void
e_to_sys(num)1132 e_to_sys(num)
1133 int num;
1134 {
1135 /* Temporary failures override hard errors. */
1136 if (eval == EX_TEMPFAIL)
1137 return;
1138
1139 switch (num) /* Hopefully temporary errors. */
1140 {
1141 #ifdef EDQUOT
1142 case EDQUOT: /* Disc quota exceeded */
1143 if (bouncequota)
1144 {
1145 eval = EX_UNAVAILABLE;
1146 break;
1147 }
1148 /* FALLTHROUGH */
1149 #endif /* EDQUOT */
1150 #ifdef EAGAIN
1151 case EAGAIN: /* Resource temporarily unavailable */
1152 #endif
1153 #ifdef EBUSY
1154 case EBUSY: /* Device busy */
1155 #endif
1156 #ifdef EPROCLIM
1157 case EPROCLIM: /* Too many processes */
1158 #endif
1159 #ifdef EUSERS
1160 case EUSERS: /* Too many users */
1161 #endif
1162 #ifdef ECONNABORTED
1163 case ECONNABORTED: /* Software caused connection abort */
1164 #endif
1165 #ifdef ECONNREFUSED
1166 case ECONNREFUSED: /* Connection refused */
1167 #endif
1168 #ifdef ECONNRESET
1169 case ECONNRESET: /* Connection reset by peer */
1170 #endif
1171 #ifdef EDEADLK
1172 case EDEADLK: /* Resource deadlock avoided */
1173 #endif
1174 #ifdef EFBIG
1175 case EFBIG: /* File too large */
1176 #endif
1177 #ifdef EHOSTDOWN
1178 case EHOSTDOWN: /* Host is down */
1179 #endif
1180 #ifdef EHOSTUNREACH
1181 case EHOSTUNREACH: /* No route to host */
1182 #endif
1183 #ifdef EMFILE
1184 case EMFILE: /* Too many open files */
1185 #endif
1186 #ifdef ENETDOWN
1187 case ENETDOWN: /* Network is down */
1188 #endif
1189 #ifdef ENETRESET
1190 case ENETRESET: /* Network dropped connection on reset */
1191 #endif
1192 #ifdef ENETUNREACH
1193 case ENETUNREACH: /* Network is unreachable */
1194 #endif
1195 #ifdef ENFILE
1196 case ENFILE: /* Too many open files in system */
1197 #endif
1198 #ifdef ENOBUFS
1199 case ENOBUFS: /* No buffer space available */
1200 #endif
1201 #ifdef ENOMEM
1202 case ENOMEM: /* Cannot allocate memory */
1203 #endif
1204 #ifdef ENOSPC
1205 case ENOSPC: /* No space left on device */
1206 #endif
1207 #ifdef EROFS
1208 case EROFS: /* Read-only file system */
1209 #endif
1210 #ifdef ESTALE
1211 case ESTALE: /* Stale NFS file handle */
1212 #endif
1213 #ifdef ETIMEDOUT
1214 case ETIMEDOUT: /* Connection timed out */
1215 #endif
1216 #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1217 case EWOULDBLOCK: /* Operation would block. */
1218 #endif
1219 eval = EX_TEMPFAIL;
1220 break;
1221 default:
1222 eval = EX_UNAVAILABLE;
1223 break;
1224 }
1225 }
1226