xref: /freebsd/contrib/sendmail/mail.local/mail.local.c (revision 6b7c9af44b73355e01755ac7a0fb8ffc608b25ab)
1 /*
2  * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1990, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12 
13 #include <sm/gen.h>
14 
15 SM_IDSTR(copyright,
16 "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
17 	All rights reserved.\n\
18      Copyright (c) 1990, 1993, 1994\n\
19 	The Regents of the University of California.  All rights reserved.\n")
20 
21 SM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.238 2002/03/30 07:43:21 geir Exp $")
22 
23 #include <stdlib.h>
24 #include <sm/errstring.h>
25 #include <sm/io.h>
26 #include <sm/limits.h>
27 # include <unistd.h>
28 # ifdef EX_OK
29 #  undef EX_OK		/* unistd.h may have another use for this */
30 # endif /* EX_OK */
31 #include <sm/mbdb.h>
32 #include <sm/sysexits.h>
33 
34 /*
35 **  This is not intended to work on System V derived systems
36 **  such as Solaris or HP-UX, since they use a totally different
37 **  approach to mailboxes (essentially, they have a set-group-ID program
38 **  rather than set-user-ID, and they rely on the ability to "give away"
39 **  files to do their work).  IT IS NOT A BUG that this doesn't
40 **  work on such architectures.
41 */
42 
43 
44 #include <stdio.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 #include <stdlib.h>
51 # include <sys/socket.h>
52 # include <sys/file.h>
53 # include <netinet/in.h>
54 # include <arpa/nameser.h>
55 # include <netdb.h>
56 # include <pwd.h>
57 
58 #include <sm/string.h>
59 #include <syslog.h>
60 #include <ctype.h>
61 
62 #include <sm/conf.h>
63 #include <sendmail/pathnames.h>
64 
65 
66 /* additional mode for open() */
67 # define EXTRA_MODE 0
68 
69 
70 #ifndef LOCKTO_RM
71 # define LOCKTO_RM	300	/* timeout for stale lockfile removal */
72 #endif /* ! LOCKTO_RM */
73 #ifndef LOCKTO_GLOB
74 # define LOCKTO_GLOB	400	/* global timeout for lockfile creation */
75 #endif /* ! LOCKTO_GLOB */
76 
77 /* define a realloc() which works for NULL pointers */
78 #define REALLOC(ptr, size)	(((ptr) == NULL) ? malloc(size) : realloc(ptr, size))
79 
80 /*
81 **  If you don't have flock, you could try using lockf instead.
82 */
83 
84 #ifdef LDA_USE_LOCKF
85 # define flock(a, b)	lockf(a, b, 0)
86 # ifdef LOCK_EX
87 #  undef LOCK_EX
88 # endif /* LOCK_EX */
89 # define LOCK_EX	F_LOCK
90 #endif /* LDA_USE_LOCKF */
91 
92 #ifndef LOCK_EX
93 # include <sys/file.h>
94 #endif /* ! LOCK_EX */
95 
96 /*
97 **  If you don't have setreuid, and you have saved uids, and you have
98 **  a seteuid() call that doesn't try to emulate using setuid(), then
99 **  you can try defining LDA_USE_SETEUID.
100 */
101 
102 #ifdef LDA_USE_SETEUID
103 # define setreuid(r, e)		seteuid(e)
104 #endif /* LDA_USE_SETEUID */
105 
106 #ifdef LDA_CONTENTLENGTH
107 # define CONTENTLENGTH	1
108 #endif /* LDA_CONTENTLENGTH */
109 
110 #ifndef INADDRSZ
111 # define INADDRSZ	4		/* size of an IPv4 address in bytes */
112 #endif /* ! INADDRSZ */
113 
114 #ifdef MAILLOCK
115 # include <maillock.h>
116 #endif /* MAILLOCK */
117 
118 #ifndef MAILER_DAEMON
119 # define MAILER_DAEMON	"MAILER-DAEMON"
120 #endif /* ! MAILER_DAEMON */
121 
122 #ifdef CONTENTLENGTH
123 char	ContentHdr[40] = "Content-Length: ";
124 off_t	HeaderLength;
125 off_t	BodyLength;
126 #endif /* CONTENTLENGTH */
127 
128 bool	EightBitMime = true;		/* advertise 8BITMIME in LMTP */
129 char	ErrBuf[10240];			/* error buffer */
130 int	ExitVal = EX_OK;		/* sysexits.h error value. */
131 bool	HoldErrs = false;		/* Hold errors in ErrBuf */
132 bool	LMTPMode = false;
133 bool	BounceQuota = false;		/* permanent error when over quota */
134 char	*HomeMailFile = NULL;		/* store mail in homedir */
135 
136 void	deliver __P((int, char *));
137 int	e_to_sys __P((int));
138 void	notifybiff __P((char *));
139 int	store __P((char *, bool *));
140 void	usage __P((void));
141 int	lockmbox __P((char *));
142 void	unlockmbox __P((void));
143 void	mailerr __P((const char *, const char *, ...));
144 void	flush_error __P((void));
145 
146 
147 int
148 main(argc, argv)
149 	int argc;
150 	char *argv[];
151 {
152 	struct passwd *pw;
153 	int ch, fd;
154 	uid_t uid;
155 	char *from;
156 	char *mbdbname = "pw";
157 	int err;
158 	extern char *optarg;
159 	extern int optind;
160 
161 
162 	/* make sure we have some open file descriptors */
163 	for (fd = 10; fd < 30; fd++)
164 		(void) close(fd);
165 
166 	/* use a reasonable umask */
167 	(void) umask(0077);
168 
169 # ifdef LOG_MAIL
170 	openlog("mail.local", 0, LOG_MAIL);
171 # else /* LOG_MAIL */
172 	openlog("mail.local", 0);
173 # endif /* LOG_MAIL */
174 
175 	from = NULL;
176 	while ((ch = getopt(argc, argv, "7bdD:f:h:r:l")) != -1)
177 	{
178 		switch(ch)
179 		{
180 		  case '7':		/* Do not advertise 8BITMIME */
181 			EightBitMime = false;
182 			break;
183 
184 		  case 'b':		/* bounce mail when over quota. */
185 			BounceQuota = true;
186 			break;
187 
188 		  case 'd':		/* Backward compatible. */
189 			break;
190 
191 		  case 'D':		/* mailbox database type */
192 			mbdbname = optarg;
193 			break;
194 
195 		  case 'f':
196 		  case 'r':		/* Backward compatible. */
197 			if (from != NULL)
198 			{
199 				mailerr(NULL, "Multiple -f options");
200 				usage();
201 			}
202 			from = optarg;
203 			break;
204 
205 		  case 'h':
206 			if (optarg != NULL || *optarg != '\0')
207 				HomeMailFile = optarg;
208 			else
209 			{
210 				mailerr(NULL, "-h: missing filename");
211 				usage();
212 			}
213 			break;
214 
215 		  case 'l':
216 			LMTPMode = true;
217 			break;
218 
219 		  case '?':
220 		  default:
221 			usage();
222 		}
223 	}
224 	argc -= optind;
225 	argv += optind;
226 
227 	/* initialize biff structures */
228 	notifybiff(NULL);
229 
230 	err = sm_mbdb_initialize(mbdbname);
231 	if (err != EX_OK)
232 	{
233 		char *errcode = "521";
234 
235 		if (err == EX_TEMPFAIL)
236 			errcode = "421";
237 
238 		mailerr(errcode, "Can not open mailbox database %s: %s",
239 			mbdbname, sm_strexit(err));
240 		exit(err);
241 	}
242 
243 	if (LMTPMode)
244 	{
245 		extern void dolmtp __P((void));
246 
247 		if (argc > 0)
248 		{
249 			mailerr("421", "Users should not be specified in command line if LMTP required");
250 			exit(EX_TEMPFAIL);
251 		}
252 
253 		dolmtp();
254 		/* NOTREACHED */
255 		exit(EX_OK);
256 	}
257 
258 	/* Non-LMTP from here on out */
259 	if (*argv == '\0')
260 		usage();
261 
262 	/*
263 	**  If from not specified, use the name from getlogin() if the
264 	**  uid matches, otherwise, use the name from the password file
265 	**  corresponding to the uid.
266 	*/
267 
268 	uid = getuid();
269 	if (from == NULL && ((from = getlogin()) == NULL ||
270 			     (pw = getpwnam(from)) == NULL ||
271 			     pw->pw_uid != uid))
272 		from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???";
273 
274 	/*
275 	**  There is no way to distinguish the error status of one delivery
276 	**  from the rest of the deliveries.  So, if we failed hard on one
277 	**  or more deliveries, but had no failures on any of the others, we
278 	**  return a hard failure.  If we failed temporarily on one or more
279 	**  deliveries, we return a temporary failure regardless of the other
280 	**  failures.  This results in the delivery being reattempted later
281 	**  at the expense of repeated failures and multiple deliveries.
282 	*/
283 
284 	HoldErrs = true;
285 	fd = store(from, NULL);
286 	HoldErrs = false;
287 	if (fd < 0)
288 	{
289 		flush_error();
290 		exit(ExitVal);
291 	}
292 	for (; *argv != NULL; ++argv)
293 		deliver(fd, *argv);
294 	exit(ExitVal);
295 	/* NOTREACHED */
296 	return ExitVal;
297 }
298 
299 char *
300 parseaddr(s, rcpt)
301 	char *s;
302 	bool rcpt;
303 {
304 	char *p;
305 	int l;
306 
307 	if (*s++ != '<')
308 		return NULL;
309 
310 	p = s;
311 
312 	/* at-domain-list */
313 	while (*p == '@')
314 	{
315 		p++;
316 		while (*p != ',' && *p != ':' && *p != '\0')
317 			p++;
318 		if (*p == '\0')
319 			return NULL;
320 
321 		/* Skip over , or : */
322 		p++;
323 	}
324 
325 	s = p;
326 
327 	/* local-part */
328 	while (*p != '\0' && *p != '@' && *p != '>')
329 	{
330 		if (*p == '\\')
331 		{
332 			if (*++p == '\0')
333 				return NULL;
334 		}
335 		else if (*p == '\"')
336 		{
337 			p++;
338 			while (*p != '\0' && *p != '\"')
339 			{
340 				if (*p == '\\')
341 				{
342 					if (*++p == '\0')
343 						return NULL;
344 				}
345 				p++;
346 			}
347 			if (*p == '\0' || *(p + 1) == '\0')
348 				return NULL;
349 		}
350 		/* +detail ? */
351 		if (*p == '+' && rcpt)
352 			*p = '\0';
353 		p++;
354 	}
355 
356 	/* @domain */
357 	if (*p == '@')
358 	{
359 		if (rcpt)
360 			*p++ = '\0';
361 		while (*p != '\0' && *p != '>')
362 			p++;
363 	}
364 
365 	if (*p != '>')
366 		return NULL;
367 	else
368 		*p = '\0';
369 	p++;
370 
371 	if (*p != '\0' && *p != ' ')
372 		return NULL;
373 
374 	if (*s == '\0')
375 		s = MAILER_DAEMON;
376 
377 	l = strlen(s) + 1;
378 	if (l < 0)
379 		return NULL;
380 	p = malloc(l);
381 	if (p == NULL)
382 	{
383 		mailerr("421 4.3.0", "Memory exhausted");
384 		exit(EX_TEMPFAIL);
385 	}
386 
387 	(void) sm_strlcpy(p, s, l);
388 	return p;
389 }
390 
391 char *
392 process_recipient(addr)
393 	char *addr;
394 {
395 	SM_MBDB_T user;
396 
397 	switch (sm_mbdb_lookup(addr, &user))
398 	{
399 	  case EX_OK:
400 		return NULL;
401 
402 	  case EX_NOUSER:
403 		return "550 5.1.1 User unknown";
404 
405 	  case EX_TEMPFAIL:
406 		return "451 4.3.0 User database failure; retry later";
407 
408 	  default:
409 		return "550 5.3.0 User database failure";
410 	}
411 }
412 
413 #define RCPT_GROW	30
414 
415 void
416 dolmtp()
417 {
418 	char *return_path = NULL;
419 	char **rcpt_addr = NULL;
420 	int rcpt_num = 0;
421 	int rcpt_alloc = 0;
422 	bool gotlhlo = false;
423 	char *err;
424 	int msgfd;
425 	char *p;
426 	int i;
427 	char myhostname[1024];
428 	char buf[4096];
429 
430 	memset(myhostname, '\0', sizeof myhostname);
431 	(void) gethostname(myhostname, sizeof myhostname - 1);
432 	if (myhostname[0] == '\0')
433 		sm_strlcpy(myhostname, "localhost", sizeof myhostname);
434 
435 	printf("220 %s LMTP ready\r\n", myhostname);
436 	for (;;)
437 	{
438 		(void) fflush(stdout);
439 		if (fgets(buf, sizeof(buf) - 1, stdin) == NULL)
440 			exit(EX_OK);
441 		p = buf + strlen(buf) - 1;
442 		if (p >= buf && *p == '\n')
443 			*p-- = '\0';
444 		if (p >= buf && *p == '\r')
445 			*p-- = '\0';
446 
447 		switch (buf[0])
448 		{
449 		  case 'd':
450 		  case 'D':
451 			if (sm_strcasecmp(buf, "data") == 0)
452 			{
453 				bool inbody = false;
454 
455 				if (rcpt_num == 0)
456 				{
457 					mailerr("503 5.5.1", "No recipients");
458 					continue;
459 				}
460 				HoldErrs = true;
461 				msgfd = store(return_path, &inbody);
462 				HoldErrs = false;
463 				if (msgfd < 0 && !inbody)
464 				{
465 					flush_error();
466 					continue;
467 				}
468 
469 				for (i = 0; i < rcpt_num; i++)
470 				{
471 					if (msgfd < 0)
472 					{
473 						/* print error for rcpt */
474 						flush_error();
475 						continue;
476 					}
477 					p = strchr(rcpt_addr[i], '+');
478 					if (p != NULL)
479 						*p = '\0';
480 					deliver(msgfd, rcpt_addr[i]);
481 				}
482 				if (msgfd >= 0)
483 					(void) close(msgfd);
484 				goto rset;
485 			}
486 			goto syntaxerr;
487 			/* NOTREACHED */
488 			break;
489 
490 		  case 'l':
491 		  case 'L':
492 			if (sm_strncasecmp(buf, "lhlo ", 5) == 0)
493 			{
494 				/* check for duplicate per RFC 1651 4.2 */
495 				if (gotlhlo)
496 				{
497 					mailerr("503", "%s Duplicate LHLO",
498 					       myhostname);
499 					continue;
500 				}
501 				gotlhlo = true;
502 				printf("250-%s\r\n", myhostname);
503 				if (EightBitMime)
504 					printf("250-8BITMIME\r\n");
505 				printf("250-ENHANCEDSTATUSCODES\r\n");
506 				printf("250 PIPELINING\r\n");
507 				continue;
508 			}
509 			goto syntaxerr;
510 			/* NOTREACHED */
511 			break;
512 
513 		  case 'm':
514 		  case 'M':
515 			if (sm_strncasecmp(buf, "mail ", 5) == 0)
516 			{
517 				if (return_path != NULL)
518 				{
519 					mailerr("503 5.5.1",
520 						"Nested MAIL command");
521 					continue;
522 				}
523 				if (sm_strncasecmp(buf+5, "from:", 5) != 0 ||
524 				    ((return_path = parseaddr(buf + 10,
525 							      false)) == NULL))
526 				{
527 					mailerr("501 5.5.4",
528 						"Syntax error in parameters");
529 					continue;
530 				}
531 				printf("250 2.5.0 Ok\r\n");
532 				continue;
533 			}
534 			goto syntaxerr;
535 			/* NOTREACHED */
536 			break;
537 
538 		  case 'n':
539 		  case 'N':
540 			if (sm_strcasecmp(buf, "noop") == 0)
541 			{
542 				printf("250 2.0.0 Ok\r\n");
543 				continue;
544 			}
545 			goto syntaxerr;
546 			/* NOTREACHED */
547 			break;
548 
549 		  case 'q':
550 		  case 'Q':
551 			if (sm_strcasecmp(buf, "quit") == 0)
552 			{
553 				printf("221 2.0.0 Bye\r\n");
554 				exit(EX_OK);
555 			}
556 			goto syntaxerr;
557 			/* NOTREACHED */
558 			break;
559 
560 		  case 'r':
561 		  case 'R':
562 			if (sm_strncasecmp(buf, "rcpt ", 5) == 0)
563 			{
564 				if (return_path == NULL)
565 				{
566 					mailerr("503 5.5.1",
567 						"Need MAIL command");
568 					continue;
569 				}
570 				if (rcpt_num >= rcpt_alloc)
571 				{
572 					rcpt_alloc += RCPT_GROW;
573 					rcpt_addr = (char **)
574 						REALLOC((char *) rcpt_addr,
575 							rcpt_alloc *
576 							sizeof(char **));
577 					if (rcpt_addr == NULL)
578 					{
579 						mailerr("421 4.3.0",
580 							"Memory exhausted");
581 						exit(EX_TEMPFAIL);
582 					}
583 				}
584 				if (sm_strncasecmp(buf + 5, "to:", 3) != 0 ||
585 				    ((rcpt_addr[rcpt_num] = parseaddr(buf + 8,
586 								      true)) == NULL))
587 				{
588 					mailerr("501 5.5.4",
589 						"Syntax error in parameters");
590 					continue;
591 				}
592 				err = process_recipient(rcpt_addr[rcpt_num]);
593 				if (err != NULL)
594 				{
595 					mailerr(NULL, "%s", err);
596 					continue;
597 				}
598 				rcpt_num++;
599 				printf("250 2.1.5 Ok\r\n");
600 				continue;
601 			}
602 			else if (sm_strcasecmp(buf, "rset") == 0)
603 			{
604 				printf("250 2.0.0 Ok\r\n");
605 
606 rset:
607 				while (rcpt_num > 0)
608 					free(rcpt_addr[--rcpt_num]);
609 				if (return_path != NULL)
610 					free(return_path);
611 				return_path = NULL;
612 				continue;
613 			}
614 			goto syntaxerr;
615 			/* NOTREACHED */
616 			break;
617 
618 		  case 'v':
619 		  case 'V':
620 			if (sm_strncasecmp(buf, "vrfy ", 5) == 0)
621 			{
622 				printf("252 2.3.3 Try RCPT to attempt delivery\r\n");
623 				continue;
624 			}
625 			goto syntaxerr;
626 			/* NOTREACHED */
627 			break;
628 
629 		  default:
630   syntaxerr:
631 			mailerr("500 5.5.2", "Syntax error");
632 			continue;
633 			/* NOTREACHED */
634 			break;
635 		}
636 	}
637 }
638 
639 int
640 store(from, inbody)
641 	char *from;
642 	bool *inbody;
643 {
644 	FILE *fp = NULL;
645 	time_t tval;
646 	bool eline;		/* previous line was empty */
647 	bool fullline = true;	/* current line is terminated */
648 	bool prevfl;		/* previous line was terminated */
649 	char line[2048];
650 	int fd;
651 	char tmpbuf[sizeof _PATH_LOCTMP + 1];
652 
653 	if (inbody != NULL)
654 		*inbody = false;
655 
656 	(void) umask(0077);
657 	(void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf);
658 	if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL)
659 	{
660 		mailerr("451 4.3.0", "Unable to open temporary file");
661 		return -1;
662 	}
663 	(void) unlink(tmpbuf);
664 
665 	if (LMTPMode)
666 	{
667 		printf("354 Go ahead\r\n");
668 		(void) fflush(stdout);
669 	}
670 	if (inbody != NULL)
671 		*inbody = true;
672 
673 	(void) time(&tval);
674 	(void) fprintf(fp, "From %s %s", from, ctime(&tval));
675 
676 #ifdef CONTENTLENGTH
677 	HeaderLength = 0;
678 	BodyLength = -1;
679 #endif /* CONTENTLENGTH */
680 
681 	line[0] = '\0';
682 	eline = true;
683 	while (fgets(line, sizeof(line), stdin) != (char *) NULL)
684 	{
685 		size_t line_len = 0;
686 		int peek;
687 
688 		prevfl = fullline;	/* preserve state of previous line */
689 		while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
690 			line_len++;
691 		line_len++;
692 
693 		/* Check for dot-stuffing */
694 		if (prevfl && LMTPMode && line[0] == '.')
695 		{
696 			if (line[1] == '\n' ||
697 			    (line[1] == '\r' && line[2] == '\n'))
698 				goto lmtpdot;
699 			memcpy(line, line + 1, line_len);
700 			line_len--;
701 		}
702 
703 		/* Check to see if we have the full line from fgets() */
704 		fullline = false;
705 		if (line_len > 0)
706 		{
707 			if (line[line_len - 1] == '\n')
708 			{
709 				if (line_len >= 2 &&
710 				    line[line_len - 2] == '\r')
711 				{
712 					line[line_len - 2] = '\n';
713 					line[line_len - 1] = '\0';
714 					line_len--;
715 				}
716 				fullline = true;
717 			}
718 			else if (line[line_len - 1] == '\r')
719 			{
720 				/* Did we just miss the CRLF? */
721 				peek = fgetc(stdin);
722 				if (peek == '\n')
723 				{
724 					line[line_len - 1] = '\n';
725 					fullline = true;
726 				}
727 				else
728 					(void) ungetc(peek, stdin);
729 			}
730 		}
731 		else
732 			fullline = true;
733 
734 #ifdef CONTENTLENGTH
735 		if (prevfl && line[0] == '\n' && HeaderLength == 0)
736 		{
737 			eline = false;
738 			if (fp != NULL)
739 				HeaderLength = ftell(fp);
740 			if (HeaderLength <= 0)
741 			{
742 				/*
743 				**  shouldn't happen, unless ftell() is
744 				**  badly broken
745 				*/
746 
747 				HeaderLength = -1;
748 			}
749 		}
750 #else /* CONTENTLENGTH */
751 		if (prevfl && line[0] == '\n')
752 			eline = true;
753 #endif /* CONTENTLENGTH */
754 		else
755 		{
756 			if (eline && line[0] == 'F' &&
757 			    fp != NULL &&
758 			    !memcmp(line, "From ", 5))
759 				(void) putc('>', fp);
760 			eline = false;
761 #ifdef CONTENTLENGTH
762 			/* discard existing "Content-Length:" headers */
763 			if (prevfl && HeaderLength == 0 &&
764 			    (line[0] == 'C' || line[0] == 'c') &&
765 			    sm_strncasecmp(line, ContentHdr, 15) == 0)
766 			{
767 				/*
768 				**  be paranoid: clear the line
769 				**  so no "wrong matches" may occur later
770 				*/
771 				line[0] = '\0';
772 				continue;
773 			}
774 #endif /* CONTENTLENGTH */
775 
776 		}
777 		if (fp != NULL)
778 		{
779 			(void) fwrite(line, sizeof(char), line_len, fp);
780 			if (ferror(fp))
781 			{
782 				mailerr("451 4.3.0",
783 					"Temporary file write error");
784 				(void) fclose(fp);
785 				fp = NULL;
786 				continue;
787 			}
788 		}
789 	}
790 
791 	/* check if an error occurred */
792 	if (fp == NULL)
793 		return -1;
794 
795 	if (LMTPMode)
796 	{
797 		/* Got a premature EOF -- toss message and exit */
798 		exit(EX_OK);
799 	}
800 
801 	/* If message not newline terminated, need an extra. */
802 	if (fp != NULL && strchr(line, '\n') == NULL)
803 		(void) putc('\n', fp);
804 
805   lmtpdot:
806 
807 #ifdef CONTENTLENGTH
808 	if (fp != NULL)
809 		BodyLength = ftell(fp);
810 	if (HeaderLength == 0 && BodyLength > 0)	/* empty body */
811 	{
812 		HeaderLength = BodyLength;
813 		BodyLength = 0;
814 	}
815 	else
816 		BodyLength = BodyLength - HeaderLength - 1 ;
817 
818 	if (HeaderLength > 0 && BodyLength >= 0)
819 	{
820 		(void) sm_snprintf(line, sizeof line, "%lld\n",
821 				   (LONGLONG_T) BodyLength);
822 		(void) sm_strlcpy(&ContentHdr[16], line,
823 				  sizeof(ContentHdr) - 16);
824 	}
825 	else
826 		BodyLength = -1;	/* Something is wrong here */
827 #endif /* CONTENTLENGTH */
828 
829 	/* Output a newline; note, empty messages are allowed. */
830 	if (fp != NULL)
831 		(void) putc('\n', fp);
832 
833 	if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0)
834 	{
835 		mailerr("451 4.3.0", "Temporary file write error");
836 		if (fp != NULL)
837 			(void) fclose(fp);
838 		return -1;
839 	}
840 	return fd;
841 }
842 
843 void
844 deliver(fd, name)
845 	int fd;
846 	char *name;
847 {
848 	struct stat fsb;
849 	struct stat sb;
850 	char path[MAXPATHLEN];
851 	int mbfd = -1, nr = 0, nw, off;
852 	int exitval;
853 	char *p;
854 	char *errcode;
855 	off_t curoff;
856 #ifdef CONTENTLENGTH
857 	off_t headerbytes;
858 	int readamount;
859 #endif /* CONTENTLENGTH */
860 	char biffmsg[100], buf[8*1024];
861 	SM_MBDB_T user;
862 
863 	/*
864 	**  Disallow delivery to unknown names -- special mailboxes can be
865 	**  handled in the sendmail aliases file.
866 	*/
867 
868 	exitval = sm_mbdb_lookup(name, &user);
869 	switch (exitval)
870 	{
871 	  case EX_OK:
872 		break;
873 
874 	  case EX_NOUSER:
875 		exitval = EX_UNAVAILABLE;
876 		mailerr("550 5.1.1", "%s: User unknown", name);
877 		break;
878 
879 	  case EX_TEMPFAIL:
880 		mailerr("451 4.3.0", "%s: User database failure; retry later",
881 			name);
882 		break;
883 
884 	  default:
885 		exitval = EX_UNAVAILABLE;
886 		mailerr("550 5.3.0", "%s: User database failure", name);
887 		break;
888 	}
889 
890 	if (exitval != EX_OK)
891 	{
892 		if (ExitVal != EX_TEMPFAIL)
893 			ExitVal = exitval;
894 		return;
895 	}
896 
897 	endpwent();
898 
899 	/*
900 	**  Keep name reasonably short to avoid buffer overruns.
901 	**	This isn't necessary on BSD because of the proper
902 	**	definition of snprintf(), but it can cause problems
903 	**	on other systems.
904 	**  Also, clear out any bogus characters.
905 	*/
906 
907 	if (strlen(name) > 40)
908 		name[40] = '\0';
909 	for (p = name; *p != '\0'; p++)
910 	{
911 		if (!isascii(*p))
912 			*p &= 0x7f;
913 		else if (!isprint(*p))
914 			*p = '.';
915 	}
916 
917 
918 	if (HomeMailFile == NULL)
919 	{
920 		if (sm_snprintf(path, sizeof(path), "%s/%s",
921 				_PATH_MAILDIR, name) >= sizeof(path))
922 		{
923 			exitval = EX_UNAVAILABLE;
924 			mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
925 			return;
926 		}
927 	}
928 	else if (*user.mbdb_homedir == '\0')
929 	{
930 		exitval = EX_UNAVAILABLE;
931 		mailerr("550 5.1.1", "%s: User missing home directory", name);
932 		return;
933 	}
934 	else if (sm_snprintf(path, sizeof(path), "%s/%s",
935 			     user.mbdb_homedir, HomeMailFile) >= sizeof(path))
936 	{
937 		exitval = EX_UNAVAILABLE;
938 		mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
939 		return;
940 	}
941 
942 
943 	/*
944 	**  If the mailbox is linked or a symlink, fail.  There's an obvious
945 	**  race here, that the file was replaced with a symbolic link after
946 	**  the lstat returned, but before the open.  We attempt to detect
947 	**  this by comparing the original stat information and information
948 	**  returned by an fstat of the file descriptor returned by the open.
949 	**
950 	**  NB: this is a symptom of a larger problem, that the mail spooling
951 	**  directory is writeable by the wrong users.  If that directory is
952 	**  writeable, system security is compromised for other reasons, and
953 	**  it cannot be fixed here.
954 	**
955 	**  If we created the mailbox, set the owner/group.  If that fails,
956 	**  just return.  Another process may have already opened it, so we
957 	**  can't unlink it.  Historically, binmail set the owner/group at
958 	**  each mail delivery.  We no longer do this, assuming that if the
959 	**  ownership or permissions were changed there was a reason.
960 	**
961 	**  XXX
962 	**  open(2) should support flock'ing the file.
963 	*/
964 
965 tryagain:
966 #ifdef MAILLOCK
967 	p = name;
968 #else /* MAILLOCK */
969 	p = path;
970 #endif /* MAILLOCK */
971 	if ((off = lockmbox(p)) != 0)
972 	{
973 		if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL)
974 		{
975 			ExitVal = EX_TEMPFAIL;
976 			errcode = "451 4.3.0";
977 		}
978 		else
979 			errcode = "551 5.3.0";
980 
981 		mailerr(errcode, "lockmailbox %s failed; error code %d %s",
982 			p, off, errno > 0 ? sm_errstring(errno) : "");
983 		return;
984 	}
985 
986 	if (lstat(path, &sb) < 0)
987 	{
988 		int save_errno;
989 		int mode = S_IRUSR|S_IWUSR;
990 		gid_t gid = user.mbdb_gid;
991 
992 #ifdef MAILGID
993 		(void) umask(0007);
994 		gid = MAILGID;
995 		mode |= S_IRGRP|S_IWGRP;
996 #endif /* MAILGID */
997 
998 		mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|EXTRA_MODE,
999 			    mode);
1000 		save_errno = errno;
1001 
1002 		if (lstat(path, &sb) < 0)
1003 		{
1004 			ExitVal = EX_CANTCREAT;
1005 			mailerr("550 5.2.0",
1006 				"%s: lstat: file changed after open", path);
1007 			goto err1;
1008 		}
1009 		if (mbfd < 0)
1010 		{
1011 			if (save_errno == EEXIST)
1012 				goto tryagain;
1013 
1014 			/* open failed, don't try again */
1015 			mailerr("450 4.2.0", "%s: %s", path,
1016 				sm_errstring(save_errno));
1017 			goto err0;
1018 		}
1019 		else if (fchown(mbfd, user.mbdb_uid, gid) < 0)
1020 		{
1021 			mailerr("451 4.3.0", "chown %u.%u: %s",
1022 				user.mbdb_uid, gid, name);
1023 			goto err1;
1024 		}
1025 		else
1026 		{
1027 			/*
1028 			**  open() was successful, now close it so can
1029 			**  be opened as the right owner again.
1030 			**  Paranoia: reset mbdf since the file descriptor
1031 			**  is no longer valid; better safe than sorry.
1032 			*/
1033 
1034 			sb.st_uid = user.mbdb_uid;
1035 			(void) close(mbfd);
1036 			mbfd = -1;
1037 		}
1038 	}
1039 	else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode))
1040 	{
1041 		mailerr("550 5.2.0", "%s: irregular file", path);
1042 		goto err0;
1043 	}
1044 	else if (sb.st_uid != user.mbdb_uid)
1045 	{
1046 		ExitVal = EX_CANTCREAT;
1047 		mailerr("550 5.2.0", "%s: wrong ownership (%d)",
1048 			path, (int) sb.st_uid);
1049 		goto err0;
1050 	}
1051 
1052 	/* change UID for quota checks */
1053 	if (setreuid(0, user.mbdb_uid) < 0)
1054 	{
1055 		mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
1056 			(int) user.mbdb_uid, sm_errstring(errno),
1057 			(int) getuid(), (int) geteuid());
1058 		goto err1;
1059 	}
1060 #ifdef DEBUG
1061 	fprintf(stderr, "new euid = %d\n", (int) geteuid());
1062 #endif /* DEBUG */
1063 	mbfd = open(path, O_APPEND|O_WRONLY|EXTRA_MODE, 0);
1064 	if (mbfd < 0)
1065 	{
1066 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1067 		goto err0;
1068 	}
1069 	else if (fstat(mbfd, &fsb) < 0 ||
1070 		 fsb.st_nlink != 1 ||
1071 		 sb.st_nlink != 1 ||
1072 		 !S_ISREG(fsb.st_mode) ||
1073 		 sb.st_dev != fsb.st_dev ||
1074 		 sb.st_ino != fsb.st_ino ||
1075 # if HAS_ST_GEN && 0		/* AFS returns random values for st_gen */
1076 		 sb.st_gen != fsb.st_gen ||
1077 # endif /* HAS_ST_GEN && 0 */
1078 		 sb.st_uid != fsb.st_uid)
1079 	{
1080 		ExitVal = EX_TEMPFAIL;
1081 		mailerr("550 5.2.0", "%s: fstat: file changed after open",
1082 			path);
1083 		goto err1;
1084 	}
1085 
1086 #if 0
1087 	/*
1088 	**  This code could be reused if we decide to add a
1089 	**  per-user quota field to the sm_mbdb interface.
1090 	*/
1091 
1092 	/*
1093 	**  Fail if the user has a quota specified, and delivery of this
1094 	**  message would exceed that quota.  We bounce such failures using
1095 	**  EX_UNAVAILABLE, unless there were internal problems, since
1096 	**  storing immense messages for later retries can cause queueing
1097 	**  issues.
1098 	*/
1099 
1100 	if (ui.quota > 0)
1101 	{
1102 		struct stat dsb;
1103 
1104 		if (fstat(fd, &dsb) < 0)
1105 		{
1106 			ExitVal = EX_TEMPFAIL;
1107 			mailerr("451 4.3.0",
1108 				"%s: fstat: can't stat temporary storage: %s",
1109 				ui.mailspool, sm_errstring(errno));
1110 			goto err1;
1111 		}
1112 
1113 		if (dsb.st_size + sb.st_size + 1 > ui.quota)
1114 		{
1115 			ExitVal = EX_UNAVAILABLE;
1116 			mailerr("551 5.2.2",
1117 				"%s: Mailbox full or quota exceeded",
1118 				ui.mailspool);
1119 			goto err1;
1120 		}
1121 	}
1122 #endif /* 0 */
1123 
1124 	/* Wait until we can get a lock on the file. */
1125 	if (flock(mbfd, LOCK_EX) < 0)
1126 	{
1127 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1128 		goto err1;
1129 	}
1130 
1131 	/* Get the starting offset of the new message for biff. */
1132 	curoff = lseek(mbfd, (off_t) 0, SEEK_END);
1133 	(void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n",
1134 			   name, (LONGLONG_T) curoff);
1135 
1136 	/* Copy the message into the file. */
1137 	if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1)
1138 	{
1139 		mailerr("450 4.2.0", "Temporary file: %s",
1140 			sm_errstring(errno));
1141 		goto err1;
1142 	}
1143 #ifdef DEBUG
1144 	fprintf(stderr, "before writing: euid = %d\n", (int) geteuid());
1145 #endif /* DEBUG */
1146 #ifdef CONTENTLENGTH
1147 	headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ;
1148 	for (;;)
1149 	{
1150 		if (headerbytes == 0)
1151 		{
1152 			(void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr);
1153 			nr = strlen(buf);
1154 			headerbytes = -1;
1155 			readamount = 0;
1156 		}
1157 		else if (headerbytes > sizeof(buf) || headerbytes < 0)
1158 			readamount = sizeof(buf);
1159 		else
1160 			readamount = headerbytes;
1161 		if (readamount != 0)
1162 			nr = read(fd, buf, readamount);
1163 		if (nr <= 0)
1164 			break;
1165 		if (headerbytes > 0)
1166 			headerbytes -= nr ;
1167 
1168 #else /* CONTENTLENGTH */
1169 	while ((nr = read(fd, buf, sizeof(buf))) > 0)
1170 	{
1171 #endif /* CONTENTLENGTH */
1172 		for (off = 0; off < nr; off += nw)
1173 		{
1174 			if ((nw = write(mbfd, buf + off, nr - off)) < 0)
1175 			{
1176 				errcode = "450 4.2.0";
1177 #ifdef EDQUOT
1178 				if (errno == EDQUOT && BounceQuota)
1179 					errcode = "552 5.2.2";
1180 #endif /* EDQUOT */
1181 				mailerr(errcode, "%s: %s",
1182 					path, sm_errstring(errno));
1183 				goto err3;
1184 			}
1185 		}
1186 	}
1187 	if (nr < 0)
1188 	{
1189 		mailerr("450 4.2.0", "Temporary file: %s",
1190 			sm_errstring(errno));
1191 		goto err3;
1192 	}
1193 
1194 	/* Flush to disk, don't wait for update. */
1195 	if (fsync(mbfd) < 0)
1196 	{
1197 		mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno));
1198 err3:
1199 		(void) setreuid(0, 0);
1200 #ifdef DEBUG
1201 		fprintf(stderr, "reset euid = %d\n", (int) geteuid());
1202 #endif /* DEBUG */
1203 		(void) ftruncate(mbfd, curoff);
1204 err1:		if (mbfd >= 0)
1205 			(void) close(mbfd);
1206 err0:		unlockmbox();
1207 		return;
1208 	}
1209 
1210 	/* Close and check -- NFS doesn't write until the close. */
1211 	if (close(mbfd))
1212 	{
1213 		errcode = "450 4.2.0";
1214 #ifdef EDQUOT
1215 		if (errno == EDQUOT && BounceQuota)
1216 			errcode = "552 5.2.2";
1217 #endif /* EDQUOT */
1218 		mailerr(errcode, "%s: %s", path, sm_errstring(errno));
1219 		(void) truncate(path, curoff);
1220 	}
1221 	else
1222 		notifybiff(biffmsg);
1223 
1224 	if (setreuid(0, 0) < 0)
1225 	{
1226 		mailerr("450 4.2.0", "setreuid(0, 0): %s",
1227 			sm_errstring(errno));
1228 		goto err0;
1229 	}
1230 #ifdef DEBUG
1231 	fprintf(stderr, "reset euid = %d\n", (int) geteuid());
1232 #endif /* DEBUG */
1233 	unlockmbox();
1234 	if (LMTPMode)
1235 		printf("250 2.1.5 %s Ok\r\n", name);
1236 }
1237 
1238 /*
1239 **  user.lock files are necessary for compatibility with other
1240 **  systems, e.g., when the mail spool file is NFS exported.
1241 **  Alas, mailbox locking is more than just a local matter.
1242 **  EPA 11/94.
1243 */
1244 
1245 bool	Locked = false;
1246 
1247 #ifdef MAILLOCK
1248 int
1249 lockmbox(name)
1250 	char *name;
1251 {
1252 	int r = 0;
1253 
1254 	if (Locked)
1255 		return 0;
1256 	if ((r = maillock(name, 15)) == L_SUCCESS)
1257 	{
1258 		Locked = true;
1259 		return 0;
1260 	}
1261 	switch (r)
1262 	{
1263 	  case L_TMPLOCK:	/* Can't create tmp file */
1264 	  case L_TMPWRITE:	/* Can't write pid into lockfile */
1265 	  case L_MAXTRYS:	/* Failed after retrycnt attempts */
1266 		errno = 0;
1267 		r = EX_TEMPFAIL;
1268 		break;
1269 	  case L_ERROR:		/* Check errno for reason */
1270 		r = errno;
1271 		break;
1272 	  default:		/* other permanent errors */
1273 		errno = 0;
1274 		r = EX_UNAVAILABLE;
1275 		break;
1276 	}
1277 	return r;
1278 }
1279 
1280 void
1281 unlockmbox()
1282 {
1283 	if (Locked)
1284 		mailunlock();
1285 	Locked = false;
1286 }
1287 #else /* MAILLOCK */
1288 
1289 char	LockName[MAXPATHLEN];
1290 
1291 int
1292 lockmbox(path)
1293 	char *path;
1294 {
1295 	int statfailed = 0;
1296 	time_t start;
1297 
1298 	if (Locked)
1299 		return 0;
1300 	if (strlen(path) + 6 > sizeof LockName)
1301 		return EX_SOFTWARE;
1302 	(void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path);
1303 	(void) time(&start);
1304 	for (; ; sleep(5))
1305 	{
1306 		int fd;
1307 		struct stat st;
1308 		time_t now;
1309 
1310 		/* global timeout */
1311 		(void) time(&now);
1312 		if (now > start + LOCKTO_GLOB)
1313 		{
1314 			errno = 0;
1315 			return EX_TEMPFAIL;
1316 		}
1317 		fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, 0);
1318 		if (fd >= 0)
1319 		{
1320 			/* defeat lock checking programs which test pid */
1321 			(void) write(fd, "0", 2);
1322 			Locked = true;
1323 			(void) close(fd);
1324 			return 0;
1325 		}
1326 		if (stat(LockName, &st) < 0)
1327 		{
1328 			if (statfailed++ > 5)
1329 			{
1330 				errno = 0;
1331 				return EX_TEMPFAIL;
1332 			}
1333 			continue;
1334 		}
1335 		statfailed = 0;
1336 		(void) time(&now);
1337 		if (now < st.st_ctime + LOCKTO_RM)
1338 			continue;
1339 
1340 		/* try to remove stale lockfile */
1341 		if (unlink(LockName) < 0)
1342 			return errno;
1343 	}
1344 }
1345 
1346 void
1347 unlockmbox()
1348 {
1349 	if (!Locked)
1350 		return;
1351 	(void) unlink(LockName);
1352 	Locked = false;
1353 }
1354 #endif /* MAILLOCK */
1355 
1356 void
1357 notifybiff(msg)
1358 	char *msg;
1359 {
1360 	static bool initialized = false;
1361 	static int f = -1;
1362 	struct hostent *hp;
1363 	struct servent *sp;
1364 	int len;
1365 	static struct sockaddr_in addr;
1366 
1367 	if (!initialized)
1368 	{
1369 		initialized = true;
1370 
1371 		/* Be silent if biff service not available. */
1372 		if ((sp = getservbyname("biff", "udp")) == NULL ||
1373 		    (hp = gethostbyname("localhost")) == NULL ||
1374 		    hp->h_length != INADDRSZ)
1375 			return;
1376 
1377 		addr.sin_family = hp->h_addrtype;
1378 		memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ);
1379 		addr.sin_port = sp->s_port;
1380 	}
1381 
1382 	/* No message, just return */
1383 	if (msg == NULL)
1384 		return;
1385 
1386 	/* Couldn't initialize addr struct */
1387 	if (addr.sin_family == AF_UNSPEC)
1388 		return;
1389 
1390 	if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1391 		return;
1392 	len = strlen(msg) + 1;
1393 	(void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr));
1394 }
1395 
1396 void
1397 usage()
1398 {
1399 	ExitVal = EX_USAGE;
1400 	mailerr(NULL, "usage: mail.local [-7] [-b] [-d] [-l] [-f from|-r from] [-h filename] user ...");
1401 	exit(ExitVal);
1402 }
1403 
1404 void
1405 /*VARARGS2*/
1406 #ifdef __STDC__
1407 mailerr(const char *hdr, const char *fmt, ...)
1408 #else /* __STDC__ */
1409 mailerr(hdr, fmt, va_alist)
1410 	const char *hdr;
1411 	const char *fmt;
1412 	va_dcl
1413 #endif /* __STDC__ */
1414 {
1415 	size_t len = 0;
1416 	SM_VA_LOCAL_DECL
1417 
1418 	(void) e_to_sys(errno);
1419 
1420 	SM_VA_START(ap, fmt);
1421 
1422 	if (LMTPMode && hdr != NULL)
1423 	{
1424 		sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr);
1425 		len = strlen(ErrBuf);
1426 	}
1427 	(void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap);
1428 	SM_VA_END(ap);
1429 
1430 	if (!HoldErrs)
1431 		flush_error();
1432 
1433 	/* Log the message to syslog. */
1434 	if (!LMTPMode)
1435 		syslog(LOG_ERR, "%s", ErrBuf);
1436 }
1437 
1438 void
1439 flush_error()
1440 {
1441 	if (LMTPMode)
1442 		printf("%s\r\n", ErrBuf);
1443 	else
1444 	{
1445 		if (ExitVal != EX_USAGE)
1446 			(void) fprintf(stderr, "mail.local: ");
1447 		fprintf(stderr, "%s\n", ErrBuf);
1448 	}
1449 }
1450 
1451 /*
1452  * e_to_sys --
1453  *	Guess which errno's are temporary.  Gag me.
1454  */
1455 
1456 int
1457 e_to_sys(num)
1458 	int num;
1459 {
1460 	/* Temporary failures override hard errors. */
1461 	if (ExitVal == EX_TEMPFAIL)
1462 		return ExitVal;
1463 
1464 	switch (num)		/* Hopefully temporary errors. */
1465 	{
1466 #ifdef EDQUOT
1467 	  case EDQUOT:		/* Disc quota exceeded */
1468 		if (BounceQuota)
1469 		{
1470 			ExitVal = EX_UNAVAILABLE;
1471 			break;
1472 		}
1473 		/* FALLTHROUGH */
1474 #endif /* EDQUOT */
1475 #ifdef EAGAIN
1476 	  case EAGAIN:		/* Resource temporarily unavailable */
1477 #endif /* EAGAIN */
1478 #ifdef EBUSY
1479 	  case EBUSY:		/* Device busy */
1480 #endif /* EBUSY */
1481 #ifdef EPROCLIM
1482 	  case EPROCLIM:	/* Too many processes */
1483 #endif /* EPROCLIM */
1484 #ifdef EUSERS
1485 	  case EUSERS:		/* Too many users */
1486 #endif /* EUSERS */
1487 #ifdef ECONNABORTED
1488 	  case ECONNABORTED:	/* Software caused connection abort */
1489 #endif /* ECONNABORTED */
1490 #ifdef ECONNREFUSED
1491 	  case ECONNREFUSED:	/* Connection refused */
1492 #endif /* ECONNREFUSED */
1493 #ifdef ECONNRESET
1494 	  case ECONNRESET:	/* Connection reset by peer */
1495 #endif /* ECONNRESET */
1496 #ifdef EDEADLK
1497 	  case EDEADLK:		/* Resource deadlock avoided */
1498 #endif /* EDEADLK */
1499 #ifdef EFBIG
1500 	  case EFBIG:		/* File too large */
1501 #endif /* EFBIG */
1502 #ifdef EHOSTDOWN
1503 	  case EHOSTDOWN:	/* Host is down */
1504 #endif /* EHOSTDOWN */
1505 #ifdef EHOSTUNREACH
1506 	  case EHOSTUNREACH:	/* No route to host */
1507 #endif /* EHOSTUNREACH */
1508 #ifdef EMFILE
1509 	  case EMFILE:		/* Too many open files */
1510 #endif /* EMFILE */
1511 #ifdef ENETDOWN
1512 	  case ENETDOWN:	/* Network is down */
1513 #endif /* ENETDOWN */
1514 #ifdef ENETRESET
1515 	  case ENETRESET:	/* Network dropped connection on reset */
1516 #endif /* ENETRESET */
1517 #ifdef ENETUNREACH
1518 	  case ENETUNREACH:	/* Network is unreachable */
1519 #endif /* ENETUNREACH */
1520 #ifdef ENFILE
1521 	  case ENFILE:		/* Too many open files in system */
1522 #endif /* ENFILE */
1523 #ifdef ENOBUFS
1524 	  case ENOBUFS:		/* No buffer space available */
1525 #endif /* ENOBUFS */
1526 #ifdef ENOMEM
1527 	  case ENOMEM:		/* Cannot allocate memory */
1528 #endif /* ENOMEM */
1529 #ifdef ENOSPC
1530 	  case ENOSPC:		/* No space left on device */
1531 #endif /* ENOSPC */
1532 #ifdef EROFS
1533 	  case EROFS:		/* Read-only file system */
1534 #endif /* EROFS */
1535 #ifdef ESTALE
1536 	  case ESTALE:		/* Stale NFS file handle */
1537 #endif /* ESTALE */
1538 #ifdef ETIMEDOUT
1539 	  case ETIMEDOUT:	/* Connection timed out */
1540 #endif /* ETIMEDOUT */
1541 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
1542 	  case EWOULDBLOCK:	/* Operation would block. */
1543 #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */
1544 		ExitVal = EX_TEMPFAIL;
1545 		break;
1546 
1547 	  default:
1548 		ExitVal = EX_UNAVAILABLE;
1549 		break;
1550 	}
1551 	return ExitVal;
1552 }
1553 
1554 #if defined(ultrix) || defined(_CRAY)
1555 /*
1556  * Copyright (c) 1987, 1993
1557  *	The Regents of the University of California.  All rights reserved.
1558  *
1559  * Redistribution and use in source and binary forms, with or without
1560  * modification, are permitted provided that the following conditions
1561  * are met:
1562  * 1. Redistributions of source code must retain the above copyright
1563  *    notice, this list of conditions and the following disclaimer.
1564  * 2. Redistributions in binary form must reproduce the above copyright
1565  *    notice, this list of conditions and the following disclaimer in the
1566  *    documentation and/or other materials provided with the distribution.
1567  * 3. All advertising materials mentioning features or use of this software
1568  *    must display the following acknowledgement:
1569  *	This product includes software developed by the University of
1570  *	California, Berkeley and its contributors.
1571  * 4. Neither the name of the University nor the names of its contributors
1572  *    may be used to endorse or promote products derived from this software
1573  *    without specific prior written permission.
1574  *
1575  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1576  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1577  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1578  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1579  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1580  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1581  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1582  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1583  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1584  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1585  * SUCH DAMAGE.
1586  */
1587 
1588 # if defined(LIBC_SCCS) && !defined(lint)
1589 static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
1590 # endif /* defined(LIBC_SCCS) && !defined(lint) */
1591 
1592 # include <sys/types.h>
1593 # include <sys/stat.h>
1594 # include <fcntl.h>
1595 # include <errno.h>
1596 # include <stdio.h>
1597 # include <ctype.h>
1598 
1599 static int _gettemp();
1600 
1601 mkstemp(path)
1602 	char *path;
1603 {
1604 	int fd;
1605 
1606 	return (_gettemp(path, &fd) ? fd : -1);
1607 }
1608 
1609 static
1610 _gettemp(path, doopen)
1611 	char *path;
1612 	register int *doopen;
1613 {
1614 	extern int errno;
1615 	register char *start, *trv;
1616 	struct stat sbuf;
1617 	unsigned int pid;
1618 
1619 	pid = getpid();
1620 	for (trv = path; *trv; ++trv);		/* extra X's get set to 0's */
1621 	while (*--trv == 'X')
1622 	{
1623 		*trv = (pid % 10) + '0';
1624 		pid /= 10;
1625 	}
1626 
1627 	/*
1628 	 * check the target directory; if you have six X's and it
1629 	 * doesn't exist this runs for a *very* long time.
1630 	 */
1631 	for (start = trv + 1;; --trv)
1632 	{
1633 		if (trv <= path)
1634 			break;
1635 		if (*trv == '/')
1636 		{
1637 			*trv = '\0';
1638 			if (stat(path, &sbuf) < 0)
1639 				return(0);
1640 			if (!S_ISDIR(sbuf.st_mode))
1641 			{
1642 				errno = ENOTDIR;
1643 				return(0);
1644 			}
1645 			*trv = '/';
1646 			break;
1647 		}
1648 	}
1649 
1650 	for (;;)
1651 	{
1652 		if (doopen)
1653 		{
1654 			if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR,
1655 					    0600)) >= 0)
1656 				return(1);
1657 			if (errno != EEXIST)
1658 				return(0);
1659 		}
1660 		else if (stat(path, &sbuf) < 0)
1661 			return(errno == ENOENT ? 1 : 0);
1662 
1663 		/* tricky little algorithm for backward compatibility */
1664 		for (trv = start;;)
1665 		{
1666 			if (!*trv)
1667 				return(0);
1668 			if (*trv == 'z')
1669 				*trv++ = 'a';
1670 			else
1671 			{
1672 				if (isascii(*trv) && isdigit(*trv))
1673 					*trv = 'a';
1674 				else
1675 					++*trv;
1676 				break;
1677 			}
1678 		}
1679 	}
1680 	/* NOTREACHED */
1681 }
1682 #endif /* defined(ultrix) || defined(_CRAY) */
1683