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