10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
30Sstevel@tonic-gate * Copyright (c) 1990, 1993, 1994
40Sstevel@tonic-gate * The Regents of the University of California. All rights reserved.
50Sstevel@tonic-gate *
60Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set
70Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level
80Sstevel@tonic-gate * of the sendmail distribution.
90Sstevel@tonic-gate */
100Sstevel@tonic-gate
110Sstevel@tonic-gate /*
12*4509Sjbeck * Copyright 1994-2007 Sun Microsystems, Inc. All rights reserved.
130Sstevel@tonic-gate * Use is subject to license terms.
140Sstevel@tonic-gate */
150Sstevel@tonic-gate
160Sstevel@tonic-gate #ifndef lint
170Sstevel@tonic-gate static char copyright[] =
180Sstevel@tonic-gate "@(#) Copyright (c) 1990, 1993, 1994\n\
190Sstevel@tonic-gate The Regents of the University of California. All rights reserved.\n";
200Sstevel@tonic-gate #endif /* not lint */
210Sstevel@tonic-gate
220Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
230Sstevel@tonic-gate
240Sstevel@tonic-gate #ifndef lint
250Sstevel@tonic-gate static char sccsid[] = "@(#)mail.local.c 8.83 (Berkeley) 12/17/98";
260Sstevel@tonic-gate static char sccsi2[] = "%W% (Sun) %G%";
270Sstevel@tonic-gate #endif /* not lint */
280Sstevel@tonic-gate
290Sstevel@tonic-gate #include <sys/param.h>
300Sstevel@tonic-gate #include <sys/stat.h>
310Sstevel@tonic-gate #include <sys/socket.h>
320Sstevel@tonic-gate #include <sys/file.h>
330Sstevel@tonic-gate
340Sstevel@tonic-gate #include <netinet/in.h>
350Sstevel@tonic-gate
360Sstevel@tonic-gate #include <errno.h>
370Sstevel@tonic-gate #include <fcntl.h>
380Sstevel@tonic-gate #include <netdb.h>
390Sstevel@tonic-gate #include <pwd.h>
400Sstevel@tonic-gate #include <stdio.h>
410Sstevel@tonic-gate #include <stdlib.h>
420Sstevel@tonic-gate #include <signal.h>
430Sstevel@tonic-gate #include <ctype.h>
440Sstevel@tonic-gate #include <string.h>
450Sstevel@tonic-gate #include <sysexits.h>
460Sstevel@tonic-gate #include <time.h>
470Sstevel@tonic-gate #include <unistd.h>
480Sstevel@tonic-gate #include <maillock.h>
490Sstevel@tonic-gate #include <grp.h>
500Sstevel@tonic-gate
510Sstevel@tonic-gate #ifdef __STDC__
520Sstevel@tonic-gate #include <stdarg.h>
530Sstevel@tonic-gate #else
540Sstevel@tonic-gate #include <varargs.h>
550Sstevel@tonic-gate #endif
560Sstevel@tonic-gate
570Sstevel@tonic-gate #include <syslog.h>
580Sstevel@tonic-gate
590Sstevel@tonic-gate #include <sysexits.h>
600Sstevel@tonic-gate #include <ctype.h>
610Sstevel@tonic-gate
620Sstevel@tonic-gate #include <sm/conf.h>
630Sstevel@tonic-gate #include <sendmail/pathnames.h>
640Sstevel@tonic-gate
650Sstevel@tonic-gate /*
660Sstevel@tonic-gate ** If you don't have flock, you could try using lockf instead.
670Sstevel@tonic-gate */
680Sstevel@tonic-gate
690Sstevel@tonic-gate #ifdef LDA_USE_LOCKF
700Sstevel@tonic-gate # define flock(a, b) lockf(a, b, 0)
710Sstevel@tonic-gate # ifdef LOCK_EX
720Sstevel@tonic-gate # undef LOCK_EX
730Sstevel@tonic-gate # endif /* LOCK_EX */
740Sstevel@tonic-gate # define LOCK_EX F_LOCK
750Sstevel@tonic-gate #endif /* LDA_USE_LOCKF */
760Sstevel@tonic-gate
770Sstevel@tonic-gate #ifndef LOCK_EX
780Sstevel@tonic-gate # include <sys/file.h>
790Sstevel@tonic-gate #endif /* ! LOCK_EX */
800Sstevel@tonic-gate
810Sstevel@tonic-gate #ifndef MAILER_DAEMON
820Sstevel@tonic-gate # define MAILER_DAEMON "MAILER-DAEMON"
830Sstevel@tonic-gate #endif
840Sstevel@tonic-gate
850Sstevel@tonic-gate typedef int bool;
860Sstevel@tonic-gate
870Sstevel@tonic-gate #define FALSE 0
880Sstevel@tonic-gate #define TRUE 1
890Sstevel@tonic-gate
900Sstevel@tonic-gate bool EightBitMime = TRUE; /* advertise 8BITMIME in LMTP */
910Sstevel@tonic-gate static int eval = EX_OK; /* sysexits.h error value. */
920Sstevel@tonic-gate static int lmtpmode = 0;
930Sstevel@tonic-gate bool bouncequota = FALSE; /* permanent error when over quota */
940Sstevel@tonic-gate
950Sstevel@tonic-gate #define _PATH_MAILDIR "/var/mail"
960Sstevel@tonic-gate #define _PATH_LOCTMP "/tmp/local.XXXXXX"
970Sstevel@tonic-gate #define _PATH_LOCHTMP "/tmp/lochd.XXXXXX"
980Sstevel@tonic-gate #define FALSE 0
990Sstevel@tonic-gate #define TRUE 1
1000Sstevel@tonic-gate #define MAXLINE 2048
1010Sstevel@tonic-gate
1020Sstevel@tonic-gate static void deliver(int, int, char *, bool);
1030Sstevel@tonic-gate static void e_to_sys(int);
1040Sstevel@tonic-gate static void err(const char *fmt, ...);
1050Sstevel@tonic-gate static void notifybiff(char *);
1060Sstevel@tonic-gate static void store(char *, int);
1070Sstevel@tonic-gate static void usage(void);
1080Sstevel@tonic-gate static void vwarn();
1090Sstevel@tonic-gate static void warn(const char *fmt, ...);
1100Sstevel@tonic-gate static void mailerr(const char *, const char *, ...);
1110Sstevel@tonic-gate static void sigterm_handler();
1120Sstevel@tonic-gate
1130Sstevel@tonic-gate static char unix_from_line[MAXLINE];
1140Sstevel@tonic-gate static int ulen;
1150Sstevel@tonic-gate static int content_length;
1160Sstevel@tonic-gate static int bfd, hfd; /* temp file */
1170Sstevel@tonic-gate static uid_t src_uid, targ_uid, saved_uid;
1180Sstevel@tonic-gate static int sigterm_caught;
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate int
main(argc,argv)1210Sstevel@tonic-gate main(argc, argv)
1220Sstevel@tonic-gate int argc;
1230Sstevel@tonic-gate char *argv[];
1240Sstevel@tonic-gate {
1250Sstevel@tonic-gate struct passwd *pw;
1260Sstevel@tonic-gate int ch;
1270Sstevel@tonic-gate uid_t uid;
1280Sstevel@tonic-gate char *from;
1290Sstevel@tonic-gate struct group *grpptr;
1300Sstevel@tonic-gate void dolmtp();
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate openlog("mail.local", 0, LOG_MAIL);
1330Sstevel@tonic-gate
1340Sstevel@tonic-gate from = NULL;
1350Sstevel@tonic-gate pw = NULL;
1360Sstevel@tonic-gate sigterm_caught = FALSE;
1370Sstevel@tonic-gate
1380Sstevel@tonic-gate (void) sigset(SIGTERM, sigterm_handler);
1390Sstevel@tonic-gate
1400Sstevel@tonic-gate while ((ch = getopt(argc, argv, "7bdf:r:l")) != EOF)
1410Sstevel@tonic-gate switch (ch) {
1420Sstevel@tonic-gate case '7': /* Do not advertise 8BITMIME */
1430Sstevel@tonic-gate EightBitMime = FALSE;
1440Sstevel@tonic-gate break;
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate case 'b': /* bounce mail when over quota. */
1470Sstevel@tonic-gate bouncequota = TRUE;
1480Sstevel@tonic-gate break;
1490Sstevel@tonic-gate
1500Sstevel@tonic-gate case 'd': /* Backward compatible. */
1510Sstevel@tonic-gate break;
1520Sstevel@tonic-gate case 'f':
1530Sstevel@tonic-gate case 'r': /* Backward compatible. */
1540Sstevel@tonic-gate if (from != NULL) {
1550Sstevel@tonic-gate warn("multiple -f options");
1560Sstevel@tonic-gate usage();
1570Sstevel@tonic-gate }
1580Sstevel@tonic-gate from = optarg;
1590Sstevel@tonic-gate break;
1600Sstevel@tonic-gate case 'l':
1610Sstevel@tonic-gate lmtpmode++;
1620Sstevel@tonic-gate break;
1630Sstevel@tonic-gate case '?':
1640Sstevel@tonic-gate default:
1650Sstevel@tonic-gate usage();
1660Sstevel@tonic-gate }
1670Sstevel@tonic-gate argc -= optind;
1680Sstevel@tonic-gate argv += optind;
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate notifybiff(NULL); /* initialize biff structures */
1710Sstevel@tonic-gate
1720Sstevel@tonic-gate /*
1730Sstevel@tonic-gate * We expect sendmail will invoke us with saved id 0
1740Sstevel@tonic-gate * We then do setgid and setuid defore delivery
1750Sstevel@tonic-gate * setgid to mail group
1760Sstevel@tonic-gate */
1770Sstevel@tonic-gate if ((grpptr = getgrnam("mail")) != NULL)
1780Sstevel@tonic-gate (void) setgid(grpptr->gr_gid);
1790Sstevel@tonic-gate saved_uid = geteuid();
1800Sstevel@tonic-gate
1810Sstevel@tonic-gate if (lmtpmode) {
1820Sstevel@tonic-gate if (saved_uid != 0) {
1830Sstevel@tonic-gate warn("only super-user can use -l option");
1840Sstevel@tonic-gate exit(EX_CANTCREAT);
1850Sstevel@tonic-gate }
1860Sstevel@tonic-gate dolmtp(bouncequota);
1870Sstevel@tonic-gate }
1880Sstevel@tonic-gate
1890Sstevel@tonic-gate if (!*argv)
1900Sstevel@tonic-gate usage();
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate /*
1930Sstevel@tonic-gate * If from not specified, use the name from getlogin() if the
1940Sstevel@tonic-gate * uid matches, otherwise, use the name from the password file
1950Sstevel@tonic-gate * corresponding to the uid.
1960Sstevel@tonic-gate */
1970Sstevel@tonic-gate uid = getuid();
1980Sstevel@tonic-gate if (!from && (!(from = getlogin()) ||
1990Sstevel@tonic-gate !(pw = getpwnam(from)) || pw->pw_uid != uid))
2000Sstevel@tonic-gate from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
2010Sstevel@tonic-gate src_uid = pw ? pw->pw_uid : uid;
2020Sstevel@tonic-gate
2030Sstevel@tonic-gate /*
2040Sstevel@tonic-gate * There is no way to distinguish the error status of one delivery
2050Sstevel@tonic-gate * from the rest of the deliveries. So, if we failed hard on one
2060Sstevel@tonic-gate * or more deliveries, but had no failures on any of the others, we
2070Sstevel@tonic-gate * return a hard failure. If we failed temporarily on one or more
2080Sstevel@tonic-gate * deliveries, we return a temporary failure regardless of the other
2090Sstevel@tonic-gate * failures. This results in the delivery being reattempted later
2100Sstevel@tonic-gate * at the expense of repeated failures and multiple deliveries.
2110Sstevel@tonic-gate */
2120Sstevel@tonic-gate
2130Sstevel@tonic-gate for (store(from, 0); *argv; ++argv)
2140Sstevel@tonic-gate deliver(hfd, bfd, *argv, bouncequota);
2150Sstevel@tonic-gate return (eval);
2160Sstevel@tonic-gate }
2170Sstevel@tonic-gate
2180Sstevel@tonic-gate void
sigterm_handler()2190Sstevel@tonic-gate sigterm_handler()
2200Sstevel@tonic-gate {
2210Sstevel@tonic-gate sigterm_caught = TRUE;
2220Sstevel@tonic-gate (void) sigignore(SIGTERM);
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate
2250Sstevel@tonic-gate char *
parseaddr(s)2260Sstevel@tonic-gate parseaddr(s)
2270Sstevel@tonic-gate char *s;
2280Sstevel@tonic-gate {
2290Sstevel@tonic-gate char *p;
2300Sstevel@tonic-gate int len;
2310Sstevel@tonic-gate
2320Sstevel@tonic-gate if (*s++ != '<')
2330Sstevel@tonic-gate return NULL;
2340Sstevel@tonic-gate
2350Sstevel@tonic-gate p = s;
2360Sstevel@tonic-gate
2370Sstevel@tonic-gate /* at-domain-list */
2380Sstevel@tonic-gate while (*p == '@') {
2390Sstevel@tonic-gate p++;
2400Sstevel@tonic-gate if (*p == '[') {
2410Sstevel@tonic-gate p++;
2420Sstevel@tonic-gate while (isascii(*p) &&
2430Sstevel@tonic-gate (isalnum(*p) || *p == '.' ||
2440Sstevel@tonic-gate *p == '-' || *p == ':'))
2450Sstevel@tonic-gate p++;
2460Sstevel@tonic-gate if (*p++ != ']')
2470Sstevel@tonic-gate return NULL;
2480Sstevel@tonic-gate } else {
2490Sstevel@tonic-gate while ((isascii(*p) && isalnum(*p)) ||
2500Sstevel@tonic-gate strchr(".-_", *p))
2510Sstevel@tonic-gate p++;
2520Sstevel@tonic-gate }
2530Sstevel@tonic-gate if (*p == ',' && p[1] == '@')
2540Sstevel@tonic-gate p++;
2550Sstevel@tonic-gate else if (*p == ':' && p[1] != '@')
2560Sstevel@tonic-gate p++;
2570Sstevel@tonic-gate else
2580Sstevel@tonic-gate return NULL;
2590Sstevel@tonic-gate }
2600Sstevel@tonic-gate
2610Sstevel@tonic-gate s = p;
2620Sstevel@tonic-gate
2630Sstevel@tonic-gate /* local-part */
2640Sstevel@tonic-gate if (*p == '\"') {
2650Sstevel@tonic-gate p++;
2660Sstevel@tonic-gate while (*p && *p != '\"') {
2670Sstevel@tonic-gate if (*p == '\\') {
2680Sstevel@tonic-gate if (!*++p)
2690Sstevel@tonic-gate return NULL;
2700Sstevel@tonic-gate }
2710Sstevel@tonic-gate p++;
2720Sstevel@tonic-gate }
2730Sstevel@tonic-gate if (!*p++)
2740Sstevel@tonic-gate return NULL;
2750Sstevel@tonic-gate } else {
2760Sstevel@tonic-gate while (*p && *p != '@' && *p != '>') {
2770Sstevel@tonic-gate if (*p == '\\') {
2780Sstevel@tonic-gate if (!*++p)
2790Sstevel@tonic-gate return NULL;
2800Sstevel@tonic-gate } else {
2810Sstevel@tonic-gate if (*p <= ' ' || (*p & 128) ||
2820Sstevel@tonic-gate strchr("<>()[]\\,;:\"", *p))
2830Sstevel@tonic-gate return NULL;
2840Sstevel@tonic-gate }
2850Sstevel@tonic-gate p++;
2860Sstevel@tonic-gate }
2870Sstevel@tonic-gate }
2880Sstevel@tonic-gate
2890Sstevel@tonic-gate /* @domain */
2900Sstevel@tonic-gate if (*p == '@') {
2910Sstevel@tonic-gate p++;
2920Sstevel@tonic-gate if (*p == '[') {
2930Sstevel@tonic-gate p++;
2940Sstevel@tonic-gate while (isascii(*p) &&
2950Sstevel@tonic-gate (isalnum(*p) || *p == '.' ||
2960Sstevel@tonic-gate *p == '-' || *p == ':'))
2970Sstevel@tonic-gate p++;
2980Sstevel@tonic-gate if (*p++ != ']')
2990Sstevel@tonic-gate return NULL;
3000Sstevel@tonic-gate } else {
3010Sstevel@tonic-gate while ((isascii(*p) && isalnum(*p)) ||
3020Sstevel@tonic-gate strchr(".-_", *p))
3030Sstevel@tonic-gate p++;
3040Sstevel@tonic-gate }
3050Sstevel@tonic-gate }
3060Sstevel@tonic-gate
3070Sstevel@tonic-gate if (*p++ != '>')
3080Sstevel@tonic-gate return NULL;
3090Sstevel@tonic-gate if (*p && *p != ' ')
3100Sstevel@tonic-gate return NULL;
3110Sstevel@tonic-gate len = p - s - 1;
3120Sstevel@tonic-gate
3130Sstevel@tonic-gate if (*s == '\0' || len <= 0)
3140Sstevel@tonic-gate {
3150Sstevel@tonic-gate s = MAILER_DAEMON;
3160Sstevel@tonic-gate len = strlen(s);
3170Sstevel@tonic-gate }
3180Sstevel@tonic-gate
3190Sstevel@tonic-gate p = malloc(len + 1);
3200Sstevel@tonic-gate if (p == NULL) {
3210Sstevel@tonic-gate printf("421 4.3.0 memory exhausted\r\n");
3220Sstevel@tonic-gate exit(EX_TEMPFAIL);
3230Sstevel@tonic-gate }
3240Sstevel@tonic-gate
3250Sstevel@tonic-gate strncpy(p, s, len);
3260Sstevel@tonic-gate p[len] = '\0';
3270Sstevel@tonic-gate return p;
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate
3300Sstevel@tonic-gate char *
process_recipient(addr)3310Sstevel@tonic-gate process_recipient(addr)
3320Sstevel@tonic-gate char *addr;
3330Sstevel@tonic-gate {
3340Sstevel@tonic-gate if (getpwnam(addr) == NULL) {
3350Sstevel@tonic-gate return "550 5.1.1 user unknown";
3360Sstevel@tonic-gate }
3370Sstevel@tonic-gate
3380Sstevel@tonic-gate return NULL;
3390Sstevel@tonic-gate }
3400Sstevel@tonic-gate
3410Sstevel@tonic-gate #define RCPT_GROW 30
3420Sstevel@tonic-gate
3430Sstevel@tonic-gate void
dolmtp(bouncequota)3440Sstevel@tonic-gate dolmtp(bouncequota)
3450Sstevel@tonic-gate bool bouncequota;
3460Sstevel@tonic-gate {
3470Sstevel@tonic-gate char *return_path = NULL;
3480Sstevel@tonic-gate char **rcpt_addr = NULL;
3490Sstevel@tonic-gate int rcpt_num = 0;
3500Sstevel@tonic-gate int rcpt_alloc = 0;
3510Sstevel@tonic-gate bool gotlhlo = FALSE;
3520Sstevel@tonic-gate char myhostname[MAXHOSTNAMELEN];
3530Sstevel@tonic-gate char buf[4096];
3540Sstevel@tonic-gate char *err;
3550Sstevel@tonic-gate char *p;
3560Sstevel@tonic-gate int i;
3570Sstevel@tonic-gate
3580Sstevel@tonic-gate gethostname(myhostname, sizeof myhostname - 1);
3590Sstevel@tonic-gate
3600Sstevel@tonic-gate printf("220 %s LMTP ready\r\n", myhostname);
3610Sstevel@tonic-gate for (;;) {
3620Sstevel@tonic-gate if (sigterm_caught) {
3630Sstevel@tonic-gate for (; rcpt_num > 0; rcpt_num--)
3640Sstevel@tonic-gate printf("451 4.3.0 shutting down\r\n");
3650Sstevel@tonic-gate exit(EX_OK);
3660Sstevel@tonic-gate }
3670Sstevel@tonic-gate fflush(stdout);
3680Sstevel@tonic-gate if (fgets(buf, sizeof(buf)-1, stdin) == NULL) {
3690Sstevel@tonic-gate exit(EX_OK);
3700Sstevel@tonic-gate }
3710Sstevel@tonic-gate p = buf + strlen(buf) - 1;
3720Sstevel@tonic-gate if (p >= buf && *p == '\n')
3730Sstevel@tonic-gate *p-- = '\0';
3740Sstevel@tonic-gate if (p >= buf && *p == '\r')
3750Sstevel@tonic-gate *p-- = '\0';
3760Sstevel@tonic-gate
3770Sstevel@tonic-gate switch (buf[0]) {
3780Sstevel@tonic-gate
3790Sstevel@tonic-gate case 'd':
3800Sstevel@tonic-gate case 'D':
3810Sstevel@tonic-gate if (strcasecmp(buf, "data") == 0) {
3820Sstevel@tonic-gate if (rcpt_num == 0) {
3830Sstevel@tonic-gate printf("503 5.5.1 No recipients\r\n");
3840Sstevel@tonic-gate continue;
3850Sstevel@tonic-gate }
3860Sstevel@tonic-gate store(return_path, rcpt_num);
3870Sstevel@tonic-gate if (bfd == -1 || hfd == -1)
3880Sstevel@tonic-gate continue;
3890Sstevel@tonic-gate
3900Sstevel@tonic-gate for (i = 0; i < rcpt_num; i++) {
3910Sstevel@tonic-gate p = strchr(rcpt_addr[i], '+');
3920Sstevel@tonic-gate if (p != NULL)
3930Sstevel@tonic-gate *p++ = '\0';
3940Sstevel@tonic-gate deliver(hfd, bfd, rcpt_addr[i],
3950Sstevel@tonic-gate bouncequota);
3960Sstevel@tonic-gate }
3970Sstevel@tonic-gate close(bfd);
3980Sstevel@tonic-gate close(hfd);
3990Sstevel@tonic-gate goto rset;
4000Sstevel@tonic-gate }
4010Sstevel@tonic-gate goto syntaxerr;
4020Sstevel@tonic-gate /* NOTREACHED */
4030Sstevel@tonic-gate break;
4040Sstevel@tonic-gate
4050Sstevel@tonic-gate case 'l':
4060Sstevel@tonic-gate case 'L':
4070Sstevel@tonic-gate if (strncasecmp(buf, "lhlo ", 5) == 0)
4080Sstevel@tonic-gate {
4090Sstevel@tonic-gate /* check for duplicate per RFC 1651 4.2 */
4100Sstevel@tonic-gate if (gotlhlo)
4110Sstevel@tonic-gate {
4120Sstevel@tonic-gate printf("503 %s Duplicate LHLO\r\n",
4130Sstevel@tonic-gate myhostname);
4140Sstevel@tonic-gate continue;
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate gotlhlo = TRUE;
4170Sstevel@tonic-gate printf("250-%s\r\n", myhostname);
4180Sstevel@tonic-gate if (EightBitMime)
4190Sstevel@tonic-gate printf("250-8BITMIME\r\n");
4200Sstevel@tonic-gate printf("250-ENHANCEDSTATUSCODES\r\n");
4210Sstevel@tonic-gate printf("250 PIPELINING\r\n");
4220Sstevel@tonic-gate continue;
4230Sstevel@tonic-gate }
4240Sstevel@tonic-gate goto syntaxerr;
4250Sstevel@tonic-gate /* NOTREACHED */
4260Sstevel@tonic-gate break;
4270Sstevel@tonic-gate
4280Sstevel@tonic-gate case 'm':
4290Sstevel@tonic-gate case 'M':
4300Sstevel@tonic-gate if (strncasecmp(buf, "mail ", 5) == 0) {
4310Sstevel@tonic-gate if (return_path != NULL) {
4320Sstevel@tonic-gate printf("503 5.5.1 Nested MAIL command\r\n");
4330Sstevel@tonic-gate continue;
4340Sstevel@tonic-gate }
4350Sstevel@tonic-gate if (strncasecmp(buf+5, "from:", 5) != 0 ||
4360Sstevel@tonic-gate ((return_path = parseaddr(buf+10)) == NULL)) {
4370Sstevel@tonic-gate printf("501 5.5.4 Syntax error in parameters\r\n");
4380Sstevel@tonic-gate continue;
4390Sstevel@tonic-gate }
4400Sstevel@tonic-gate printf("250 2.5.0 ok\r\n");
4410Sstevel@tonic-gate continue;
4420Sstevel@tonic-gate }
4430Sstevel@tonic-gate goto syntaxerr;
4440Sstevel@tonic-gate
4450Sstevel@tonic-gate case 'n':
4460Sstevel@tonic-gate case 'N':
4470Sstevel@tonic-gate if (strcasecmp(buf, "noop") == 0) {
4480Sstevel@tonic-gate printf("250 2.0.0 ok\r\n");
4490Sstevel@tonic-gate continue;
4500Sstevel@tonic-gate }
4510Sstevel@tonic-gate goto syntaxerr;
4520Sstevel@tonic-gate
4530Sstevel@tonic-gate case 'q':
4540Sstevel@tonic-gate case 'Q':
4550Sstevel@tonic-gate if (strcasecmp(buf, "quit") == 0) {
4560Sstevel@tonic-gate printf("221 2.0.0 bye\r\n");
4570Sstevel@tonic-gate exit(EX_OK);
4580Sstevel@tonic-gate }
4590Sstevel@tonic-gate goto syntaxerr;
4600Sstevel@tonic-gate
4610Sstevel@tonic-gate case 'r':
4620Sstevel@tonic-gate case 'R':
4630Sstevel@tonic-gate if (strncasecmp(buf, "rcpt ", 5) == 0) {
4640Sstevel@tonic-gate if (return_path == NULL) {
4650Sstevel@tonic-gate printf("503 5.5.1 Need MAIL command\r\n");
4660Sstevel@tonic-gate continue;
4670Sstevel@tonic-gate }
4680Sstevel@tonic-gate if (rcpt_num >= rcpt_alloc) {
4690Sstevel@tonic-gate rcpt_alloc += RCPT_GROW;
4700Sstevel@tonic-gate rcpt_addr = (char **)
4710Sstevel@tonic-gate realloc((char *)rcpt_addr,
4720Sstevel@tonic-gate rcpt_alloc * sizeof(char **));
4730Sstevel@tonic-gate if (rcpt_addr == NULL) {
4740Sstevel@tonic-gate printf("421 4.3.0 memory exhausted\r\n");
4750Sstevel@tonic-gate exit(EX_TEMPFAIL);
4760Sstevel@tonic-gate }
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate if (strncasecmp(buf+5, "to:", 3) != 0 ||
4790Sstevel@tonic-gate ((rcpt_addr[rcpt_num] = parseaddr(buf+8)) == NULL)) {
4800Sstevel@tonic-gate printf("501 5.5.4 Syntax error in parameters\r\n");
4810Sstevel@tonic-gate continue;
4820Sstevel@tonic-gate }
4830Sstevel@tonic-gate if ((err = process_recipient(rcpt_addr[rcpt_num])) != NULL) {
4840Sstevel@tonic-gate printf("%s\r\n", err);
4850Sstevel@tonic-gate continue;
4860Sstevel@tonic-gate }
4870Sstevel@tonic-gate rcpt_num++;
4880Sstevel@tonic-gate printf("250 2.1.5 ok\r\n");
4890Sstevel@tonic-gate continue;
4900Sstevel@tonic-gate }
4910Sstevel@tonic-gate else if (strcasecmp(buf, "rset") == 0) {
4920Sstevel@tonic-gate printf("250 2.0.0 ok\r\n");
4930Sstevel@tonic-gate
4940Sstevel@tonic-gate rset:
4950Sstevel@tonic-gate while (rcpt_num > 0) {
4960Sstevel@tonic-gate free(rcpt_addr[--rcpt_num]);
4970Sstevel@tonic-gate }
4980Sstevel@tonic-gate if (return_path != NULL)
4990Sstevel@tonic-gate free(return_path);
5000Sstevel@tonic-gate return_path = NULL;
5010Sstevel@tonic-gate continue;
5020Sstevel@tonic-gate }
5030Sstevel@tonic-gate goto syntaxerr;
5040Sstevel@tonic-gate
5050Sstevel@tonic-gate case 'v':
5060Sstevel@tonic-gate case 'V':
5070Sstevel@tonic-gate if (strncasecmp(buf, "vrfy ", 5) == 0) {
5080Sstevel@tonic-gate printf("252 2.3.3 try RCPT to attempt delivery\r\n");
5090Sstevel@tonic-gate continue;
5100Sstevel@tonic-gate }
5110Sstevel@tonic-gate goto syntaxerr;
5120Sstevel@tonic-gate
5130Sstevel@tonic-gate default:
5140Sstevel@tonic-gate syntaxerr:
5150Sstevel@tonic-gate printf("500 5.5.2 Syntax error\r\n");
5160Sstevel@tonic-gate continue;
5170Sstevel@tonic-gate }
5180Sstevel@tonic-gate }
5190Sstevel@tonic-gate }
5200Sstevel@tonic-gate
5210Sstevel@tonic-gate static void
store(from,lmtprcpts)5220Sstevel@tonic-gate store(from, lmtprcpts)
5230Sstevel@tonic-gate char *from;
5240Sstevel@tonic-gate int lmtprcpts;
5250Sstevel@tonic-gate {
5260Sstevel@tonic-gate FILE *fp = NULL;
5270Sstevel@tonic-gate time_t tval;
5280Sstevel@tonic-gate bool fullline = TRUE; /* current line is terminated */
5290Sstevel@tonic-gate bool prevfl; /* previous line was terminated */
5300Sstevel@tonic-gate char line[MAXLINE];
5310Sstevel@tonic-gate FILE *bfp, *hfp;
5320Sstevel@tonic-gate char *btn, *htn;
5330Sstevel@tonic-gate int in_header_section;
534*4509Sjbeck int newfd;
5350Sstevel@tonic-gate
5360Sstevel@tonic-gate bfd = -1;
5370Sstevel@tonic-gate hfd = -1;
5380Sstevel@tonic-gate btn = strdup(_PATH_LOCTMP);
5390Sstevel@tonic-gate if ((bfd = mkstemp(btn)) == -1 || (bfp = fdopen(bfd, "w+")) == NULL) {
5400Sstevel@tonic-gate if (bfd != -1)
5410Sstevel@tonic-gate (void) close(bfd);
5420Sstevel@tonic-gate if (lmtprcpts) {
5430Sstevel@tonic-gate printf("451 4.3.0 unable to open temporary file\r\n");
5440Sstevel@tonic-gate return;
5450Sstevel@tonic-gate } else {
5460Sstevel@tonic-gate mailerr("451 4.3.0", "unable to open temporary file");
5470Sstevel@tonic-gate exit(eval);
5480Sstevel@tonic-gate }
5490Sstevel@tonic-gate }
5500Sstevel@tonic-gate (void) unlink(btn);
5510Sstevel@tonic-gate free(btn);
5520Sstevel@tonic-gate
5530Sstevel@tonic-gate if (lmtpmode) {
5540Sstevel@tonic-gate printf("354 go ahead\r\n");
5550Sstevel@tonic-gate fflush(stdout);
5560Sstevel@tonic-gate }
5570Sstevel@tonic-gate
5580Sstevel@tonic-gate htn = strdup(_PATH_LOCHTMP);
5590Sstevel@tonic-gate if ((hfd = mkstemp(htn)) == -1 || (hfp = fdopen(hfd, "w+")) == NULL) {
5600Sstevel@tonic-gate if (hfd != -1)
5610Sstevel@tonic-gate (void) close(hfd);
5620Sstevel@tonic-gate e_to_sys(errno);
5630Sstevel@tonic-gate err("unable to open temporary file");
5640Sstevel@tonic-gate }
5650Sstevel@tonic-gate (void) unlink(htn);
5660Sstevel@tonic-gate free(htn);
5670Sstevel@tonic-gate
5680Sstevel@tonic-gate in_header_section = TRUE;
5690Sstevel@tonic-gate content_length = 0;
5700Sstevel@tonic-gate fp = hfp;
5710Sstevel@tonic-gate
5720Sstevel@tonic-gate line[0] = '\0';
5730Sstevel@tonic-gate while (fgets(line, sizeof(line), stdin) != (char *)NULL)
5740Sstevel@tonic-gate {
5750Sstevel@tonic-gate size_t line_len = 0;
5760Sstevel@tonic-gate int peek;
5770Sstevel@tonic-gate
5780Sstevel@tonic-gate prevfl = fullline; /* preserve state of previous line */
5790Sstevel@tonic-gate while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
5800Sstevel@tonic-gate line_len++;
5810Sstevel@tonic-gate line_len++;
5820Sstevel@tonic-gate
5830Sstevel@tonic-gate /* Check for dot-stuffing */
5840Sstevel@tonic-gate if (prevfl && lmtprcpts && line[0] == '.')
5850Sstevel@tonic-gate {
5860Sstevel@tonic-gate if (line[1] == '\n' ||
5870Sstevel@tonic-gate (line[1] == '\r' && line[2] == '\n'))
5880Sstevel@tonic-gate goto lmtpdot;
5890Sstevel@tonic-gate memcpy(line, line + 1, line_len);
5900Sstevel@tonic-gate line_len--;
5910Sstevel@tonic-gate }
5920Sstevel@tonic-gate
5930Sstevel@tonic-gate /* Check to see if we have the full line from fgets() */
5940Sstevel@tonic-gate fullline = FALSE;
5950Sstevel@tonic-gate if (line_len > 0)
5960Sstevel@tonic-gate {
5970Sstevel@tonic-gate if (line[line_len - 1] == '\n')
5980Sstevel@tonic-gate {
5990Sstevel@tonic-gate if (line_len >= 2 &&
6000Sstevel@tonic-gate line[line_len - 2] == '\r')
6010Sstevel@tonic-gate {
6020Sstevel@tonic-gate line[line_len - 2] = '\n';
6030Sstevel@tonic-gate line[line_len - 1] = '\0';
6040Sstevel@tonic-gate line_len--;
6050Sstevel@tonic-gate }
6060Sstevel@tonic-gate fullline = TRUE;
6070Sstevel@tonic-gate }
6080Sstevel@tonic-gate else if (line[line_len - 1] == '\r')
6090Sstevel@tonic-gate {
6100Sstevel@tonic-gate /* Did we just miss the CRLF? */
6110Sstevel@tonic-gate peek = fgetc(stdin);
6120Sstevel@tonic-gate if (peek == '\n')
6130Sstevel@tonic-gate {
6140Sstevel@tonic-gate line[line_len - 1] = '\n';
6150Sstevel@tonic-gate fullline = TRUE;
6160Sstevel@tonic-gate }
6170Sstevel@tonic-gate else
6180Sstevel@tonic-gate (void) ungetc(peek, stdin);
6190Sstevel@tonic-gate }
6200Sstevel@tonic-gate }
6210Sstevel@tonic-gate else
6220Sstevel@tonic-gate fullline = TRUE;
6230Sstevel@tonic-gate
6240Sstevel@tonic-gate if (prevfl && line[0] == '\n' && in_header_section) {
6250Sstevel@tonic-gate in_header_section = FALSE;
6260Sstevel@tonic-gate if (fflush(fp) == EOF || ferror(fp)) {
6270Sstevel@tonic-gate if (lmtprcpts) {
6280Sstevel@tonic-gate while (lmtprcpts--)
6290Sstevel@tonic-gate printf("451 4.3.0 temporary file write error\r\n");
6300Sstevel@tonic-gate fclose(fp);
6310Sstevel@tonic-gate return;
6320Sstevel@tonic-gate } else {
6330Sstevel@tonic-gate mailerr("451 4.3.0",
6340Sstevel@tonic-gate "temporary file write error");
6350Sstevel@tonic-gate fclose(fp);
6360Sstevel@tonic-gate exit(eval);
6370Sstevel@tonic-gate }
6380Sstevel@tonic-gate }
6390Sstevel@tonic-gate fp = bfp;
6400Sstevel@tonic-gate continue;
6410Sstevel@tonic-gate }
6420Sstevel@tonic-gate
6430Sstevel@tonic-gate if (in_header_section) {
6440Sstevel@tonic-gate if (strncasecmp("Content-Length:", line, 15) == 0) {
6450Sstevel@tonic-gate continue; /* skip this header */
6460Sstevel@tonic-gate }
6470Sstevel@tonic-gate } else
6480Sstevel@tonic-gate content_length += strlen(line);
6490Sstevel@tonic-gate (void) fwrite(line, sizeof(char), line_len, fp);
6500Sstevel@tonic-gate if (ferror(fp)) {
6510Sstevel@tonic-gate if (lmtprcpts) {
6520Sstevel@tonic-gate while (lmtprcpts--)
6530Sstevel@tonic-gate printf("451 4.3.0 temporary file write error\r\n");
6540Sstevel@tonic-gate fclose(fp);
6550Sstevel@tonic-gate return;
6560Sstevel@tonic-gate } else {
6570Sstevel@tonic-gate mailerr("451 4.3.0",
6580Sstevel@tonic-gate "temporary file write error");
6590Sstevel@tonic-gate fclose(fp);
6600Sstevel@tonic-gate exit(eval);
6610Sstevel@tonic-gate }
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate }
6640Sstevel@tonic-gate if (sigterm_caught) {
6650Sstevel@tonic-gate if (lmtprcpts)
6660Sstevel@tonic-gate while (lmtprcpts--)
6670Sstevel@tonic-gate printf("451 4.3.0 shutting down\r\n");
6680Sstevel@tonic-gate else
6690Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down");
6700Sstevel@tonic-gate fclose(fp);
6710Sstevel@tonic-gate exit(eval);
6720Sstevel@tonic-gate }
6730Sstevel@tonic-gate
6740Sstevel@tonic-gate if (lmtprcpts) {
6750Sstevel@tonic-gate /* Got a premature EOF -- toss message and exit */
6760Sstevel@tonic-gate exit(EX_OK);
6770Sstevel@tonic-gate }
6780Sstevel@tonic-gate
6790Sstevel@tonic-gate /* If message not newline terminated, need an extra. */
6800Sstevel@tonic-gate if (!strchr(line, '\n')) {
6810Sstevel@tonic-gate (void) putc('\n', fp);
6820Sstevel@tonic-gate content_length++;
6830Sstevel@tonic-gate }
6840Sstevel@tonic-gate
6850Sstevel@tonic-gate lmtpdot:
6860Sstevel@tonic-gate
6870Sstevel@tonic-gate /* Output a newline; note, empty messages are allowed. */
6880Sstevel@tonic-gate (void) putc('\n', fp);
6890Sstevel@tonic-gate
6900Sstevel@tonic-gate if (fflush(fp) == EOF || ferror(fp)) {
6910Sstevel@tonic-gate if (lmtprcpts) {
6920Sstevel@tonic-gate while (lmtprcpts--) {
6930Sstevel@tonic-gate printf("451 4.3.0 temporary file write error\r\n");
6940Sstevel@tonic-gate }
6950Sstevel@tonic-gate fclose(fp);
6960Sstevel@tonic-gate return;
6970Sstevel@tonic-gate } else {
6980Sstevel@tonic-gate mailerr("451 4.3.0", "temporary file write error");
6990Sstevel@tonic-gate fclose(fp);
7000Sstevel@tonic-gate exit(eval);
7010Sstevel@tonic-gate }
7020Sstevel@tonic-gate }
7030Sstevel@tonic-gate
704*4509Sjbeck if ((newfd = dup(bfd)) >= 0) {
705*4509Sjbeck fclose(bfp);
706*4509Sjbeck bfd = newfd;
707*4509Sjbeck }
708*4509Sjbeck if ((newfd = dup(hfd)) >= 0) {
709*4509Sjbeck fclose(hfp);
710*4509Sjbeck hfd = newfd;
711*4509Sjbeck }
7120Sstevel@tonic-gate (void) time(&tval);
7130Sstevel@tonic-gate (void) snprintf(unix_from_line, sizeof (unix_from_line), "From %s %s",
7140Sstevel@tonic-gate from, ctime(&tval));
7150Sstevel@tonic-gate ulen = strlen(unix_from_line);
7160Sstevel@tonic-gate }
7170Sstevel@tonic-gate
7180Sstevel@tonic-gate static void
handle_error(err_num,bouncequota,path)7192897Sjbeck handle_error(err_num, bouncequota, path)
7202897Sjbeck int err_num;
7212897Sjbeck bool bouncequota;
7222897Sjbeck char *path;
7232897Sjbeck {
7242897Sjbeck #ifdef EDQUOT
7252897Sjbeck if (err_num == EDQUOT && bouncequota) {
7262897Sjbeck mailerr("552 5.2.2", "%s: %s", path, sm_errstring(err_num));
7272897Sjbeck } else
7282897Sjbeck #endif /* EDQUOT */
7292897Sjbeck mailerr("450 4.2.0", "%s: %s", path, sm_errstring(err_num));
7302897Sjbeck }
7312897Sjbeck
7322897Sjbeck static void
deliver(hfd,bfd,name,bouncequota)7330Sstevel@tonic-gate deliver(hfd, bfd, name, bouncequota)
7340Sstevel@tonic-gate int hfd;
7350Sstevel@tonic-gate int bfd;
7360Sstevel@tonic-gate char *name;
7370Sstevel@tonic-gate bool bouncequota;
7380Sstevel@tonic-gate {
7390Sstevel@tonic-gate struct stat fsb, sb;
7400Sstevel@tonic-gate int mbfd = -1, nr, nw = 0, off;
7410Sstevel@tonic-gate char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
7420Sstevel@tonic-gate off_t curoff, cursize;
7430Sstevel@tonic-gate int len;
7440Sstevel@tonic-gate struct passwd *pw = NULL;
7450Sstevel@tonic-gate
7460Sstevel@tonic-gate /*
7470Sstevel@tonic-gate * Disallow delivery to unknown names -- special mailboxes
7480Sstevel@tonic-gate * can be handled in the sendmail aliases file.
7490Sstevel@tonic-gate */
7500Sstevel@tonic-gate if ((pw = getpwnam(name)) == NULL) {
7510Sstevel@tonic-gate eval = EX_TEMPFAIL;
7520Sstevel@tonic-gate mailerr("451 4.3.0", "cannot lookup name: %s", name);
7530Sstevel@tonic-gate return;
7540Sstevel@tonic-gate }
7550Sstevel@tonic-gate endpwent();
7560Sstevel@tonic-gate
7570Sstevel@tonic-gate if (sigterm_caught) {
7580Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down");
7590Sstevel@tonic-gate return;
7600Sstevel@tonic-gate }
7610Sstevel@tonic-gate
7620Sstevel@tonic-gate /* mailbox may be NFS mounted, seteuid to user */
7630Sstevel@tonic-gate targ_uid = pw->pw_uid;
7640Sstevel@tonic-gate (void) seteuid(targ_uid);
7650Sstevel@tonic-gate
7660Sstevel@tonic-gate if ((saved_uid != 0) && (src_uid != targ_uid)) {
7670Sstevel@tonic-gate /*
7680Sstevel@tonic-gate * If saved_uid == 0 (root), anything is OK; this is
7690Sstevel@tonic-gate * as it should be. But to prevent a random user from
7700Sstevel@tonic-gate * calling "mail.local foo" in an attempt to hijack
7710Sstevel@tonic-gate * foo's mail-box, make sure src_uid == targ_uid o/w.
7720Sstevel@tonic-gate */
7730Sstevel@tonic-gate warn("%s: wrong owner (is %d, should be %d)",
7740Sstevel@tonic-gate name, src_uid, targ_uid);
7750Sstevel@tonic-gate eval = EX_CANTCREAT;
7760Sstevel@tonic-gate return;
7770Sstevel@tonic-gate }
7780Sstevel@tonic-gate
7790Sstevel@tonic-gate path[0] = '\0';
7800Sstevel@tonic-gate (void) snprintf(path, sizeof (path), "%s/%s", _PATH_MAILDIR, name);
7810Sstevel@tonic-gate
7820Sstevel@tonic-gate /*
7830Sstevel@tonic-gate * If the mailbox is linked or a symlink, fail. There's an obvious
7840Sstevel@tonic-gate * race here, that the file was replaced with a symbolic link after
7850Sstevel@tonic-gate * the lstat returned, but before the open. We attempt to detect
7860Sstevel@tonic-gate * this by comparing the original stat information and information
7870Sstevel@tonic-gate * returned by an fstat of the file descriptor returned by the open.
7880Sstevel@tonic-gate *
7890Sstevel@tonic-gate * NB: this is a symptom of a larger problem, that the mail spooling
7900Sstevel@tonic-gate * directory is writeable by the wrong users. If that directory is
7910Sstevel@tonic-gate * writeable, system security is compromised for other reasons, and
7920Sstevel@tonic-gate * it cannot be fixed here.
7930Sstevel@tonic-gate *
7940Sstevel@tonic-gate * If we created the mailbox, set the owner/group. If that fails,
7950Sstevel@tonic-gate * just return. Another process may have already opened it, so we
7960Sstevel@tonic-gate * can't unlink it. Historically, binmail set the owner/group at
7970Sstevel@tonic-gate * each mail delivery. We no longer do this, assuming that if the
7980Sstevel@tonic-gate * ownership or permissions were changed there was a reason.
7990Sstevel@tonic-gate *
8000Sstevel@tonic-gate * XXX
8010Sstevel@tonic-gate * open(2) should support flock'ing the file.
8020Sstevel@tonic-gate */
8030Sstevel@tonic-gate tryagain:
8040Sstevel@tonic-gate /* should check lock status, but... maillock return no value */
8050Sstevel@tonic-gate maillock(name, 10);
8060Sstevel@tonic-gate
8070Sstevel@tonic-gate if (sigterm_caught) {
8080Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down");
8090Sstevel@tonic-gate goto err0;
8100Sstevel@tonic-gate }
8110Sstevel@tonic-gate
8120Sstevel@tonic-gate if (lstat(path, &sb)) {
8130Sstevel@tonic-gate mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY,
8140Sstevel@tonic-gate S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
8150Sstevel@tonic-gate if (mbfd != -1)
8160Sstevel@tonic-gate (void) fchmod(mbfd, 0660);
8170Sstevel@tonic-gate
8180Sstevel@tonic-gate
8190Sstevel@tonic-gate if (mbfd == -1) {
8200Sstevel@tonic-gate if (errno == EEXIST) {
8210Sstevel@tonic-gate mailunlock();
8220Sstevel@tonic-gate goto tryagain;
8230Sstevel@tonic-gate }
8240Sstevel@tonic-gate }
8250Sstevel@tonic-gate } else if (sb.st_nlink != 1) {
8260Sstevel@tonic-gate mailerr("550 5.2.0", "%s: too many links", path);
8270Sstevel@tonic-gate goto err0;
8280Sstevel@tonic-gate } else if (!S_ISREG(sb.st_mode)) {
8290Sstevel@tonic-gate mailerr("550 5.2.0", "%s: irregular file", path);
8300Sstevel@tonic-gate goto err0;
8310Sstevel@tonic-gate } else {
8320Sstevel@tonic-gate mbfd = open(path, O_APPEND|O_WRONLY, 0);
8330Sstevel@tonic-gate if (mbfd != -1 &&
8340Sstevel@tonic-gate (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
8350Sstevel@tonic-gate S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
8360Sstevel@tonic-gate sb.st_ino != fsb.st_ino)) {
8370Sstevel@tonic-gate eval = EX_TEMPFAIL;
8380Sstevel@tonic-gate mailerr("550 5.2.0",
8390Sstevel@tonic-gate "%s: fstat: file changed after open", path);
8400Sstevel@tonic-gate goto err1;
8410Sstevel@tonic-gate }
8420Sstevel@tonic-gate }
8430Sstevel@tonic-gate
8440Sstevel@tonic-gate if (mbfd == -1) {
8450Sstevel@tonic-gate mailerr("450 4.2.0", "%s: %s", path, strerror(errno));
8460Sstevel@tonic-gate goto err0;
8470Sstevel@tonic-gate }
8480Sstevel@tonic-gate
8490Sstevel@tonic-gate if (sigterm_caught) {
8500Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down");
8510Sstevel@tonic-gate goto err0;
8520Sstevel@tonic-gate }
8530Sstevel@tonic-gate
8540Sstevel@tonic-gate /* Get the starting offset of the new message for biff. */
8550Sstevel@tonic-gate curoff = lseek(mbfd, (off_t)0, SEEK_END);
8560Sstevel@tonic-gate (void) snprintf(biffmsg, sizeof (biffmsg), "%s@%ld\n", name, curoff);
8570Sstevel@tonic-gate
8580Sstevel@tonic-gate /* Copy the message into the file. */
8590Sstevel@tonic-gate if (lseek(hfd, (off_t)0, SEEK_SET) == (off_t)-1) {
8600Sstevel@tonic-gate mailerr("450 4.2.0", "temporary file: %s", strerror(errno));
8610Sstevel@tonic-gate goto err1;
8620Sstevel@tonic-gate }
8630Sstevel@tonic-gate /* Copy the message into the file. */
8640Sstevel@tonic-gate if (lseek(bfd, (off_t)0, SEEK_SET) == (off_t)-1) {
8650Sstevel@tonic-gate mailerr("450 4.2.0", "temporary file: %s", strerror(errno));
8660Sstevel@tonic-gate goto err1;
8670Sstevel@tonic-gate }
8680Sstevel@tonic-gate if ((write(mbfd, unix_from_line, ulen)) != ulen) {
8692897Sjbeck handle_error(errno, bouncequota, path);
8700Sstevel@tonic-gate goto err2;
8710Sstevel@tonic-gate }
8720Sstevel@tonic-gate
8730Sstevel@tonic-gate if (sigterm_caught) {
8740Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down");
8750Sstevel@tonic-gate goto err2;
8760Sstevel@tonic-gate }
8770Sstevel@tonic-gate
8780Sstevel@tonic-gate while ((nr = read(hfd, buf, sizeof (buf))) > 0)
8790Sstevel@tonic-gate for (off = 0; off < nr; nr -= nw, off += nw)
8800Sstevel@tonic-gate if ((nw = write(mbfd, buf + off, nr)) < 0)
8810Sstevel@tonic-gate {
8822897Sjbeck handle_error(errno, bouncequota, path);
8830Sstevel@tonic-gate goto err2;
8840Sstevel@tonic-gate }
8850Sstevel@tonic-gate if (nr < 0) {
8862897Sjbeck handle_error(errno, bouncequota, path);
8870Sstevel@tonic-gate goto err2;
8880Sstevel@tonic-gate }
8890Sstevel@tonic-gate
8900Sstevel@tonic-gate if (sigterm_caught) {
8910Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down");
8920Sstevel@tonic-gate goto err2;
8930Sstevel@tonic-gate }
8940Sstevel@tonic-gate
8950Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "Content-Length: %d\n\n",
8960Sstevel@tonic-gate content_length);
8970Sstevel@tonic-gate len = strlen(buf);
8980Sstevel@tonic-gate if (write(mbfd, buf, len) != len) {
8992897Sjbeck handle_error(errno, bouncequota, path);
9000Sstevel@tonic-gate goto err2;
9010Sstevel@tonic-gate }
9020Sstevel@tonic-gate
9030Sstevel@tonic-gate if (sigterm_caught) {
9040Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down");
9050Sstevel@tonic-gate goto err2;
9060Sstevel@tonic-gate }
9070Sstevel@tonic-gate
9080Sstevel@tonic-gate while ((nr = read(bfd, buf, sizeof (buf))) > 0) {
9090Sstevel@tonic-gate for (off = 0; off < nr; nr -= nw, off += nw)
9100Sstevel@tonic-gate if ((nw = write(mbfd, buf + off, nr)) < 0) {
9112897Sjbeck handle_error(errno, bouncequota, path);
9120Sstevel@tonic-gate goto err2;
9130Sstevel@tonic-gate }
9140Sstevel@tonic-gate if (sigterm_caught) {
9150Sstevel@tonic-gate mailerr("451 4.3.0", "shutting down");
9160Sstevel@tonic-gate goto err2;
9170Sstevel@tonic-gate }
9180Sstevel@tonic-gate }
9190Sstevel@tonic-gate if (nr < 0) {
9202897Sjbeck handle_error(errno, bouncequota, path);
9210Sstevel@tonic-gate goto err2;
9220Sstevel@tonic-gate }
9230Sstevel@tonic-gate
9240Sstevel@tonic-gate /* Flush to disk, don't wait for update. */
9250Sstevel@tonic-gate if (fsync(mbfd)) {
9262897Sjbeck handle_error(errno, bouncequota, path);
9270Sstevel@tonic-gate err2: if (mbfd >= 0)
9280Sstevel@tonic-gate (void)ftruncate(mbfd, curoff);
9290Sstevel@tonic-gate err1: (void)close(mbfd);
9300Sstevel@tonic-gate err0: mailunlock();
9310Sstevel@tonic-gate (void)seteuid(saved_uid);
9320Sstevel@tonic-gate return;
9330Sstevel@tonic-gate }
9340Sstevel@tonic-gate
9350Sstevel@tonic-gate /*
9360Sstevel@tonic-gate ** Save the current size so if the close() fails below
9370Sstevel@tonic-gate ** we can make sure no other process has changed the mailbox
9380Sstevel@tonic-gate ** between the failed close and the re-open()/re-lock().
9390Sstevel@tonic-gate ** If something else has changed the size, we shouldn't
9400Sstevel@tonic-gate ** try to truncate it as we may do more harm then good
9410Sstevel@tonic-gate ** (e.g., truncate a later message delivery).
9420Sstevel@tonic-gate */
9430Sstevel@tonic-gate
9440Sstevel@tonic-gate if (fstat(mbfd, &sb) < 0)
9450Sstevel@tonic-gate cursize = 0;
9460Sstevel@tonic-gate else
9470Sstevel@tonic-gate cursize = sb.st_size;
9480Sstevel@tonic-gate
9490Sstevel@tonic-gate /* Close and check -- NFS doesn't write until the close. */
9500Sstevel@tonic-gate if (close(mbfd))
9510Sstevel@tonic-gate {
9522897Sjbeck handle_error(errno, bouncequota, path);
9530Sstevel@tonic-gate mbfd = open(path, O_WRONLY, 0);
9540Sstevel@tonic-gate if (mbfd < 0 ||
9550Sstevel@tonic-gate cursize == 0
9560Sstevel@tonic-gate || flock(mbfd, LOCK_EX) < 0 ||
9570Sstevel@tonic-gate fstat(mbfd, &sb) < 0 ||
9580Sstevel@tonic-gate sb.st_size != cursize ||
9590Sstevel@tonic-gate sb.st_nlink != 1 ||
9600Sstevel@tonic-gate !S_ISREG(sb.st_mode) ||
9610Sstevel@tonic-gate sb.st_dev != fsb.st_dev ||
9620Sstevel@tonic-gate sb.st_ino != fsb.st_ino ||
9630Sstevel@tonic-gate sb.st_uid != fsb.st_uid)
9640Sstevel@tonic-gate {
9650Sstevel@tonic-gate /* Don't use a bogus file */
9660Sstevel@tonic-gate if (mbfd >= 0)
9670Sstevel@tonic-gate {
9680Sstevel@tonic-gate (void) close(mbfd);
9690Sstevel@tonic-gate mbfd = -1;
9700Sstevel@tonic-gate }
9710Sstevel@tonic-gate }
9720Sstevel@tonic-gate
9730Sstevel@tonic-gate /* Attempt to truncate back to pre-write size */
9740Sstevel@tonic-gate goto err2;
9750Sstevel@tonic-gate } else
9760Sstevel@tonic-gate notifybiff(biffmsg);
9770Sstevel@tonic-gate
9780Sstevel@tonic-gate mailunlock();
9790Sstevel@tonic-gate
9800Sstevel@tonic-gate (void)seteuid(saved_uid);
9810Sstevel@tonic-gate
9820Sstevel@tonic-gate if (lmtpmode) {
9830Sstevel@tonic-gate printf("250 2.1.5 %s OK\r\n", name);
9840Sstevel@tonic-gate }
9850Sstevel@tonic-gate }
9860Sstevel@tonic-gate
9870Sstevel@tonic-gate static void
notifybiff(msg)9880Sstevel@tonic-gate notifybiff(msg)
9890Sstevel@tonic-gate char *msg;
9900Sstevel@tonic-gate {
9910Sstevel@tonic-gate static struct sockaddr_in addr;
9920Sstevel@tonic-gate static int f = -1;
9930Sstevel@tonic-gate struct hostent *hp;
9940Sstevel@tonic-gate struct servent *sp;
9950Sstevel@tonic-gate int len;
9960Sstevel@tonic-gate
9970Sstevel@tonic-gate if (msg == NULL) {
9980Sstevel@tonic-gate /* Be silent if biff service not available. */
9990Sstevel@tonic-gate if ((sp = getservbyname("biff", "udp")) == NULL)
10000Sstevel@tonic-gate return;
10010Sstevel@tonic-gate if ((hp = gethostbyname("localhost")) == NULL) {
10020Sstevel@tonic-gate warn("localhost: %s", strerror(errno));
10030Sstevel@tonic-gate return;
10040Sstevel@tonic-gate }
10050Sstevel@tonic-gate addr.sin_family = hp->h_addrtype;
10060Sstevel@tonic-gate (void) memmove(&addr.sin_addr, hp->h_addr, hp->h_length);
10070Sstevel@tonic-gate addr.sin_port = sp->s_port;
10080Sstevel@tonic-gate return;
10090Sstevel@tonic-gate }
10100Sstevel@tonic-gate
10110Sstevel@tonic-gate if (addr.sin_family == 0)
10120Sstevel@tonic-gate return; /* did not initialize */
10130Sstevel@tonic-gate
10140Sstevel@tonic-gate if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
10150Sstevel@tonic-gate warn("socket: %s", strerror(errno));
10160Sstevel@tonic-gate return;
10170Sstevel@tonic-gate }
10180Sstevel@tonic-gate len = strlen(msg) + 1;
10190Sstevel@tonic-gate if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof (addr))
10200Sstevel@tonic-gate != len)
10210Sstevel@tonic-gate warn("sendto biff: %s", strerror(errno));
10220Sstevel@tonic-gate }
10230Sstevel@tonic-gate
10240Sstevel@tonic-gate static void
usage()10250Sstevel@tonic-gate usage()
10260Sstevel@tonic-gate {
10270Sstevel@tonic-gate eval = EX_USAGE;
10280Sstevel@tonic-gate err("usage: mail.local [-l] [-f from] user ...");
10290Sstevel@tonic-gate }
10300Sstevel@tonic-gate
10310Sstevel@tonic-gate static void
10320Sstevel@tonic-gate /*VARARGS2*/
10330Sstevel@tonic-gate #ifdef __STDC__
mailerr(const char * hdr,const char * fmt,...)10340Sstevel@tonic-gate mailerr(const char *hdr, const char *fmt, ...)
10350Sstevel@tonic-gate #else
10360Sstevel@tonic-gate mailerr(hdr, fmt, va_alist)
10370Sstevel@tonic-gate const char *hdr;
10380Sstevel@tonic-gate const char *fmt;
10390Sstevel@tonic-gate va_dcl
10400Sstevel@tonic-gate #endif
10410Sstevel@tonic-gate {
10420Sstevel@tonic-gate va_list ap;
10430Sstevel@tonic-gate
10440Sstevel@tonic-gate #ifdef __STDC__
10450Sstevel@tonic-gate va_start(ap, fmt);
10460Sstevel@tonic-gate #else
10470Sstevel@tonic-gate va_start(ap);
10480Sstevel@tonic-gate #endif
10490Sstevel@tonic-gate if (lmtpmode)
10500Sstevel@tonic-gate {
10510Sstevel@tonic-gate if (hdr != NULL)
10520Sstevel@tonic-gate printf("%s ", hdr);
10530Sstevel@tonic-gate vprintf(fmt, ap);
10540Sstevel@tonic-gate printf("\r\n");
10550Sstevel@tonic-gate }
10560Sstevel@tonic-gate else
10570Sstevel@tonic-gate {
10580Sstevel@tonic-gate e_to_sys(errno);
10590Sstevel@tonic-gate vwarn(fmt, ap);
10600Sstevel@tonic-gate }
10610Sstevel@tonic-gate }
10620Sstevel@tonic-gate
10630Sstevel@tonic-gate static void
10640Sstevel@tonic-gate /*VARARGS1*/
10650Sstevel@tonic-gate #ifdef __STDC__
err(const char * fmt,...)10660Sstevel@tonic-gate err(const char *fmt, ...)
10670Sstevel@tonic-gate #else
10680Sstevel@tonic-gate err(fmt, va_alist)
10690Sstevel@tonic-gate const char *fmt;
10700Sstevel@tonic-gate va_dcl
10710Sstevel@tonic-gate #endif
10720Sstevel@tonic-gate {
10730Sstevel@tonic-gate va_list ap;
10740Sstevel@tonic-gate
10750Sstevel@tonic-gate #ifdef __STDC__
10760Sstevel@tonic-gate va_start(ap, fmt);
10770Sstevel@tonic-gate #else
10780Sstevel@tonic-gate va_start(ap);
10790Sstevel@tonic-gate #endif
10800Sstevel@tonic-gate vwarn(fmt, ap);
10810Sstevel@tonic-gate va_end(ap);
10820Sstevel@tonic-gate
10830Sstevel@tonic-gate exit(eval);
10840Sstevel@tonic-gate }
10850Sstevel@tonic-gate
10860Sstevel@tonic-gate static void
10870Sstevel@tonic-gate /*VARARGS1*/
10880Sstevel@tonic-gate #ifdef __STDC__
warn(const char * fmt,...)10890Sstevel@tonic-gate warn(const char *fmt, ...)
10900Sstevel@tonic-gate #else
10910Sstevel@tonic-gate warn(fmt, va_alist)
10920Sstevel@tonic-gate const char *fmt;
10930Sstevel@tonic-gate va_dcl
10940Sstevel@tonic-gate #endif
10950Sstevel@tonic-gate {
10960Sstevel@tonic-gate va_list ap;
10970Sstevel@tonic-gate
10980Sstevel@tonic-gate #ifdef __STDC__
10990Sstevel@tonic-gate va_start(ap, fmt);
11000Sstevel@tonic-gate #else
11010Sstevel@tonic-gate va_start(ap);
11020Sstevel@tonic-gate #endif
11030Sstevel@tonic-gate vwarn(fmt, ap);
11040Sstevel@tonic-gate va_end(ap);
11050Sstevel@tonic-gate }
11060Sstevel@tonic-gate
11070Sstevel@tonic-gate static void
vwarn(fmt,ap)11080Sstevel@tonic-gate vwarn(fmt, ap)
11090Sstevel@tonic-gate const char *fmt;
11100Sstevel@tonic-gate va_list ap;
11110Sstevel@tonic-gate {
11120Sstevel@tonic-gate /*
11130Sstevel@tonic-gate * Log the message to stderr.
11140Sstevel@tonic-gate *
11150Sstevel@tonic-gate * Don't use LOG_PERROR as an openlog() flag to do this,
11160Sstevel@tonic-gate * it's not portable enough.
11170Sstevel@tonic-gate */
11180Sstevel@tonic-gate if (eval != EX_USAGE)
11190Sstevel@tonic-gate (void) fprintf(stderr, "mail.local: ");
11200Sstevel@tonic-gate (void) vfprintf(stderr, fmt, ap);
11210Sstevel@tonic-gate (void) fprintf(stderr, "\n");
11220Sstevel@tonic-gate
11230Sstevel@tonic-gate /* Log the message to syslog. */
11240Sstevel@tonic-gate vsyslog(LOG_ERR, fmt, ap);
11250Sstevel@tonic-gate }
11260Sstevel@tonic-gate
11270Sstevel@tonic-gate /*
11280Sstevel@tonic-gate * e_to_sys --
11290Sstevel@tonic-gate * Guess which errno's are temporary. Gag me.
11300Sstevel@tonic-gate */
11310Sstevel@tonic-gate static void
e_to_sys(num)11320Sstevel@tonic-gate e_to_sys(num)
11330Sstevel@tonic-gate int num;
11340Sstevel@tonic-gate {
11350Sstevel@tonic-gate /* Temporary failures override hard errors. */
11360Sstevel@tonic-gate if (eval == EX_TEMPFAIL)
11370Sstevel@tonic-gate return;
11380Sstevel@tonic-gate
11390Sstevel@tonic-gate switch (num) /* Hopefully temporary errors. */
11400Sstevel@tonic-gate {
11410Sstevel@tonic-gate #ifdef EDQUOT
11420Sstevel@tonic-gate case EDQUOT: /* Disc quota exceeded */
11430Sstevel@tonic-gate if (bouncequota)
11440Sstevel@tonic-gate {
11450Sstevel@tonic-gate eval = EX_UNAVAILABLE;
11460Sstevel@tonic-gate break;
11470Sstevel@tonic-gate }
11480Sstevel@tonic-gate /* FALLTHROUGH */
11490Sstevel@tonic-gate #endif /* EDQUOT */
11500Sstevel@tonic-gate #ifdef EAGAIN
11510Sstevel@tonic-gate case EAGAIN: /* Resource temporarily unavailable */
11520Sstevel@tonic-gate #endif
11530Sstevel@tonic-gate #ifdef EBUSY
11540Sstevel@tonic-gate case EBUSY: /* Device busy */
11550Sstevel@tonic-gate #endif
11560Sstevel@tonic-gate #ifdef EPROCLIM
11570Sstevel@tonic-gate case EPROCLIM: /* Too many processes */
11580Sstevel@tonic-gate #endif
11590Sstevel@tonic-gate #ifdef EUSERS
11600Sstevel@tonic-gate case EUSERS: /* Too many users */
11610Sstevel@tonic-gate #endif
11620Sstevel@tonic-gate #ifdef ECONNABORTED
11630Sstevel@tonic-gate case ECONNABORTED: /* Software caused connection abort */
11640Sstevel@tonic-gate #endif
11650Sstevel@tonic-gate #ifdef ECONNREFUSED
11660Sstevel@tonic-gate case ECONNREFUSED: /* Connection refused */
11670Sstevel@tonic-gate #endif
11680Sstevel@tonic-gate #ifdef ECONNRESET
11690Sstevel@tonic-gate case ECONNRESET: /* Connection reset by peer */
11700Sstevel@tonic-gate #endif
11710Sstevel@tonic-gate #ifdef EDEADLK
11720Sstevel@tonic-gate case EDEADLK: /* Resource deadlock avoided */
11730Sstevel@tonic-gate #endif
11740Sstevel@tonic-gate #ifdef EFBIG
11750Sstevel@tonic-gate case EFBIG: /* File too large */
11760Sstevel@tonic-gate #endif
11770Sstevel@tonic-gate #ifdef EHOSTDOWN
11780Sstevel@tonic-gate case EHOSTDOWN: /* Host is down */
11790Sstevel@tonic-gate #endif
11800Sstevel@tonic-gate #ifdef EHOSTUNREACH
11810Sstevel@tonic-gate case EHOSTUNREACH: /* No route to host */
11820Sstevel@tonic-gate #endif
11830Sstevel@tonic-gate #ifdef EMFILE
11840Sstevel@tonic-gate case EMFILE: /* Too many open files */
11850Sstevel@tonic-gate #endif
11860Sstevel@tonic-gate #ifdef ENETDOWN
11870Sstevel@tonic-gate case ENETDOWN: /* Network is down */
11880Sstevel@tonic-gate #endif
11890Sstevel@tonic-gate #ifdef ENETRESET
11900Sstevel@tonic-gate case ENETRESET: /* Network dropped connection on reset */
11910Sstevel@tonic-gate #endif
11920Sstevel@tonic-gate #ifdef ENETUNREACH
11930Sstevel@tonic-gate case ENETUNREACH: /* Network is unreachable */
11940Sstevel@tonic-gate #endif
11950Sstevel@tonic-gate #ifdef ENFILE
11960Sstevel@tonic-gate case ENFILE: /* Too many open files in system */
11970Sstevel@tonic-gate #endif
11980Sstevel@tonic-gate #ifdef ENOBUFS
11990Sstevel@tonic-gate case ENOBUFS: /* No buffer space available */
12000Sstevel@tonic-gate #endif
12010Sstevel@tonic-gate #ifdef ENOMEM
12020Sstevel@tonic-gate case ENOMEM: /* Cannot allocate memory */
12030Sstevel@tonic-gate #endif
12040Sstevel@tonic-gate #ifdef ENOSPC
12050Sstevel@tonic-gate case ENOSPC: /* No space left on device */
12060Sstevel@tonic-gate #endif
12070Sstevel@tonic-gate #ifdef EROFS
12080Sstevel@tonic-gate case EROFS: /* Read-only file system */
12090Sstevel@tonic-gate #endif
12100Sstevel@tonic-gate #ifdef ESTALE
12110Sstevel@tonic-gate case ESTALE: /* Stale NFS file handle */
12120Sstevel@tonic-gate #endif
12130Sstevel@tonic-gate #ifdef ETIMEDOUT
12140Sstevel@tonic-gate case ETIMEDOUT: /* Connection timed out */
12150Sstevel@tonic-gate #endif
12160Sstevel@tonic-gate #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
12170Sstevel@tonic-gate case EWOULDBLOCK: /* Operation would block. */
12180Sstevel@tonic-gate #endif
12190Sstevel@tonic-gate eval = EX_TEMPFAIL;
12200Sstevel@tonic-gate break;
12210Sstevel@tonic-gate default:
12220Sstevel@tonic-gate eval = EX_UNAVAILABLE;
12230Sstevel@tonic-gate break;
12240Sstevel@tonic-gate }
12250Sstevel@tonic-gate }
1226