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