Deleted Added
sdiff udiff text old ( 38089 ) new ( 42580 )
full compact
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 of
8 * the sendmail distribution.
9 *
10 */
11
12#ifndef lint
13static char copyright[] =
14"@(#) Copyright (c) 1990, 1993, 1994\n\
15 The Regents of the University of California. All rights reserved.\n";
16#endif /* not lint */
17
18#ifndef lint
19static char sccsid[] = "@(#)mail.local.c 8.78 (Berkeley) 5/19/98";
20#endif /* not lint */
21
22/*
23 * This is not intended to work on System V derived systems
24 * such as Solaris or HP-UX, since they use a totally different
25 * approach to mailboxes (essentially, they have a setgid program
26 * rather than setuid, and they rely on the ability to "give away"
27 * files to do their work). IT IS NOT A BUG that this doesn't
28 * work on such architectures.
29 */
30
31#include <sys/param.h>
32#include <sys/stat.h>
33#include <sys/socket.h>
34#include <sys/file.h>
35
36#include <netinet/in.h>
37
38#include <errno.h>
39#include <fcntl.h>
40#include <netdb.h>
41#include <pwd.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <syslog.h>
46#include <time.h>
47#include <unistd.h>
48#ifdef EX_OK
49# undef EX_OK /* unistd.h may have another use for this */
50#endif
51#include <sysexits.h>
52#include <ctype.h>
53
54#ifdef __STDC__
55#include <stdarg.h>
56#else
57#include <varargs.h>
58#endif
59
60#if (defined(sun) && defined(__svr4__)) || defined(__SVR4)
61# define USE_LOCKF 1
62# define USE_SETEUID 1
63# define _PATH_MAILDIR "/var/mail"
64#endif
65
66#if (defined(sun) && !defined(__svr4__)) && !defined(__SVR4)
67# ifdef __dead
68# undef __dead
69# define __dead
70# endif
71#endif
72
73#if defined(_AIX)
74# define USE_LOCKF 1
75# define USE_SETEUID 1
76# define USE_VSYSLOG 0
77#endif
78
79#if defined(__hpux)
80# define USE_LOCKF 1
81# define USE_SETRESUID 1
82# define USE_VSYSLOG 0
83# ifdef __dead
84# undef __dead
85# define __dead
86# endif
87#endif
88
89#if defined(_CRAY)
90# if !defined(MAXPATHLEN)
91# define MAXPATHLEN PATHSIZE
92# endif
93# define USE_VSYSLOG 0
94# define _PATH_MAILDIR "/usr/spool/mail"
95#endif
96
97#if defined(ultrix)
98# define USE_VSYSLOG 0
99#endif
100
101#if defined(__osf__)
102# define USE_VSYSLOG 0
103#endif
104
105#if defined(NeXT)
106# include <libc.h>
107# define _PATH_MAILDIR "/usr/spool/mail"
108# define __dead /* empty */
109# define S_IRUSR S_IREAD
110# define S_IWUSR S_IWRITE
111#endif
112
113#if defined(IRIX64) || defined(IRIX5) || defined(IRIX6)
114# include <paths.h>
115# define HASSTRERROR 1 /* has strerror(3) */
116#endif
117
118/*
119 * If you don't have flock, you could try using lockf instead.
120 */
121
122#ifdef USE_LOCKF
123# define flock(a, b) lockf(a, b, 0)
124# define LOCK_EX F_LOCK
125#endif
126
127#ifndef USE_VSYSLOG
128# define USE_VSYSLOG 1
129#endif
130
131#ifndef LOCK_EX
132# include <sys/file.h>
133#endif
134
135#if defined(BSD4_4) || defined(__GLIBC__)
136# include "pathnames.h"
137#endif
138
139#ifndef __P
140# ifdef __STDC__
141# define __P(protos) protos
142# else
143# define __P(protos) ()
144# define const
145# endif
146#endif
147#ifndef __dead
148# if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__)
149# define __dead __volatile
150# else
151# define __dead
152# endif
153#endif
154
155#ifdef BSD4_4
156# define HAS_ST_GEN 1
157#else
158# ifndef _BSD_VA_LIST_
159# define _BSD_VA_LIST_ va_list
160# endif
161#endif
162
163#if defined(BSD4_4) || defined(linux)
164# define HASSNPRINTF 1
165#else
166# ifndef ultrix
167extern FILE *fdopen __P((int, const char *));
168# endif
169#endif
170
171#if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206)
172# define HASSNPRINTF 1 /* has snprintf starting in 2.6 */
173#endif
174
175#if !HASSNPRINTF
176extern int snprintf __P((char *, size_t, const char *, ...));
177# ifndef _CRAY
178extern int vsnprintf __P((char *, size_t, const char *, ...));
179# endif
180#endif
181
182#if defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__)
183# ifndef HASSTRERROR
184# define HASSTRERROR 1
185# endif
186#endif
187
188#if !HASSTRERROR
189extern char *strerror __P((int));
190#endif
191
192/*
193 * If you don't have setreuid, and you have saved uids, and you have
194 * a seteuid() call that doesn't try to emulate using setuid(), then
195 * you can try defining USE_SETEUID.
196 */
197#ifdef USE_SETEUID
198# define setreuid(r, e) seteuid(e)
199#endif
200
201/*
202 * And of course on hpux you have setresuid()
203 */
204#ifdef USE_SETRESUID
205# define setreuid(r, e) setresuid(-1, e, -1)
206#endif
207
208#ifndef _PATH_LOCTMP
209# define _PATH_LOCTMP "/tmp/local.XXXXXX"
210#endif
211#ifndef _PATH_MAILDIR
212# define _PATH_MAILDIR "/var/spool/mail"
213#endif
214
215#ifndef S_ISREG
216# define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG)
217#endif
218
219int eval = EX_OK; /* sysexits.h error value. */
220int lmtpmode = 0;
221u_char tTdvect[100];
222
223void deliver __P((int, char *, int, int));
224void e_to_sys __P((int));
225void err __P((const char *, ...)) __dead2;
226void notifybiff __P((char *));
227int store __P((char *, int));
228void usage __P((void));
229void vwarn __P((const char *, _BSD_VA_LIST_));
230void warn __P((const char *, ...));
231void lockmbox __P((char *));
232void unlockmbox __P((void));
233void mailerr __P((const char *, const char *, ...));
234void dolmtp __P((int, int));
235
236int
237main(argc, argv)
238 int argc;
239 char *argv[];
240{
241 struct passwd *pw;
242 int ch, fd, nobiff, nofsync;
243 uid_t uid;
244 char *from;
245 extern char *optarg;
246 extern int optind;
247
248 /* make sure we have some open file descriptors */
249 for (fd = 10; fd < 30; fd++)
250 (void) close(fd);
251
252 /* use a reasonable umask */
253 (void) umask(0077);
254
255#ifdef LOG_MAIL
256 openlog("mail.local", 0, LOG_MAIL);
257#else
258 openlog("mail.local", 0);
259#endif
260
261 from = NULL;
262 nobiff = 0;
263 nofsync = 0;
264 while ((ch = getopt(argc, argv, "bdf:r:ls")) != -1)
265 switch(ch) {
266 case 'b':
267 nobiff++;
268 break;
269 case 'd': /* Backward compatible. */
270 break;
271 case 'f':
272 case 'r': /* Backward compatible. */
273 if (from != NULL) {
274 warn("multiple -f options");
275 usage();
276 }
277 from = optarg;
278 break;
279 case 'l':
280 lmtpmode++;
281 break;
282 case 's':
283 nofsync++;
284 break;
285 case '?':
286 default:
287 usage();
288 }
289 argc -= optind;
290 argv += optind;
291
292 if (lmtpmode)
293 dolmtp(nobiff, nofsync);
294
295 if (!*argv)
296 usage();
297
298 /*
299 * If from not specified, use the name from getlogin() if the
300 * uid matches, otherwise, use the name from the password file
301 * corresponding to the uid.
302 */
303 uid = getuid();
304 if (!from && (!(from = getlogin()) ||
305 !(pw = getpwnam(from)) || pw->pw_uid != uid))
306 from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
307
308 /*
309 * There is no way to distinguish the error status of one delivery
310 * from the rest of the deliveries. So, if we failed hard on one
311 * or more deliveries, but had no failures on any of the others, we
312 * return a hard failure. If we failed temporarily on one or more
313 * deliveries, we return a temporary failure regardless of the other
314 * failures. This results in the delivery being reattempted later
315 * at the expense of repeated failures and multiple deliveries.
316 */
317 for (fd = store(from, 0); *argv; ++argv)
318 deliver(fd, *argv, nobiff, nofsync);
319 exit(eval);
320}
321
322char *
323parseaddr(s)
324 char *s;
325{
326 char *p;
327 int len;
328
329 if (*s++ != '<')
330 return NULL;
331
332 p = s;
333
334 /* at-domain-list */
335 while (*p == '@') {
336 p++;
337 if (*p == '[') {
338 p++;
339 while (isascii(*p) &&
340 (isalnum(*p) || *p == '.' ||
341 *p == '-' || *p == ':'))
342 p++;
343 if (*p++ != ']')
344 return NULL;
345 } else {
346 while ((isascii(*p) && isalnum(*p)) ||
347 *p == '.' || *p == '-')
348 p++;
349 }
350 if (*p == ',' && p[1] == '@')
351 p++;
352 else if (*p == ':' && p[1] != '@')
353 p++;
354 else
355 return NULL;
356 }
357
358 /* local-part */
359 if (*p == '\"') {
360 p++;
361 while (*p && *p != '\"') {
362 if (*p == '\\') {
363 if (!*++p)
364 return NULL;
365 }
366 p++;
367 }
368 if (!*p++)
369 return NULL;
370 } else {
371 while (*p && *p != '@' && *p != '>') {
372 if (*p == '\\') {
373 if (!*++p)
374 return NULL;
375 } else {
376 if (*p <= ' ' || (*p & 128) ||
377 strchr("<>()[]\\,;:\"", *p))
378 return NULL;
379 }
380 p++;
381 }
382 }
383
384 /* @domain */
385 if (*p == '@') {
386 p++;
387 if (*p == '[') {
388 p++;
389 while (isascii(*p) &&
390 (isalnum(*p) || *p == '.' ||
391 *p == '-' || *p == ':'))
392 p++;
393 if (*p++ != ']')
394 return NULL;
395 } else {
396 while ((isascii(*p) && isalnum(*p)) ||
397 *p == '.' || *p == '-')
398 p++;
399 }
400 }
401
402 if (*p++ != '>')
403 return NULL;
404 if (*p && *p != ' ')
405 return NULL;
406 len = p - s - 1;
407
408 p = malloc(len + 1);
409 if (p == NULL) {
410 printf("421 4.3.0 memory exhausted\r\n");
411 exit(EX_TEMPFAIL);
412 }
413
414 strncpy(p, s, len);
415 p[len] = '\0';
416 return p;
417}
418
419char *
420process_recipient(addr)
421 char *addr;
422{
423 if (getpwnam(addr) == NULL) {
424 return "550 5.1.1 user unknown";
425 }
426
427 return NULL;
428}
429
430
431#define RCPT_GROW 30
432
433void
434dolmtp(nobiff, nofsync)
435 int nobiff, nofsync;
436{
437 char *return_path = NULL;
438 char **rcpt_addr = NULL;
439 int rcpt_num = 0;
440 int rcpt_alloc = 0;
441 char myhostname[1024];
442 char buf[4096];
443 char *err;
444 int msgfd;
445 char *p;
446 int i;
447
448 gethostname(myhostname, sizeof myhostname - 1);
449
450 printf("220 %s LMTP ready\r\n", myhostname);
451 for (;;) {
452 fflush(stdout);
453 if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
454 exit(EX_OK);
455 }
456 p = buf + strlen(buf) - 1;
457 if (p >= buf && *p == '\n')
458 *p-- = '\0';
459 if (p >= buf && *p == '\r')
460 *p-- = '\0';
461
462 switch (buf[0]) {
463
464 case 'd':
465 case 'D':
466 if (strcasecmp(buf, "data") == 0) {
467 if (rcpt_num == 0) {
468 printf("503 5.5.1 No recipients\r\n");
469 continue;
470 }
471 msgfd = store(return_path, rcpt_num);
472 if (msgfd == -1)
473 continue;
474
475 for (i = 0; i < rcpt_num; i++) {
476 p = strchr(rcpt_addr[i], '+');
477 if (p != NULL)
478 *p++ = '\0';
479 deliver(msgfd, rcpt_addr[i], nobiff,
480 nofsync);
481 }
482 close(msgfd);
483 goto rset;
484 }
485 goto syntaxerr;
486
487 case 'l':
488 case 'L':
489 if (strncasecmp(buf, "lhlo ", 5) == 0) {
490 printf("250-%s\r\n250-8BITMIME\r\n250-ENHANCEDSTATUSCODES\r\n250 PIPELINING\r\n",
491 myhostname);
492 continue;
493 }
494 goto syntaxerr;
495
496 case 'm':
497 case 'M':
498 if (strncasecmp(buf, "mail ", 5) == 0) {
499 if (return_path != NULL) {
500 printf("503 5.5.1 Nested MAIL command\r\n");
501 continue;
502 }
503 if (strncasecmp(buf+5, "from:", 5) != 0 ||
504 ((return_path = parseaddr(buf+10)) == NULL)) {
505 printf("501 5.5.4 Syntax error in parameters\r\n");
506 continue;
507 }
508 printf("250 2.5.0 ok\r\n");
509 continue;
510 }
511 goto syntaxerr;
512
513 case 'n':
514 case 'N':
515 if (strcasecmp(buf, "noop") == 0) {
516 printf("250 2.0.0 ok\r\n");
517 continue;
518 }
519 goto syntaxerr;
520
521 case 'q':
522 case 'Q':
523 if (strcasecmp(buf, "quit") == 0) {
524 printf("221 2.0.0 bye\r\n");
525 exit(EX_OK);
526 }
527 goto syntaxerr;
528
529 case 'r':
530 case 'R':
531 if (strncasecmp(buf, "rcpt ", 5) == 0) {
532 if (return_path == NULL) {
533 printf("503 5.5.1 Need MAIL command\r\n");
534 continue;
535 }
536 if (rcpt_num >= rcpt_alloc) {
537 rcpt_alloc += RCPT_GROW;
538 rcpt_addr = (char **)
539 realloc((char *)rcpt_addr,
540 rcpt_alloc * sizeof(char **));
541 if (rcpt_addr == NULL) {
542 printf("421 4.3.0 memory exhausted\r\n");
543 exit(EX_TEMPFAIL);
544 }
545 }
546 if (strncasecmp(buf+5, "to:", 3) != 0 ||
547 ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) {
548 printf("501 5.5.4 Syntax error in parameters\r\n");
549 continue;
550 }
551 if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) {
552 printf("%s\r\n", err);
553 continue;
554 }
555 rcpt_num++;
556 printf("250 2.1.5 ok\r\n");
557 continue;
558 }
559 else if (strcasecmp(buf, "rset") == 0) {
560 printf("250 2.0.0 ok\r\n");
561
562 rset:
563 while (rcpt_num) {
564 free(rcpt_addr[--rcpt_num]);
565 }
566 if (return_path != NULL)
567 free(return_path);
568 return_path = NULL;
569 continue;
570 }
571 goto syntaxerr;
572
573 case 'v':
574 case 'V':
575 if (strncasecmp(buf, "vrfy ", 5) == 0) {
576 printf("252 2.3.3 try RCPT to attempt delivery\r\n");
577 continue;
578 }
579 goto syntaxerr;
580
581 default:
582 syntaxerr:
583 printf("500 5.5.2 Syntax error\r\n");
584 continue;
585 }
586 }
587}
588
589int
590store(from, lmtprcpts)
591 char *from;
592 int lmtprcpts;
593{
594 FILE *fp;
595 time_t tval;
596 int fd, eline;
597 char line[2048];
598 char tmpbuf[sizeof _PATH_LOCTMP + 1];
599
600 strcpy(tmpbuf, _PATH_LOCTMP);
601 if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) {
602 if (lmtprcpts) {
603 printf("451 4.3.0 unable to open temporary file\r\n");
604 return -1;
605 } else {
606 e_to_sys(errno);
607 err("unable to open temporary file");
608 }
609 }
610 (void)unlink(tmpbuf);
611
612 if (lmtpmode) {
613 printf("354 go ahead\r\n");
614 fflush(stdout);
615 }
616
617 (void)time(&tval);
618 (void)fprintf(fp, "From %s %s", from, ctime(&tval));
619
620 line[0] = '\0';
621 for (eline = 1; fgets(line, sizeof(line), stdin);) {
622 if (line[strlen(line)-2] == '\r') {
623 strcpy(line+strlen(line)-2, "\n");
624 }
625 if (lmtprcpts && line[0] == '.') {
626 if (line[1] == '\n')
627 goto lmtpdot;
628 strcpy(line, line+1);
629 }
630 if (line[0] == '\n')
631 eline = 1;
632 else {
633 if (eline && line[0] == 'F' &&
634 !memcmp(line, "From ", 5))
635 (void)putc('>', fp);
636 eline = 0;
637 }
638 (void)fprintf(fp, "%s", line);
639 if (ferror(fp)) {
640 if (lmtprcpts) {
641 while (lmtprcpts--) {
642 printf("451 4.3.0 temporary file write error\r\n");
643 }
644 fclose(fp);
645 return -1;
646 } else {
647 e_to_sys(errno);
648 err("temporary file write error");
649 }
650 }
651 }
652
653 if (lmtprcpts) {
654 /* Got a premature EOF -- toss message and exit */
655 exit(EX_OK);
656 }
657
658 /* If message not newline terminated, need an extra. */
659 if (strchr(line, '\n') == NULL)
660 (void)putc('\n', fp);
661
662 lmtpdot:
663
664 /* Output a newline; note, empty messages are allowed. */
665 (void)putc('\n', fp);
666
667 if (fflush(fp) == EOF || ferror(fp)) {
668 if (lmtprcpts) {
669 while (lmtprcpts--) {
670 printf("451 4.3.0 temporary file write error\r\n");
671 }
672 fclose(fp);
673 return -1;
674 } else {
675 e_to_sys(errno);
676 err("temporary file write error");
677 }
678 }
679 return (fd);
680}
681
682void
683deliver(fd, name, nobiff, nofsync)
684 int fd;
685 char *name;
686 int nobiff, nofsync;
687{
688 struct stat fsb, sb;
689 struct passwd *pw;
690 int mbfd, nr, nw, off;
691 char *p;
692 char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
693 off_t curoff;
694 extern char *quad_to_string();
695
696 /*
697 * Disallow delivery to unknown names -- special mailboxes can be
698 * handled in the sendmail aliases file.
699 */
700 if ((pw = getpwnam(name)) == NULL) {
701 if (eval != EX_TEMPFAIL)
702 eval = EX_UNAVAILABLE;
703 if (lmtpmode) {
704 if (eval == EX_TEMPFAIL) {
705 printf("451 4.3.0 cannot lookup name: %s\r\n", name);
706 } else {
707 printf("550 5.1.1 unknown name: %s\r\n", name);
708 }
709 }
710 else {
711 warn("unknown name: %s", name);
712 }
713 return;
714 }
715 endpwent();
716
717 /*
718 * Keep name reasonably short to avoid buffer overruns.
719 * This isn't necessary on BSD because of the proper
720 * definition of snprintf(), but it can cause problems
721 * on other systems.
722 * Also, clear out any bogus characters.
723 */
724
725 if (strlen(name) > 40)
726 name[40] = '\0';
727 for (p = name; *p != '\0'; p++)
728 {
729 if (!isascii(*p))
730 *p &= 0x7f;
731 else if (!isprint(*p))
732 *p = '.';
733 }
734
735 (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
736
737 /*
738 * If the mailbox is linked or a symlink, fail. There's an obvious
739 * race here, that the file was replaced with a symbolic link after
740 * the lstat returned, but before the open. We attempt to detect
741 * this by comparing the original stat information and information
742 * returned by an fstat of the file descriptor returned by the open.
743 *
744 * NB: this is a symptom of a larger problem, that the mail spooling
745 * directory is writeable by the wrong users. If that directory is
746 * writeable, system security is compromised for other reasons, and
747 * it cannot be fixed here.
748 *
749 * If we created the mailbox, set the owner/group. If that fails,
750 * just return. Another process may have already opened it, so we
751 * can't unlink it. Historically, binmail set the owner/group at
752 * each mail delivery. We no longer do this, assuming that if the
753 * ownership or permissions were changed there was a reason.
754 *
755 * XXX
756 * open(2) should support flock'ing the file.
757 */
758tryagain:
759 lockmbox(path);
760 if (lstat(path, &sb) < 0) {
761 mbfd = open(path,
762 O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
763 if (lstat(path, &sb) < 0)
764 {
765 eval = EX_CANTCREAT;
766 warn("%s: lstat: file changed after open", path);
767 goto err1;
768 }
769 else
770 sb.st_uid = pw->pw_uid;
771 if (mbfd == -1) {
772 if (errno == EEXIST)
773 goto tryagain;
774 } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
775 mailerr("451 4.3.0", "chown %u.%u: %s",
776 pw->pw_uid, pw->pw_gid, name);
777 goto err1;
778 }
779 } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
780 mailerr("550 5.2.0", "%s: irregular file", path);
781 goto err0;
782 } else if (sb.st_uid != pw->pw_uid) {
783 eval = EX_CANTCREAT;
784 mailerr("550 5.2.0", "%s: wrong ownership (%d)",
785 path, sb.st_uid);
786 goto err0;
787 } else {
788 mbfd = open(path, O_APPEND|O_WRONLY, 0);
789 }
790
791 if (mbfd == -1) {
792 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
793 goto err0;
794 } else if (fstat(mbfd, &fsb) < 0 ||
795 fsb.st_nlink != 1 ||
796 sb.st_nlink != 1 ||
797 !S_ISREG(fsb.st_mode) ||
798 sb.st_dev != fsb.st_dev ||
799 sb.st_ino != fsb.st_ino ||
800#if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */
801 sb.st_gen != fsb.st_gen ||
802#endif
803 sb.st_uid != fsb.st_uid) {
804 eval = EX_TEMPFAIL;
805 warn("%s: fstat: file changed after open", path);
806 goto err1;
807 }
808
809 /* Wait until we can get a lock on the file. */
810 if (flock(mbfd, LOCK_EX)) {
811 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
812 goto err1;
813 }
814
815 if (!nobiff) {
816 /* Get the starting offset of the new message for biff. */
817 curoff = lseek(mbfd, (off_t)0, SEEK_END);
818 if (sizeof curoff > sizeof(long))
819 (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%s\n",
820 name, quad_to_string(curoff));
821 else
822 (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%ld\n",
823 name, curoff);
824 }
825
826 /* Copy the message into the file. */
827 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
828 mailerr("450 4.2.0", "temporary file: %s",
829 strerror(errno));
830 goto err1;
831 }
832 if (setreuid(0, pw->pw_uid) < 0) {
833 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
834 pw->pw_uid, strerror(errno), getuid(), geteuid());
835 goto err1;
836 }
837#ifdef DEBUG
838 printf("new euid = %d\n", geteuid());
839#endif
840 while ((nr = read(fd, buf, sizeof(buf))) > 0)
841 for (off = 0; off < nr; off += nw)
842 if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
843 mailerr("450 4.2.0", "%s: %s",
844 path, strerror(errno));
845 goto err3;
846 }
847 if (nr < 0) {
848 mailerr("450 4.2.0", "temporary file: %s",
849 strerror(errno));
850 goto err3;
851 }
852
853 /* Flush to disk, don't wait for update. */
854 if (!nofsync && fsync(mbfd)) {
855 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
856err3:
857 if (setreuid(0, 0) < 0) {
858 e_to_sys(errno);
859 mailerr("450 4.2.0", "setreuid(0, 0): %s",
860 strerror(errno));
861 }
862#ifdef DEBUG
863 printf("reset euid = %d\n", geteuid());
864#endif
865 (void)ftruncate(mbfd, curoff);
866err1: (void)close(mbfd);
867err0: unlockmbox();
868 return;
869 }
870
871 /* Close and check -- NFS doesn't write until the close. */
872 if (close(mbfd)) {
873 mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
874 truncate(path, curoff);
875 } else if (!nobiff)
876 notifybiff(biffmsg);
877
878 if (setreuid(0, 0) < 0) {
879 mailerr("450 4.2.0", "setreuid(0, 0): %s",
880 strerror(errno));
881 goto err0;
882 }
883#ifdef DEBUG
884 printf("reset euid = %d\n", geteuid());
885#endif
886 unlockmbox();
887 if (lmtpmode) {
888 printf("250 2.1.5 %s OK\r\n", name);
889 }
890}
891
892/*
893 * user.lock files are necessary for compatibility with other
894 * systems, e.g., when the mail spool file is NFS exported.
895 * Alas, mailbox locking is more than just a local matter.
896 * EPA 11/94.
897 */
898
899char lockname[MAXPATHLEN];
900int locked = 0;
901
902void
903lockmbox(path)
904 char *path;
905{
906 int statfailed = 0;
907
908 if (locked)
909 return;
910 if (strlen(path) + 6 > sizeof lockname)
911 return;
912 snprintf(lockname, sizeof lockname, "%s.lock", path);
913 for (;; sleep(5)) {
914 int fd;
915 struct stat st;
916 time_t now;
917
918 fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0);
919 if (fd >= 0) {
920 /* defeat lock checking programs which test pid */
921 write(fd, "0", 2);
922 locked = 1;
923 close(fd);
924 return;
925 }
926 if (stat(lockname, &st) < 0) {
927 if (statfailed++ > 5)
928 return;
929 continue;
930 }
931 statfailed = 0;
932 time(&now);
933 if (now < st.st_ctime + 300)
934 continue;
935 unlink(lockname);
936 }
937}
938
939void
940unlockmbox()
941{
942 if (!locked)
943 return;
944 unlink(lockname);
945 locked = 0;
946}
947
948void
949notifybiff(msg)
950 char *msg;
951{
952 static struct sockaddr_in addr;
953 static int f = -1;
954 struct hostent *hp;
955 struct servent *sp;
956 int len;
957
958 if (addr.sin_family == 0) {
959 /* Be silent if biff service not available. */
960 if ((sp = getservbyname("biff", "udp")) == NULL)
961 return;
962 if ((hp = gethostbyname("localhost")) == NULL) {
963 warn("localhost: %s", strerror(errno));
964 return;
965 }
966 addr.sin_family = hp->h_addrtype;
967 memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
968 addr.sin_port = sp->s_port;
969 }
970 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
971 warn("socket: %s", strerror(errno));
972 return;
973 }
974 len = strlen(msg) + 1;
975 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
976 != len)
977 warn("sendto biff: %s", strerror(errno));
978}
979
980void
981usage()
982{
983 eval = EX_USAGE;
984 err("usage: mail.local [-b] [-l] [-f from] [-s] user ...");
985}
986
987void
988#ifdef __STDC__
989mailerr(const char *hdr, const char *fmt, ...)
990#else
991mailerr(hdr, fmt, va_alist)
992 const char *hdr;
993 const char *fmt;
994 va_dcl
995#endif
996{
997 va_list ap;
998
999#ifdef __STDC__
1000 va_start(ap, fmt);
1001#else
1002 va_start(ap);
1003#endif
1004 if (lmtpmode)
1005 {
1006 printf("%s ", hdr);
1007 vprintf(fmt, ap);
1008 printf("\r\n");
1009 }
1010 else
1011 {
1012 e_to_sys(errno);
1013 vwarn(fmt, ap);
1014 }
1015}
1016
1017#ifdef __STDC__
1018void
1019err(const char *fmt, ...)
1020#else
1021void
1022err(fmt, va_alist)
1023 const char *fmt;
1024 va_dcl
1025#endif
1026{
1027 va_list ap;
1028
1029#ifdef __STDC__
1030 va_start(ap, fmt);
1031#else
1032 va_start(ap);
1033#endif
1034 vwarn(fmt, ap);
1035 va_end(ap);
1036
1037 exit(eval);
1038}
1039
1040void
1041#ifdef __STDC__
1042warn(const char *fmt, ...)
1043#else
1044warn(fmt, va_alist)
1045 const char *fmt;
1046 va_dcl
1047#endif
1048{
1049 va_list ap;
1050
1051#ifdef __STDC__
1052 va_start(ap, fmt);
1053#else
1054 va_start(ap);
1055#endif
1056 vwarn(fmt, ap);
1057 va_end(ap);
1058}
1059
1060void
1061vwarn(fmt, ap)
1062 const char *fmt;
1063 _BSD_VA_LIST_ ap;
1064{
1065 /*
1066 * Log the message to stderr.
1067 *
1068 * Don't use LOG_PERROR as an openlog() flag to do this,
1069 * it's not portable enough.
1070 */
1071 if (eval != EX_USAGE)
1072 (void)fprintf(stderr, "mail.local: ");
1073 (void)vfprintf(stderr, fmt, ap);
1074 (void)fprintf(stderr, "\n");
1075
1076#if USE_VSYSLOG
1077 /* Log the message to syslog. */
1078 vsyslog(LOG_ERR, fmt, ap);
1079#else
1080 {
1081 char fmtbuf[10240];
1082
1083 (void) vsnprintf(fmtbuf, sizeof fmtbuf, fmt, ap);
1084 syslog(LOG_ERR, "%s", fmtbuf);
1085 }
1086#endif
1087}
1088
1089/*
1090 * e_to_sys --
1091 * Guess which errno's are temporary. Gag me.
1092 */
1093void
1094e_to_sys(num)
1095 int num;
1096{
1097 /* Temporary failures override hard errors. */
1098 if (eval == EX_TEMPFAIL)
1099 return;
1100
1101 switch(num) { /* Hopefully temporary errors. */
1102#ifdef EAGAIN
1103 case EAGAIN: /* Resource temporarily unavailable */
1104#endif
1105#ifdef EDQUOT
1106 case EDQUOT: /* Disc quota exceeded */
1107#endif
1108#ifdef EBUSY
1109 case EBUSY: /* Device busy */
1110#endif
1111#ifdef EPROCLIM
1112 case EPROCLIM: /* Too many processes */
1113#endif
1114#ifdef EUSERS
1115 case EUSERS: /* Too many users */
1116#endif
1117#ifdef ECONNABORTED
1118 case ECONNABORTED: /* Software caused connection abort */
1119#endif
1120#ifdef ECONNREFUSED
1121 case ECONNREFUSED: /* Connection refused */
1122#endif
1123#ifdef ECONNRESET
1124 case ECONNRESET: /* Connection reset by peer */
1125#endif
1126#ifdef EDEADLK
1127 case EDEADLK: /* Resource deadlock avoided */
1128#endif
1129#ifdef EFBIG
1130 case EFBIG: /* File too large */
1131#endif
1132#ifdef EHOSTDOWN
1133 case EHOSTDOWN: /* Host is down */
1134#endif
1135#ifdef EHOSTUNREACH
1136 case EHOSTUNREACH: /* No route to host */
1137#endif
1138#ifdef EMFILE
1139 case EMFILE: /* Too many open files */
1140#endif
1141#ifdef ENETDOWN
1142 case ENETDOWN: /* Network is down */
1143#endif
1144#ifdef ENETRESET
1145 case ENETRESET: /* Network dropped connection on reset */
1146#endif
1147#ifdef ENETUNREACH
1148 case ENETUNREACH: /* Network is unreachable */
1149#endif
1150#ifdef ENFILE
1151 case ENFILE: /* Too many open files in system */
1152#endif
1153#ifdef ENOBUFS
1154 case ENOBUFS: /* No buffer space available */
1155#endif
1156#ifdef ENOMEM
1157 case ENOMEM: /* Cannot allocate memory */
1158#endif
1159#ifdef ENOSPC
1160 case ENOSPC: /* No space left on device */
1161#endif
1162#ifdef EROFS
1163 case EROFS: /* Read-only file system */
1164#endif
1165#ifdef ESTALE
1166 case ESTALE: /* Stale NFS file handle */
1167#endif
1168#ifdef ETIMEDOUT
1169 case ETIMEDOUT: /* Connection timed out */
1170#endif
1171#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
1172 case EWOULDBLOCK: /* Operation would block. */
1173#endif
1174 eval = EX_TEMPFAIL;
1175 break;
1176 default:
1177 eval = EX_UNAVAILABLE;
1178 break;
1179 }
1180}
1181
1182#if !HASSTRERROR
1183
1184char *
1185strerror(eno)
1186 int eno;
1187{
1188 extern int sys_nerr;
1189 extern char *sys_errlist[];
1190 static char ebuf[60];
1191
1192 if (eno >= 0 && eno < sys_nerr)
1193 return sys_errlist[eno];
1194 (void) sprintf(ebuf, "Error %d", eno);
1195 return ebuf;
1196}
1197
1198#endif /* !HASSTRERROR */
1199
1200#if defined(ultrix) || defined(_CRAY)
1201
1202/*
1203 * Copyright (c) 1987, 1993
1204 * The Regents of the University of California. All rights reserved.
1205 *
1206 * Redistribution and use in source and binary forms, with or without
1207 * modification, are permitted provided that the following conditions
1208 * are met:
1209 * 1. Redistributions of source code must retain the above copyright
1210 * notice, this list of conditions and the following disclaimer.
1211 * 2. Redistributions in binary form must reproduce the above copyright
1212 * notice, this list of conditions and the following disclaimer in the
1213 * documentation and/or other materials provided with the distribution.
1214 * 3. All advertising materials mentioning features or use of this software
1215 * must display the following acknowledgement:
1216 * This product includes software developed by the University of
1217 * California, Berkeley and its contributors.
1218 * 4. Neither the name of the University nor the names of its contributors
1219 * may be used to endorse or promote products derived from this software
1220 * without specific prior written permission.
1221 *
1222 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1223 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1224 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1225 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1226 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1227 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1228 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1229 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1230 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1231 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1232 * SUCH DAMAGE.
1233 */
1234
1235#if defined(LIBC_SCCS) && !defined(lint)
1236static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
1237#endif /* LIBC_SCCS and not lint */
1238
1239#include <sys/types.h>
1240#include <sys/stat.h>
1241#include <fcntl.h>
1242#include <errno.h>
1243#include <stdio.h>
1244#include <ctype.h>
1245
1246static int _gettemp();
1247
1248mkstemp(path)
1249 char *path;
1250{
1251 int fd;
1252
1253 return (_gettemp(path, &fd) ? fd : -1);
1254}
1255
1256/*
1257char *
1258mktemp(path)
1259 char *path;
1260{
1261 return(_gettemp(path, (int *)NULL) ? path : (char *)NULL);
1262}
1263*/
1264
1265static
1266_gettemp(path, doopen)
1267 char *path;
1268 register int *doopen;
1269{
1270 extern int errno;
1271 register char *start, *trv;
1272 struct stat sbuf;
1273 u_int pid;
1274
1275 pid = getpid();
1276 for (trv = path; *trv; ++trv); /* extra X's get set to 0's */
1277 while (*--trv == 'X') {
1278 *trv = (pid % 10) + '0';
1279 pid /= 10;
1280 }
1281
1282 /*
1283 * check the target directory; if you have six X's and it
1284 * doesn't exist this runs for a *very* long time.
1285 */
1286 for (start = trv + 1;; --trv) {
1287 if (trv <= path)
1288 break;
1289 if (*trv == '/') {
1290 *trv = '\0';
1291 if (stat(path, &sbuf) < 0)
1292 return(0);
1293 if (!S_ISDIR(sbuf.st_mode)) {
1294 errno = ENOTDIR;
1295 return(0);
1296 }
1297 *trv = '/';
1298 break;
1299 }
1300 }
1301
1302 for (;;) {
1303 if (doopen) {
1304 if ((*doopen =
1305 open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
1306 return(1);
1307 if (errno != EEXIST)
1308 return(0);
1309 }
1310 else if (stat(path, &sbuf) < 0)
1311 return(errno == ENOENT ? 1 : 0);
1312
1313 /* tricky little algorithm for backward compatibility */
1314 for (trv = start;;) {
1315 if (!*trv)
1316 return(0);
1317 if (*trv == 'z')
1318 *trv++ = 'a';
1319 else {
1320 if (isascii(*trv) && isdigit(*trv))
1321 *trv = 'a';
1322 else
1323 ++*trv;
1324 break;
1325 }
1326 }
1327 }
1328 /*NOTREACHED*/
1329}
1330
1331#endif /* ultrix */