Index: head/usr.sbin/sendmail/mail.local/mail.local.c =================================================================== --- head/usr.sbin/sendmail/mail.local/mail.local.c (revision 26988) +++ head/usr.sbin/sendmail/mail.local/mail.local.c (revision 26989) @@ -1,935 +1,948 @@ /*- * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: mail.local.c,v 1.7 1997/02/22 16:13:23 peter Exp $ + * $Id: mail.local.c,v 1.8 1997/03/31 05:11:16 imp Exp $ */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1990, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mail.local.c 8.34 (Berkeley) 11/24/96"; +static char sccsid[] = "@(#)mail.local.c 8.39 (Berkeley) 5/28/97"; #endif /* not lint */ /* * This is not intended to compile on System V derived systems * such as Solaris or HP-UX, since they use a totally different * approach to mailboxes (essentially, they have a setgid program * rather than setuid, and they rely on the ability to "give away" * files to do their work). IT IS NOT A BUG that this doesn't * compile on such architectures. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EX_OK # undef EX_OK /* unistd.h may have another use for this */ #endif #include #include #if __STDC__ #include #else #include #endif #if (defined(sun) && defined(__svr4__)) || defined(__SVR4) # define USE_LOCKF 1 # define USE_SETEUID 1 # define _PATH_MAILDIR "/var/mail" #endif #if defined(_AIX) # define USE_LOCKF 1 +# define USET_SETEUID 1 # define USE_VSYSLOG 0 #endif #if defined(ultrix) # define USE_VSYSLOG 0 #endif #if defined(__osf__) # define USE_VSYSLOG 0 #endif #if defined(NeXT) # include # define _PATH_MAILDIR "/usr/spool/mail" # define __dead /* empty */ # define S_IRUSR S_IREAD # define S_IWUSR S_IWRITE #endif /* * If you don't have flock, you could try using lockf instead. */ #ifdef USE_LOCKF # define flock(a, b) lockf(a, b, 0) # define LOCK_EX F_LOCK #endif #ifndef USE_VSYSLOG # define USE_VSYSLOG 1 #endif #ifndef LOCK_EX # include #endif #ifdef BSD4_4 # include "pathnames.h" #endif #ifndef __P # ifdef __STDC__ # define __P(protos) protos # else # define __P(protos) () # define const # endif #endif #ifndef __dead # if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__) # define __dead __volatile # else # define __dead # endif #endif #ifndef BSD4_4 # define _BSD_VA_LIST_ va_list #endif +#if defined(BSD4_4) || defined(linux) +# define HASSNPRINTF 1 +#endif + +#if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) +# define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ +#endif + #if !defined(BSD4_4) && !defined(linux) extern char *strerror __P((int)); -extern int snprintf __P((char *, int, const char *, ...)); +extern int snprintf __P((char *, size_t, const char *, ...)); extern FILE *fdopen __P((int, const char *)); #endif /* * If you don't have setreuid, and you have saved uids, and you have * a seteuid() call that doesn't try to emulate using setuid(), then * you can try defining USE_SETEUID. */ #ifdef USE_SETEUID # define setreuid(r, e) seteuid(e) #endif #ifndef _PATH_LOCTMP # define _PATH_LOCTMP "/tmp/local.XXXXXX" #endif #ifndef _PATH_MAILDIR # define _PATH_MAILDIR "/var/spool/mail" #endif #ifndef S_ISREG # define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG) #endif int eval = EX_OK; /* sysexits.h error value. */ void deliver __P((int, char *, int, int)); void e_to_sys __P((int)); void err __P((const char *, ...)) __dead2; void notifybiff __P((char *)); int store __P((char *)); void usage __P((void)); void vwarn __P((const char *, _BSD_VA_LIST_)); void warn __P((const char *, ...)); void lockmbox __P((char *)); void unlockmbox __P((void)); int main(argc, argv) int argc; char *argv[]; { struct passwd *pw; int ch, fd, nobiff, nofsync; uid_t uid; char *from; extern char *optarg; extern int optind; /* make sure we have some open file descriptors */ for (fd = 10; fd < 30; fd++) (void) close(fd); /* use a reasonable umask */ (void) umask(0077); #ifdef LOG_MAIL openlog("mail.local", 0, LOG_MAIL); #else openlog("mail.local", 0); #endif from = NULL; nobiff = 0; nofsync = 0; while ((ch = getopt(argc, argv, "bdf:r:s")) != -1) switch(ch) { case 'b': nobiff++; break; case 'd': /* Backward compatible. */ break; case 'f': case 'r': /* Backward compatible. */ if (from != NULL) { warn("multiple -f options"); usage(); } from = optarg; break; case 's': nofsync++; break; case '?': default: usage(); } argc -= optind; argv += optind; if (!*argv) usage(); /* * If from not specified, use the name from getlogin() if the * uid matches, otherwise, use the name from the password file * corresponding to the uid. */ uid = getuid(); if (!from && (!(from = getlogin()) || !(pw = getpwnam(from)) || pw->pw_uid != uid)) from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; /* * There is no way to distinguish the error status of one delivery * from the rest of the deliveries. So, if we failed hard on one * or more deliveries, but had no failures on any of the others, we * return a hard failure. If we failed temporarily on one or more * deliveries, we return a temporary failure regardless of the other * failures. This results in the delivery being reattempted later * at the expense of repeated failures and multiple deliveries. */ for (fd = store(from); *argv; ++argv) deliver(fd, *argv, nobiff, nofsync); exit(eval); } int store(from) char *from; { FILE *fp; time_t tval; int fd, eline; char line[2048]; char tmpbuf[sizeof _PATH_LOCTMP + 1]; strcpy(tmpbuf, _PATH_LOCTMP); if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { e_to_sys(errno); err("unable to open temporary file"); } (void)unlink(tmpbuf); (void)time(&tval); (void)fprintf(fp, "From %s %s", from, ctime(&tval)); line[0] = '\0'; for (eline = 1; fgets(line, sizeof(line), stdin);) { if (line[0] == '\n') eline = 1; else { if (eline && line[0] == 'F' && !memcmp(line, "From ", 5)) (void)putc('>', fp); eline = 0; } (void)fprintf(fp, "%s", line); if (ferror(fp)) { e_to_sys(errno); err("temporary file write error"); } } /* If message not newline terminated, need an extra. */ if (!strchr(line, '\n')) (void)putc('\n', fp); /* Output a newline; note, empty messages are allowed. */ (void)putc('\n', fp); if (fflush(fp) == EOF || ferror(fp)) { e_to_sys(errno); err("temporary file write error"); } return (fd); } void deliver(fd, name, nobiff, nofsync) int fd, nobiff, nofsync; char *name; { struct stat fsb, sb; struct passwd *pw; int mbfd, nr, nw, off; char *p; char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; off_t curoff; /* * Disallow delivery to unknown names -- special mailboxes can be * handled in the sendmail aliases file. */ if (!(pw = getpwnam(name))) { if (eval != EX_TEMPFAIL) eval = EX_UNAVAILABLE; warn("unknown name: %s", name); return; } endpwent(); /* * Keep name reasonably short to avoid buffer overruns. * This isn't necessary on BSD because of the proper * definition of snprintf(), but it can cause problems * on other systems. * Also, clear out any bogus characters. */ if (strlen(name) > 40) name[40] = '\0'; for (p = name; *p != '\0'; p++) { if (!isascii(*p)) *p &= 0x7f; else if (!isprint(*p)) *p = '.'; } (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name); /* * If the mailbox is linked or a symlink, fail. There's an obvious * race here, that the file was replaced with a symbolic link after * the lstat returned, but before the open. We attempt to detect * this by comparing the original stat information and information * returned by an fstat of the file descriptor returned by the open. * * NB: this is a symptom of a larger problem, that the mail spooling * directory is writeable by the wrong users. If that directory is * writeable, system security is compromised for other reasons, and * it cannot be fixed here. * * If we created the mailbox, set the owner/group. If that fails, * just return. Another process may have already opened it, so we * can't unlink it. Historically, binmail set the owner/group at * each mail delivery. We no longer do this, assuming that if the * ownership or permissions were changed there was a reason. * * XXX * open(2) should support flock'ing the file. */ tryagain: lockmbox(path); - if (lstat(path, &sb)) { + if (lstat(path, &sb) < 0) { mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); + if (lstat(path, &sb) < 0) + goto filechanged; + else + sb.st_uid = pw->pw_uid; if (mbfd == -1) { if (errno == EEXIST) goto tryagain; } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) { e_to_sys(errno); warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name); goto err1; } } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) { e_to_sys(errno); warn("%s: irregular file", path); goto err0; } else if (sb.st_uid != pw->pw_uid) { eval = EX_CANTCREAT; warn("%s: wrong ownership (%d)", path, sb.st_uid); goto err0; } else { mbfd = open(path, O_APPEND|O_WRONLY, 0); - if (mbfd != -1 && - (fstat(mbfd, &fsb) || fsb.st_nlink != 1 || - !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev || - sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid)) { - eval = EX_CANTCREAT; - warn("%s: file changed after open", path); - goto err1; - } } if (mbfd == -1) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); goto err0; + } else if (fstat(mbfd, &fsb) < 0 || + fsb.st_nlink != 1 || sb.st_nlink != 1 || + !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev || + sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid) { +filechanged: + eval = EX_CANTCREAT; + warn("%s: file changed after open", path); + goto err1; } /* Wait until we can get a lock on the file. */ if (flock(mbfd, LOCK_EX)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); goto err1; } if (!nobiff) { /* Get the starting offset of the new message for biff. */ curoff = lseek(mbfd, (off_t)0, SEEK_END); (void)snprintf(biffmsg, sizeof(biffmsg), sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n", name, curoff); } /* Copy the message into the file. */ if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { e_to_sys(errno); warn("temporary file: %s", strerror(errno)); goto err1; } if (setreuid(0, pw->pw_uid) < 0) { e_to_sys(errno); warn("setreuid(0, %d): %s (r=%d, e=%d)", pw->pw_uid, strerror(errno), getuid(), geteuid()); goto err1; } #ifdef DEBUG printf("new euid = %d\n", geteuid()); #endif while ((nr = read(fd, buf, sizeof(buf))) > 0) for (off = 0; off < nr; off += nw) if ((nw = write(mbfd, buf + off, nr - off)) < 0) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); goto err3; } if (nr < 0) { e_to_sys(errno); warn("temporary file: %s", strerror(errno)); goto err3; } /* Flush to disk, don't wait for update. */ if (!nofsync && fsync(mbfd)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); err3: if (setreuid(0, 0) < 0) { e_to_sys(errno); warn("setreuid(0, 0): %s", strerror(errno)); } #ifdef DEBUG printf("reset euid = %d\n", geteuid()); #endif err2: (void)ftruncate(mbfd, curoff); err1: (void)close(mbfd); err0: unlockmbox(); return; } /* Close and check -- NFS doesn't write until the close. */ if (close(mbfd)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); - unlockmbox(); - return; - } + truncate(path, curoff); + } else if (!nobiff) + notifybiff(biffmsg); if (setreuid(0, 0) < 0) { e_to_sys(errno); warn("setreuid(0, 0): %s", strerror(errno)); } #ifdef DEBUG printf("reset euid = %d\n", geteuid()); #endif unlockmbox(); - if (!nobiff) - notifybiff(biffmsg); } /* * user.lock files are necessary for compatibility with other * systems, e.g., when the mail spool file is NFS exported. * Alas, mailbox locking is more than just a local matter. * EPA 11/94. */ char lockname[MAXPATHLEN]; int locked = 0; void lockmbox(path) char *path; { int statfailed = 0; if (locked) return; + if (strlen(path) + 6 > sizeof lockname) + return; sprintf(lockname, "%s.lock", path); for (;; sleep(5)) { int fd; struct stat st; time_t now; fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0); if (fd >= 0) { locked = 1; close(fd); return; } if (stat(lockname, &st) < 0) { if (statfailed++ > 5) return; continue; } statfailed = 0; time(&now); if (now < st.st_ctime + 300) continue; unlink(lockname); } } void unlockmbox() { if (!locked) return; unlink(lockname); locked = 0; } void notifybiff(msg) char *msg; { static struct sockaddr_in addr; static int f = -1; struct hostent *hp; struct servent *sp; int len; if (!addr.sin_family) { /* Be silent if biff service not available. */ if (!(sp = getservbyname("biff", "udp"))) return; if (!(hp = gethostbyname("localhost"))) { warn("localhost: %s", strerror(errno)); return; } addr.sin_family = hp->h_addrtype; memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); addr.sin_port = sp->s_port; } if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { warn("socket: %s", strerror(errno)); return; } len = strlen(msg) + 1; if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)) != len) warn("sendto biff: %s", strerror(errno)); } void usage() { eval = EX_USAGE; err("usage: mail.local [-b] [-f from] [-s] user ..."); } #if __STDC__ void err(const char *fmt, ...) #else void err(fmt, va_alist) const char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif vwarn(fmt, ap); va_end(ap); exit(eval); } void #if __STDC__ warn(const char *fmt, ...) #else warn(fmt, va_alist) const char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif vwarn(fmt, ap); va_end(ap); } void vwarn(fmt, ap) const char *fmt; _BSD_VA_LIST_ ap; { /* * Log the message to stderr. * * Don't use LOG_PERROR as an openlog() flag to do this, * it's not portable enough. */ if (eval != EX_USAGE) (void)fprintf(stderr, "mail.local: "); (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, "\n"); #if USE_VSYSLOG /* Log the message to syslog. */ vsyslog(LOG_ERR, fmt, ap); #else { char fmtbuf[10240]; (void) vsprintf(fmtbuf, fmt, ap); syslog(LOG_ERR, "%s", fmtbuf); } #endif } /* * e_to_sys -- * Guess which errno's are temporary. Gag me. */ void e_to_sys(num) int num; { /* Temporary failures override hard errors. */ if (eval == EX_TEMPFAIL) return; switch(num) { /* Hopefully temporary errors. */ #ifdef EAGAIN case EAGAIN: /* Resource temporarily unavailable */ #endif #ifdef EDQUOT case EDQUOT: /* Disc quota exceeded */ #endif #ifdef EBUSY case EBUSY: /* Device busy */ #endif #ifdef EPROCLIM case EPROCLIM: /* Too many processes */ #endif #ifdef EUSERS case EUSERS: /* Too many users */ #endif #ifdef ECONNABORTED case ECONNABORTED: /* Software caused connection abort */ #endif #ifdef ECONNREFUSED case ECONNREFUSED: /* Connection refused */ #endif #ifdef ECONNRESET case ECONNRESET: /* Connection reset by peer */ #endif #ifdef EDEADLK case EDEADLK: /* Resource deadlock avoided */ #endif #ifdef EFBIG case EFBIG: /* File too large */ #endif #ifdef EHOSTDOWN case EHOSTDOWN: /* Host is down */ #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: /* No route to host */ #endif #ifdef EMFILE case EMFILE: /* Too many open files */ #endif #ifdef ENETDOWN case ENETDOWN: /* Network is down */ #endif #ifdef ENETRESET case ENETRESET: /* Network dropped connection on reset */ #endif #ifdef ENETUNREACH case ENETUNREACH: /* Network is unreachable */ #endif #ifdef ENFILE case ENFILE: /* Too many open files in system */ #endif #ifdef ENOBUFS case ENOBUFS: /* No buffer space available */ #endif #ifdef ENOMEM case ENOMEM: /* Cannot allocate memory */ #endif #ifdef ENOSPC case ENOSPC: /* No space left on device */ #endif #ifdef EROFS case EROFS: /* Read-only file system */ #endif #ifdef ESTALE case ESTALE: /* Stale NFS file handle */ #endif #ifdef ETIMEDOUT case ETIMEDOUT: /* Connection timed out */ #endif #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK case EWOULDBLOCK: /* Operation would block. */ #endif eval = EX_TEMPFAIL; break; default: eval = EX_UNAVAILABLE; break; } } #if !defined(BSD4_4) && !defined(__osf__) char * strerror(eno) int eno; { extern int sys_nerr; extern char *sys_errlist[]; static char ebuf[60]; if (eno >= 0 && eno <= sys_nerr) return sys_errlist[eno]; (void) sprintf(ebuf, "Error %d", eno); return ebuf; } -# endif +#endif /* !defined(BSD4_4) && !defined(__osf__) */ -#if !defined(BSD4_4) && !defined(linux) +#if !HASSNPRINTF # if __STDC__ -snprintf(char *buf, int bufsiz, const char *fmt, ...) +snprintf(char *buf, size_t bufsiz, const char *fmt, ...) # else snprintf(buf, bufsiz, fmt, va_alist) char *buf; - int bufsiz; + size_t bufsiz; const char *fmt; va_dcl # endif { va_list ap; # if __STDC__ va_start(ap, fmt); # else va_start(ap); # endif vsprintf(buf, fmt, ap); va_end(ap); } -#endif +#endif /* !HASSNPRINTF */ #ifdef ultrix /* * Copyright (c) 1987, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #include static int _gettemp(); mkstemp(path) char *path; { int fd; return (_gettemp(path, &fd) ? fd : -1); } /* char * mktemp(path) char *path; { return(_gettemp(path, (int *)NULL) ? path : (char *)NULL); } */ static _gettemp(path, doopen) char *path; register int *doopen; { extern int errno; register char *start, *trv; struct stat sbuf; u_int pid; pid = getpid(); for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ while (*--trv == 'X') { *trv = (pid % 10) + '0'; pid /= 10; } /* * check the target directory; if you have six X's and it * doesn't exist this runs for a *very* long time. */ for (start = trv + 1;; --trv) { if (trv <= path) break; if (*trv == '/') { *trv = '\0'; - if (stat(path, &sbuf)) + if (stat(path, &sbuf) < 0) return(0); if (!S_ISDIR(sbuf.st_mode)) { errno = ENOTDIR; return(0); } *trv = '/'; break; } } for (;;) { if (doopen) { if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) return(1); if (errno != EEXIST) return(0); } - else if (stat(path, &sbuf)) + else if (stat(path, &sbuf) < 0) return(errno == ENOENT ? 1 : 0); /* tricky little algorithm for backward compatibility */ for (trv = start;;) { if (!*trv) return(0); if (*trv == 'z') *trv++ = 'a'; else { if (isdigit(*trv)) *trv = 'a'; else ++*trv; break; } } } /*NOTREACHED*/ } -#endif +#endif /* ultrix */ Index: head/usr.sbin/sendmail/mailstats/mailstats.c =================================================================== --- head/usr.sbin/sendmail/mailstats/mailstats.c (revision 26988) +++ head/usr.sbin/sendmail/mailstats/mailstats.c (revision 26989) @@ -1,240 +1,247 @@ /* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mailstats.c 8.8 (Berkeley) 9/25/96"; +static char sccsid[] = "@(#)mailstats.c 8.10 (Berkeley) 5/30/97"; #endif /* not lint */ #define NOT_SENDMAIL #include #include #include #define MNAMELEN 20 /* max length of mailer name */ main(argc, argv) int argc; char **argv; { extern char *optarg; extern int optind; struct statistics stat; register int i; int mno; int ch, fd; char *sfile; char *cfile; FILE *cfp; bool mnames; long frmsgs = 0, frbytes = 0, tomsgs = 0, tobytes = 0; char mtable[MAXMAILERS][MNAMELEN+1]; - char sfilebuf[100]; + char sfilebuf[MAXLINE]; char buf[MAXLINE]; extern char *ctime(); cfile = _PATH_SENDMAILCF; sfile = NULL; mnames = TRUE; while ((ch = getopt(argc, argv, "C:f:o")) != -1) { switch (ch) { case 'C': cfile = optarg; break; case 'f': sfile = optarg; break; case 'o': mnames = FALSE; break; case '?': default: usage: fputs("usage: mailstats [-o] [-C cffile] [-f stfile]\n", stderr); exit(EX_USAGE); } } argc -= optind; argv += optind; if (argc != 0) goto usage; if ((cfp = fopen(cfile, "r")) == NULL) { fprintf(stderr, "mailstats: "); perror(cfile); exit(EX_NOINPUT); } mno = 0; (void) strcpy(mtable[mno++], "prog"); (void) strcpy(mtable[mno++], "*file*"); (void) strcpy(mtable[mno++], "*include*"); while (fgets(buf, sizeof(buf), cfp) != NULL) { register char *b; char *s; register char *m; b = buf; switch (*b++) { case 'M': /* mailer definition */ break; case 'O': /* option -- see if .st file */ if (strncasecmp(b, " StatusFile", 11) == 0 && !isalnum(b[11])) { /* new form -- find value */ b = strchr(b, '='); if (b == NULL) continue; while (isspace(*++b)) continue; } else if (*b++ != 'S') { /* something else boring */ continue; } /* this is the S or StatusFile option -- save it */ + if (strlen(b) >= sizeof sfilebuf) + { + fprintf(stderr, + "StatusFile filename too long: %.30s...\n", + s); + exit(EX_CONFIG); + } strcpy(sfilebuf, b); b = strchr(sfilebuf, '#'); if (b == NULL) b = strchr(sfilebuf, '\n'); if (b == NULL) b = &sfilebuf[strlen(sfilebuf)]; while (isspace(*--b)) continue; *++b = '\0'; if (sfile == NULL) sfile = sfilebuf; default: continue; } if (mno >= MAXMAILERS) { fprintf(stderr, "Too many mailers defined, %d max.\n", MAXMAILERS); exit(EX_SOFTWARE); } m = mtable[mno]; s = m + MNAMELEN; /* is [MNAMELEN+1] */ while (*b != ',' && !isspace(*b) && *b != '\0' && m < s) *m++ = *b++; *m = '\0'; for (i = 0; i < mno; i++) { if (strcmp(mtable[i], mtable[mno]) == 0) break; } if (i == mno) mno++; } (void) fclose(cfp); for (; mno < MAXMAILERS; mno++) mtable[mno][0]='\0'; if (sfile == NULL) { fprintf(stderr, "mailstats: no statistics file located\n"); exit (EX_OSFILE); } if ((fd = open(sfile, O_RDONLY)) < 0 || (i = read(fd, &stat, sizeof stat)) < 0) { fputs("mailstats: ", stderr); perror(sfile); exit(EX_NOINPUT); } if (i == 0) { sleep(1); i = read(fd, &stat, sizeof stat); if (i == 0) { bzero((ARBPTR_T) &stat, sizeof stat); (void) time(&stat.stat_itime); } } else if (i != sizeof stat || stat.stat_size != sizeof(stat)) { fputs("mailstats: file size changed.\n", stderr); exit(EX_OSERR); } printf("Statistics from %s", ctime(&stat.stat_itime)); printf(" M msgsfr bytes_from msgsto bytes_to%s\n", mnames ? " Mailer" : ""); for (i = 0; i < MAXMAILERS; i++) { if (stat.stat_nf[i] || stat.stat_nt[i]) { printf("%2d %6ld %10ldK %6ld %10ldK", i, stat.stat_nf[i], stat.stat_bf[i], stat.stat_nt[i], stat.stat_bt[i]); if (mnames) printf(" %s", mtable[i]); printf("\n"); frmsgs += stat.stat_nf[i]; frbytes += stat.stat_bf[i]; tomsgs += stat.stat_nt[i]; tobytes += stat.stat_bt[i]; } } printf("========================================\n"); printf(" T %6ld %10ldK %6ld %10ldK\n", frmsgs, frbytes, tomsgs, tobytes); exit(EX_OK); } Index: head/usr.sbin/sendmail/makemap/Makefile =================================================================== --- head/usr.sbin/sendmail/makemap/Makefile (revision 26988) +++ head/usr.sbin/sendmail/makemap/Makefile (revision 26989) @@ -1,8 +1,13 @@ -# @(#)Makefile 8.1 (Berkeley) 6/7/93 +# @(#)Makefile 8.4 (Berkeley) 6/10/97 PROG= makemap MAN8= makemap.8 -CFLAGS+=-I${.CURDIR}/../src -DNEWDB +CFLAGS+=-I${.CURDIR}/../src -DNEWDB -DNOT_SENDMAIL + +SRCS= makemap.c safefile.c + +safefile.c: ${.CURDIR}/../src/safefile.c + ln -s ${.CURDIR}/../src/safefile.c .include "../../Makefile.inc" .include Index: head/usr.sbin/sendmail/makemap/makemap.c =================================================================== --- head/usr.sbin/sendmail/makemap/makemap.c (revision 26988) +++ head/usr.sbin/sendmail/makemap/makemap.c (revision 26989) @@ -1,534 +1,775 @@ /* * Copyright (c) 1992 Eric P. Allman. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)makemap.c 8.19 (Berkeley) 11/18/96"; +static char sccsid[] = "@(#)makemap.c 8.35 (Berkeley) 6/10/97"; #endif /* not lint */ -#include -#include #include -#include -#include #include #ifndef ISC_UNIX # include #endif -#define NOT_SENDMAIL -#include "useful.h" -#include "conf.h" +#include "sendmail.h" #ifdef NDBM #include #endif #ifdef NEWDB #include #endif enum type { T_DBM, T_BTREE, T_HASH, T_ERR, T_UNKNOWN }; union dbent { #ifdef NDBM datum dbm; #endif #ifdef NEWDB DBT db; #endif struct { char *data; size_t size; } xx; }; +uid_t RealUid; +gid_t RealGid; +char *RealUserName; +uid_t RunAsUid; +uid_t RunAsGid; +char *RunAsUserName; +int Verbose = 2; +bool DontInitGroups = TRUE; +bool UnsafeGroupWrites = FALSE; +u_char tTdvect[100]; + #define BUFSIZE 1024 main(argc, argv) int argc; char **argv; { char *progname; bool inclnull = FALSE; bool notrunc = FALSE; bool allowreplace = FALSE; bool allowdups = FALSE; bool verbose = FALSE; bool foldcase = TRUE; + bool ignoresafeties = FALSE; int exitstat; int opt; char *typename; char *mapname; char *ext; - char *lext; int lineno; int st; int mode; int putflags; long dbcachesize = 1024 * 1024; enum type type; int fd; union { #ifdef NDBM DBM *dbm; #endif #ifdef NEWDB DB *db; #endif void *dbx; } dbp; union dbent key, val; #ifdef NEWDB BTREEINFO bti; HASHINFO hinfo; #endif char ibuf[BUFSIZE]; char fbuf[MAXNAME]; - char lbuf[MAXNAME]; + char dbuf[MAXNAME]; + char pbuf[MAXNAME]; + static char rnamebuf[MAXNAME]; /* holds RealUserName */ + struct passwd *pw; + int sff = SFF_ROOTOK|SFF_REGONLY|SFF_NOLINK|SFF_NOWLINK; + struct stat std, stp; extern char *optarg; extern int optind; extern bool lockfile(); progname = argv[0]; -#ifdef FFR_CFLAG -#define OPTIONS "Nc:dforv" + RunAsUid = RealUid = getuid(); + RunAsGid = RealGid = getgid(); + pw = getpwuid(RealUid); + if (pw != NULL) + { + if (strlen(pw->pw_name) > MAXNAME - 1) + pw->pw_name[MAXNAME] = 0; + sprintf(rnamebuf, "%s", pw->pw_name); + } + else + sprintf(rnamebuf, "Unknown UID %d", RealUid); + RunAsUserName = RealUserName = rnamebuf; + +#if _FFR_NEW_MAKEMAP_FLAGS +#define OPTIONS "Nc:dforsv" #else #define OPTIONS "Ndforv" #endif while ((opt = getopt(argc, argv, OPTIONS)) != -1) { switch (opt) { case 'N': inclnull = TRUE; break; -#ifdef FFR_CFLAG +#if _FFR_NEW_MAKEMAP_FLAGS case 'c': dbcachesize = atol(optarg); break; #endif case 'd': allowdups = TRUE; break; case 'f': foldcase = FALSE; break; case 'o': notrunc = TRUE; break; case 'r': allowreplace = TRUE; break; +#if _FFR_NEW_MAKEMAP_FLAGS + case 's': + ignoresafeties = TRUE; + break; +#endif + case 'v': verbose = TRUE; break; default: type = T_ERR; break; } } argc -= optind; argv += optind; if (argc != 2) type = T_ERR; else { typename = argv[0]; mapname = argv[1]; ext = NULL; - lext = NULL; if (strcmp(typename, "dbm") == 0) { type = T_DBM; - lext = ".dir"; } else if (strcmp(typename, "btree") == 0) { type = T_BTREE; ext = ".db"; } else if (strcmp(typename, "hash") == 0) { type = T_HASH; ext = ".db"; } else type = T_UNKNOWN; } switch (type) { case T_ERR: -#ifdef FFR_CFLAG - fprintf(stderr, "Usage: %s [-N] [-c cachesize] [-d] [-f] [-o] [-r] [-v] type mapname\n", progname); +#if _FFR_NEW_MAKEMAP_FLAGS + fprintf(stderr, + "Usage: %s [-N] [-c cachesize] [-d] [-f] [-o] [-r] [-s] [-v] type mapname\n", + progname); #else fprintf(stderr, "Usage: %s [-N] [-d] [-f] [-o] [-r] [-v] type mapname\n", progname); #endif exit(EX_USAGE); case T_UNKNOWN: fprintf(stderr, "%s: Unknown database type %s\n", progname, typename); exit(EX_USAGE); #ifndef NDBM case T_DBM: #endif #ifndef NEWDB case T_BTREE: case T_HASH: #endif fprintf(stderr, "%s: Type %s not supported in this version\n", progname, typename); exit(EX_UNAVAILABLE); #ifdef NEWDB case T_BTREE: bzero(&bti, sizeof bti); if (allowdups) bti.flags |= R_DUP; if (allowdups || allowreplace) putflags = 0; else putflags = R_NOOVERWRITE; break; case T_HASH: bzero(&hinfo, sizeof hinfo); if (allowreplace) putflags = 0; else putflags = R_NOOVERWRITE; break; #endif #ifdef NDBM case T_DBM: if (allowdups) { fprintf(stderr, "%s: Type %s does not support -d (allow dups)\n", progname, typename); exit(EX_UNAVAILABLE); } if (allowreplace) putflags = DBM_REPLACE; else putflags = DBM_INSERT; break; #endif } /* ** Adjust file names. */ if (ext != NULL) { int el, fl; el = strlen(ext); fl = strlen(mapname); + if (el + fl + 1 >= sizeof fbuf) + { + fprintf(stderr, "%s: file name too long", mapname); + exit(EX_USAGE); + } if (fl < el || strcmp(&mapname[fl - el], ext) != 0) { strcpy(fbuf, mapname); strcat(fbuf, ext); mapname = fbuf; } } - strcpy(lbuf, mapname); - if (lext != NULL) - strcat(lbuf, lext); + if (!notrunc) + sff |= SFF_CREAT; + switch (type) + { +#ifdef NEWDB + case T_BTREE: + case T_HASH: + if (strlen(mapname) >= sizeof dbuf) + { + fprintf(stderr, + "%s: map name too long\n", mapname); + exit(EX_USAGE); + } + strcpy(dbuf, mapname); + if (!ignoresafeties && + (st = safefile(dbuf, RealUid, RealGid, RealUserName, + sff, S_IWUSR, &std)) != 0) + { + fprintf(stderr, + "%s: could not create: %s\n", + dbuf, errstring(st)); + exit(EX_CANTCREAT); + } + break; +#endif +#ifdef NDBM + case T_DBM: + if (strlen(mapname) + 5 > sizeof dbuf) + { + fprintf(stderr, + "%s: map name too long\n", mapname); + exit(EX_USAGE); + } + sprintf(dbuf, "%s.dir", mapname); + if (!ignoresafeties && + (st = safefile(dbuf, RealUid, RealGid, RealUserName, + sff, S_IWUSR, &std)) != 0) + { + fprintf(stderr, + "%s: could not create: %s\n", + dbuf, errstring(st)); + exit(EX_CANTCREAT); + } + sprintf(pbuf, "%s.pag", mapname); + if (!ignoresafeties && + (st = safefile(pbuf, RealUid, RealGid, RealUserName, + sff, S_IWUSR, &stp)) != 0) + { + fprintf(stderr, + "%s: could not create: %s\n", + pbuf, errstring(st)); + exit(EX_CANTCREAT); + } + break; +#endif + default: + fprintf(stderr, + "%s: internal error: type %d\n", + progname, + type); + exit(EX_SOFTWARE); + } /* ** Create the database. */ mode = O_RDWR; if (!notrunc) mode |= O_CREAT|O_TRUNC; -#ifdef O_EXLOCK +#if O_EXLOCK mode |= O_EXLOCK; #else /* pre-lock the database */ - fd = open(lbuf, mode & ~O_TRUNC, 0644); + if (ignoresafeties) + fd = dfopen(dbuf, mode & ~O_TRUNC, 0644, sff); + else + fd = safeopen(dbuf, mode & ~O_TRUNC, 0644, sff); if (fd < 0) { fprintf(stderr, "%s: cannot create type %s map %s\n", progname, typename, mapname); exit(EX_CANTCREAT); } - (void) lockfile(fd); #endif switch (type) { #ifdef NDBM case T_DBM: dbp.dbm = dbm_open(mapname, mode, 0644); + if (!ignoresafeties && dbp.dbm != NULL && + (filechanged(dbuf, dbm_dirfno(dbp.dbm), &std, sff) || + filechanged(pbuf, dbm_pagfno(dbp.dbm), &stp, sff))) + { + fprintf(stderr, + "dbm map %s: file changed after open\n", + mapname); + dbm_close(dbp.dbm); + exit(EX_CANTCREAT); + } break; #endif #ifdef NEWDB case T_HASH: /* tweak some parameters for performance */ hinfo.nelem = 4096; hinfo.cachesize = dbcachesize; - + dbp.db = dbopen(mapname, mode, 0644, DB_HASH, &hinfo); if (dbp.db != NULL) { + if (!ignoresafeties && + filechanged(dbuf, dbp.db->fd(dbp.db), &std, sff)) + { + fprintf(stderr, + "db map %s: file changed after open\n", + mapname); + dbp.db->close(dbp.db); + exit(EX_CANTCREAT); + } # if OLD_NEWDB (void) (*dbp.db->sync)(dbp.db); # else (void) (*dbp.db->sync)(dbp.db, 0); # endif } break; case T_BTREE: /* tweak some parameters for performance */ bti.cachesize = dbcachesize; dbp.db = dbopen(mapname, mode, 0644, DB_BTREE, &bti); if (dbp.db != NULL) { + if (!ignoresafeties && + filechanged(dbuf, dbp.db->fd(dbp.db), &std, sff)) + { + fprintf(stderr, + "db map %s: file changed after open\n", + mapname); + dbp.db->close(dbp.db); + exit(EX_CANTCREAT); + } # if OLD_NEWDB (void) (*dbp.db->sync)(dbp.db); # else (void) (*dbp.db->sync)(dbp.db, 0); # endif } break; #endif default: - fprintf(stderr, "%s: internal error: type %d\n", progname, type); + fprintf(stderr, "%s: internal error: type %d\n", + progname, type); exit(EX_SOFTWARE); } if (dbp.dbx == NULL) { fprintf(stderr, "%s: cannot open type %s map %s\n", progname, typename, mapname); exit(EX_CANTCREAT); } /* ** Copy the data */ lineno = 0; exitstat = EX_OK; while (fgets(ibuf, sizeof ibuf, stdin) != NULL) { register char *p; lineno++; /* ** Parse the line. */ p = strchr(ibuf, '\n'); if (p != NULL) *p = '\0'; else if (!feof(stdin)) { fprintf(stderr, "%s: %s: line %d: line too long (%d bytes max)\n", progname, mapname, lineno, sizeof ibuf); continue; } if (ibuf[0] == '\0' || ibuf[0] == '#') continue; if (isspace(ibuf[0])) { fprintf(stderr, "%s: %s: line %d: syntax error (leading space)\n", progname, mapname, lineno); continue; } key.xx.data = ibuf; for (p = ibuf; *p != '\0' && !isspace(*p); p++) { if (foldcase && isupper(*p)) *p = tolower(*p); } key.xx.size = p - key.xx.data; if (inclnull) key.xx.size++; if (*p != '\0') *p++ = '\0'; while (isspace(*p)) p++; if (*p == '\0') { fprintf(stderr, "%s: %s: line %d: no RHS for LHS %s\n", progname, mapname, lineno, key.xx.data); continue; } val.xx.data = p; val.xx.size = strlen(p); if (inclnull) val.xx.size++; /* ** Do the database insert. */ if (verbose) { printf("key=`%s', val=`%s'\n", key.xx.data, val.xx.data); } switch (type) { #ifdef NDBM case T_DBM: st = dbm_store(dbp.dbm, key.dbm, val.dbm, putflags); break; #endif #ifdef NEWDB case T_BTREE: case T_HASH: st = (*dbp.db->put)(dbp.db, &key.db, &val.db, putflags); break; #endif } if (st < 0) { fprintf(stderr, "%s: %s: line %d: key %s: put error\n", progname, mapname, lineno, key.xx.data); perror(mapname); exitstat = EX_IOERR; } else if (st > 0) { - fprintf(stderr, "%s: %s: line %d: key %s: duplicate key\n", + fprintf(stderr, + "%s: %s: line %d: key %s: duplicate key\n", progname, mapname, lineno, key.xx.data); } } /* ** Now close the database. */ switch (type) { #ifdef NDBM case T_DBM: dbm_close(dbp.dbm); break; #endif #ifdef NEWDB case T_HASH: case T_BTREE: if ((*dbp.db->close)(dbp.db) < 0) { fprintf(stderr, "%s: %s: error on close\n", progname, mapname); perror(mapname); exitstat = EX_IOERR; } #endif } -#ifndef O_EXLOCK +#if !O_EXLOCK /* release locks */ close(fd); #endif exit (exitstat); } /* ** LOCKFILE -- lock a file using flock or (shudder) fcntl locking ** ** Parameters: ** fd -- the file descriptor of the file. +** filename -- the file name (for error messages). +** ext -- the filename extension. +** type -- type of the lock. Bits can be: +** LOCK_EX -- exclusive lock. +** LOCK_NB -- non-blocking. ** ** Returns: ** TRUE if the lock was acquired. ** FALSE otherwise. */ bool -lockfile(fd) +lockfile(fd, filename, ext, type) int fd; + char *filename; + char *ext; + int type; { # if !HASFLOCK int action; struct flock lfd; extern int errno; bzero(&lfd, sizeof lfd); - lfd.l_type = F_WRLCK; - action = F_SETLKW; + if (bitset(LOCK_UN, type)) + lfd.l_type = F_UNLCK; + else if (bitset(LOCK_EX, type)) + lfd.l_type = F_WRLCK; + else + lfd.l_type = F_RDLCK; + if (bitset(LOCK_NB, type)) + action = F_SETLK; + else + action = F_SETLKW; if (fcntl(fd, action, &lfd) >= 0) return TRUE; /* ** On SunOS, if you are testing using -oQ/tmp/mqueue or ** -oA/tmp/aliases or anything like that, and /tmp is mounted ** as type "tmp" (that is, served from swap space), the ** previous fcntl will fail with "Invalid argument" errors. ** Since this is fairly common during testing, we will assume ** that this indicates that the lock is successfully grabbed. */ if (errno == EINVAL) return TRUE; # else /* HASFLOCK */ - if (flock(fd, LOCK_EX) >= 0) + if (flock(fd, type) >= 0) return TRUE; # endif return FALSE; +} + +/*VARARGS2*/ +void +#ifdef __STDC__ +message(const char *msg, ...) +#else +message(msg, va_alist) + const char *msg; + va_dcl +#endif +{ + const char *m; + VA_LOCAL_DECL + + m = msg; + if (isdigit(m[0]) && isdigit(m[1]) && isdigit(m[2]) && m[3] == ' ') + m += 4; + VA_START(msg); + vfprintf(stderr, m, ap); + VA_END; + fprintf(stderr, "\n"); +} + +/*VARARGS2*/ +void +#ifdef __STDC__ +syserr(const char *msg, ...) +#else +syserr(msg, va_alist) + const char *msg; + va_dcl +#endif +{ + const char *m; + VA_LOCAL_DECL + + m = msg; + if (isdigit(m[0]) && isdigit(m[1]) && isdigit(m[2]) && m[3] == ' ') + m += 4; + VA_START(msg); + vfprintf(stderr, m, ap); + VA_END; + fprintf(stderr, "\n"); +} + +const char * +errstring(err) + int err; +{ + static char errstr[64]; +#if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) + extern char *sys_errlist[]; + extern int sys_nerr; +#endif + + /* handle pseudo-errors internal to sendmail */ + switch (err) + { + case E_SM_OPENTIMEOUT: + return "Timeout on file open"; + + case E_SM_NOSLINK: + return "Symbolic links not allowed"; + + case E_SM_NOHLINK: + return "Hard links not allowed"; + + case E_SM_REGONLY: + return "Regular files only"; + + case E_SM_ISEXEC: + return "Executable files not allowed"; + + case E_SM_WWDIR: + return "World writable directory"; + + case E_SM_GWDIR: + return "Group writable directory"; + + case E_SM_FILECHANGE: + return "File changed after open"; + + case E_SM_WWFILE: + return "World writable file"; + + case E_SM_GWFILE: + return "Group writable file"; + } + +#if HASSTRERROR + return strerror(err); +#else + if (err < 0 || err > sys_nerr) + { + sprintf(errstr, "Error %d", err); + return errstr; + } + return sys_errlist[err]; +#endif } Index: head/usr.sbin/sendmail/praliases/praliases.c =================================================================== --- head/usr.sbin/sendmail/praliases/praliases.c (revision 26988) +++ head/usr.sbin/sendmail/praliases/praliases.c (revision 26989) @@ -1,139 +1,144 @@ /* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)praliases.c 8.4 (Berkeley) 9/25/96"; +static char sccsid[] = "@(#)praliases.c 8.5 (Berkeley) 5/28/97"; #endif /* not lint */ #include #define NOT_SENDMAIL #include #ifdef NEWDB #include #endif int main(argc, argv) int argc; char **argv; { extern char *optarg; extern int optind; #ifdef NDBM DBM *dbp; datum content, key; #endif char *filename; int ch; #ifdef NEWDB const DB *db; DBT newdbkey, newdbcontent; char buf[MAXNAME]; #endif filename = "/etc/aliases"; while ((ch = getopt(argc, argv, "f:")) != -1) switch((char)ch) { case 'f': filename = optarg; break; case '?': default: (void)fprintf(stderr, "usage: praliases [-f file]\n"); exit(EX_USAGE); } argc -= optind; argv += optind; #ifdef NEWDB + if (strlen(filename) + 4 >= sizeof buf) + { + fprintf(stderr, "Alias filename too long: %.30s...\n", filename); + exit(EX_USAGE); + } (void) strcpy(buf, filename); (void) strcat(buf, ".db"); if (db = dbopen(buf, O_RDONLY, 0444 , DB_HASH, NULL)) { if (!argc) { while(!db->seq(db, &newdbkey, &newdbcontent, R_NEXT)) printf("%.*s:%.*s\n", newdbkey.size, newdbkey.data, newdbcontent.size, newdbcontent.data); } else for (; *argv; ++argv) { newdbkey.data = *argv; newdbkey.size = strlen(*argv) + 1; if (!db->get(db, &newdbkey, &newdbcontent, 0)) printf("%s:%.*s\n", newdbkey.data, newdbcontent.size, newdbcontent.data); else printf("%s: No such key\n", newdbkey.data); } } #endif #ifdef NDBM #ifdef NEWDB else { #endif /* NEWDB */ if ((dbp = dbm_open(filename, O_RDONLY, 0)) == NULL) { (void)fprintf(stderr, "praliases: %s: %s\n", filename, strerror(errno)); exit(EX_OSFILE); } if (!argc) for (key = dbm_firstkey(dbp); key.dptr != NULL; key = dbm_nextkey(dbp)) { content = dbm_fetch(dbp, key); (void)printf("%.*s:%.*s\n", key.dsize, key.dptr, content.dsize, content.dptr); } else for (; *argv; ++argv) { key.dptr = *argv; key.dsize = strlen(*argv) + 1; content = dbm_fetch(dbp, key); if (!content.dptr) (void)printf("%s: No such key\n", key.dptr); else (void)printf("%s:%.*s\n", key.dptr, content.dsize, content.dptr); } #ifdef NEWDB } #endif /* NEWDB */ #endif /* NDBM */ exit(EX_OK); } Index: head/usr.sbin/sendmail/src/Makefile =================================================================== --- head/usr.sbin/sendmail/src/Makefile (revision 26988) +++ head/usr.sbin/sendmail/src/Makefile (revision 26989) @@ -1,50 +1,50 @@ -# @(#)Makefile 8.7 (Berkeley) 10/31/95 +# @(#)Makefile 8.8 (Berkeley) 3/28/97 ######################################################################### # This Makefile is for 4.4BSD only!!! For all other systems, use # # the "makesendmail" script. # ######################################################################### PROG= sendmail # define the database format to use for aliases et al. Can be -DNEWDB (for # the new BSD database package -- this is preferred) or -DNDBM for the NDBM # database package. The old putrescent V7 DBM package is no longer # supported. # You can define both NEWDB and NDBM during a transition period; old # databases are read, but the new format will be used on any rebuilds. On # really gnarly systems, you can set this to null; it will crawl like a high # spiral snail, but it will work. DBMDEF= -DNEWDB # if you don't want NIS support, comment out this line # FreeBSD supports NIS NIS= -DNIS CFLAGS+=-I${.CURDIR} ${DBMDEF} ${NIS} #-DNETISO SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \ deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \ - mci.c mime.c parseaddr.c queue.c readcf.c recipient.c savemail.c \ - srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \ - util.c version.c + mci.c mime.c parseaddr.c queue.c readcf.c recipient.c safefile.c \ + savemail.c srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c \ + usersmtp.c util.c version.c DPADD= ${LIBUTIL} LDADD= -lutil MAN1= mailq.1 newaliases.1 MAN5= aliases.5 -MAN8= sendmail.8 +MAN8= sendmail.8 LINKS= /usr/sbin/sendmail /usr/bin/newaliases \ /usr/sbin/sendmail /usr/bin/mailq \ /usr/sbin/sendmail /usr/bin/hoststat \ /usr/sbin/sendmail /usr/sbin/purgestat BINDIR= /usr/sbin BINOWN= root BINMODE=4555 beforeinstall: ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \ ${DESTDIR}/var/log/sendmail.st ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \ ${DESTDIR}/usr/share/misc .include Index: head/usr.sbin/sendmail/src/collect.c =================================================================== --- head/usr.sbin/sendmail/src/collect.c (revision 26988) +++ head/usr.sbin/sendmail/src/collect.c (revision 26989) @@ -1,767 +1,760 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)collect.c 8.62 (Berkeley) 12/11/96"; +static char sccsid[] = "@(#)collect.c 8.69 (Berkeley) 5/29/97"; #endif /* not lint */ # include # include "sendmail.h" /* ** COLLECT -- read & parse message header & make temp file. ** ** Creates a temporary file name and copies the standard ** input to that file. Leading UNIX-style "From" lines are ** stripped off (after important information is extracted). ** ** Parameters: ** fp -- file to read. ** smtpmode -- if set, we are running SMTP: give an RFC821 ** style message to say we are ready to collect ** input, and never ignore a single dot to mean ** end of message. -** requeueflag -- this message will be requeued later, so -** don't do final processing on it. ** hdrp -- the location to stash the header. ** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** Temp file is created and filled. ** The from person may be set. */ static jmp_buf CtxCollectTimeout; static void collecttimeout(); static bool CollectProgress; static EVENT *CollectTimeout; /* values for input state machine */ #define IS_NORM 0 /* middle of line */ #define IS_BOL 1 /* beginning of line */ #define IS_DOT 2 /* read a dot at beginning of line */ #define IS_DOTCR 3 /* read ".\r" at beginning of line */ #define IS_CR 4 /* read a carriage return */ /* values for message state machine */ #define MS_UFROM 0 /* reading Unix from line */ #define MS_HEADER 1 /* reading message header */ #define MS_BODY 2 /* reading message body */ void -collect(fp, smtpmode, requeueflag, hdrp, e) +collect(fp, smtpmode, hdrp, e) FILE *fp; bool smtpmode; - bool requeueflag; HDR **hdrp; register ENVELOPE *e; { register FILE *volatile tf; volatile bool ignrdot = smtpmode ? FALSE : IgnrDot; volatile time_t dbto = smtpmode ? TimeOuts.to_datablock : 0; register char *volatile bp; volatile int c = EOF; volatile bool inputerr = FALSE; bool headeronly; char *volatile buf; volatile int buflen; volatile int istate; volatile int mstate; u_char *volatile pbp; u_char peekbuf[8]; - char dfname[20]; + char dfname[MAXQFNAME]; char bufbuf[MAXLINE]; extern bool isheader(); extern void eatheader(); extern void tferror(); headeronly = hdrp != NULL; /* ** Create the temp file name and create the file. */ if (!headeronly) { + int tfd; struct stat stbuf; strcpy(dfname, queuename(e, 'd')); - if ((tf = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL) + tfd = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode, SFF_ANYFILE); + if (tfd < 0 || (tf = fdopen(tfd, "w")) == NULL) { syserr("Cannot create %s", dfname); e->e_flags |= EF_NO_BODY_RETN; finis(); } if (fstat(fileno(tf), &stbuf) < 0) e->e_dfino = -1; else { e->e_dfdev = stbuf.st_dev; e->e_dfino = stbuf.st_ino; } HasEightBits = FALSE; e->e_msgsize = 0; e->e_flags |= EF_HAS_DF; } /* ** Tell ARPANET to go ahead. */ if (smtpmode) message("354 Enter mail, end with \".\" on a line by itself"); if (tTd(30, 2)) printf("collect\n"); /* ** Read the message. ** ** This is done using two interleaved state machines. ** The input state machine is looking for things like ** hidden dots; the message state machine is handling ** the larger picture (e.g., header versus body). */ buf = bp = bufbuf; buflen = sizeof bufbuf; pbp = peekbuf; istate = IS_BOL; mstate = SaveFrom ? MS_HEADER : MS_UFROM; CollectProgress = FALSE; if (dbto != 0) { /* handle possible input timeout */ if (setjmp(CtxCollectTimeout) != 0) { -#ifdef LOG if (LogLevel > 2) - syslog(LOG_NOTICE, + sm_syslog(LOG_NOTICE, e->e_id, "timeout waiting for input from %s during message collect", CurHostName ? CurHostName : ""); -#endif errno = 0; usrerr("451 timeout waiting for input during message collect"); goto readerr; } CollectTimeout = setevent(dbto, collecttimeout, dbto); } for (;;) { if (tTd(30, 35)) printf("top, istate=%d, mstate=%d\n", istate, mstate); for (;;) { if (pbp > peekbuf) c = *--pbp; else { while (!feof(fp) && !ferror(fp)) { errno = 0; c = getc(fp); if (errno != EINTR) break; clearerr(fp); } CollectProgress = TRUE; if (TrafficLogFile != NULL && !headeronly) { if (istate == IS_BOL) fprintf(TrafficLogFile, "%05d <<< ", (int) getpid()); if (c == EOF) fprintf(TrafficLogFile, "[EOF]\n"); else putc(c, TrafficLogFile); } if (c == EOF) goto readerr; if (SevenBitInput) c &= 0x7f; else HasEightBits |= bitset(0x80, c); } if (tTd(30, 94)) printf("istate=%d, c=%c (0x%x)\n", istate, c, c); switch (istate) { case IS_BOL: if (c == '.') { istate = IS_DOT; continue; } break; case IS_DOT: if (c == '\n' && !ignrdot && !bitset(EF_NL_NOT_EOL, e->e_flags)) goto readerr; else if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags)) { istate = IS_DOTCR; continue; } else if (c != '.' || (OpMode != MD_SMTP && OpMode != MD_DAEMON && OpMode != MD_ARPAFTP)) { *pbp++ = c; c = '.'; } break; case IS_DOTCR: if (c == '\n' && !ignrdot) goto readerr; else { /* push back the ".\rx" */ *pbp++ = c; *pbp++ = '\r'; c = '.'; } break; case IS_CR: if (c == '\n') istate = IS_BOL; else { ungetc(c, fp); c = '\r'; istate = IS_NORM; } goto bufferchar; } if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags)) { istate = IS_CR; continue; } else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags)) istate = IS_BOL; else istate = IS_NORM; bufferchar: if (!headeronly) e->e_msgsize++; if (mstate == MS_BODY) { /* just put the character out */ if (MaxMessageSize <= 0 || e->e_msgsize <= MaxMessageSize) putc(c, tf); continue; } /* header -- buffer up */ if (bp >= &buf[buflen - 2]) { char *obuf; if (mstate != MS_HEADER) break; /* out of space for header */ obuf = buf; if (buflen < MEMCHUNKSIZE) buflen *= 2; else buflen += MEMCHUNKSIZE; buf = xalloc(buflen); bcopy(obuf, buf, bp - obuf); bp = &buf[bp - obuf]; if (obuf != bufbuf) free(obuf); } if (c >= 0200 && c <= 0237) { #if 0 /* causes complaints -- figure out something for 8.9 */ usrerr("Illegal character 0x%x in header", c); #endif } else if (c != '\0') *bp++ = c; if (istate == IS_BOL) break; } *bp = '\0'; nextstate: if (tTd(30, 35)) printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n", istate, mstate, buf); switch (mstate) { extern int chompheader(); case MS_UFROM: mstate = MS_HEADER; #ifndef NOTUNIX if (strncmp(buf, "From ", 5) == 0) { extern void eatfrom(); bp = buf; eatfrom(buf, e); continue; } #endif /* fall through */ case MS_HEADER: if (!isheader(buf)) { mstate = MS_BODY; goto nextstate; } /* check for possible continuation line */ do { clearerr(fp); errno = 0; c = getc(fp); } while (errno == EINTR); if (c != EOF) ungetc(c, fp); if (c == ' ' || c == '\t') { /* yep -- defer this */ continue; } /* trim off trailing CRLF or NL */ if (*--bp != '\n' || *--bp != '\r') bp++; *bp = '\0'; if (bitset(H_EOH, chompheader(buf, FALSE, hdrp, e))) mstate = MS_BODY; break; case MS_BODY: if (tTd(30, 1)) printf("EOH\n"); if (headeronly) goto readerr; bp = buf; /* toss blank line */ if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) && bp[0] == '\r' && bp[1] == '\n') || (!bitset(EF_NL_NOT_EOL, e->e_flags) && bp[0] == '\n')) { break; } /* if not a blank separator, write it out */ if (MaxMessageSize <= 0 || e->e_msgsize <= MaxMessageSize) { while (*bp != '\0') putc(*bp++, tf); } break; } bp = buf; } readerr: if ((feof(fp) && smtpmode) || ferror(fp)) { const char *errmsg = errstring(errno); if (tTd(30, 1)) printf("collect: premature EOM: %s\n", errmsg); -#ifdef LOG if (LogLevel >= 2) - syslog(LOG_WARNING, "collect: premature EOM: %s", errmsg); -#endif + sm_syslog(LOG_WARNING, e->e_id, + "collect: premature EOM: %s", errmsg); inputerr = TRUE; } /* reset global timer */ clrevent(CollectTimeout); if (headeronly) return; if (tf != NULL && (fflush(tf) != 0 || ferror(tf) || fsync(fileno(tf)) < 0 || fclose(tf) < 0)) { tferror(tf, e); flush_errors(TRUE); finis(); } /* An EOF when running SMTP is an error */ if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) { char *host; char *problem; host = RealHostName; if (host == NULL) host = "localhost"; if (feof(fp)) problem = "unexpected close"; else if (ferror(fp)) problem = "I/O error"; else problem = "read timeout"; -# ifdef LOG if (LogLevel > 0 && feof(fp)) - syslog(LOG_NOTICE, + sm_syslog(LOG_NOTICE, e->e_id, "collect: %s on connection from %.100s, sender=%s: %s", problem, host, shortenstring(e->e_from.q_paddr, 203), errstring(errno)); -# endif if (feof(fp)) usrerr("451 collect: %s on connection from %s, from=%s", problem, host, shortenstring(e->e_from.q_paddr, 203)); else syserr("451 collect: %s on connection from %s, from=%s", problem, host, shortenstring(e->e_from.q_paddr, 203)); /* don't return an error indication */ e->e_to = NULL; e->e_flags &= ~EF_FATALERRS; e->e_flags |= EF_CLRQUEUE; /* and don't try to deliver the partial message either */ if (InChild) ExitStat = EX_QUIT; finis(); } /* ** Find out some information from the headers. ** Examples are who is the from person & the date. */ eatheader(e, TRUE); if (GrabTo && e->e_sendqueue == NULL) usrerr("No recipient addresses found in header"); /* collect statistics */ if (OpMode != MD_VERIFY) { extern void markstats(); markstats(e, (ADDRESS *) NULL); } -#ifdef _FFR_DSN_RRT +#if _FFR_DSN_RRT_OPTION /* ** If we have a Return-Receipt-To:, turn it into a DSN. */ if (RrtImpliesDsn && hvalue("return-receipt-to", e->e_header) != NULL) { ADDRESS *q; for (q = e->e_sendqueue; q != NULL; q = q->q_next) if (!bitset(QHASNOTIFY, q->q_flags)) q->q_flags |= QHASNOTIFY|QPINGONSUCCESS; } #endif /* ** Add an Apparently-To: line if we have no recipient lines. */ if (hvalue("to", e->e_header) != NULL || hvalue("cc", e->e_header) != NULL || hvalue("apparently-to", e->e_header) != NULL) { /* have a valid recipient header -- delete Bcc: headers */ e->e_flags |= EF_DELETE_BCC; } else if (hvalue("bcc", e->e_header) == NULL) { /* no valid recipient headers */ register ADDRESS *q; char *hdr = NULL; extern void addheader(); /* create an Apparently-To: field */ /* that or reject the message.... */ switch (NoRecipientAction) { case NRA_ADD_APPARENTLY_TO: hdr = "Apparently-To"; break; case NRA_ADD_TO: hdr = "To"; break; case NRA_ADD_BCC: addheader("Bcc", "", &e->e_header); break; case NRA_ADD_TO_UNDISCLOSED: addheader("To", "undisclosed-recipients:;", &e->e_header); break; } if (hdr != NULL) { for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (q->q_alias != NULL) continue; if (tTd(30, 3)) printf("Adding %s: %s\n", hdr, q->q_paddr); addheader(hdr, q->q_paddr, &e->e_header); } } } /* check for message too large */ if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) { e->e_flags |= EF_NO_BODY_RETN; e->e_status = "5.2.3"; usrerr("552 Message exceeds maximum fixed size (%ld)", MaxMessageSize); -# ifdef LOG if (LogLevel > 6) - syslog(LOG_NOTICE, "%s: message size (%ld) exceeds maximum (%ld)", - e->e_id, e->e_msgsize, MaxMessageSize); -# endif + sm_syslog(LOG_NOTICE, e->e_id, + "message size (%ld) exceeds maximum (%ld)", + e->e_msgsize, MaxMessageSize); } /* check for illegal 8-bit data */ if (HasEightBits) { e->e_flags |= EF_HAS8BIT; if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode) && !bitset(EF_IS_MIME, e->e_flags)) { e->e_status = "5.6.1"; usrerr("554 Eight bit data not allowed"); } } else { /* if it claimed to be 8 bits, well, it lied.... */ if (e->e_bodytype != NULL && strcasecmp(e->e_bodytype, "8BITMIME") == 0) e->e_bodytype = "7BIT"; } if ((e->e_dfp = fopen(dfname, "r")) == NULL) { /* we haven't acked receipt yet, so just chuck this */ syserr("Cannot reopen %s", dfname); finis(); } } static void collecttimeout(timeout) time_t timeout; { /* if no progress was made, die now */ if (!CollectProgress) longjmp(CtxCollectTimeout, 1); /* otherwise reset the timeout */ CollectTimeout = setevent(timeout, collecttimeout, timeout); CollectProgress = FALSE; } /* ** TFERROR -- signal error on writing the temporary file. ** ** Parameters: ** tf -- the file pointer for the temporary file. ** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** Gives an error message. ** Arranges for following output to go elsewhere. */ void tferror(tf, e) FILE *tf; register ENVELOPE *e; { setstat(EX_IOERR); if (errno == ENOSPC) { struct stat st; long avail; long bsize; extern long freediskspace __P((char *, long *)); e->e_flags |= EF_NO_BODY_RETN; if (fstat(fileno(tf), &st) < 0) st.st_size = 0; (void) freopen(queuename(e, 'd'), "w", tf); if (st.st_size <= 0) fprintf(tf, "\n*** Mail could not be accepted"); else if (sizeof st.st_size > sizeof (long)) fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", st.st_size); else fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", (long) st.st_size); fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", MyHostName); avail = freediskspace(QueueDir, &bsize); if (avail > 0) { if (bsize > 1024) avail *= bsize / 1024; else if (bsize < 1024) avail /= 1024 / bsize; fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", avail); } e->e_status = "4.3.1"; usrerr("452 Out of disk space for temp file"); } else syserr("collect: Cannot write tf%s", e->e_id); (void) freopen("/dev/null", "w", tf); } /* ** EATFROM -- chew up a UNIX style from line and process ** ** This does indeed make some assumptions about the format ** of UNIX messages. ** ** Parameters: ** fm -- the from line. ** ** Returns: ** none. ** ** Side Effects: ** extracts what information it can from the header, ** such as the date. */ # ifndef NOTUNIX char *DowList[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL }; char *MonthList[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; void eatfrom(fm, e) char *fm; register ENVELOPE *e; { register char *p; register char **dt; if (tTd(30, 2)) printf("eatfrom(%s)\n", fm); /* find the date part */ p = fm; while (*p != '\0') { /* skip a word */ while (*p != '\0' && *p != ' ') p++; while (*p == ' ') p++; if (!(isascii(*p) && isupper(*p)) || p[3] != ' ' || p[13] != ':' || p[16] != ':') continue; /* we have a possible date */ for (dt = DowList; *dt != NULL; dt++) if (strncmp(*dt, p, 3) == 0) break; if (*dt == NULL) continue; for (dt = MonthList; *dt != NULL; dt++) if (strncmp(*dt, &p[4], 3) == 0) break; if (*dt != NULL) break; } if (*p != '\0') { char *q; /* we have found a date */ q = xalloc(25); (void) strncpy(q, p, 25); q[24] = '\0'; q = arpadate(q); define('a', newstr(q), e); } } # endif /* NOTUNIX */ Index: head/usr.sbin/sendmail/src/conf.c =================================================================== --- head/usr.sbin/sendmail/src/conf.c (revision 26988) +++ head/usr.sbin/sendmail/src/conf.c (revision 26989) @@ -1,4777 +1,5028 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)conf.c 8.333 (Berkeley) 1/21/97"; +static char sccsid[] = "@(#)conf.c 8.362 (Berkeley) 6/14/97"; #endif /* not lint */ # include "sendmail.h" # include "pathnames.h" # include # include /* ** CONF.C -- Sendmail Configuration Tables. ** ** Defines the configuration of this installation. ** ** Configuration Variables: ** HdrInfo -- a table describing well-known header fields. ** Each entry has the field name and some flags, ** which are described in sendmail.h. ** ** Notes: ** I have tried to put almost all the reasonable ** configuration information into the configuration ** file read at runtime. My intent is that anything ** here is a function of the version of UNIX you ** are running, or is really static -- for example ** the headers are a superset of widely used ** protocols. If you find yourself playing with ** this file too much, you may be making a mistake! */ /* ** Header info table ** Final (null) entry contains the flags used for any other field. ** ** Not all of these are actually handled specially by sendmail ** at this time. They are included as placeholders, to let ** you know that "someday" I intend to have sendmail do ** something with them. */ struct hdrinfo HdrInfo[] = { /* originator fields, most to least significant */ { "resent-sender", H_FROM|H_RESENT }, { "resent-from", H_FROM|H_RESENT }, { "resent-reply-to", H_FROM|H_RESENT }, { "sender", H_FROM }, { "from", H_FROM }, { "reply-to", H_FROM }, { "errors-to", H_FROM|H_ERRORSTO }, { "full-name", H_ACHECK }, { "return-receipt-to", H_RECEIPTTO }, /* destination fields */ { "to", H_RCPT }, { "resent-to", H_RCPT|H_RESENT }, { "cc", H_RCPT }, { "resent-cc", H_RCPT|H_RESENT }, { "bcc", H_RCPT|H_BCC }, { "resent-bcc", H_RCPT|H_BCC|H_RESENT }, { "apparently-to", H_RCPT }, /* message identification and control */ { "message-id", 0 }, { "resent-message-id", H_RESENT }, { "message", H_EOH }, { "text", H_EOH }, /* date fields */ { "date", 0 }, { "resent-date", H_RESENT }, /* trace fields */ { "received", H_TRACE|H_FORCE }, { "x400-received", H_TRACE|H_FORCE }, { "via", H_TRACE|H_FORCE }, { "mail-from", H_TRACE|H_FORCE }, /* miscellaneous fields */ { "comments", H_FORCE|H_ENCODABLE }, { "return-path", H_FORCE|H_ACHECK }, { "content-transfer-encoding", H_CTE }, { "content-type", H_CTYPE }, { "content-length", H_ACHECK }, { "subject", H_ENCODABLE }, { NULL, 0 } }; /* ** Privacy values */ struct prival PrivacyValues[] = { { "public", PRIV_PUBLIC }, { "needmailhelo", PRIV_NEEDMAILHELO }, { "needexpnhelo", PRIV_NEEDEXPNHELO }, { "needvrfyhelo", PRIV_NEEDVRFYHELO }, { "noexpn", PRIV_NOEXPN }, { "novrfy", PRIV_NOVRFY }, { "restrictmailq", PRIV_RESTRICTMAILQ }, { "restrictqrun", PRIV_RESTRICTQRUN }, +#if _FFR_PRIVACY_NOETRN + { "noetrn", PRIV_NOETRN }, +#endif { "authwarnings", PRIV_AUTHWARNINGS }, { "noreceipts", PRIV_NORECEIPTS }, { "goaway", PRIV_GOAWAY }, { NULL, 0 } }; /* ** Miscellaneous stuff. */ int DtableSize = 50; /* max open files; reset in 4.2bsd */ /* ** SETDEFAULTS -- set default values ** ** Because of the way freezing is done, these must be initialized ** using direct code. ** ** Parameters: ** e -- the default envelope. ** ** Returns: ** none. ** ** Side Effects: ** Initializes a bunch of global variables to their ** default values. */ #define MINUTES * 60 #define HOURS * 60 MINUTES #define DAYS * 24 HOURS #ifndef MAXRULERECURSION # define MAXRULERECURSION 50 /* max ruleset recursion depth */ #endif void setdefaults(e) register ENVELOPE *e; { int i; extern void inittimeouts(); extern void setdefuser(); extern void setupmaps(); extern void setupmailers(); + extern void setupheaders(); SpaceSub = ' '; /* option B */ QueueLA = 8; /* option x */ RefuseLA = 12; /* option X */ WkRecipFact = 30000L; /* option y */ WkClassFact = 1800L; /* option z */ WkTimeFact = 90000L; /* option Z */ QueueFactor = WkRecipFact * 20; /* option q */ FileMode = (RealUid != geteuid()) ? 0644 : 0600; /* option F */ DefUid = 1; /* option u */ DefGid = 1; /* option g */ CheckpointInterval = 10; /* option C */ MaxHopCount = 25; /* option h */ e->e_sendmode = SM_FORK; /* option d */ e->e_errormode = EM_PRINT; /* option e */ SevenBitInput = FALSE; /* option 7 */ MaxMciCache = 1; /* option k */ MciCacheTimeout = 5 MINUTES; /* option K */ LogLevel = 9; /* option L */ inittimeouts(NULL); /* option r */ PrivacyFlags = 0; /* option p */ #if MIME8TO7 MimeMode = MM_CVTMIME|MM_PASS8BIT; /* option 8 */ #else MimeMode = MM_PASS8BIT; #endif for (i = 0; i < MAXTOCLASS; i++) { TimeOuts.to_q_return[i] = 5 DAYS; /* option T */ TimeOuts.to_q_warning[i] = 0; /* option T */ } ServiceSwitchFile = "/etc/service.switch"; ServiceCacheMaxAge = (time_t) 10; HostsFile = _PATH_HOSTS; PidFile = newstr(_PATH_SENDMAILPID); MustQuoteChars = "@,;:\\()[].'"; MciInfoTimeout = 30 MINUTES; MaxRuleRecursion = MAXRULERECURSION; MaxAliasRecursion = 10; MaxMacroRecursion = 10; ColonOkInAddr = TRUE; DoubleBounceAddr = "postmaster"; setdefuser(); setupmaps(); setupmailers(); + setupheaders(); } /* ** SETDEFUSER -- set/reset DefUser using DefUid (for initgroups()) */ void setdefuser() { struct passwd *defpwent; static char defuserbuf[40]; DefUser = defuserbuf; defpwent = sm_getpwuid(DefUid); snprintf(defuserbuf, sizeof defuserbuf, "%s", defpwent == NULL ? "nobody" : defpwent->pw_name); } /* -** HOST_MAP_INIT -- initialize host class structures -*/ - -bool host_map_init __P((MAP *map, char *args)); - -bool -host_map_init(MAP *map, char *args) -{ - register char *p = args; - - for (;;) - { - while (isascii(*p) && isspace(*p)) - p++; - if (*p != '-') - break; - switch (*++p) - { - case 'a': - map->map_app = ++p; - break; - - case 'm': - map->map_mflags |= MF_MATCHONLY; - break; - - case 't': - map->map_mflags |= MF_NODEFER; - break; - } - while (*p != '\0' && !(isascii(*p) && isspace(*p))) - p++; - if (*p != '\0') - *p++ = '\0'; - } - if (map->map_app != NULL) - map->map_app = newstr(map->map_app); - return TRUE; -} - /* ** SETUPMAILERS -- initialize default mailers */ void setupmailers() { char buf[100]; extern void makemailer(); strcpy(buf, "prog, P=/bin/sh, F=lsoDq9, T=DNS/RFC822/X-Unix, A=sh -c \201u"); makemailer(buf); strcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=DNS/RFC822/X-Unix, A=FILE \201u"); makemailer(buf); strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u"); makemailer(buf); } /* ** SETUPMAPS -- set up map classes */ #define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \ { \ extern bool parse __P((MAP *, char *)); \ extern bool open __P((MAP *, int)); \ extern void close __P((MAP *)); \ extern char *lookup __P((MAP *, char *, char **, int *)); \ extern void store __P((MAP *, char *, char *)); \ s = stab(name, ST_MAPCLASS, ST_ENTER); \ s->s_mapclass.map_cname = name; \ s->s_mapclass.map_ext = ext; \ s->s_mapclass.map_cflags = flags; \ s->s_mapclass.map_parse = parse; \ s->s_mapclass.map_open = open; \ s->s_mapclass.map_close = close; \ s->s_mapclass.map_lookup = lookup; \ s->s_mapclass.map_store = store; \ } void setupmaps() { register STAB *s; #ifdef NEWDB MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, hash_map_open, db_map_close, db_map_lookup, db_map_store); MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, bt_map_open, db_map_close, db_map_lookup, db_map_store); #endif #ifdef NDBM MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE, map_parseargs, ndbm_map_open, ndbm_map_close, ndbm_map_lookup, ndbm_map_store); #endif #ifdef NIS MAPDEF("nis", NULL, MCF_ALIASOK, map_parseargs, nis_map_open, null_map_close, nis_map_lookup, null_map_store); #endif #ifdef NISPLUS MAPDEF("nisplus", NULL, MCF_ALIASOK, map_parseargs, nisplus_map_open, null_map_close, nisplus_map_lookup, null_map_store); #endif #ifdef LDAPMAP MAPDEF("ldapx", NULL, 0, - ldap_map_parseargs, ldap_map_open, ldap_map_close, - ldap_map_lookup, null_map_store); + ldap_map_parseargs, ldap_map_open, ldap_map_close, + ldap_map_lookup, null_map_store); #endif #ifdef HESIOD MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY, map_parseargs, hes_map_open, null_map_close, hes_map_lookup, null_map_store); #endif #if NETINFO MAPDEF("netinfo", NULL, MCF_ALIASOK, map_parseargs, ni_map_open, null_map_close, ni_map_lookup, null_map_store); #endif #if 0 MAPDEF("dns", NULL, 0, dns_map_init, null_map_open, null_map_close, dns_map_lookup, null_map_store); #endif #if NAMED_BIND /* best MX DNS lookup */ MAPDEF("bestmx", NULL, MCF_OPTFILE, map_parseargs, null_map_open, null_map_close, bestmx_map_lookup, null_map_store); #endif MAPDEF("host", NULL, 0, host_map_init, null_map_open, null_map_close, host_map_lookup, null_map_store); MAPDEF("text", NULL, MCF_ALIASOK, map_parseargs, text_map_open, null_map_close, text_map_lookup, null_map_store); MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY, map_parseargs, stab_map_open, null_map_close, stab_map_lookup, stab_map_store); MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE, map_parseargs, impl_map_open, impl_map_close, impl_map_lookup, impl_map_store); /* access to system passwd file */ MAPDEF("user", NULL, MCF_OPTFILE, map_parseargs, user_map_open, null_map_close, user_map_lookup, null_map_store); /* dequote map */ MAPDEF("dequote", NULL, 0, dequote_init, null_map_open, null_map_close, dequote_map, null_map_store); #if USERDB /* user database */ MAPDEF("userdb", ".db", 0, map_parseargs, null_map_open, null_map_close, udb_map_lookup, null_map_store); #endif /* arbitrary programs */ MAPDEF("program", NULL, MCF_ALIASOK, map_parseargs, null_map_open, null_map_close, prog_map_lookup, null_map_store); /* sequenced maps */ MAPDEF("sequence", NULL, MCF_ALIASOK, seq_map_parse, null_map_open, null_map_close, seq_map_lookup, seq_map_store); /* switched interface to sequenced maps */ MAPDEF("switch", NULL, MCF_ALIASOK, map_parseargs, switch_map_open, null_map_close, seq_map_lookup, seq_map_store); /* null map lookup -- really for internal use only */ MAPDEF("null", NULL, MCF_ALIASOK|MCF_OPTFILE, map_parseargs, null_map_open, null_map_close, null_map_lookup, null_map_store); } #undef MAPDEF /* ** INITHOSTMAPS -- initial host-dependent maps ** ** This should act as an interface to any local service switch ** provided by the host operating system. ** ** Parameters: ** none ** ** Returns: ** none ** ** Side Effects: ** Should define maps "host" and "users" as necessary ** for this OS. If they are not defined, they will get ** a default value later. It should check to make sure ** they are not defined first, since it's possible that ** the config file has provided an override. */ void inithostmaps() { register int i; int nmaps; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; char buf[MAXLINE]; /* ** Set up default hosts maps. */ #if 0 nmaps = switch_map_find("hosts", maptype, mapreturn); for (i = 0; i < nmaps; i++) { if (strcmp(maptype[i], "files") == 0 && stab("hosts.files", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "hosts.files text -k 0 -v 1 /etc/hosts"); (void) makemapentry(buf); } #if NAMED_BIND else if (strcmp(maptype[i], "dns") == 0 && stab("hosts.dns", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "hosts.dns dns A"); (void) makemapentry(buf); } #endif #ifdef NISPLUS else if (strcmp(maptype[i], "nisplus") == 0 && stab("hosts.nisplus", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "hosts.nisplus nisplus -k name -v address -d hosts.org_dir"); (void) makemapentry(buf); } #endif #ifdef NIS else if (strcmp(maptype[i], "nis") == 0 && stab("hosts.nis", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "hosts.nis nis -d -k 0 -v 1 hosts.byname"); (void) makemapentry(buf); } #endif #if NETINFO else if (strcmp(maptype[i], "netinfo") == 0) && stab("hosts.netinfo", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "hosts.netinfo netinfo -v name /machines"); (void) makemapentry(buf); } #endif } #endif /* ** Make sure we have a host map. */ if (stab("host", ST_MAP, ST_FIND) == NULL) { /* user didn't initialize: set up host map */ strcpy(buf, "host host"); #if NAMED_BIND if (ConfigLevel >= 2) strcat(buf, " -a."); #endif (void) makemapentry(buf); } /* ** Set up default aliases maps */ nmaps = switch_map_find("aliases", maptype, mapreturn); for (i = 0; i < nmaps; i++) { if (strcmp(maptype[i], "files") == 0 && stab("aliases.files", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases.files null"); (void) makemapentry(buf); } #ifdef NISPLUS else if (strcmp(maptype[i], "nisplus") == 0 && stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion -d mail_aliases.org_dir"); (void) makemapentry(buf); } #endif #ifdef NIS else if (strcmp(maptype[i], "nis") == 0 && stab("aliases.nis", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases.nis nis -d mail.aliases"); (void) makemapentry(buf); } #endif #ifdef NETINFO else if (strcmp(maptype[i], "netinfo") == 0 && stab("aliases.netinfo", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases.netinfo netinfo -z, /aliases"); (void) makemapentry(buf); } #endif #ifdef HESIOD else if (strcmp(maptype[i], "hesiod") == 0 && stab("aliases.hesiod", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases.hesiod hesiod aliases"); (void) makemapentry(buf); } #endif } if (stab("aliases", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "aliases switch aliases"); (void) makemapentry(buf); } #if 0 /* "user" map class is a better choice */ /* ** Set up default users maps. */ nmaps = switch_map_find("passwd", maptype, mapreturn); for (i = 0; i < nmaps; i++) { if (strcmp(maptype[i], "files") == 0 && stab("users.files", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "users.files text -m -z: -k0 -v6 /etc/passwd"); (void) makemapentry(buf); } #ifdef NISPLUS else if (strcmp(maptype[i], "nisplus") == 0 && stab("users.nisplus", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "users.nisplus nisplus -m -kname -vhome -d passwd.org_dir"); (void) makemapentry(buf); } #endif #ifdef NIS else if (strcmp(maptype[i], "nis") == 0 && stab("users.nis", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "users.nis nis -m -d passwd.byname"); (void) makemapentry(buf); } #endif #ifdef HESIOD else if (strcmp(maptype[i], "hesiod") == 0) && stab("users.hesiod", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "users.hesiod hesiod"); (void) makemapentry(buf); } #endif } if (stab("users", ST_MAP, ST_FIND) == NULL) { strcpy(buf, "users switch -m passwd"); (void) makemapentry(buf); } #endif } /* ** SWITCH_MAP_FIND -- find the list of types associated with a map ** ** This is the system-dependent interface to the service switch. ** ** Parameters: ** service -- the name of the service of interest. ** maptype -- an out-array of strings containing the types ** of access to use for this service. There can ** be at most MAXMAPSTACK types for a single service. ** mapreturn -- an out-array of return information bitmaps ** for the map. ** ** Returns: ** The number of map types filled in, or -1 for failure. */ #if defined(SOLARIS) || (defined(sony_news) && defined(__svr4)) # define _USE_SUN_NSSWITCH_ #endif #ifdef _USE_SUN_NSSWITCH_ # include #endif #if defined(ultrix) || (defined(__osf__) && defined(__alpha)) # define _USE_DEC_SVC_CONF_ #endif #ifdef _USE_DEC_SVC_CONF_ # include #endif int switch_map_find(service, maptype, mapreturn) char *service; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; { int svcno; #ifdef _USE_SUN_NSSWITCH_ struct __nsw_switchconfig *nsw_conf; enum __nsw_parse_err pserr; struct __nsw_lookup *lk; static struct __nsw_lookup lkp0 = { "files", {1, 0, 0, 0}, NULL, NULL }; static struct __nsw_switchconfig lkp_default = { 0, "sendmail", 3, &lkp0 }; for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; if ((nsw_conf = __nsw_getconfig(service, &pserr)) == NULL) lk = lkp_default.lookups; else lk = nsw_conf->lookups; svcno = 0; while (lk != NULL) { maptype[svcno] = lk->service_name; if (lk->actions[__NSW_NOTFOUND] == __NSW_RETURN) mapreturn[MA_NOTFOUND] |= 1 << svcno; if (lk->actions[__NSW_TRYAGAIN] == __NSW_RETURN) mapreturn[MA_TRYAGAIN] |= 1 << svcno; if (lk->actions[__NSW_UNAVAIL] == __NSW_RETURN) mapreturn[MA_TRYAGAIN] |= 1 << svcno; svcno++; lk = lk->next; } return svcno; #endif #ifdef _USE_DEC_SVC_CONF_ struct svcinfo *svcinfo; int svc; for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; svcinfo = getsvc(); if (svcinfo == NULL) goto punt; if (strcmp(service, "hosts") == 0) svc = SVC_HOSTS; else if (strcmp(service, "aliases") == 0) svc = SVC_ALIASES; else if (strcmp(service, "passwd") == 0) svc = SVC_PASSWD; else return -1; for (svcno = 0; svcno < SVC_PATHSIZE; svcno++) { switch (svcinfo->svcpath[svc][svcno]) { case SVC_LOCAL: maptype[svcno] = "files"; break; case SVC_YP: maptype[svcno] = "nis"; break; case SVC_BIND: maptype[svcno] = "dns"; break; #ifdef SVC_HESIOD case SVC_HESIOD: maptype[svcno] = "hesiod"; break; #endif case SVC_LAST: return svcno; } } return svcno; #endif #if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) /* ** Fall-back mechanism. */ STAB *st; time_t now = curtime(); for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; if ((now - ServiceCacheTime) > (time_t) ServiceCacheMaxAge) { /* (re)read service switch */ register FILE *fp; if (ConfigFileRead) ServiceCacheTime = now; fp = fopen(ServiceSwitchFile, "r"); if (fp != NULL) { char buf[MAXLINE]; while (fgets(buf, sizeof buf, fp) != NULL) { register char *p; p = strpbrk(buf, "#\n"); if (p != NULL) *p = '\0'; p = strpbrk(buf, " \t"); if (p != NULL) *p++ = '\0'; if (buf[0] == '\0') continue; while (isspace(*p)) p++; if (*p == '\0') continue; /* ** Find/allocate space for this service entry. ** Space for all of the service strings ** are allocated at once. This means ** that we only have to free the first ** one to free all of them. */ st = stab(buf, ST_SERVICE, ST_ENTER); if (st->s_service[0] != NULL) free((void *) st->s_service[0]); p = newstr(p); for (svcno = 0; svcno < MAXMAPSTACK; ) { if (*p == '\0') break; st->s_service[svcno++] = p; p = strpbrk(p, " \t"); if (p == NULL) break; *p++ = '\0'; while (isspace(*p)) p++; } if (svcno < MAXMAPSTACK) st->s_service[svcno] = NULL; } fclose(fp); } } /* look up entry in cache */ st = stab(service, ST_SERVICE, ST_FIND); if (st != NULL && st->s_service[0] != NULL) { /* extract data */ svcno = 0; while (svcno < MAXMAPSTACK) { maptype[svcno] = st->s_service[svcno]; if (maptype[svcno++] == NULL) break; } return --svcno; } #endif /* if the service file doesn't work, use an absolute fallback */ punt: for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) mapreturn[svcno] = 0; svcno = 0; if (strcmp(service, "aliases") == 0) { maptype[svcno++] = "files"; #ifdef AUTO_NIS_ALIASES # ifdef NISPLUS maptype[svcno++] = "nisplus"; # endif # ifdef NIS maptype[svcno++] = "nis"; # endif #endif return svcno; } if (strcmp(service, "hosts") == 0) { # if NAMED_BIND maptype[svcno++] = "dns"; # else # if defined(sun) && !defined(BSD) && !defined(_USE_SUN_NSSWITCH_) /* SunOS */ maptype[svcno++] = "nis"; # endif # endif maptype[svcno++] = "files"; return svcno; } return -1; } /* ** USERNAME -- return the user id of the logged in user. ** ** Parameters: ** none. ** ** Returns: ** The login name of the logged in user. ** ** Side Effects: ** none. ** ** Notes: ** The return value is statically allocated. */ char * username() { static char *myname = NULL; extern char *getlogin(); register struct passwd *pw; /* cache the result */ if (myname == NULL) { myname = getlogin(); if (myname == NULL || myname[0] == '\0') { pw = sm_getpwuid(RealUid); if (pw != NULL) myname = newstr(pw->pw_name); } else { uid_t uid = RealUid; myname = newstr(myname); if ((pw = sm_getpwnam(myname)) == NULL || (uid != 0 && uid != pw->pw_uid)) { pw = sm_getpwuid(uid); if (pw != NULL) myname = newstr(pw->pw_name); } } if (myname == NULL || myname[0] == '\0') { syserr("554 Who are you?"); myname = "postmaster"; } } return (myname); } /* ** TTYPATH -- Get the path of the user's tty ** ** Returns the pathname of the user's tty. Returns NULL if ** the user is not logged in or if s/he has write permission ** denied. ** ** Parameters: ** none ** ** Returns: ** pathname of the user's tty. ** NULL if not logged in or write permission denied. ** ** Side Effects: ** none. ** ** WARNING: ** Return value is in a local buffer. ** ** Called By: ** savemail */ char * ttypath() { struct stat stbuf; register char *pathn; extern char *ttyname(); extern char *getlogin(); /* compute the pathname of the controlling tty */ if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL && (pathn = ttyname(0)) == NULL) { errno = 0; return (NULL); } /* see if we have write permission */ if (stat(pathn, &stbuf) < 0 || !bitset(02, stbuf.st_mode)) { errno = 0; return (NULL); } /* see if the user is logged in */ if (getlogin() == NULL) return (NULL); /* looks good */ return (pathn); } /* ** CHECKCOMPAT -- check for From and To person compatible. ** ** This routine can be supplied on a per-installation basis ** to determine whether a person is allowed to send a message. ** This allows restriction of certain types of internet ** forwarding or registration of users. ** ** If the hosts are found to be incompatible, an error ** message should be given using "usrerr" and an EX_ code ** should be returned. You can also set to->q_status to ** a DSN-style status code. ** ** EF_NO_BODY_RETN can be set in e->e_flags to suppress the ** body during the return-to-sender function; this should be done ** on huge messages. This bit may already be set by the ESMTP ** protocol. ** ** Parameters: ** to -- the person being sent to. ** ** Returns: ** an exit status ** ** Side Effects: ** none (unless you include the usrerr stuff) */ int checkcompat(to, e) register ADDRESS *to; register ENVELOPE *e; { # ifdef lint if (to == NULL) to++; # endif /* lint */ if (tTd(49, 1)) printf("checkcompat(to=%s, from=%s)\n", to->q_paddr, e->e_from.q_paddr); # ifdef EXAMPLE_CODE /* this code is intended as an example only */ register STAB *s; s = stab("arpa", ST_MAILER, ST_FIND); if (s != NULL && strcmp(e->e_from.q_mailer->m_name, "local") != 0 && to->q_mailer == s->s_mailer) { usrerr("553 No ARPA mail through this machine: see your system administration"); /* e->e_flags |= EF_NO_BODY_RETN; to supress body on return */ to->q_status = "5.7.1"; return (EX_UNAVAILABLE); } # endif /* EXAMPLE_CODE */ return (EX_OK); } /* ** SETSIGNAL -- set a signal handler ** ** This is essentially old BSD "signal(3)". */ sigfunc_t setsignal(sig, handler) int sig; sigfunc_t handler; { #if defined(SYS5SIGNALS) || defined(BSD4_3) +# ifdef BSD4_3 return signal(sig, handler); +# else + return sigset(sig, handler); +# endif #else struct sigaction n, o; bzero(&n, sizeof n); # if USE_SA_SIGACTION n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler; n.sa_flags = SA_RESTART|SA_SIGINFO; # else n.sa_handler = handler; # ifdef SA_RESTART n.sa_flags = SA_RESTART; # endif # endif if (sigaction(sig, &n, &o) < 0) return SIG_ERR; return o.sa_handler; #endif } /* ** BLOCKSIGNAL -- hold a signal to prevent delivery ** ** Parameters: ** sig -- the signal to block. ** ** Returns: ** 1 signal was previously blocked ** 0 signal was not previously blocked ** -1 on failure. */ int blocksignal(sig) int sig; { #ifdef BSD4_3 # ifndef sigmask # define sigmask(s) (1 << ((s) - 1)) # endif return (sigblock(sigmask(sig)) & sigmask(sig)) != 0; #else # ifdef ALTOS_SYSTEM_V sigfunc_t handler; handler = sigset(sig, SIG_HOLD); if (handler == SIG_ERR) return -1; else return handler == SIG_HOLD; # else sigset_t sset, oset; sigemptyset(&sset); sigaddset(&sset, sig); if (sigprocmask(SIG_BLOCK, &sset, &oset) < 0) return -1; else return sigismember(&oset, sig); # endif #endif } /* ** RELEASESIGNAL -- release a held signal ** ** Parameters: ** sig -- the signal to release. ** ** Returns: ** 1 signal was previously blocked ** 0 signal was not previously blocked ** -1 on failure. */ int releasesignal(sig) int sig; { #ifdef BSD4_3 return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0; #else # ifdef ALTOS_SYSTEM_V sigfunc_t handler; handler = sigset(sig, SIG_HOLD); if (sigrelse(sig) < 0) return -1; else return handler == SIG_HOLD; # else sigset_t sset, oset; sigemptyset(&sset); sigaddset(&sset, sig); if (sigprocmask(SIG_UNBLOCK, &sset, &oset) < 0) return -1; else return sigismember(&oset, sig); # endif #endif } /* ** HOLDSIGS -- arrange to hold all signals ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Arranges that signals are held. */ void holdsigs() { } /* ** RLSESIGS -- arrange to release all signals ** ** This undoes the effect of holdsigs. ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Arranges that signals are released. */ void rlsesigs() { } /* ** INIT_MD -- do machine dependent initializations ** ** Systems that have global modes that should be set should do ** them here rather than in main. */ #ifdef _AUX_SOURCE # include #endif #if SHARE_V1 # include #endif void init_md(argc, argv) int argc; char **argv; { #ifdef _AUX_SOURCE setcompat(getcompat() | COMPAT_BSDPROT); #endif #ifdef SUN_EXTENSIONS init_md_sun(); #endif #if _CONVEX_SOURCE /* keep gethostby*() from stripping the local domain name */ set_domain_trim_off(); #endif #if SECUREWARE || defined(_SCO_unix_) set_auth_parameters(argc, argv); # ifdef _SCO_unix_ /* ** This is required for highest security levels (the kernel ** won't let it call set*uid() or run setuid binaries without ** it). It may be necessary on other SECUREWARE systems. */ if (getluid() == -1) setluid(0); # endif #endif #ifdef VENDOR_DEFAULT VendorCode = VENDOR_DEFAULT; #else VendorCode = VENDOR_BERKELEY; #endif } /* ** INIT_VENDOR_MACROS -- vendor-dependent macro initializations ** ** Called once, on startup. ** ** Parameters: ** e -- the global envelope. ** ** Returns: ** none. ** ** Side Effects: ** vendor-dependent. */ void init_vendor_macros(e) register ENVELOPE *e; { } /* ** GETLA -- get the current load average ** ** This code stolen from la.c. ** ** Parameters: ** none. ** ** Returns: ** The current load average as an integer. ** ** Side Effects: ** none. */ /* try to guess what style of load average we have */ #define LA_ZERO 1 /* always return load average as zero */ #define LA_INT 2 /* read kmem for avenrun; interpret as long */ #define LA_FLOAT 3 /* read kmem for avenrun; interpret as float */ #define LA_SUBR 4 /* call getloadavg */ #define LA_MACH 5 /* MACH load averages (as on NeXT boxes) */ #define LA_SHORT 6 /* read kmem for avenrun; interpret as short */ #define LA_PROCSTR 7 /* read string ("1.17") from /proc/loadavg */ #define LA_READKSYM 8 /* SVR4: use MIOC_READKSYM ioctl call */ #define LA_DGUX 9 /* special DGUX implementation */ #define LA_HPUX 10 /* special HPUX implementation */ #define LA_IRIX6 11 /* special IRIX 6.2 implementation */ #define LA_KSTAT 12 /* special Solaris kstat(3k) implementation */ #define LA_DEVSHORT 13 /* read short from a device */ #define LA_ALPHAOSF 14 /* Digital UNIX (OSF/1 on Alpha) table() call */ /* do guesses based on general OS type */ #ifndef LA_TYPE # define LA_TYPE LA_ZERO #endif #ifndef FSHIFT # if defined(unixpc) # define FSHIFT 5 # endif # if defined(__alpha) || defined(IRIX) # define FSHIFT 10 # endif #endif #ifndef FSHIFT # define FSHIFT 8 #endif #ifndef FSCALE # define FSCALE (1 << FSHIFT) #endif #ifndef LA_AVENRUN # ifdef SYSTEM5 # define LA_AVENRUN "avenrun" # else # define LA_AVENRUN "_avenrun" # endif #endif /* _PATH_KMEM should be defined in */ #ifndef _PATH_KMEM # define _PATH_KMEM "/dev/kmem" #endif #if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT) #include #ifdef IRIX64 # define nlist nlist64 #endif /* _PATH_UNIX should be defined in */ #ifndef _PATH_UNIX # if defined(SYSTEM5) # define _PATH_UNIX "/unix" # else # define _PATH_UNIX "/vmunix" # endif #endif #ifdef _AUX_SOURCE struct nlist Nl[2]; #else struct nlist Nl[] = { { LA_AVENRUN }, { 0 }, }; #endif #define X_AVENRUN 0 int getla() { static int kmem = -1; #if LA_TYPE == LA_INT long avenrun[3]; #else # if LA_TYPE == LA_SHORT short avenrun[3]; # else double avenrun[3]; # endif #endif extern int errno; extern off_t lseek(); if (kmem < 0) { #ifdef _AUX_SOURCE strcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN); Nl[1].n_name[0] = '\0'; #endif #if defined(_AIX3) || defined(_AIX4) if (knlist(Nl, 1, sizeof Nl[0]) < 0) #else if (nlist(_PATH_UNIX, Nl) < 0) #endif { if (tTd(3, 1)) printf("getla: nlist(%s): %s\n", _PATH_UNIX, errstring(errno)); return (-1); } if (Nl[X_AVENRUN].n_value == 0) { if (tTd(3, 1)) printf("getla: nlist(%s, %s) ==> 0\n", _PATH_UNIX, LA_AVENRUN); return (-1); } #ifdef NAMELISTMASK Nl[X_AVENRUN].n_value &= NAMELISTMASK; #endif kmem = open(_PATH_KMEM, 0, 0); if (kmem < 0) { if (tTd(3, 1)) printf("getla: open(/dev/kmem): %s\n", errstring(errno)); return (-1); } (void) fcntl(kmem, F_SETFD, 1); } if (tTd(3, 20)) printf("getla: symbol address = %#lx\n", (u_long) Nl[X_AVENRUN].n_value); if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 || read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) { /* thank you Ian */ if (tTd(3, 1)) printf("getla: lseek or read: %s\n", errstring(errno)); return (-1); } # if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT) if (tTd(3, 5)) { # if LA_TYPE == LA_SHORT printf("getla: avenrun = %d", avenrun[0]); if (tTd(3, 15)) printf(", %d, %d", avenrun[1], avenrun[2]); # else printf("getla: avenrun = %ld", avenrun[0]); if (tTd(3, 15)) printf(", %ld, %ld", avenrun[1], avenrun[2]); # endif printf("\n"); } if (tTd(3, 1)) printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); # else /* LA_TYPE == LA_FLOAT */ if (tTd(3, 5)) { printf("getla: avenrun = %g", avenrun[0]); if (tTd(3, 15)) printf(", %g, %g", avenrun[1], avenrun[2]); printf("\n"); } if (tTd(3, 1)) printf("getla: %d\n", (int) (avenrun[0] +0.5)); return ((int) (avenrun[0] + 0.5)); # endif } #endif /* LA_TYPE == LA_INT or LA_SHORT or LA_FLOAT */ #if LA_TYPE == LA_READKSYM # include getla() { static int kmem = -1; long avenrun[3]; extern int errno; struct mioc_rksym mirk; if (kmem < 0) { kmem = open("/dev/kmem", 0, 0); if (kmem < 0) { if (tTd(3, 1)) printf("getla: open(/dev/kmem): %s\n", errstring(errno)); return (-1); } (void) fcntl(kmem, F_SETFD, 1); } mirk.mirk_symname = LA_AVENRUN; mirk.mirk_buf = avenrun; mirk.mirk_buflen = sizeof(avenrun); if (ioctl(kmem, MIOC_READKSYM, &mirk) < 0) { if (tTd(3, 1)) printf("getla: ioctl(MIOC_READKSYM) failed: %s\n", errstring(errno)); return -1; } if (tTd(3, 5)) { printf("getla: avenrun = %d", avenrun[0]); if (tTd(3, 15)) printf(", %d, %d", avenrun[1], avenrun[2]); printf("\n"); } if (tTd(3, 1)) printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT); return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); } #endif /* LA_TYPE == LA_READKSYM */ #if LA_TYPE == LA_DGUX # include int getla() { struct dg_sys_info_load_info load_info; dg_sys_info((long *)&load_info, DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0); - if (tTd(3, 1)) - printf("getla: %d\n", (int) (load_info.one_minute + 0.5)); + if (tTd(3, 1)) + printf("getla: %d\n", (int) (load_info.one_minute + 0.5)); return((int) (load_info.one_minute + 0.5)); } #endif /* LA_TYPE == LA_DGUX */ #if LA_TYPE == LA_HPUX /* forward declarations to keep gcc from complaining */ struct pst_dynamic; struct pst_status; struct pst_static; struct pst_vminfo; struct pst_diskinfo; struct pst_processor; struct pst_lv; struct pst_swapinfo; # include # include int getla() { struct pst_dynamic pstd; if (pstat_getdynamic(&pstd, sizeof(struct pst_dynamic), (size_t) 1, 0) == -1) return 0; - if (tTd(3, 1)) - printf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5)); + if (tTd(3, 1)) + printf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5)); return (int) (pstd.psd_avg_1_min + 0.5); } #endif /* LA_TYPE == LA_HPUX */ #if LA_TYPE == LA_SUBR int getla() { double avenrun[3]; if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0) { if (tTd(3, 1)) perror("getla: getloadavg failed:"); return (-1); } if (tTd(3, 1)) printf("getla: %d\n", (int) (avenrun[0] +0.5)); return ((int) (avenrun[0] + 0.5)); } #endif /* LA_TYPE == LA_SUBR */ #if LA_TYPE == LA_MACH /* ** This has been tested on NEXTSTEP release 2.1/3.X. */ #if defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0 # include #else # include #endif int getla() { processor_set_t default_set; kern_return_t error; unsigned int info_count; struct processor_set_basic_info info; host_t host; error = processor_set_default(host_self(), &default_set); if (error != KERN_SUCCESS) { if (tTd(3, 1)) perror("getla: processor_set_default failed:"); return -1; } info_count = PROCESSOR_SET_BASIC_INFO_COUNT; if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO, &host, (processor_set_info_t)&info, &info_count) != KERN_SUCCESS) { if (tTd(3, 1)) perror("getla: processor_set_info failed:"); return -1; } if (tTd(3, 1)) printf("getla: %d\n", (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE); return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE; } #endif /* LA_TYPE == LA_MACH */ #if LA_TYPE == LA_PROCSTR /* ** Read /proc/loadavg for the load average. This is assumed to be ** in a format like "0.15 0.12 0.06". ** ** Initially intended for Linux. This has been in the kernel ** since at least 0.99.15. */ # ifndef _PATH_LOADAVG # define _PATH_LOADAVG "/proc/loadavg" # endif int getla() { double avenrun; register int result; FILE *fp; fp = fopen(_PATH_LOADAVG, "r"); if (fp == NULL) { if (tTd(3, 1)) printf("getla: fopen(%s): %s\n", _PATH_LOADAVG, errstring(errno)); return -1; } result = fscanf(fp, "%lf", &avenrun); fclose(fp); if (result != 1) { if (tTd(3, 1)) printf("getla: fscanf() = %d: %s\n", result, errstring(errno)); return -1; } if (tTd(3, 1)) printf("getla(): %.2f\n", avenrun); return ((int) (avenrun + 0.5)); } #endif /* LA_TYPE == LA_PROCSTR */ #if LA_TYPE == LA_IRIX6 #include #include #include #define X_AVENRUN 0 struct nlist Nl32[] = { { LA_AVENRUN }, { 0 }, }; struct nlist64 Nl64[] = { { LA_AVENRUN }, { 0 }, }; int getla(void) { static int kmem = -1; static enum { getla_none, getla_32, getla_64 } kernel_type = getla_none; - uint32_t avenrun32[3]; - uint64_t avenrun64[3]; + uint32_t avenrun[3]; if (kernel_type == getla_none) { /* Try 32 bit kernel ... */ errno = 0; if (nlist(_PATH_UNIX, Nl32) == 0) { if (tTd(3, 20)) printf("getla: Kernel is 32bit\n"); if (Nl32[X_AVENRUN].n_value == 0) { if (tTd(3, 1)) printf("getla: nlist(%s, %s) ==> 0\n", _PATH_UNIX, LA_AVENRUN); } else kernel_type = getla_32; } else if (errno != 0) { if (tTd(3, 1)) printf("getla: nlist(%s): %s\n", _PATH_UNIX, errstring(errno)); } else { if (tTd(3, 20)) printf("getla: Kernel is not 32bit\n"); } /* Try 64 bit kernel ... */ errno = 0; if (nlist64(_PATH_UNIX, Nl64) == 0) { if (tTd(3, 20)) printf("getla: Kernel is 64bit\n"); if (Nl64[X_AVENRUN].n_value == 0) { if (tTd(3, 1)) printf("getla: nlist(%s, %s) ==> 0\n", _PATH_UNIX, LA_AVENRUN); } else kernel_type = getla_64; } else if (errno != 0) { if (tTd(3, 1)) printf("getla: nlist64(%s): %s\n", _PATH_UNIX, errstring(errno)); } else { if (tTd(3, 20)) printf("getla: Kernel is not 64bit\n"); } } if (kernel_type == getla_none) { if (tTd(3, 1)) printf("getla: Failed to determine kernel type\n"); return -1; } if (kmem < 0) { kmem = open(_PATH_KMEM, 0, 0); if (kmem < 0) { if (tTd(3, 1)) printf("getla: open(/dev/kmem): %s\n", errstring(errno)); return -1; } (void) fcntl(kmem, F_SETFD, 1); } switch (kernel_type) { + case getla_none: + return -1; + case getla_32: if (lseek(kmem, (off_t) Nl32[X_AVENRUN].n_value, SEEK_SET) == -1 || - read(kmem, (char *) avenrun32, sizeof(avenrun32)) < sizeof(avenrun32)) + read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) { if (tTd(3, 1)) printf("getla: lseek or read: %s\n", errstring(errno)); return -1; } - if (tTd(3, 5)) - { - printf("getla: avenrun{32} = %ld", - (long int) avenrun32[0]); - if (tTd(3, 15)) - printf(", %ld, %ld", - (long int)avenrun32[1], - (long int)avenrun32[2]); - printf("\n"); - } - if (tTd(3, 1)) - printf("getla: %d\n", - (int) (avenrun32[0] + FSCALE/2) >> FSHIFT); - return ((int) (avenrun32[0] + FSCALE/2) >> FSHIFT); + break; case getla_64: /* Using of lseek64 is perhaps overkill ... */ if (lseek64(kmem, (off64_t) Nl64[X_AVENRUN].n_value, SEEK_SET) == -1 || - read(kmem, (char *) avenrun64, sizeof(avenrun64)) < - sizeof(avenrun64)) + read(kmem, (char *) avenrun, sizeof(avenrun)) < + sizeof(avenrun)) { if (tTd(3, 1)) printf("getla: lseek64 or read: %s\n", errstring(errno)); return -1; } - if (tTd(3, 5)) - { - printf("getla: avenrun{64} = %lld", - (long long int) avenrun64[0]); - if (tTd(3, 15)) - printf(", %lld, %lld", - (long long int) avenrun64[1], - (long long int) avenrun64[2]); - printf("\n"); - } - if (tTd(3, 1)) - printf("getla: %d\n", - (int) (avenrun64[0] + FSCALE/2) >> FSHIFT); - return ((int) (avenrun64[0] + FSCALE/2) >> FSHIFT); + break; } - return -1; + if (tTd(3, 5)) + { + printf("getla: avenrun = %ld", + (long int) avenrun[0]); + if (tTd(3, 15)) + printf(", %ld, %ld", + (long int)avenrun[1], + (long int)avenrun[2]); + printf("\n"); + } + if (tTd(3, 1)) + printf("getla: %d\n", + (int) (avenrun[0] + FSCALE/2) >> FSHIFT); + return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); } #endif #if LA_TYPE == LA_KSTAT #include int getla() { static kstat_ctl_t *kc = NULL; static kstat_t *ksp = NULL; kstat_named_t *ksn; int la; if (kc == NULL) /* if not initialized before */ kc = kstat_open(); if (kc == NULL) { if (tTd(3, 1)) printf("getla: kstat_open(): %s\n", errstring(errno)); return -1; } if (ksp == NULL) ksp = kstat_lookup(kc, "unix", 0, "system_misc"); if (ksp == NULL) { if (tTd(3, 1)) printf("getla: kstat_lookup(): %s\n", errstring(errno)); return -1; } if (kstat_read(kc, ksp, NULL) < 0) { if (tTd(3, 1)) printf("getla: kstat_read(): %s\n", errstring(errno)); return -1; } ksn = (kstat_named_t *) kstat_data_lookup(ksp, "avenrun_1min"); la = ((double)ksn->value.ul + FSCALE/2) / FSCALE; /* kstat_close(kc); /o do not close for fast access */ return la; } #endif /* LA_TYPE == LA_KSTAT */ #if LA_TYPE == LA_DEVSHORT /* ** Read /dev/table/avenrun for the load average. This should contain ** three shorts for the 1, 5, and 15 minute loads. We only read the ** first, since that's all we care about. ** ** Intended for SCO OpenServer 5. */ # ifndef _PATH_AVENRUN # define _PATH_AVENRUN "/dev/table/avenrun" # endif int getla() { static int afd = -1; short avenrun; int loadav; int r; errno = EBADF; if (afd == -1 || lseek(afd, 0L, SEEK_SET) == -1) { if (errno != EBADF) return -1; afd = open(_PATH_AVENRUN, O_RDONLY|O_SYNC); if (afd < 0) { - syslog(LOG_ERR, "can't open %s: %m", _PATH_AVENRUN); + sm_syslog(LOG_ERR, NOQID, + "can't open %s: %m", + _PATH_AVENRUN); return -1; } } r = read(afd, &avenrun, sizeof avenrun); if (tTd(3, 5)) printf("getla: avenrun = %d\n", avenrun); loadav = (int) (avenrun + FSCALE/2) >> FSHIFT; if (tTd(3, 1)) printf("getla: %d\n", loadav); return loadav; } #endif /* LA_TYPE == LA_DEVSHORT */ #if LA_TYPE == LA_ALPHAOSF # include int getla() { int ave = 0; struct tbl_loadavg tab; if (table(TBL_LOADAVG, 0, &tab, 1, sizeof(tab)) == -1) { if (tTd(3, 1)) printf("getla: table %s\n", errstring(errno)); return (-1); } if (tTd(3, 1)) printf("getla: scale = %d\n", tab.tl_lscale); if (tab.tl_lscale) ave = (tab.tl_avenrun.l[0] + (tab.tl_lscale/2)) / tab.tl_lscale; else ave = (int) (tab.tl_avenrun.d[0] + 0.5); if (tTd(3, 1)) printf("getla: %d\n", ave); return ave; } #endif #if LA_TYPE == LA_ZERO int getla() { if (tTd(3, 1)) printf("getla: ZERO\n"); return (0); } #endif /* LA_TYPE == LA_ZERO */ /* * Copyright 1989 Massachusetts Institute of Technology * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. M.I.T. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: Many and varied... */ /* Non Apollo stuff removed by Don Lewis 11/15/93 */ #ifdef apollo # undef volatile # include /* ARGSUSED */ int getloadavg( call_data ) caddr_t call_data; /* pointer to (double) return value */ { double *avenrun = (double *) call_data; int i; status_$t st; long loadav[3]; proc1_$get_loadav(loadav, &st); *avenrun = loadav[0] / (double) (1 << 16); return(0); } # endif /* apollo */ /* ** SHOULDQUEUE -- should this message be queued or sent? ** ** Compares the message cost to the load average to decide. ** ** Parameters: ** pri -- the priority of the message in question. ** ctime -- the message creation time. ** ** Returns: ** TRUE -- if this message should be queued up for the ** time being. ** FALSE -- if the load is low enough to send this message. ** ** Side Effects: ** none. */ +extern int get_num_procs_online __P((void)); + bool shouldqueue(pri, ctime) long pri; time_t ctime; { bool rval; + int queuela = QueueLA * get_num_procs_online(); if (tTd(3, 30)) printf("shouldqueue: CurrentLA=%d, pri=%ld: ", CurrentLA, pri); - if (CurrentLA < QueueLA) + if (CurrentLA < queuela) { if (tTd(3, 30)) printf("FALSE (CurrentLA < QueueLA)\n"); return (FALSE); } #if 0 /* this code is reported to cause oscillation around RefuseLA */ if (CurrentLA >= RefuseLA && QueueLA < RefuseLA) { if (tTd(3, 30)) printf("TRUE (CurrentLA >= RefuseLA)\n"); return (TRUE); } #endif - rval = pri > (QueueFactor / (CurrentLA - QueueLA + 1)); + rval = pri > (QueueFactor / (CurrentLA - queuela + 1)); if (tTd(3, 30)) printf("%s (by calculation)\n", rval ? "TRUE" : "FALSE"); return rval; } /* ** REFUSECONNECTIONS -- decide if connections should be refused ** ** Parameters: ** port -- port number (for error messages only) ** ** Returns: ** TRUE if incoming SMTP connections should be refused ** (for now). ** FALSE if we should accept new work. ** ** Side Effects: ** Sets process title when it is rejecting connections. */ bool refuseconnections(port) int port; { + int refusela = RefuseLA * get_num_procs_online(); time_t now; static time_t lastconn = (time_t) 0; static int conncnt = 0; extern bool enoughdiskspace(); extern void setproctitle __P((const char *, ...)); #ifdef XLA if (!xla_smtp_ok()) return TRUE; #endif now = curtime(); if (now != lastconn) { lastconn = now; conncnt = 0; } else if (conncnt++ > ConnRateThrottle && ConnRateThrottle > 0) { /* sleep to flatten out connection load */ setproctitle("deferring connections on port %d: %d per second", port, ConnRateThrottle); -#ifdef LOG if (LogLevel >= 14) - syslog(LOG_INFO, "deferring connections on port %d: %d per second", + sm_syslog(LOG_INFO, NOQID, + "deferring connections on port %d: %d per second", port, ConnRateThrottle); -#endif sleep(1); } CurrentLA = getla(); - if (CurrentLA >= RefuseLA) + if (CurrentLA >= refusela) { setproctitle("rejecting connections on port %d: load average: %d", port, CurrentLA); -#ifdef LOG if (LogLevel >= 14) - syslog(LOG_INFO, "rejecting connections on port %d: load average: %d", + sm_syslog(LOG_INFO, NOQID, + "rejecting connections on port %d: load average: %d", port, CurrentLA); -#endif return TRUE; } if (!enoughdiskspace(MinBlocksFree + 1)) { setproctitle("rejecting connections on port %d: min free: %d", port, MinBlocksFree); -#ifdef LOG if (LogLevel >= 14) - syslog(LOG_INFO, "rejecting connections on port %d: min free: %d", + sm_syslog(LOG_INFO, NOQID, + "rejecting connections on port %d: min free: %d", port, MinBlocksFree); -#endif return TRUE; } if (MaxChildren > 0 && CurChildren >= MaxChildren) { extern void proc_list_probe __P((void)); proc_list_probe(); if (CurChildren >= MaxChildren) { setproctitle("rejecting connections on port %d: %d children, max %d", port, CurChildren, MaxChildren); -#ifdef LOG if (LogLevel >= 14) - syslog(LOG_INFO, "rejecting connections on port %d: %d children, max %d", + sm_syslog(LOG_INFO, NOQID, + "rejecting connections on port %d: %d children, max %d", port, CurChildren, MaxChildren); -#endif return TRUE; } } return FALSE; } /* ** SETPROCTITLE -- set process title for ps ** ** Parameters: ** fmt -- a printf style format string. ** a, b, c -- possible parameters to fmt. ** ** Returns: ** none. ** ** Side Effects: ** Clobbers argv of our main procedure so ps(1) will ** display the title. */ #define SPT_NONE 0 /* don't use it at all */ #define SPT_REUSEARGV 1 /* cover argv with title information */ #define SPT_BUILTIN 2 /* use libc builtin */ #define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */ #define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */ #define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */ #define SPT_SCO 6 /* write kernel u. area */ +#define SPT_CHANGEARGV 7 /* write our own strings into argv[] */ #ifndef SPT_TYPE # define SPT_TYPE SPT_REUSEARGV #endif #if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN # if SPT_TYPE == SPT_PSTAT # include # endif # if SPT_TYPE == SPT_PSSTRINGS # include # include # ifndef PS_STRINGS /* hmmmm.... apparently not available after all */ # undef SPT_TYPE # define SPT_TYPE SPT_REUSEARGV # else # ifndef NKPDE /* FreeBSD 2.0 */ # define NKPDE 63 typedef unsigned int *pt_entry_t; # endif # endif # endif -# if SPT_TYPE == SPT_PSSTRINGS +# if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV # define SETPROC_STATIC static # else # define SETPROC_STATIC # endif # if SPT_TYPE == SPT_SYSMIPS # include # include # endif # if SPT_TYPE == SPT_SCO # include # include # include # include # if PSARGSZ > MAXLINE # define SPT_BUFSIZE PSARGSZ # endif # endif # ifndef SPT_PADCHAR # define SPT_PADCHAR ' ' # endif # ifndef SPT_BUFSIZE # define SPT_BUFSIZE MAXLINE # endif #endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */ /* ** Pointers for setproctitle. ** This allows "ps" listings to give more useful information. */ char **Argv = NULL; /* pointer to argument vector */ char *LastArgv = NULL; /* end of argv */ void initsetproctitle(argc, argv, envp) int argc; char **argv; char **envp; { register int i; extern char **environ; /* - ** Move the environment so setproctitle can use the space at + ** Move the environment so setproctitle can use the space at ** the top of memory. */ for (i = 0; envp[i] != NULL; i++) continue; environ = (char **) xalloc(sizeof (char *) * (i + 1)); for (i = 0; envp[i] != NULL; i++) environ[i] = newstr(envp[i]); environ[i] = NULL; /* ** Save start and extent of argv for setproctitle. */ Argv = argv; if (i > 0) LastArgv = envp[i - 1] + strlen(envp[i - 1]); else LastArgv = argv[argc - 1] + strlen(argv[argc - 1]); } #if SPT_TYPE != SPT_BUILTIN /*VARARGS1*/ void # ifdef __STDC__ setproctitle(const char *fmt, ...) # else setproctitle(fmt, va_alist) const char *fmt; va_dcl # endif { # if SPT_TYPE != SPT_NONE register char *p; register int i; SETPROC_STATIC char buf[SPT_BUFSIZE]; VA_LOCAL_DECL # if SPT_TYPE == SPT_PSTAT union pstun pst; # endif # if SPT_TYPE == SPT_SCO off_t seek_off; static int kmem = -1; static int kmempid = -1; struct user u; # endif p = buf; /* print sendmail: heading for grep */ (void) strcpy(p, "sendmail: "); p += strlen(p); /* print the argument string */ VA_START(fmt); (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap); VA_END; i = strlen(buf); # if SPT_TYPE == SPT_PSTAT pst.pst_command = buf; pstat(PSTAT_SETCMD, pst, i, 0, 0); # endif # if SPT_TYPE == SPT_PSSTRINGS PS_STRINGS->ps_nargvstr = 1; PS_STRINGS->ps_argvstr = buf; # endif # if SPT_TYPE == SPT_SYSMIPS sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf); # endif # if SPT_TYPE == SPT_SCO if (kmem < 0 || kmempid != getpid()) { if (kmem >= 0) close(kmem); kmem = open(_PATH_KMEM, O_RDWR, 0); if (kmem < 0) return; (void) fcntl(kmem, F_SETFD, 1); kmempid = getpid(); } buf[PSARGSZ - 1] = '\0'; seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u; if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off) (void) write(kmem, buf, PSARGSZ); # endif # if SPT_TYPE == SPT_REUSEARGV if (i > LastArgv - Argv[0] - 2) { i = LastArgv - Argv[0] - 2; buf[i] = '\0'; } (void) strcpy(Argv[0], buf); p = &Argv[0][i]; while (p < LastArgv) *p++ = SPT_PADCHAR; Argv[1] = NULL; # endif +# if SPT_TYPE == SPT_CHANGEARGV + Argv[0] = buf; + Argv[1] = 0; +# endif # endif /* SPT_TYPE != SPT_NONE */ } #endif /* SPT_TYPE != SPT_BUILTIN */ /* +** WAITFOR -- wait for a particular process id. +** +** Parameters: +** pid -- process id to wait for. +** +** Returns: +** status of pid. +** -1 if pid never shows up. +** +** Side Effects: +** none. +*/ + +int +waitfor(pid) + pid_t pid; +{ +#ifdef WAITUNION + union wait st; +#else + auto int st; +#endif + pid_t i; +#if defined(ISC_UNIX) || defined(_SCO_unix_) + int savesig; +#endif + + do + { + errno = 0; +#if defined(ISC_UNIX) || defined(_SCO_unix_) + savesig = releasesignal(SIGCHLD); +#endif + i = wait(&st); +#if defined(ISC_UNIX) || defined(_SCO_unix_) + if (savesig > 0) + blocksignal(SIGCHLD); +#endif + if (i > 0) + proc_list_drop(i); + } while ((i >= 0 || errno == EINTR) && i != pid); + if (i < 0) + return -1; +#ifdef WAITUNION + return st.w_status; +#else + return st; +#endif +} + /* ** REAPCHILD -- pick up the body of my child, lest it become a zombie ** ** Parameters: ** sig -- the signal that got us here (unused). ** ** Returns: ** none. ** ** Side Effects: ** Picks up extant zombies. */ SIGFUNC_DECL reapchild(sig) int sig; { int olderrno = errno; pid_t pid; # ifdef HASWAITPID auto int status; int count; count = 0; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (count++ > 1000) { -#ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, + sm_syslog(LOG_ALERT, NOQID, "reapchild: waitpid loop: pid=%d, status=%x", pid, status); -#endif break; } proc_list_drop(pid); } # else # ifdef WNOHANG union wait status; while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0) proc_list_drop(pid); # else /* WNOHANG */ auto int status; - while ((pid = wait(&status)) > 0) + /* + ** Catch one zombie -- we will be re-invoked (we hope) if there + ** are more. Unreliable signals probably break this, but this + ** is the "old system" situation -- waitpid or wait3 are to be + ** strongly preferred. + */ + + if ((pid = wait(&status)) > 0) proc_list_drop(pid); # endif /* WNOHANG */ # endif # ifdef SYS5SIGNALS (void) setsignal(SIGCHLD, reapchild); # endif errno = olderrno; return SIGFUNC_RETURN; } /* ** PUTENV -- emulation of putenv() in terms of setenv() ** ** Not needed on Posix-compliant systems. ** This doesn't have full Posix semantics, but it's good enough ** for sendmail. ** ** Parameter: ** env -- the environment to put. ** ** Returns: ** none. */ #ifdef NEEDPUTENV # if NEEDPUTENV == 2 /* no setenv(3) call available */ int putenv(str) char *str; { char **current; int matchlen, envlen=0; char *tmp; char **newenv; static int first=1; extern char **environ; /* * find out how much of str to match when searching * for a string to replace. */ if ((tmp = strchr(str, '=')) == NULL || tmp == str) matchlen = strlen(str); else matchlen = (int) (tmp - str); ++matchlen; /* * Search for an existing string in the environment and find the * length of environ. If found, replace and exit. */ for (current=environ; *current; current++) { ++envlen; if (strncmp(str, *current, matchlen) == 0) { /* found it, now insert the new version */ *current = (char *)str; return(0); } } /* * There wasn't already a slot so add space for a new slot. * If this is our first time through, use malloc(), else realloc(). */ if (first) { newenv = (char **) malloc(sizeof(char *) * (envlen + 2)); if (newenv == NULL) return(-1); first=0; (void) memcpy(newenv, environ, sizeof(char *) * envlen); } else { newenv = (char **) realloc((char *)environ, sizeof(char *) * (envlen + 2)); if (newenv == NULL) return(-1); } /* actually add in the new entry */ environ = newenv; environ[envlen] = (char *)str; environ[envlen+1] = NULL; return(0); } #else /* implement putenv() in terms of setenv() */ int putenv(env) char *env; { char *p; int l; char nbuf[100]; p = strchr(env, '='); if (p == NULL) return 0; l = p - env; if (l > sizeof nbuf - 1) l = sizeof nbuf - 1; bcopy(env, nbuf, l); nbuf[l] = '\0'; return setenv(nbuf, ++p, 1); } # endif #endif /* ** UNSETENV -- remove a variable from the environment ** ** Not needed on newer systems. ** ** Parameters: ** name -- the string name of the environment variable to be ** deleted from the current environment. ** ** Returns: ** none. ** ** Globals: ** environ -- a pointer to the current environment. ** ** Side Effects: ** Modifies environ. */ #ifndef HASUNSETENV void unsetenv(name) char *name; { extern char **environ; register char **pp; int len = strlen(name); for (pp = environ; *pp != NULL; pp++) { if (strncmp(name, *pp, len) == 0 && ((*pp)[len] == '=' || (*pp)[len] == '\0')) break; } for (; *pp != NULL; pp++) *pp = pp[1]; } #endif /* ** GETDTABLESIZE -- return number of file descriptors ** ** Only on non-BSD systems ** ** Parameters: ** none ** ** Returns: ** size of file descriptor table ** ** Side Effects: ** none */ #ifdef SOLARIS # include #endif int getdtsize() { #ifdef RLIMIT_NOFILE struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) >= 0) return rl.rlim_cur; #endif # ifdef HASGETDTABLESIZE return getdtablesize(); # else # ifdef _SC_OPEN_MAX return sysconf(_SC_OPEN_MAX); # else return NOFILE; # endif # endif } /* ** UNAME -- get the UUCP name of this system. */ #ifndef HASUNAME int uname(name) struct utsname *name; { FILE *file; char *n; name->nodename[0] = '\0'; /* try /etc/whoami -- one line with the node name */ if ((file = fopen("/etc/whoami", "r")) != NULL) { (void) fgets(name->nodename, NODE_LENGTH + 1, file); (void) fclose(file); n = strchr(name->nodename, '\n'); if (n != NULL) *n = '\0'; if (name->nodename[0] != '\0') return (0); } /* try /usr/include/whoami.h -- has a #define somewhere */ if ((file = fopen("/usr/include/whoami.h", "r")) != NULL) { char buf[MAXLINE]; while (fgets(buf, MAXLINE, file) != NULL) if (sscanf(buf, "#define sysname \"%*[^\"]\"", NODE_LENGTH, name->nodename) > 0) break; (void) fclose(file); if (name->nodename[0] != '\0') return (0); } #ifdef TRUST_POPEN /* ** Popen is known to have security holes. */ /* try uuname -l to return local name */ if ((file = popen("uuname -l", "r")) != NULL) { (void) fgets(name, NODE_LENGTH + 1, file); (void) pclose(file); n = strchr(name, '\n'); if (n != NULL) *n = '\0'; if (name->nodename[0] != '\0') return (0); } #endif return (-1); } #endif /* HASUNAME */ /* ** INITGROUPS -- initialize groups ** ** Stub implementation for System V style systems */ #ifndef HASINITGROUPS initgroups(name, basegid) char *name; int basegid; { return 0; } #endif /* ** SETSID -- set session id (for non-POSIX systems) */ #ifndef HASSETSID pid_t setsid __P ((void)) { #ifdef TIOCNOTTY int fd; fd = open("/dev/tty", O_RDWR, 0); if (fd >= 0) { (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0); (void) close(fd); } #endif /* TIOCNOTTY */ # ifdef SYS5SETPGRP return setpgrp(); # else return setpgid(0, getpid()); # endif } #endif /* ** FSYNC -- dummy fsync */ #ifdef NEEDFSYNC fsync(fd) int fd; { # ifdef O_SYNC return fcntl(fd, F_SETFL, O_SYNC); # else /* nothing we can do */ return 0; # endif } #endif /* ** DGUX_INET_ADDR -- inet_addr for DG/UX ** ** Data General DG/UX version of inet_addr returns a struct in_addr ** instead of a long. This patches things. Only needed on versions ** prior to 5.4.3. */ #ifdef DGUX_5_4_2 #undef inet_addr long dgux_inet_addr(host) char *host; { struct in_addr haddr; haddr = inet_addr(host); return haddr.s_addr; } #endif /* ** GETOPT -- for old systems or systems with bogus implementations */ #ifdef NEEDGETOPT /* * Copyright (c) 1985 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* ** this version hacked to add `atend' flag to allow state machine ** to reset if invoked by the program to scan args for a 2nd time */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getopt.c 4.3 (Berkeley) 3/9/86"; #endif /* LIBC_SCCS and not lint */ #include /* * get option letter from argument vector */ #ifdef _CONVEX_SOURCE extern int optind, opterr, optopt; extern char *optarg; #else int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = 0; /* character checked for validity */ char *optarg = NULL; /* argument associated with option */ #endif #define BADCH (int)'?' #define EMSG "" #define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \ fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);} getopt(nargc,nargv,ostr) int nargc; char *const *nargv; const char *ostr; { static char *place = EMSG; /* option letter processing */ static char atend = 0; register char *oli; /* option letter list index */ if (atend) { atend = 0; place = EMSG; } if(!*place) { /* update scanning pointer */ if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) { atend++; return -1; } if (*place == '-') { /* found "--" */ ++optind; atend++; return -1; } } /* option letter okay? */ if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) { if (!*place) ++optind; tell(": illegal option -- "); } if (oli && *++oli != ':') { /* don't need argument */ optarg = NULL; if (!*place) ++optind; } else { /* need an argument */ if (*place) optarg = place; /* no white space */ else if (nargc <= ++optind) { /* no arg */ place = EMSG; tell(": option requires an argument -- "); } else optarg = nargv[optind]; /* white space */ place = EMSG; ++optind; } return(optopt); /* dump back option letter */ } #endif /* ** VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version */ #ifdef NEEDVPRINTF #define MAXARG 16 vfprintf(fp, fmt, ap) FILE *fp; char *fmt; char **ap; { char *bp[MAXARG]; int i = 0; while (*ap && i < MAXARG) bp[i++] = *ap++; fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7], bp[8], bp[9], bp[10], bp[11], bp[12], bp[13], bp[14], bp[15]); } vsprintf(s, fmt, ap) char *s; char *fmt; char **ap; { char *bp[MAXARG]; int i = 0; while (*ap && i < MAXARG) bp[i++] = *ap++; sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3], bp[4], bp[5], bp[6], bp[7], bp[8], bp[9], bp[10], bp[11], bp[12], bp[13], bp[14], bp[15]); } #endif /* ** SNPRINTF, VSNPRINT -- counted versions of printf ** ** These versions have been grabbed off the net. They have been ** cleaned up to compile properly and support for .precision and ** %lx has been added. */ -#if !HASSNPRINTF - /************************************************************** * Original: * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 - * A bombproof version of doprnt (dopr) included. + * A bombproof version of doprnt (sm_dopr) included. * Sigh. This sort of thing is always nasty do deal with. Note that * the version here does not include floating point... * * snprintf() is used instead of sprintf() as it does limit checks * for string length. This covers a nasty loophole. * * The other functions are there to prevent NULL pointers from * causing nast effects. **************************************************************/ -static void dopr(); -static char *end; +static void sm_dopr(); +static char *DoprEnd; static int SnprfOverflow; +#if !HASSNPRINTF + /* VARARGS3 */ int # ifdef __STDC__ snprintf(char *str, size_t count, const char *fmt, ...) # else snprintf(str, count, fmt, va_alist) char *str; size_t count; const char *fmt; va_dcl #endif { int len; VA_LOCAL_DECL VA_START(fmt); len = vsnprintf(str, count, fmt, ap); VA_END; return len; } # ifndef luna2 int vsnprintf(str, count, fmt, args) char *str; size_t count; const char *fmt; va_list args; { str[0] = 0; - end = str + count - 1; + DoprEnd = str + count - 1; SnprfOverflow = 0; - dopr( str, fmt, args ); + sm_dopr( str, fmt, args ); if (count > 0) - end[0] = 0; + DoprEnd[0] = 0; if (SnprfOverflow && tTd(57, 2)) printf("\nvsnprintf overflow, len = %d, str = %s", count, shortenstring(str, 203)); return strlen(str); } +# endif /* !luna2 */ +#endif /* !HASSNPRINTF */ + /* - * dopr(): poor man's version of doprintf + * sm_dopr(): poor man's version of doprintf */ static void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth)); static void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad)); static void dostr __P(( char * , int )); static char *output; static void dopr_outch __P(( int c )); +static int SyslogErrno; static void -dopr( buffer, format, args ) +sm_dopr( buffer, format, args ) char *buffer; const char *format; va_list args; { int ch; long value; int longflag = 0; int pointflag = 0; int maxwidth = 0; char *strvalue; int ljust; int len; int zpad; +# if !HASSTRERROR && !defined(ERRLIST_PREDEFINED) + extern char *sys_errlist[]; + extern int sys_nerr; +# endif + output = buffer; while( (ch = *format++) ){ - switch( ch ){ - case '%': - ljust = len = zpad = maxwidth = 0; - longflag = pointflag = 0; - nextch: - ch = *format++; - switch( ch ){ - case 0: - dostr( "**end of format**" , 0); - return; - case '-': ljust = 1; goto nextch; - case '0': /* set zero padding if len not set */ - if(len==0 && !pointflag) zpad = '0'; - case '1': case '2': case '3': - case '4': case '5': case '6': - case '7': case '8': case '9': + switch( ch ){ + case '%': + ljust = len = zpad = maxwidth = 0; + longflag = pointflag = 0; + nextch: + ch = *format++; + switch( ch ){ + case 0: + dostr( "**end of format**" , 0); + return; + case '-': ljust = 1; goto nextch; + case '0': /* set zero padding if len not set */ + if(len==0 && !pointflag) zpad = '0'; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': if (pointflag) maxwidth = maxwidth*10 + ch - '0'; else len = len*10 + ch - '0'; - goto nextch; + goto nextch; case '*': if (pointflag) maxwidth = va_arg( args, int ); else len = va_arg( args, int ); goto nextch; case '.': pointflag = 1; goto nextch; - case 'l': longflag = 1; goto nextch; - case 'u': case 'U': - /*fmtnum(value,base,dosign,ljust,len,zpad) */ - if( longflag ){ - value = va_arg( args, long ); - } else { - value = va_arg( args, int ); - } - fmtnum( value, 10,0, ljust, len, zpad ); break; - case 'o': case 'O': - /*fmtnum(value,base,dosign,ljust,len,zpad) */ - if( longflag ){ - value = va_arg( args, long ); - } else { - value = va_arg( args, int ); - } - fmtnum( value, 8,0, ljust, len, zpad ); break; - case 'd': case 'D': - if( longflag ){ - value = va_arg( args, long ); - } else { - value = va_arg( args, int ); - } - fmtnum( value, 10,1, ljust, len, zpad ); break; - case 'x': - if( longflag ){ - value = va_arg( args, long ); - } else { - value = va_arg( args, int ); - } - fmtnum( value, 16,0, ljust, len, zpad ); break; - case 'X': - if( longflag ){ - value = va_arg( args, long ); - } else { - value = va_arg( args, int ); - } - fmtnum( value,-16,0, ljust, len, zpad ); break; - case 's': - strvalue = va_arg( args, char *); + case 'l': longflag = 1; goto nextch; + case 'u': case 'U': + /*fmtnum(value,base,dosign,ljust,len,zpad) */ + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 10,0, ljust, len, zpad ); break; + case 'o': case 'O': + /*fmtnum(value,base,dosign,ljust,len,zpad) */ + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 8,0, ljust, len, zpad ); break; + case 'd': case 'D': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 10,1, ljust, len, zpad ); break; + case 'x': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 16,0, ljust, len, zpad ); break; + case 'X': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value,-16,0, ljust, len, zpad ); break; + case 's': + strvalue = va_arg( args, char *); if (maxwidth > 0 || !pointflag) { if (pointflag && len > maxwidth) len = maxwidth; /* Adjust padding */ fmtstr( strvalue,ljust,len,zpad, maxwidth); } break; - case 'c': - ch = va_arg( args, int ); - dopr_outch( ch ); break; - case '%': dopr_outch( ch ); continue; - default: - dostr( "???????" , 0); - } - break; - default: - dopr_outch( ch ); - break; - } + case 'c': + ch = va_arg( args, int ); + dopr_outch( ch ); break; + case 'm': +#if HASSTRERROR + dostr(strerror(SyslogErrno), 0); +#else + if (SyslogErrno < 0 || SyslogErrno > sys_nerr) + { + dostr("Error ", 0); + fmtnum(SyslogErrno, 10, 0, 0, 0, 0); + } + else + dostr(sys_errlist[SyslogErrno], 0); +#endif + break; + + case '%': dopr_outch( ch ); continue; + default: + dostr( "???????" , 0); + } + break; + default: + dopr_outch( ch ); + break; + } } *output = 0; } static void fmtstr( value, ljust, len, zpad, maxwidth ) char *value; int ljust, len, zpad, maxwidth; { int padlen, strlen; /* amount to pad */ if( value == 0 ){ - value = ""; + value = ""; } for( strlen = 0; value[strlen]; ++ strlen ); /* strlen */ if (strlen > maxwidth && maxwidth) strlen = maxwidth; padlen = len - strlen; if( padlen < 0 ) padlen = 0; if( ljust ) padlen = -padlen; while( padlen > 0 ) { - dopr_outch( ' ' ); - --padlen; + dopr_outch( ' ' ); + --padlen; } dostr( value, maxwidth ); while( padlen < 0 ) { - dopr_outch( ' ' ); - ++padlen; + dopr_outch( ' ' ); + ++padlen; } } static void fmtnum( value, base, dosign, ljust, len, zpad ) long value; int base, dosign, ljust, len, zpad; { int signvalue = 0; unsigned long uvalue; char convert[20]; int place = 0; int padlen = 0; /* amount to pad */ int caps = 0; /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n", - value, base, dosign, ljust, len, zpad )); */ + value, base, dosign, ljust, len, zpad )); */ uvalue = value; if( dosign ){ - if( value < 0 ) { - signvalue = '-'; - uvalue = -value; - } + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } } if( base < 0 ){ - caps = 1; - base = -base; + caps = 1; + base = -base; } do{ - convert[place++] = - (caps? "0123456789ABCDEF":"0123456789abcdef") - [uvalue % (unsigned)base ]; - uvalue = (uvalue / (unsigned)base ); + convert[place++] = + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; + uvalue = (uvalue / (unsigned)base ); }while(uvalue); convert[place] = 0; padlen = len - place; if( padlen < 0 ) padlen = 0; if( ljust ) padlen = -padlen; /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n", - convert,place,signvalue,padlen)); */ + convert,place,signvalue,padlen)); */ if( zpad && padlen > 0 ){ - if( signvalue ){ - dopr_outch( signvalue ); - --padlen; - signvalue = 0; - } - while( padlen > 0 ){ - dopr_outch( zpad ); - --padlen; - } + if( signvalue ){ + dopr_outch( signvalue ); + --padlen; + signvalue = 0; + } + while( padlen > 0 ){ + dopr_outch( zpad ); + --padlen; + } } while( padlen > 0 ) { - dopr_outch( ' ' ); - --padlen; + dopr_outch( ' ' ); + --padlen; } if( signvalue ) dopr_outch( signvalue ); while( place > 0 ) dopr_outch( convert[--place] ); while( padlen < 0 ){ - dopr_outch( ' ' ); - ++padlen; + dopr_outch( ' ' ); + ++padlen; } } static void dostr( str , cut) char *str; int cut; { if (cut) { while(*str && cut-- > 0) dopr_outch(*str++); } else { while(*str) dopr_outch(*str++); } } static void dopr_outch( c ) int c; { #if 0 if( iscntrl(c) && c != '\n' && c != '\t' ){ - c = '@' + (c & 0x1F); - if( end == 0 || output < end ) - *output++ = '^'; + c = '@' + (c & 0x1F); + if( DoprEnd == 0 || output < DoprEnd ) + *output++ = '^'; } #endif - if( end == 0 || output < end ) - *output++ = c; + if( DoprEnd == 0 || output < DoprEnd ) + *output++ = c; else SnprfOverflow++; } - -# endif /* !luna2 */ - -#endif /* !HASSNPRINTF */ /* ** USERSHELLOK -- tell if a user's shell is ok for unrestricted use ** ** Parameters: ** user -- the name of the user we are checking. ** shell -- the user's shell from /etc/passwd ** ** Returns: ** TRUE -- if it is ok to use this for unrestricted access. ** FALSE -- if the shell is restricted. */ #if !HASGETUSERSHELL # ifndef _PATH_SHELLS # define _PATH_SHELLS "/etc/shells" # endif # if defined(_AIX3) || defined(_AIX4) # include # include # endif char *DefaultUserShells[] = { "/bin/sh", /* standard shell */ "/usr/bin/sh", "/bin/csh", /* C shell */ "/usr/bin/csh", #ifdef __hpux # ifdef V4FS "/usr/bin/rsh", /* restricted Bourne shell */ "/usr/bin/ksh", /* Korn shell */ "/usr/bin/rksh", /* restricted Korn shell */ "/usr/bin/pam", "/usr/bin/keysh", /* key shell (extended Korn shell) */ "/usr/bin/posix/sh", # else "/bin/rsh", /* restricted Bourne shell */ "/bin/ksh", /* Korn shell */ "/bin/rksh", /* restricted Korn shell */ "/bin/pam", "/usr/bin/keysh", /* key shell (extended Korn shell) */ "/bin/posix/sh", # endif #endif #if defined(_AIX3) || defined(_AIX4) "/bin/ksh", /* Korn shell */ "/usr/bin/ksh", "/bin/tsh", /* trusted shell */ "/usr/bin/tsh", "/bin/bsh", /* Bourne shell */ "/usr/bin/bsh", #endif #ifdef __svr4__ "/bin/ksh", /* Korn shell */ "/usr/bin/ksh", #endif NULL }; #endif #define WILDCARD_SHELL "/SENDMAIL/ANY/SHELL/" bool usershellok(user, shell) char *user; char *shell; { #if HASGETUSERSHELL register char *p; extern char *getusershell(); if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') || ConfigLevel <= 1) return TRUE; setusershell(); while ((p = getusershell()) != NULL) if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0) break; endusershell(); return p != NULL; #else # if USEGETCONFATTR auto char *v; # endif register FILE *shellf; char buf[MAXLINE]; if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't')) return TRUE; # if USEGETCONFATTR /* ** Naturally IBM has a "better" idea..... ** ** What a crock. This interface isn't documented, it is ** considered part of the security library (-ls), and it ** only works if you are running as root (since the list ** of valid shells is obviously a source of great concern). ** I recommend that you do NOT define USEGETCONFATTR, ** especially since you are going to have to set up an ** /etc/shells anyhow to handle the cases where getconfattr ** fails. */ if (getconfattr(SC_SYS_LOGIN, SC_SHELLS, &v, SEC_LIST) == 0 && v != NULL) { while (*v != '\0') { if (strcmp(v, shell) == 0 || strcmp(v, WILDCARD_SHELL) == 0) return TRUE; v += strlen(v) + 1; } return FALSE; } # endif shellf = fopen(_PATH_SHELLS, "r"); if (shellf == NULL) { /* no /etc/shells; see if it is one of the std shells */ char **d; for (d = DefaultUserShells; *d != NULL; d++) { if (strcmp(shell, *d) == 0) return TRUE; } return FALSE; } while (fgets(buf, sizeof buf, shellf) != NULL) { register char *p, *q; p = buf; while (*p != '\0' && *p != '#' && *p != '/') p++; if (*p == '#' || *p == '\0') continue; q = p; while (*p != '\0' && *p != '#' && !isspace(*p)) p++; *p = '\0'; if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0) { fclose(shellf); return TRUE; } } fclose(shellf); return FALSE; #endif } /* ** FREEDISKSPACE -- see how much free space is on the queue filesystem ** ** Only implemented if you have statfs. ** ** Parameters: ** dir -- the directory in question. ** bsize -- a variable into which the filesystem ** block size is stored. ** ** Returns: ** The number of bytes free on the queue filesystem. ** -1 if the statfs call fails. ** ** Side effects: ** Puts the filesystem block size into bsize. */ /* statfs types */ #define SFS_NONE 0 /* no statfs implementation */ #define SFS_USTAT 1 /* use ustat */ #define SFS_4ARGS 2 /* use four-argument statfs call */ #define SFS_VFS 3 /* use implementation */ #define SFS_MOUNT 4 /* use implementation */ #define SFS_STATFS 5 /* use implementation */ #define SFS_STATVFS 6 /* use implementation */ #ifndef SFS_TYPE # define SFS_TYPE SFS_NONE #endif #if SFS_TYPE == SFS_USTAT # include #endif #if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS # include #endif #if SFS_TYPE == SFS_VFS # include #endif #if SFS_TYPE == SFS_MOUNT # include #endif #if SFS_TYPE == SFS_STATVFS # include #endif long freediskspace(dir, bsize) char *dir; long *bsize; { #if SFS_TYPE != SFS_NONE # if SFS_TYPE == SFS_USTAT struct ustat fs; struct stat statbuf; # define FSBLOCKSIZE DEV_BSIZE # define SFS_BAVAIL f_tfree # else # if defined(ultrix) struct fs_data fs; # define SFS_BAVAIL fd_bfreen # define FSBLOCKSIZE 1024L # else # if SFS_TYPE == SFS_STATVFS struct statvfs fs; # define FSBLOCKSIZE fs.f_frsize # else struct statfs fs; # define FSBLOCKSIZE fs.f_bsize # endif # endif # endif # ifndef SFS_BAVAIL # define SFS_BAVAIL f_bavail # endif # if SFS_TYPE == SFS_USTAT if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0) # else # if SFS_TYPE == SFS_4ARGS if (statfs(dir, &fs, sizeof fs, 0) == 0) # else # if SFS_TYPE == SFS_STATVFS if (statvfs(dir, &fs) == 0) # else # if defined(ultrix) if (statfs(dir, &fs) > 0) # else if (statfs(dir, &fs) == 0) # endif # endif # endif # endif { if (bsize != NULL) *bsize = FSBLOCKSIZE; - if (fs.SFS_BAVAIL < 0) + if (fs.SFS_BAVAIL <= 0) return 0; else return fs.SFS_BAVAIL; } #endif return (-1); } /* ** ENOUGHDISKSPACE -- is there enough free space on the queue fs? ** ** Only implemented if you have statfs. ** ** Parameters: ** msize -- the size to check against. If zero, we don't yet ** know how big the message will be, so just check for ** a "reasonable" amount. ** ** Returns: ** TRUE if there is enough space. ** FALSE otherwise. */ bool enoughdiskspace(msize) long msize; { long bfree, bsize; if (MinBlocksFree <= 0 && msize <= 0) { if (tTd(4, 80)) printf("enoughdiskspace: no threshold\n"); return TRUE; } if ((bfree = freediskspace(QueueDir, &bsize)) >= 0) { if (tTd(4, 80)) printf("enoughdiskspace: bavail=%ld, need=%ld\n", bfree, msize); /* convert msize to block count */ msize = msize / bsize + 1; if (MinBlocksFree >= 0) msize += MinBlocksFree; if (bfree < msize) { -#ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, - "%s: low on space (have %ld, %s needs %ld in %s)", - CurEnv->e_id == NULL ? "[NOQUEUE]" : CurEnv->e_id, + sm_syslog(LOG_ALERT, CurEnv->e_id, + "low on space (have %ld, %s needs %ld in %s)", bfree, CurHostName == NULL ? "SMTP-DAEMON" : CurHostName, msize, QueueDir); -#endif return FALSE; } } else if (tTd(4, 80)) printf("enoughdiskspace failure: min=%ld, need=%ld: %s\n", MinBlocksFree, msize, errstring(errno)); return TRUE; } /* ** TRANSIENTERROR -- tell if an error code indicates a transient failure ** ** This looks at an errno value and tells if this is likely to ** go away if retried later. ** ** Parameters: ** err -- the errno code to classify. ** ** Returns: ** TRUE if this is probably transient. ** FALSE otherwise. */ bool transienterror(err) int err; { switch (err) { case EIO: /* I/O error */ case ENXIO: /* Device not configured */ case EAGAIN: /* Resource temporarily unavailable */ case ENOMEM: /* Cannot allocate memory */ case ENODEV: /* Operation not supported by device */ case ENFILE: /* Too many open files in system */ case EMFILE: /* Too many open files */ case ENOSPC: /* No space left on device */ #ifdef ETIMEDOUT case ETIMEDOUT: /* Connection timed out */ #endif #ifdef ESTALE case ESTALE: /* Stale NFS file handle */ #endif #ifdef ENETDOWN case ENETDOWN: /* Network is down */ #endif #ifdef ENETUNREACH case ENETUNREACH: /* Network is unreachable */ #endif #ifdef ENETRESET case ENETRESET: /* Network dropped connection on reset */ #endif #ifdef ECONNABORTED case ECONNABORTED: /* Software caused connection abort */ #endif #ifdef ECONNRESET case ECONNRESET: /* Connection reset by peer */ #endif #ifdef ENOBUFS case ENOBUFS: /* No buffer space available */ #endif #ifdef ESHUTDOWN case ESHUTDOWN: /* Can't send after socket shutdown */ #endif #ifdef ECONNREFUSED case ECONNREFUSED: /* Connection refused */ #endif #ifdef EHOSTDOWN case EHOSTDOWN: /* Host is down */ #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: /* No route to host */ #endif #ifdef EDQUOT case EDQUOT: /* Disc quota exceeded */ #endif #ifdef EPROCLIM case EPROCLIM: /* Too many processes */ #endif #ifdef EUSERS case EUSERS: /* Too many users */ #endif #ifdef EDEADLK case EDEADLK: /* Resource deadlock avoided */ #endif #ifdef EISCONN case EISCONN: /* Socket already connected */ #endif #ifdef EINPROGRESS case EINPROGRESS: /* Operation now in progress */ #endif #ifdef EALREADY case EALREADY: /* Operation already in progress */ #endif #ifdef EADDRINUSE case EADDRINUSE: /* Address already in use */ #endif #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: /* Can't assign requested address */ #endif #ifdef ETXTBSY case ETXTBSY: /* (Apollo) file locked */ #endif #if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR)) case ENOSR: /* Out of streams resources */ #endif - case EOPENTIMEOUT: /* PSEUDO: open timed out */ + case E_SM_OPENTIMEOUT: /* PSEUDO: open timed out */ return TRUE; } /* nope, must be permanent */ return FALSE; } /* ** LOCKFILE -- lock a file using flock or (shudder) fcntl locking ** ** Parameters: ** fd -- the file descriptor of the file. ** filename -- the file name (for error messages). ** ext -- the filename extension. ** type -- type of the lock. Bits can be: ** LOCK_EX -- exclusive lock. ** LOCK_NB -- non-blocking. ** ** Returns: ** TRUE if the lock was acquired. ** FALSE otherwise. */ bool lockfile(fd, filename, ext, type) int fd; char *filename; char *ext; int type; { + int i; # if !HASFLOCK int action; struct flock lfd; if (ext == NULL) ext = ""; bzero(&lfd, sizeof lfd); if (bitset(LOCK_UN, type)) lfd.l_type = F_UNLCK; else if (bitset(LOCK_EX, type)) lfd.l_type = F_WRLCK; else lfd.l_type = F_RDLCK; if (bitset(LOCK_NB, type)) action = F_SETLK; else action = F_SETLKW; if (tTd(55, 60)) printf("lockfile(%s%s, action=%d, type=%d): ", filename, ext, action, lfd.l_type); - if (fcntl(fd, action, &lfd) >= 0) + while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR) + continue; + if (i >= 0) { if (tTd(55, 60)) printf("SUCCESS\n"); return TRUE; } if (tTd(55, 60)) printf("(%s) ", errstring(errno)); /* ** On SunOS, if you are testing using -oQ/tmp/mqueue or ** -oA/tmp/aliases or anything like that, and /tmp is mounted ** as type "tmp" (that is, served from swap space), the ** previous fcntl will fail with "Invalid argument" errors. ** Since this is fairly common during testing, we will assume ** that this indicates that the lock is successfully grabbed. */ if (errno == EINVAL) { if (tTd(55, 60)) printf("SUCCESS\n"); return TRUE; } if (!bitset(LOCK_NB, type) || (errno != EACCES && errno != EAGAIN)) { int omode = -1; # ifdef F_GETFL int oerrno = errno; (void) fcntl(fd, F_GETFL, &omode); errno = oerrno; # endif syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", filename, ext, fd, type, omode, geteuid()); dumpfd(fd, TRUE, TRUE); } # else if (ext == NULL) ext = ""; if (tTd(55, 60)) printf("lockfile(%s%s, type=%o): ", filename, ext, type); - if (flock(fd, type) >= 0) + while ((i = flock(fd, type)) < 0 && errno == EINTR) + continue; + if (i >= 0) { if (tTd(55, 60)) printf("SUCCESS\n"); return TRUE; } if (tTd(55, 60)) printf("(%s) ", errstring(errno)); if (!bitset(LOCK_NB, type) || errno != EWOULDBLOCK) { int omode = -1; # ifdef F_GETFL int oerrno = errno; (void) fcntl(fd, F_GETFL, &omode); errno = oerrno; # endif syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)", filename, ext, fd, type, omode, geteuid()); dumpfd(fd, TRUE, TRUE); } # endif if (tTd(55, 60)) printf("FAIL\n"); return FALSE; } /* ** CHOWNSAFE -- tell if chown is "safe" (executable only by root) ** +** Unfortunately, given that we can't predict other systems on which +** a remote mounted (NFS) filesystem will be mounted, the answer is +** almost always that this is unsafe. +** +** Note also that many operating systems have non-compliant +** implementations of the _POSIX_CHOWN_RESTRICTED variable and the +** fpathconf() routine. According to IEEE 1003.1-1990, if +** _POSIX_CHOWN_RESTRICTED is defined and not equal to -1, then +** no non-root process can give away the file. However, vendors +** don't take NFS into account, so a comfortable value of +** _POSIX_CHOWN_RESTRICTED tells us nothing. +** +** Also, some systems (e.g., IRIX 6.2) return 1 from fpathconf() +** even on files where chown is not restricted. Many systems get +** this wrong on NFS-based filesystems (that is, they say that chown +** is restricted [safe] on NFS filesystems where it may not be, since +** other systems can access the same filesystem and do file giveaway; +** only the NFS server knows for sure!) Hence, it is important to +** get the value of SAFENFSPATHCONF correct -- it should be defined +** _only_ after testing (see test/t_pathconf.c) a system on an unsafe +** NFS-based filesystem to ensure that you can get meaningful results. +** If in doubt, assume unsafe! +** +** You may also need to tweak IS_SAFE_CHOWN -- it should be a +** condition indicating whether the return from pathconf indicates +** that chown is safe (typically either > 0 or >= 0 -- there isn't +** even any agreement about whether a zero return means that a file +** is or is not safe). It defaults to "> 0". +** +** If the parent directory is safe (writable only by owner back +** to the root) then we can relax slightly and trust fpathconf +** in more circumstances. This is really a crock -- if this is an +** NFS mounted filesystem then we really know nothing about the +** underlying implementation. However, most systems pessimize and +** return an error (EINVAL or EOPNOTSUPP) on NFS filesystems, which +** we interpret as unsafe, as we should. Thus, this heuristic gets +** us into a possible problem only on systems that have a broken +** pathconf implementation and which are also poorly configured +** (have :include: files in group- or world-writable directories). +** ** Parameters: ** fd -- the file descriptor to check. +** safedir -- set if the parent directory is safe. ** ** Returns: -** TRUE -- if only root can chown the file to an arbitrary -** user. +** TRUE -- if the chown(2) operation is "safe" -- that is, +** only root can chown the file to an arbitrary user. ** FALSE -- if an arbitrary user can give away a file. */ +#ifndef IS_SAFE_CHOWN +# define IS_SAFE_CHOWN > 0 +#endif + bool -chownsafe(fd) +chownsafe(fd, safedir) int fd; + bool safedir; { -#ifdef __hpux - char *s; - int tfd; - uid_t o_uid, o_euid; - gid_t o_gid, o_egid; - bool rval; - struct stat stbuf; - - o_uid = getuid(); - o_euid = geteuid(); - o_gid = getgid(); - o_egid = getegid(); - fstat(fd, &stbuf); - setresuid(stbuf.st_uid, stbuf.st_uid, -1); - setresgid(stbuf.st_gid, stbuf.st_gid, -1); - s = tmpnam(NULL); - tfd = open(s, O_RDONLY|O_CREAT, 0600); - rval = fchown(tfd, DefUid, DefGid) != 0; - close(tfd); - setresuid(o_uid, o_euid, -1); - setresgid(o_gid, o_egid, -1); - unlink(s); - return rval; -#else -# ifdef _POSIX_CHOWN_RESTRICTED -# if _POSIX_CHOWN_RESTRICTED == -1 - return FALSE; -# else - return TRUE; -# endif -# else -# ifdef _PC_CHOWN_RESTRICTED +#if !defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1 +# if defined(_PC_CHOWN_RESTRICTED) int rval; + /* give the system administrator a chance to override */ + if (ChownAlwaysSafe) + return TRUE; + /* ** Some systems (e.g., SunOS) seem to have the call and the ** #define _PC_CHOWN_RESTRICTED, but don't actually implement ** the call. This heuristic checks for that. */ errno = 0; rval = fpathconf(fd, _PC_CHOWN_RESTRICTED); - if (errno == 0) - return rval > 0; -# endif -# ifdef BSD - return TRUE; +# if SAFENFSPATHCONF + return errno == 0 && rval IS_SAFE_CHOWN; # else - return FALSE; + return safedir && errno == 0 && rval IS_SAFE_CHOWN; # endif # endif +#else + return ChownAlwaysSafe; #endif } /* ** RESETLIMITS -- reset system controlled resource limits ** ** This is to avoid denial-of-service attacks ** ** Parameters: ** none ** ** Returns: ** none */ #if HASSETRLIMIT # ifdef RLIMIT_NEEDS_SYS_TIME_H # include # endif # include #endif #ifndef FD_SETSIZE # define FD_SETSIZE 256 #endif void resetlimits() { #if HASSETRLIMIT struct rlimit lim; lim.rlim_cur = lim.rlim_max = RLIM_INFINITY; (void) setrlimit(RLIMIT_CPU, &lim); (void) setrlimit(RLIMIT_FSIZE, &lim); # ifdef RLIMIT_NOFILE lim.rlim_cur = lim.rlim_max = FD_SETSIZE; (void) setrlimit(RLIMIT_NOFILE, &lim); # endif #else # if HASULIMIT (void) ulimit(2, 0x3fffff); (void) ulimit(4, FD_SETSIZE); # endif #endif errno = 0; } /* ** GETCFNAME -- return the name of the .cf file. ** ** Some systems (e.g., NeXT) determine this dynamically. */ char * getcfname() { if (ConfFile != NULL) return ConfFile; #if NETINFO { extern char *ni_propval(); char *cflocation; cflocation = ni_propval("/locations", NULL, "sendmail", "sendmail.cf", '\0'); if (cflocation != NULL) return cflocation; } #endif return _PATH_SENDMAILCF; } /* ** SETVENDOR -- process vendor code from V configuration line ** ** Parameters: ** vendor -- string representation of vendor. ** ** Returns: ** TRUE -- if ok. ** FALSE -- if vendor code could not be processed. ** ** Side Effects: ** It is reasonable to set mode flags here to tweak ** processing in other parts of the code if necessary. ** For example, if you are a vendor that uses $%y to ** indicate YP lookups, you could enable that here. */ bool setvendor(vendor) char *vendor; { if (strcasecmp(vendor, "Berkeley") == 0) { VendorCode = VENDOR_BERKELEY; return TRUE; } /* add vendor extensions here */ #ifdef SUN_EXTENSIONS if (strcasecmp(vendor, "Sun") == 0) { VendorCode = VENDOR_SUN; return TRUE; } #endif return FALSE; } /* ** VENDOR_PRE_DEFAULTS, VENDOR_POST_DEFAULTS -- set vendor-specific defaults ** ** Vendor_pre_defaults is called before reading the configuration ** file; vendor_post_defaults is called immediately after. ** ** Parameters: ** e -- the global environment to initialize. ** ** Returns: ** none. */ #if SHARE_V1 int DefShareUid; /* default share uid to run as -- unused??? */ #endif void vendor_pre_defaults(e) ENVELOPE *e; { #if SHARE_V1 /* OTHERUID is defined in shares.h, do not be alarmed */ DefShareUid = OTHERUID; #endif #ifdef SUN_EXTENSIONS sun_pre_defaults(e); #endif #ifdef apollo /* stupid domain/os can't even open /etc/sendmail.cf without this */ setuserenv("ISP", NULL); setuserenv("SYSTYPE", NULL); #endif } void vendor_post_defaults(e) ENVELOPE *e; { #ifdef SUN_EXTENSIONS sun_post_defaults(e); #endif } /* ** VENDOR_DAEMON_SETUP -- special vendor setup needed for daemon mode */ void vendor_daemon_setup(e) ENVELOPE *e; { #if SECUREWARE if (getluid() != -1) { usrerr("Daemon cannot have LUID"); exit(EX_USAGE); } #endif /* SECUREWARE */ } /* ** VENDOR_SET_UID -- do setup for setting a user id ** ** This is called when we are still root. ** ** Parameters: ** uid -- the uid we are about to become. ** ** Returns: ** none. */ void vendor_set_uid(uid) UID_T uid; { /* ** We need to setup the share groups (lnodes) ** and and auditing inforation (luid's) ** before we loose our ``root''ness. */ #if SHARE_V1 if (setupshares(uid, syserr) != 0) syserr("Unable to set up shares"); #endif #if SECUREWARE (void) setup_secure(uid); #endif } /* ** VALIDATE_CONNECTION -- check connection for rationality ** ** If the connection is rejected, this routine should log an ** appropriate message -- but should never issue any SMTP protocol. ** ** Parameters: ** sap -- a pointer to a SOCKADDR naming the peer. ** hostname -- the name corresponding to sap. ** e -- the current envelope. ** ** Returns: ** TRUE -- if the connection should be accepted. ** FALSE -- if it should be rejected. */ #if TCPWRAPPERS # include /* tcpwrappers does no logging, but you still have to declare these -- ugh */ int allow_severity = LOG_INFO; int deny_severity = LOG_NOTICE; #endif #if DAEMON bool validate_connection(sap, hostname, e) SOCKADDR *sap; char *hostname; ENVELOPE *e; { if (rscheck("check_relay", hostname, anynet_ntoa(sap), e) != EX_OK) return FALSE; #if TCPWRAPPERS if (!hosts_ctl("sendmail", hostname, anynet_ntoa(sap), STRING_UNKNOWN)) { -# ifdef LOG if (LogLevel >= 4) - syslog(LOG_NOTICE, "tcpwrappers (%s, %s) rejection", + sm_syslog(LOG_NOTICE, NOQID, + "tcpwrappers (%s, %s) rejection", hostname, anynet_ntoa(sap)); -# endif return FALSE; } #endif return TRUE; } #endif /* ** STRTOL -- convert string to long integer ** ** For systems that don't have it in the C library. ** ** This is taken verbatim from the 4.4-Lite C library. */ #ifdef NEEDSTRTOL #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include /* * Convert a string to a long integer. * * Ignores `locale' stuff. Assumes that the upper and lower case * alphabets and digits are each contiguous. */ long strtol(nptr, endptr, base) const char *nptr; char **endptr; register int base; { register const char *s = nptr; register unsigned long acc; register int c; register unsigned long cutoff; register int neg = 0, any, cutlim; /* * Skip white space and pick up leading +/- sign if any. * If base is 0, allow 0x for hex and 0 for octal, else * assume decimal; if base is already 16, allow 0x. */ do { c = *s++; } while (isspace(c)); if (c == '-') { neg = 1; c = *s++; } else if (c == '+') c = *s++; if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { c = s[1]; s += 2; base = 16; } if (base == 0) base = c == '0' ? 8 : 10; /* * Compute the cutoff value between legal numbers and illegal * numbers. That is the largest legal value, divided by the * base. An input number that is greater than this value, if * followed by a legal input character, is too big. One that * is equal to this value may be valid or not; the limit * between valid and invalid numbers is then based on the last * digit. For instance, if the range for longs is * [-2147483648..2147483647] and the input base is 10, * cutoff will be set to 214748364 and cutlim to either * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated * a value > 214748364, or equal but the next digit is > 7 (or 8), * the number is too big, and we will return a range error. * * Set any if any `digits' consumed; make it negative to indicate * overflow. */ cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; cutlim = cutoff % (unsigned long)base; cutoff /= (unsigned long)base; for (acc = 0, any = 0;; c = *s++) { if (isdigit(c)) c -= '0'; else if (isalpha(c)) c -= isupper(c) ? 'A' - 10 : 'a' - 10; else break; if (c >= base) break; if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim) any = -1; else { any = 1; acc *= base; acc += c; } } if (any < 0) { acc = neg ? LONG_MIN : LONG_MAX; errno = ERANGE; } else if (neg) acc = -acc; if (endptr != 0) *endptr = (char *)(any ? s - 1 : nptr); return (acc); } #endif /* ** STRSTR -- find first substring in string ** ** Parameters: ** big -- the big (full) string. ** little -- the little (sub) string. ** ** Returns: ** A pointer to the first instance of little in big. ** big if little is the null string. ** NULL if little is not contained in big. */ #ifdef NEEDSTRSTR char * strstr(big, little) char *big; char *little; { register char *p = big; int l; if (*little == '\0') return big; l = strlen(little); while ((p = strchr(p, *little)) != NULL) { if (strncmp(p, little, l) == 0) return p; p++; } return NULL; } #endif /* ** SM_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX ** ** Some operating systems have wierd problems with the gethostbyXXX ** routines. For example, Solaris versions at least through 2.3 ** don't properly deliver a canonical h_name field. This tries to ** work around these problems. */ struct hostent * sm_gethostbyname(name) char *name; { struct hostent *h; #if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) # if SOLARIS == 20300 || SOLARIS == 203 static struct hostent hp; static char buf[1000]; extern struct hostent *_switch_gethostbyname_r(); if (tTd(61, 10)) printf("_switch_gethostbyname_r(%s)... ", name); h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno); # else extern struct hostent *__switch_gethostbyname(); if (tTd(61, 10)) printf("__switch_gethostbyname(%s)... ", name); h = __switch_gethostbyname(name); # endif #else int nmaps; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; char hbuf[MAXNAME]; if (tTd(61, 10)) printf("gethostbyname(%s)... ", name); h = gethostbyname(name); if (h == NULL) { if (tTd(61, 10)) printf("failure\n"); nmaps = switch_map_find("hosts", maptype, mapreturn); while (--nmaps >= 0) if (strcmp(maptype[nmaps], "nis") == 0 || strcmp(maptype[nmaps], "files") == 0) break; if (nmaps >= 0) { /* try short name */ if (strlen(name) > (SIZE_T) sizeof hbuf - 1) return NULL; strcpy(hbuf, name); shorten_hostname(hbuf); /* if it hasn't been shortened, there's no point */ if (strcmp(hbuf, name) != 0) { if (tTd(61, 10)) printf("gethostbyname(%s)... ", hbuf); h = gethostbyname(hbuf); } } } #endif if (tTd(61, 10)) { if (h == NULL) printf("failure\n"); else printf("%s\n", h->h_name); } return h; } struct hostent * sm_gethostbyaddr(addr, len, type) char *addr; int len; int type; { #if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) # if SOLARIS == 20300 || SOLARIS == 203 static struct hostent hp; static char buf[1000]; extern struct hostent *_switch_gethostbyaddr_r(); return _switch_gethostbyaddr_r(addr, len, type, &hp, buf, sizeof(buf), &h_errno); # else extern struct hostent *__switch_gethostbyaddr(); return __switch_gethostbyaddr(addr, len, type); # endif #else return gethostbyaddr(addr, len, type); #endif } /* ** SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid */ struct passwd * sm_getpwnam(user) char *user; { #ifdef _AIX4 extern struct passwd *_getpwnam_shadow(const char *, const int); return _getpwnam_shadow(user, 0); #else return getpwnam(user); #endif } struct passwd * sm_getpwuid(uid) UID_T uid; { #if defined(_AIX4) && 0 extern struct passwd *_getpwuid_shadow(const int, const int); return _getpwuid_shadow(uid,0); #else return getpwuid(uid); #endif } /* ** SECUREWARE_SETUP_SECURE -- Convex SecureWare setup ** ** Set up the trusted computing environment for C2 level security ** under SecureWare. ** ** Parameters: ** uid -- uid of the user to initialize in the TCB ** ** Returns: ** none ** ** Side Effects: ** Initialized the user in the trusted computing base */ #if SECUREWARE # include # include void secureware_setup_secure(uid) UID_T uid; { int rc; if (getluid() != -1) return; if ((rc = set_secure_info(uid)) != SSI_GOOD_RETURN) { switch (rc) { case SSI_NO_PRPW_ENTRY: syserr("No protected passwd entry, uid = %d", uid); break; case SSI_LOCKED: syserr("Account has been disabled, uid = %d", uid); break; case SSI_RETIRED: syserr("Account has been retired, uid = %d", uid); break; case SSI_BAD_SET_LUID: syserr("Could not set LUID, uid = %d", uid); break; case SSI_BAD_SET_PRIVS: syserr("Could not set kernel privs, uid = %d", uid); default: syserr("Unknown return code (%d) from set_secure_info(%d)", rc, uid); break; } exit(EX_NOPERM); } } #endif /* SECUREWARE */ /* ** LOAD_IF_NAMES -- load interface-specific names into $=w ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Loads $=w with the names of all the interfaces. */ -#ifdef SIOCGIFCONF +#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN struct rtentry; struct mbuf; # include # ifndef SUNOS403 # include # endif # include #endif void load_if_names() { -#ifdef SIOCGIFCONF +#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN int s; int i; - struct ifconf ifc; - char interfacebuf[10240]; + struct ifconf ifc; + int numifs; s = socket(AF_INET, SOCK_DGRAM, 0); if (s == -1) return; /* get the list of known IP address from the kernel */ - ifc.ifc_buf = interfacebuf; - ifc.ifc_len = sizeof interfacebuf; +# ifdef SIOCGIFNUM + if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0) + { + /* can't get number of interfaces -- fall back */ + if (tTd(0, 4)) + printf("SIOCGIFNUM failed: %s\n", errstring(errno)); + numifs = -1; + } + else if (tTd(0, 42)) + printf("system has %d interfaces\n", numifs); + if (numifs < 0) +# endif + numifs = 512; + + if (numifs <= 0) + { + close(s); + return; + } + ifc.ifc_len = numifs * sizeof (struct ifreq); + ifc.ifc_buf = xalloc(ifc.ifc_len); if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) { if (tTd(0, 4)) printf("SIOGIFCONF failed: %s\n", errstring(errno)); close(s); return; } /* scan the list of IP address */ if (tTd(0, 40)) printf("scanning for interface specific names, ifc_len=%d\n", ifc.ifc_len); for (i = 0; i < ifc.ifc_len; ) { struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i]; struct sockaddr *sa = &ifr->ifr_addr; struct in_addr ia; struct hostent *hp; #ifdef SIOCGIFFLAGS struct ifreq ifrf; #endif char ip_addr[256]; extern char *inet_ntoa(); - extern struct hostent *gethostbyaddr(); #ifdef BSD4_4_SOCKADDR if (sa->sa_len > sizeof ifr->ifr_addr) i += sizeof ifr->ifr_name + sa->sa_len; else #endif i += sizeof *ifr; if (tTd(0, 20)) printf("%s\n", anynet_ntoa((SOCKADDR *) sa)); if (ifr->ifr_addr.sa_family != AF_INET) continue; #ifdef SIOCGIFFLAGS bzero(&ifrf, sizeof(struct ifreq)); strncpy(ifrf.ifr_name, ifr->ifr_name, sizeof(ifrf.ifr_name)); ioctl(s, SIOCGIFFLAGS, (char *) &ifrf); if (tTd(0, 41)) printf("\tflags: %x\n", ifrf.ifr_flags); - if (!bitset(IFF_UP, ifrf.ifr_flags)) - continue; +# define IFRFREF ifrf #else - if (!bitset(IFF_UP, ifr->ifr_flags)) - continue; +# define IFRFREF (*ifr) #endif + if (!bitset(IFF_UP, IFRFREF.ifr_flags)) + continue; /* extract IP address from the list*/ ia = (((struct sockaddr_in *) sa)->sin_addr); if (ia.s_addr == INADDR_ANY || ia.s_addr == INADDR_NONE) { message("WARNING: interface %s is UP with %s address", ifr->ifr_name, inet_ntoa(ia)); continue; } /* save IP address in text from */ (void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]", sizeof ip_addr - 3, inet_ntoa(ia)); if (!wordinclass(ip_addr, 'w')) { setclass('w', ip_addr); if (tTd(0, 4)) printf("\ta.k.a.: %s\n", ip_addr); } /* skip "loopback" interface "lo" */ - if (strcmp("lo0", ifr->ifr_name) == 0) + if (bitset(IFF_LOOPBACK, IFRFREF.ifr_flags)) continue; /* lookup name with IP address */ hp = sm_gethostbyaddr((char *) &ia, sizeof(ia), AF_INET); if (hp == NULL) { -#ifdef LOG if (LogLevel > 3) - syslog(LOG_WARNING, - "gethostbyaddr() failed for %.100s\n", - inet_ntoa(ia)); + sm_syslog(LOG_WARNING, NOQID, + "gethostbyaddr(%.100s) failed: %d\n", + inet_ntoa(ia), +#if NAMED_BIND + h_errno); +#else + -1); #endif continue; } /* save its cname */ if (!wordinclass((char *) hp->h_name, 'w')) { setclass('w', (char *) hp->h_name); if (tTd(0, 4)) printf("\ta.k.a.: %s\n", hp->h_name); } /* save all it aliases name */ while (*hp->h_aliases) { if (!wordinclass(*hp->h_aliases, 'w')) { setclass('w', *hp->h_aliases); if (tTd(0, 4)) printf("\ta.k.a.: %s\n", *hp->h_aliases); } hp->h_aliases++; } } + free(ifc.ifc_buf); close(s); +# undef IFRFREF #endif } /* +** GET_NUM_PROCS_ONLINE -- return the number of processors currently online +** +** Parameters: +** none. +** +** Returns: +** The number of processors online. +*/ + +int +get_num_procs_online() +{ + int nproc = 0; + +#if _FFR_SCALE_LA_BY_NUM_PROCS +#ifdef _SC_NPROCESSORS_ONLN + nproc = (int) sysconf(_SC_NPROCESSORS_ONLN); +#endif +#endif + if (nproc <= 0) + nproc = 1; + return nproc; +} + /* +** SM_SYSLOG -- syslog wrapper to keep messages under SYSLOG_BUFSIZE +** +** Parameters: +** level -- syslog level +** id -- envelope ID or NULL (NOQUEUE) +** fmt -- format string +** arg... -- arguments as implied by fmt. +** +** Returns: +** none +*/ + +/* VARARGS3 */ +void +# ifdef __STDC__ +sm_syslog(int level, const char *id, const char *fmt, ...) +# else +sm_syslog(level, id, fmt, va_alist) + int level; + const char *id; + const char *fmt; + va_dcl +#endif +{ + static char *buf = NULL; + static size_t bufsize = MAXLINE; + char *begin, *end; + int seq = 1; + int idlen; + extern int SnprfOverflow; + VA_LOCAL_DECL + + SyslogErrno = errno; + if (id == NULL) + { + id = "NOQUEUE"; + idlen = 9; + } + else if (strcmp(id, NOQID) == 0) + { + id = ""; + idlen = 0; + } + else + idlen = strlen(id + 2); +bufalloc: + if (buf == NULL) + buf = (char *) xalloc(sizeof(char) * bufsize); + + /* do a virtual vsnprintf into buf */ + VA_START(fmt); + buf[0] = 0; + DoprEnd = buf + bufsize - 1; + SnprfOverflow = 0; + sm_dopr(buf, fmt, ap); + *DoprEnd = '\0'; + VA_END; + /* end of virtual vsnprintf */ + + if (SnprfOverflow) + { + /* String too small, redo with correct size */ + bufsize += SnprfOverflow + 1; + free(buf); + buf = NULL; + goto bufalloc; + } + if ((strlen(buf) + idlen + 1) < SYSLOG_BUFSIZE) + { +#if LOG + if (*id == '\0') + syslog(level, "%s", buf); + else + syslog(level, "%s: %s", id, buf); +#else + /*XXX should do something more sensible */ + if (*id == '\0') + fprintf(stderr, "%s\n", buf); + else + fprintf(stderr, "%s: %s\n", id, buf); +#endif + return; + } + + begin = buf; + while (*begin != '\0' && + (strlen(begin) + idlen + 5) > SYSLOG_BUFSIZE) + { + char save; + + if (seq == 999) + { + /* Too many messages */ + break; + } + end = begin + SYSLOG_BUFSIZE - idlen - 12; + while (end > begin) + { + /* Break on comma or space */ + if (*end == ',' || *end == ' ') + { + end++; /* Include separator */ + break; + } + end--; + } + /* No separator, break midstring... */ + if (end == begin) + end = begin + SYSLOG_BUFSIZE - idlen - 12; + save = *end; + *end = 0; +#if LOG + syslog(level, "%s[%d]: %s ...", id, seq++, begin); +#else + fprintf(stderr, "%s[%d]: %s ...\n", id, seq++, begin); +#endif + *end = save; + begin = end; + } + if (seq == 999) +#if LOG + syslog(level, "%s[%d]: log terminated, too many parts", id, seq); +#else + fprintf(stderr, "%s[%d]: log terminated, too many parts\n", id, seq); +#endif + else if (*begin != '\0') +#if LOG + syslog(level, "%s[%d]: %s", id, seq, begin); +#else + fprintf(stderr, "%s[%d]: %s\n", id, seq, begin); +#endif +} + /* ** HARD_SYSLOG -- call syslog repeatedly until it works ** ** Needed on HP-UX, which apparently doesn't guarantee that ** syslog succeeds during interrupt handlers. */ #ifdef __hpux # define MAXSYSLOGTRIES 100 # undef syslog # ifdef V4FS # define XCNST const # define CAST (const char *) # else # define XCNST # define CAST # endif void # ifdef __STDC__ hard_syslog(int pri, XCNST char *msg, ...) # else hard_syslog(pri, msg, va_alist) int pri; XCNST char *msg; va_dcl # endif { int i; - char buf[SYSLOG_BUFSIZE * 2]; + char buf[SYSLOG_BUFSIZE]; VA_LOCAL_DECL; VA_START(msg); vsnprintf(buf, sizeof buf, msg, ap); VA_END; for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; ) continue; } # undef CAST #endif /* ** LOCAL_HOSTNAME_LENGTH ** ** This is required to get sendmail to compile against BIND 4.9.x ** on Ultrix. */ #if defined(ultrix) && NAMED_BIND # include # if __RES >= 19931104 && __RES < 19950621 int local_hostname_length(hostname) char *hostname; { int len_host, len_domain; if (!*_res.defdname) res_init(); len_host = strlen(hostname); len_domain = strlen(_res.defdname); if (len_host > len_domain && (strcasecmp(hostname + len_host - len_domain,_res.defdname) == 0) && hostname[len_host - len_domain - 1] == '.') return len_host - len_domain - 1; else return 0; } # endif #endif /* ** Compile-Time options */ char *CompileOptions[] = { #if HESIOD "HESIOD", #endif #if HES_GETMAILHOST "HES_GETMAILHOST", #endif #if LDAPMAP "LDAPMAP", #endif -#ifdef LOG +#if LOG "LOG", #endif #if MATCHGECOS "MATCHGECOS", #endif #if MIME7TO8 "MIME7TO8", #endif #if MIME8TO7 "MIME8TO7", #endif #if NAMED_BIND "NAMED_BIND", #endif #if NDBM "NDBM", #endif #if NETINET "NETINET", #endif #if NETINFO "NETINFO", #endif #if NETISO "NETISO", #endif #if NETNS "NETNS", #endif #if NETUNIX "NETUNIX", #endif #if NETX25 "NETX25", #endif #if NEWDB "NEWDB", #endif #if NIS "NIS", #endif #if NISPLUS "NISPLUS", #endif #if QUEUE "QUEUE", #endif #if SCANF "SCANF", #endif #if SMTP "SMTP", #endif #if SMTPDEBUG "SMTPDEBUG", #endif #if SUID_ROOT_FILES_OK "SUID_ROOT_FILES_OK", #endif #if TCPWRAPPERS "TCPWRAPPERS", #endif #if USERDB "USERDB", #endif #if XDEBUG "XDEBUG", #endif #if XLA "XLA", #endif NULL }; /* ** OS compile options. */ char *OsCompileOptions[] = { +#if BOGUS_O_EXCL + "BOGUS_O_EXCL", +#endif #if HASFCHMOD "HASFCHMOD", #endif #if HASFLOCK "HASFLOCK", #endif #if HASGETDTABLESIZE "HASGETDTABLESIZE", #endif #if HASGETUSERSHELL "HASGETUSERSHELL", #endif #if HASINITGROUPS "HASINITGROUPS", #endif #if HASLSTAT "HASLSTAT", #endif #if HASSETREUID "HASSETREUID", #endif #if HASSETRLIMIT "HASSETRLIMIT", #endif #if HASSETSID "HASSETSID", #endif #if HASSETUSERCONTEXT "HASSETUSERCONTEXT", #endif #if HASSETVBUF "HASSETVBUF", #endif #if HASSNPRINTF "HASSNPRINTF", #endif +#if HASSTRERROR + "HASSTRERROR", +#endif #if HASULIMIT "HASULIMIT", #endif #if HASUNAME "HASUNAME", #endif #if HASUNSETENV "HASUNSETENV", #endif #if HASWAITPID "HASWAITPID", #endif #if IDENTPROTO "IDENTPROTO", #endif #if IP_SRCROUTE "IP_SRCROUTE", #endif #if NEEDFSYNC "NEEDFSYNC", #endif #if NOFTRUNCATE "NOFTRUNCATE", #endif #if RLIMIT_NEEDS_SYS_TIME_H "RLIMIT_NEEDS_SYS_TIME_H", #endif +#if SAFENFSPATHCONF + "SAFENFSPATHCONF", +#endif #if SECUREWARE "SECUREWARE", #endif #if SHARE_V1 "SHARE_V1", +#endif +#if SIOCGIFCONF_IS_BROKEN + "SIOCGIFCONF_IS_BROKEN", #endif #if SYS5SETPGRP "SYS5SETPGRP", #endif #if SYSTEM5 "SYSTEM5", #endif #if USE_SA_SIGACTION "USE_SA_SIGACTION", #endif #if USE_SIGLONGJMP "USE_SIGLONGJMP", #endif #if USESETEUID "USESETEUID", #endif NULL }; Index: head/usr.sbin/sendmail/src/conf.h =================================================================== --- head/usr.sbin/sendmail/src/conf.h (revision 26988) +++ head/usr.sbin/sendmail/src/conf.h (revision 26989) @@ -1,2187 +1,2285 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)conf.h 8.288 (Berkeley) 1/17/97 + * @(#)conf.h 8.313 (Berkeley) 6/11/97 */ /* ** CONF.H -- All user-configurable parameters for sendmail ** ** Send updates to sendmail@Sendmail.ORG so they will be ** included in the next release. */ #ifdef __GNUC__ struct rusage; /* forward declaration to get gcc to shut up in wait.h */ #endif # include # include # include # include # include # include # include # include # include # include /********************************************************************** ** Table sizes, etc.... ** There shouldn't be much need to change these.... **********************************************************************/ # define MAXLINE 2048 /* max line length */ # define MAXNAME 256 /* max length of a name */ # define MAXPV 40 /* max # of parms to mailers */ # define MAXATOM 200 /* max atoms per address */ # define MAXMAILERS 25 /* maximum mailers known to system */ # define MAXRWSETS 200 /* max # of sets of rewriting rules */ # define MAXPRIORITIES 25 /* max values for Precedence: field */ # define MAXMXHOSTS 100 /* max # of MX records for one host */ # define SMTPLINELIM 990 /* maximum SMTP line length */ # define MAXKEY 128 /* maximum size of a database key */ # define MEMCHUNKSIZE 1024 /* chunk size for memory allocation */ # define MAXUSERENVIRON 100 /* max envars saved, must be >= 3 */ # define MAXALIASDB 12 /* max # of alias databases */ # define MAXMAPSTACK 12 /* max # of stacked or sequenced maps */ # define MAXTOCLASS 8 /* max # of message timeout classes */ # define MAXMIMEARGS 20 /* max args in Content-Type: */ # define MAXMIMENESTING 20 /* max MIME multipart nesting */ # define QUEUESEGSIZE 1000 /* increment for queue size */ +# define MAXQFNAME 20 /* max qf file name length */ /********************************************************************** ** Compilation options. ** #define these to 1 if they are available; ** #define them to 0 otherwise. ** All can be overridden from Makefile. **********************************************************************/ # ifndef NETINET # define NETINET 1 /* include internet support */ # endif # ifndef NETISO # define NETISO 0 /* do not include ISO socket support */ # endif # ifndef NAMED_BIND # define NAMED_BIND 1 /* use Berkeley Internet Domain Server */ # endif # ifndef XDEBUG # define XDEBUG 1 /* enable extended debugging */ # endif # ifndef MATCHGECOS # define MATCHGECOS 1 /* match user names from gecos field */ # endif # ifndef DSN # define DSN 1 /* include delivery status notification code */ # endif # if !defined(USERDB) && (defined(NEWDB) || defined(HESIOD)) # define USERDB 1 /* look in user database */ # endif # ifndef MIME8TO7 # define MIME8TO7 1 /* 8->7 bit MIME conversions */ # endif # ifndef MIME7TO8 # define MIME7TO8 1 /* 7->8 bit MIME conversions */ # endif /********************************************************************** ** "Hard" compilation options. ** #define these if they are available; comment them out otherwise. ** These cannot be overridden from the Makefile, and should really not ** be turned off unless absolutely necessary. **********************************************************************/ -# define LOG /* enable logging -- don't turn off */ +# define LOG 1 /* enable logging -- don't turn off */ /********************************************************************** ** End of site-specific configuration. **********************************************************************/ /* ** General "standard C" defines. ** ** These may be undone later, to cope with systems that claim to ** be Standard C but aren't. Gcc is the biggest offender -- it ** doesn't realize that the library is part of the language. ** ** Life would be much easier if we could get rid of this sort ** of bozo problems. */ #ifdef __STDC__ # define HASSETVBUF 1 /* we have setvbuf(3) in libc */ #endif /* ** Assume you have standard calls; can be #undefed below if necessary. */ # define HASLSTAT 1 /* has lstat(2) call */ /********************************************************************** ** Operating system configuration. ** ** Unless you are porting to a new OS, you shouldn't have to ** change these. **********************************************************************/ /* ** HP-UX -- tested for 8.07, 9.00, and 9.01. ** ** If V4FS is defined, compile for HP-UX 10.0. */ #ifdef __hpux /* common definitions for HP-UX 9.x and 10.x */ # undef m_flags /* conflict between db.h & sys/sysmacros.h on HP 300 */ # define SYSTEM5 1 /* include all the System V defines */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define USESETEUID 1 /* has useable seteuid(2) call */ +# define BOGUS_O_EXCL 1 /* exclusive open follows symlinks */ # define seteuid(e) setresuid(-1, e, -1) # define IP_SRCROUTE 1 /* can check IP source routing */ # define LA_TYPE LA_HPUX # define SPT_TYPE SPT_PSTAT # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define GIDSET_T gid_t # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ # endif # define syslog hard_syslog +# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */ # ifdef V4FS /* HP-UX 10.x */ # define _PATH_UNIX "/stand/vmunix" # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" # endif # ifndef IDENTPROTO # define IDENTPROTO 1 /* TCP/IP implementation fixed in 10.0 */ # endif # else /* HP-UX 9.x */ # define _PATH_UNIX "/hp-ux" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # ifdef __STDC__ extern void hard_syslog(int, char *, ...); # endif # endif #endif /* ** IBM AIX 4.x */ #ifdef _AIX4 +# include # define _AIX3 1 /* pull in AIX3 stuff */ # define USESETEUID 1 /* seteuid(2) works */ # define TZ_TYPE TZ_NAME /* use tzname[] vector */ # if _AIX4 >= 40200 # define HASSETREUID 1 /* setreuid(2) works as of AIX 4.2 */ # endif #endif /* ** IBM AIX 3.x -- actually tested for 3.2.3 */ #ifdef _AIX3 # include # include /* to get byte order */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ # define GIDSET_T gid_t # define SFS_TYPE SFS_STATFS /* use statfs() impl */ # define SPT_PADCHAR '\0' /* pad process title with nulls */ # define LA_TYPE LA_INT # define FSHIFT 16 # define LA_AVENRUN "avenrun" #endif /* ** IBM AIX 2.2.1 -- actually tested for osupdate level 2706+1773 ** ** From Mark Whetzel . */ #ifdef AIX /* AIX/RT compiler pre-defines this */ # include # include /* AIX/RT resource.h does NOT include this */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define HASFCHMOD 0 /* does not have fchmod(2) syscall */ # define HASSETREUID 1 /* use setreuid(2) -lbsd system call */ # define HASSETVBUF 1 /* use setvbuf(2) system call */ # define HASSETRLIMIT 0 /* does not have setrlimit call */ # define HASFLOCK 0 /* does not have flock call - use fcntl */ # define HASULIMIT 1 /* use ulimit instead of setrlimit call */ # define NEEDGETOPT 1 /* Do we need theirs or ours */ # define SYS5SETPGRP 1 /* don't have setpgid on AIX/RT */ # define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ # define BSD4_3 1 /* NOT bsd 4.4 or posix signals */ # define GIDSET_T int # define SFS_TYPE SFS_STATFS /* use statfs() impl */ # define SPT_PADCHAR '\0' /* pad process title with nulls */ # define LA_TYPE LA_SUBR /* use our ported loadavgd daemon */ # define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ # define ARBPTR_T int * # define void int typedef int pid_t; /* RTisms for BSD compatibility, specified in the Makefile define BSD 1 define BSD_INCLUDES 1 define BSD_REMAP_SIGNAL_TO_SIGVEC RTisms needed above */ /* make this sendmail in a completely different place */ # define _PATH_VENDORCF "/usr/local/newmail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/usr/local/newmail/sendmail.pid" # endif #endif /* ** Silicon Graphics IRIX ** ** Compiles on 4.0.1. ** ** Use IRIX64 instead of IRIX for 64-bit IRIX (6.0). ** Use IRIX5 instead of IRIX for IRIX 5.x. ** ** This version tries to be adaptive using _MIPS_SIM: ** _MIPS_SIM == _ABIO32 (= 1) Abi: -32 on IRIX 6.2 ** _MIPS_SIM == _ABIN32 (= 2) Abi: -n32 on IRIX 6.2 ** _MIPS_SIM == _ABI64 (= 3) Abi: -64 on IRIX 6.2 ** ** _MIPS_SIM is 1 also on IRIX 5.3 ** ** IRIX64 changes from Mark R. Levinson . ** IRIX5 changes from Kari E. Hurtta . ** Adaptive changes from Kari E. Hurtta . */ #if defined(__sgi) # ifndef IRIX # define IRIX # endif # if _MIPS_SIM > 0 && !defined(IRIX5) # define IRIX5 /* IRIX5 or IRIX6 */ # endif # if _MIPS_SIM > 1 && !defined(IRIX6) && !defined(IRIX64) # define IRIX6 /* IRIX6 */ # endif #endif #ifdef IRIX # define SYSTEM5 1 /* this is a System-V derived system */ # define HASSETREUID 1 /* has setreuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define IP_SRCROUTE 1 /* can check IP source routing */ # define setpgid BSDsetpgrp # define GIDSET_T gid_t # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ # define SFS_BAVAIL f_bfree /* alternate field name */ # ifdef IRIX6 # define LA_TYPE LA_IRIX6 /* figure out at run time */ +# define SAFENFSPATHCONF 0 /* pathconf(2) lies on NFS filesystems */ # else # define LA_TYPE LA_INT # ifdef IRIX64 # define NAMELISTMASK 0x7fffffffffffffff /* mask for nlist() values */ # else # define NAMELISTMASK 0x7fffffff /* mask for nlist() values */ # endif # endif # if defined(IRIX64) || defined(IRIX5) # include # include # define ARGV_T char *const * # define HASSETRLIMIT 1 /* has setrlimit(2) syscall */ # define HASGETDTABLESIZE 1 /* has getdtablesize(2) syscall */ # else # define ARGV_T const char ** # define WAITUNION 1 /* use "union wait" as wait argument type */ # endif #endif /* ** SunOS and Solaris ** ** Tested on SunOS 4.1.x (a.k.a. Solaris 1.1.x) and ** Solaris 2.4 (a.k.a. SunOS 5.4). */ #if defined(sun) && !defined(BSD) # include # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define IP_SRCROUTE 1 /* can check IP source routing */ +# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */ # ifdef SOLARIS_2_3 # define SOLARIS 20300 /* for back compat only -- use -DSOLARIS=20300 */ # endif # if defined(NOT_SENDMAIL) && !defined(SOLARIS) && defined(sun) && (defined(__svr4__) || defined(__SVR4)) # define SOLARIS 1 /* unknown Solaris version */ # endif # ifdef SOLARIS /* Solaris 2.x (a.k.a. SunOS 5.x) */ # ifndef __svr4__ # define __svr4__ /* use all System V Releae 4 defines below */ # endif # define GIDSET_T gid_t # define USE_SA_SIGACTION 1 /* use sa_sigaction field */ # ifndef _PATH_UNIX # define _PATH_UNIX "/dev/ksyms" # endif # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" # endif # ifndef _PATH_HOSTS # define _PATH_HOSTS "/etc/inet/hosts" # endif # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */ # endif +# ifndef TZ_TYPE +# define TZ_TYPE TZ_TZNAME +# endif # if SOLARIS >= 20300 || (SOLARIS < 10000 && SOLARIS >= 203) # define USESETEUID 1 /* seteuid works as of 2.3 */ # endif # if SOLARIS >= 20500 || (SOLARIS < 10000 && SOLARIS >= 205) # define HASSETREUID 1 /* setreuid works as of 2.5 */ # ifndef LA_TYPE # define LA_TYPE LA_KSTAT /* use kstat(3k) -- may work in < 2.5 */ # endif # endif # if SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) # define HASSNPRINTF 1 /* has snprintf starting in 2.6 */ # endif # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ # endif # else /* SunOS 4.0.3 or 4.1.x */ # define HASSETREUID 1 /* has setreuid(2) call */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ # include # ifdef SUNOS403 /* special tweaking for SunOS 4.0.3 */ # include # define BSD4_3 1 /* 4.3 BSD-based */ # define NEEDSTRSTR 1 /* need emulation of strstr(3) routine */ # define WAITUNION 1 /* use "union wait" as wait argument type */ # undef WIFEXITED # undef WEXITSTATUS # undef HASUNAME # define setpgid setpgrp # define MODE_T int typedef int pid_t; extern char *getenv(); # else /* 4.1.x specifics */ # define HASSETSID 1 /* has Posix setsid(2) call */ # define HASSETVBUF 1 /* we have setvbuf(3) in libc */ # endif # endif # ifndef LA_TYPE # define LA_TYPE LA_INT # endif #endif /* sun && !BSD */ /* ** DG/UX ** ** Tested on 5.4.2 and 5.4.3. Use DGUX_5_4_2 to get the ** older support. ** 5.4.3 changes from Mark T. Robinson . */ #ifdef DGUX_5_4_2 # define DGUX 1 #endif #ifdef DGUX # define SYSTEM5 1 # define LA_TYPE LA_DGUX # define HASSETREUID 1 /* has setreuid(2) call */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASSETSID 1 /* has Posix setsid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define IP_SRCROUTE 0 /* does not have */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) */ # define HASSNPRINTF 1 /* has snprintf(3) */ # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # define SPT_TYPE SPT_NONE /* don't use setproctitle */ # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ /* these include files must be included early on DG/UX */ # include # include /* compiler doesn't understand const? */ # define const # ifdef DGUX_5_4_2 # define inet_addr dgux_inet_addr extern long dgux_inet_addr(); # endif #endif /* ** Digital Ultrix 4.2A or 4.3 ** ** Apparently, fcntl locking is broken on 4.2A, in that locks are ** not dropped when the process exits. This causes major problems, ** so flock is the only alternative. */ #ifdef ultrix # define HASSETREUID 1 /* has setreuid(2) call */ # define HASUNSETENV 1 /* has unsetenv(3) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # ifndef BROKEN_RES_SEARCH # define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */ # endif # ifdef vax # define LA_TYPE LA_FLOAT # else # define LA_TYPE LA_INT # define LA_AVENRUN "avenrun" # endif # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # ifndef IDENTPROTO # define IDENTPROTO 0 /* pre-4.4 TCP/IP implementation is broken */ # endif +# define SYSLOG_BUFSIZE 256 #endif /* ** OSF/1 for KSR. ** ** Contributed by Todd C. Miller */ #ifdef __ksr__ # define __osf__ 1 /* get OSF/1 defines below */ # ifndef TZ_TYPE # define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ # endif #endif /* ** OSF/1 for Intel Paragon. ** ** Contributed by Jeff A. Earickson ** of Intel Scalable Systems Divison. */ #ifdef __PARAGON__ # define __osf__ 1 /* get OSF/1 defines below */ # ifndef TZ_TYPE # define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ # endif +# define GIDSET_T gid_t +# define MAXNAMLEN NAME_MAX #endif /* ** OSF/1 (tested on Alpha) -- now known as Digital UNIX. ** ** Tested for 3.2 and 4.0. */ #ifdef __osf__ # define HASUNSETENV 1 /* has unsetenv(3) call */ # define USESETEUID 1 /* has useable seteuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define IP_SRCROUTE 1 /* can check IP source routing */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define LA_TYPE LA_ALPHAOSF # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # define _PATH_VENDOR_CF "/var/adm/sendmail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/var/run/sendmail.pid" # endif #endif /* ** NeXTstep */ #ifdef NeXT # define HASINITGROUPS 1 /* has initgroups(3) call */ # define NEEDPUTENV 2 /* need putenv(3) call; no setenv(3) call */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define WAITUNION 1 /* use "union wait" as wait argument type */ # define UID_T int /* compiler gripes on uid_t */ # define GID_T int /* ditto for gid_t */ # define MODE_T int /* and mode_t */ -# define sleep sleepX # define setpgid setpgrp +# ifndef NOT_SENDMAIL +# define sleep sleepX +# endif # ifndef LA_TYPE # define LA_TYPE LA_MACH # endif # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # ifndef _POSIX_SOURCE typedef int pid_t; # undef WEXITSTATUS # undef WIFEXITED # endif # define _PATH_VENDOR_CF "/etc/sendmail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail/sendmail.pid" # endif #endif /* ** 4.4 BSD ** ** See also BSD defines. */ -#if defined(BSD4_4) && !defined(__bsdi__) +#if defined(BSD4_4) && !defined(__bsdi__) && !defined(__GNU__) # include # define HASUNSETENV 1 /* has unsetenv(3) call */ # define USESETEUID 1 /* has useable seteuid(2) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ +# define HASSTRERROR 1 /* has strerror(3) */ # include # define ERRLIST_PREDEFINED /* don't declare sys_errlist */ # define BSD4_4_SOCKADDR /* has sa_len */ # define NETLINK 1 /* supports AF_LINK */ # ifndef LA_TYPE # define LA_TYPE LA_SUBR # endif # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # define SPT_TYPE SPT_PSSTRINGS /* use PS_STRINGS pointer */ #endif /* ** BSD/OS (was BSD/386) (all versions) ** From Tony Sanders, BSDI */ #ifdef __bsdi__ # include # define HASUNSETENV 1 /* has the unsetenv(3) call */ # define HASSETSID 1 /* has the setsid(2) POSIX syscall */ # define USESETEUID 1 /* has useable seteuid(2) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # define HASUNAME 1 /* has uname(2) syscall */ +# define HASSTRERROR 1 /* has strerror(3) */ # include # define ERRLIST_PREDEFINED /* don't declare sys_errlist */ # define BSD4_4_SOCKADDR /* has sa_len */ # define NETLINK 1 /* supports AF_LINK */ # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # ifndef LA_TYPE # define LA_TYPE LA_SUBR # endif # define GIDSET_T gid_t # if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199312 /* version 1.1 or later */ # undef SPT_TYPE # define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ # else /* version 1.0 or earlier */ # ifndef OLD_NEWDB # define OLD_NEWDB 1 /* old version of newdb library */ # endif # define SPT_PADCHAR '\0' /* pad process title with nulls */ # endif #endif /* ** FreeBSD / NetBSD / OpenBSD (all architectures, all versions) ** ** 4.3BSD clone, closer to 4.4BSD for FreeBSD 1.x and NetBSD 0.9x ** 4.4BSD-Lite based for FreeBSD 2.x and NetBSD 1.x ** ** See also BSD defines. */ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) # include # define HASUNSETENV 1 /* has unsetenv(3) call */ # define HASSETSID 1 /* has the setsid(2) POSIX syscall */ # define USESETEUID 1 /* has useable seteuid(2) call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # define HASUNAME 1 /* has uname(2) syscall */ +# define HASSTRERROR 1 /* has strerror(3) */ +# include # define ERRLIST_PREDEFINED /* don't declare sys_errlist */ # define BSD4_4_SOCKADDR /* has sa_len */ # define NETLINK 1 /* supports AF_LINK */ +# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */ # define GIDSET_T gid_t # ifndef LA_TYPE # define LA_TYPE LA_SUBR # endif # define SFS_TYPE SFS_MOUNT /* use statfs() impl */ # if defined(__NetBSD__) && (NetBSD > 199307 || NetBSD0_9 > 1) # undef SPT_TYPE # define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ # endif # if defined(__FreeBSD__) # undef SPT_TYPE # if __FreeBSD__ >= 2 # include /* and this works */ # if __FreeBSD_version >= 199512 /* 2.2-current right now */ # include # define SPT_TYPE SPT_BUILTIN # endif # if __FreeBSD_version >= 300000 /* 3.0-current right now */ # include # define HASSETUSERCONTEXT 1 /* BSDI-style login classes */ # endif # endif # ifndef SPT_TYPE # define SPT_TYPE SPT_REUSEARGV # define SPT_PADCHAR '\0' /* pad process title with nulls */ # endif # endif # if defined(__OpenBSD__) # undef SPT_TYPE # define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */ # endif #endif /* ** Mach386 ** ** For mt Xinu's Mach386 system. */ -#if defined(MACH) && defined(i386) +#if defined(MACH) && defined(i386) && !defined(__GNU__) # define MACH386 1 # define HASUNSETENV 1 /* has unsetenv(3) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define NEEDSTRTOL 1 /* need the strtol() function */ # define setpgid setpgrp # ifndef LA_TYPE # define LA_TYPE LA_FLOAT # endif # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # undef HASSETVBUF /* don't actually have setvbuf(3) */ # undef WEXITSTATUS # undef WIFEXITED # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif #endif + /* +** GNU OS (hurd) +** Largely BSD & posix compatible. +** Port contributed by Miles Bader . +*/ + +#ifdef __GNU_HURD__ +# define SIOCGIFCONF_IS_BROKEN 1 +# define IP_SRCROUTE 0 +# define HASFCHMOD 1 +# define HASFLOCK 1 +# define HASUNAME 1 +# define HASUNSETENV 1 +# define HASSETSID 1 +# define HASINITGROUPS 1 +# define HASSETVBUF 1 +# define HASSETREUID 1 +# define USESETEUID 1 +# define HASLSTAT 1 +# define HASSETRLIMIT 1 +# define HASWAITPID 1 +# define HASGETDTABLESIZE 1 +# define HASSTRERROR 1 +/* # define NEEDGETOPT 1 */ +# define HASGETUSERSHELL 1 +# define ERRLIST_PREDEFINED 1 +# define BSD4_4_SOCKADDR 1 +# define GIDSET_T gid_t +# define LA_TYPE LA_MACH + +/* GNU uses mach[34], which renames some rpcs from mach2.x. */ +# define host_self mach_host_self +# define SFS_TYPE SFS_STATFS +# define SPT_TYPE SPT_CHANGEARGV + +/* GNU has no MAXPATHLEN; ideally the code should be changed to not use it. */ +# define MAXPATHLEN 2048 + +/* Define device num frobbing macros. */ +# define major(x) ((x)>>8) +# define minor(x) ((x)&0xFF) +#endif /* GNU */ + +/* ** 4.3 BSD -- this is for very old systems ** ** Should work for mt Xinu MORE/BSD and Mips UMIPS-BSD 2.1. ** ** You'll also have to install a new resolver library. ** I don't guarantee that support for this environment is complete. */ #if defined(oldBSD43) || defined(MORE_BSD) || defined(umipsbsd) # define NEEDVPRINTF 1 /* need a replacement for vprintf(3) */ # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define ARBPTR_T char * # define setpgid setpgrp # ifndef LA_TYPE # define LA_TYPE LA_FLOAT # endif # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # undef WEXITSTATUS # undef WIFEXITED typedef short pid_t; extern int errno; #endif /* ** SCO Unix ** ** This includes three parts: ** ** The first is for SCO OpenServer 5. ** (Contributed by Keith Reynolds ). ** ** SCO OpenServer 5 has a compiler version number macro, ** which we can use to figure out what version we're on. ** This may have to change in future releases. ** ** The second is for SCO UNIX 3.2v4.2/Open Desktop 3.0. ** (Contributed by Philippe Brand ). ** ** The third is for SCO UNIX 3.2v4.0/Open Desktop 2.0 and earlier. */ /* SCO OpenServer 5 */ #if _SCO_DS >= 1 # include # define _SCO_unix_4_2 # define HASSNPRINTF 1 /* has snprintf(3) call */ # define HASFCHMOD 1 /* has fchmod(2) call */ # define HASSETRLIMIT 1 /* has setrlimit(2) call */ # define USESETEUID 1 /* has seteuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */ # define RLIMIT_NEEDS_SYS_TIME_H 1 # ifndef LA_TYPE # define LA_TYPE LA_DEVSHORT # endif # define _PATH_AVENRUN "/dev/table/avenrun" #endif /* SCO UNIX 3.2v4.2/Open Desktop 3.0 */ #ifdef _SCO_unix_4_2 # define _SCO_unix_ # define HASSETREUID 1 /* has setreuid(2) call */ #endif /* SCO UNIX 3.2v4.0 Open Desktop 2.0 and earlier */ #ifdef _SCO_unix_ # include /* needed for IP_SRCROUTE */ # define SYSTEM5 1 /* include all the System V defines */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define NOFTRUNCATE 0 /* has (simulated) ftruncate call */ # define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */ # define MAXPATHLEN PATHSIZE # define SFS_TYPE SFS_4ARGS /* use 4-arg impl */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define SPT_TYPE SPT_SCO /* write kernel u. area */ # define TZ_TYPE TZ_TM_NAME /* use tm->tm_name */ # define UID_T uid_t # define GID_T gid_t # define GIDSET_T gid_t # define _PATH_UNIX "/unix" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif /* stuff fixed in later releases */ # ifndef _SCO_unix_4_2 # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # endif # ifndef _SCO_DS # define ftruncate chsize /* use chsize(2) to emulate ftruncate */ # define NEEDFSYNC 1 /* needs the fsync(2) call stub */ # define NETUNIX 0 /* no unix domain socket support */ # define LA_TYPE LA_SHORT # endif #endif /* ** ISC (SunSoft) Unix. ** ** Contributed by J.J. Bailey */ #ifdef ISC_UNIX # include # include /* needed for IP_SRCROUTE */ # include # define SYSTEM5 1 /* include all the System V defines */ # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define HASSETREUID 1 /* has setreuid(2) call */ # define NEEDFSYNC 1 /* needs the fsync(2) call stub */ # define NETUNIX 0 /* no unix domain socket support */ # define MAXPATHLEN 1024 # define LA_TYPE LA_SHORT # define SFS_TYPE SFS_STATFS /* use statfs() impl */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define _PATH_UNIX "/unix" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif #endif /* ** Altos System V (5.3.1) ** Contributed by Tim Rice . */ #ifdef ALTOS_SYSTEM_V # include # include # define SYSTEM5 1 /* include all the System V defines */ # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define WAITUNION 1 /* use "union wait" as wait argument type */ # define NEEDFSYNC 1 /* no fsync(2) in system library */ # define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ # define NOFTRUNCATE 1 /* do not have ftruncate(2) */ # define MAXPATHLEN PATH_MAX # define LA_TYPE LA_SHORT # define SFS_TYPE SFS_STATFS /* use statfs() impl */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ # define NETUNIX 0 /* no unix domain socket support */ # undef WIFEXITED # undef WEXITSTATUS # define strtoul strtol /* gcc library bogosity */ typedef unsigned short uid_t; typedef unsigned short gid_t; typedef short pid_t; typedef unsigned long mode_t; /* some stuff that should have been in the include files */ # include extern char *malloc(); extern struct passwd *getpwent(); extern struct passwd *getpwnam(); extern struct passwd *getpwuid(); extern char *getenv(); extern struct group *getgrgid(); extern struct group *getgrnam(); #endif /* ** ConvexOS 11.0 and later ** ** "Todd C. Miller" claims this ** works on 9.1 as well. ** ** ConvexOS 11.5 and later, should work on 11.0 as defined. ** For pre-ConvexOOS 11.0, define NEEDGETOPT, undef IDENTPROTO ** ** Eric Schnoebelen (eric@cirr.com) For CONVEX Computer Corp. ** (now the CONVEX Technologies Center of Hewlett Packard) */ #ifdef _CONVEX_SOURCE # define HASGETDTABLESIZE 1 /* has getdtablesize(2) */ # define HASINITGROUPS 1 /* has initgroups(3) */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASSETSID 1 /* has POSIX setsid(2) call */ # define HASUNSETENV 1 /* has unsetenv(3) */ # define HASFLOCK 1 /* has flock(2) */ # define HASSETRLIMIT 1 /* has setrlimit(2) */ # define HASSETREUID 1 /* has setreuid(2) */ # define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_error=0 */ # define NEEDPUTENV 1 /* needs putenv (written in terms of setenv) */ # define NEEDGETOPT 0 /* need replacement for getopt(3) */ # define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ # define LA_TYPE LA_FLOAT # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef S_IREAD # define S_IREAD _S_IREAD # define S_IWRITE _S_IWRITE # define S_IEXEC _S_IEXEC # define S_IFMT _S_IFMT # define S_IFCHR _S_IFCHR # define S_IFBLK _S_IFBLK # endif # ifndef TZ_TYPE # define TZ_TYPE TZ_TIMEZONE # endif # ifndef IDENTPROTO # define IDENTPROTO 1 # endif # ifndef SHARE_V1 # define SHARE_V1 1 /* version 1 of the fair share scheduler */ # endif # if !defined(__GNUC__ ) # define UID_T int /* GNUC gets it right, ConvexC botches */ # define GID_T int /* GNUC gets it right, ConvexC botches */ # endif # if SECUREWARE # define FORK fork /* SecureWare wants the real fork! */ # else # define FORK vfork /* the rest of the OS versions don't care */ # endif #endif /* ** RISC/os 4.52 ** ** Gives a ton of warning messages, but otherwise compiles. */ #ifdef RISCOS # define HASUNSETENV 1 /* has unsetenv(3) call */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define WAITUNION 1 /* use "union wait" as wait argument type */ # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define NEEDPUTENV 1 /* need putenv(3) call */ # define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define LA_TYPE LA_INT # define LA_AVENRUN "avenrun" # define _PATH_UNIX "/unix" # undef WIFEXITED # define setpgid setpgrp extern int errno; typedef int pid_t; #define SIGFUNC_DEFINED #define SIGFUNC_RETURN (0) #define SIGFUNC_DECL int typedef int (*sigfunc_t)(); extern char *getenv(); extern void *malloc(); # include #endif /* ** Linux 0.99pl10 and above... ** ** Thanks to, in reverse order of contact: ** ** John Kennedy ** Andrew Pam ** Florian La Roche ** Karl London ** ** Last compiled against: [06/10/96 @ 09:21:40 PM (Monday)] ** sendmail 8.8-a4 named bind-4.9.4-T4B db-1.85 ** gcc 2.7.2 libc-5.3.12 linux 2.0.0 ** ** NOTE: Override HASFLOCK as you will but, as of 1.99.6, mixed-style ** file locking is no longer allowed. In particular, make sure ** your DBM library and sendmail are both using either flock(2) ** *or* fcntl(2) file locking, but not both. */ #ifdef __linux__ # define BSD 1 /* include BSD defines */ # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASUNSETENV 1 /* has unsetenv(3) call */ # ifndef HASSNPRINTF # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # endif # define ERRLIST_PREDEFINED /* don't declare sys_errlist */ # define GIDSET_T gid_t /* from */ # define HASGETUSERSHELL 0 /* getusershell(3) broken in Slackware 2.0 */ # define IP_SRCROUTE 0 /* linux <= 1.2.8 doesn't support IP_OPTIONS */ +# define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */ # ifndef HASFLOCK # include # if LINUX_VERSION_CODE < 66399 # define HASFLOCK 0 /* flock(2) is broken after 0.99.13 */ # else # define HASFLOCK 1 /* flock(2) fixed after 1.3.95 */ # endif # endif # ifndef LA_TYPE # define LA_TYPE LA_PROCSTR # endif # define SFS_TYPE SFS_VFS /* use statfs() impl */ # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/var/run/sendmail.pid" # endif # define TZ_TYPE TZ_TNAME # include # undef atol /* wounded in */ #endif /* ** DELL SVR4 Issue 2.2, and others ** From Kimmo Suominen ** ** It's on #ifdef DELL_SVR4 because Solaris also gets __svr4__ ** defined, and the definitions conflict. ** ** Peter Wemm claims that the setreuid ** trick works on DELL 2.2 (SVR4.0/386 version 4.0) and ESIX 4.0.3A ** (SVR4.0/386 version 3.0). */ #ifdef DELL_SVR4 /* no changes necessary */ /* see general __svr4__ defines below */ #endif /* ** Apple A/UX 3.0 */ #ifdef _AUX_SOURCE # include # define BSD /* has BSD routines */ # define HASSETRLIMIT 0 /* ... but not setrlimit(2) */ # define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASFCHMOD 1 /* has fchmod(2) syscall */ # define HASINITGROUPS 1 /* has initgroups(3) call */ -# define HASSETVBUF 1 /* we have setvbuf(3) in libc */ +# define HASSETVBUF 1 /* has setvbuf(3) in libc */ +# define HASSTRERROR 1 /* has strerror(3) */ # define SIGFUNC_DEFINED /* sigfunc_t already defined */ -# define SIGFUNC_RETURN (0) /* XXX this is a guess */ -# define SIGFUNC_DECL int /* XXX this is a guess */ +# define SIGFUNC_RETURN /* POSIX-mode */ +# define SIGFUNC_DECL void /* POSIX-mode */ +# define ERRLIST_PREDEFINED 1 # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # ifndef LA_TYPE # define LA_TYPE LA_INT # define FSHIFT 16 # endif # define LA_AVENRUN "avenrun" # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define TZ_TYPE TZ_TZNAME # ifndef _PATH_UNIX # define _PATH_UNIX "/unix" /* should be in */ # endif # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # undef WIFEXITED # undef WEXITSTATUS #endif /* ** Encore UMAX V ** ** Not extensively tested. */ #ifdef UMAXV # define HASUNAME 1 /* use System V uname(2) system call */ # define HASSETVBUF 1 /* we have setvbuf(3) in libc */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ # define MAXPATHLEN PATH_MAX extern struct passwd *getpwent(), *getpwnam(), *getpwuid(); extern struct group *getgrent(), *getgrnam(), *getgrgid(); # undef WIFEXITED # undef WEXITSTATUS #endif /* ** Stardent Titan 3000 running TitanOS 4.2. ** ** Must be compiled in "cc -43" mode. ** ** From Kate Hedstrom . ** ** Note the tweaking below after the BSD defines are set. */ #ifdef titan # define setpgid setpgrp typedef int pid_t; # undef WIFEXITED # undef WEXITSTATUS #endif /* ** Sequent DYNIX 3.2.0 ** ** From Jim Davis . */ #ifdef sequent # define BSD 1 # define HASUNSETENV 1 # define BSD4_3 1 /* to get signal() in conf.c */ # define WAITUNION 1 # define LA_TYPE LA_FLOAT # ifdef _POSIX_VERSION # undef _POSIX_VERSION /* set in */ # endif # undef HASSETVBUF /* don't actually have setvbuf(3) */ # define setpgid setpgrp /* Have to redefine WIFEXITED to take an int, to work with waitfor() */ # undef WIFEXITED # define WIFEXITED(s) (((union wait*)&(s))->w_stopval != WSTOPPED && \ ((union wait*)&(s))->w_termsig == 0) # define WEXITSTATUS(s) (((union wait*)&(s))->w_retcode) typedef int pid_t; # define isgraph(c) (isprint(c) && (c != ' ')) # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # ifndef _PATH_UNIX # define _PATH_UNIX "/dynix" # endif # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" #endif /* ** Sequent DYNIX/ptx v2.0 (and higher) ** ** For DYNIX/ptx v1.x, undefine HASSETREUID. ** ** From Tim Wright . ** Update from Jack Woolley , 26 Dec 1995, ** for DYNIX/ptx 4.0.2. */ #ifdef _SEQUENT_ # include # define SYSTEM5 1 /* include all the System V defines */ # define HASSETSID 1 /* has POSIX setsid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define HASSETREUID 1 /* has setreuid(2) call */ # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define GIDSET_T gid_t # define LA_TYPE LA_INT # define SFS_TYPE SFS_STATFS /* use statfs() impl */ # define SPT_TYPE SPT_NONE /* don't use setproctitle */ # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif #endif /* ** Cray Unicos ** ** Ported by David L. Kensiski, Sterling Sofware */ #ifdef UNICOS # define SYSTEM5 1 /* include all the System V defines */ # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define MAXPATHLEN PATHSIZE # define LA_TYPE LA_ZERO # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ # define SFS_BAVAIL f_bfree /* alternate field name */ #endif /* ** Apollo DomainOS ** ** From Todd Martin & Don Lewis ** ** 15 Jan 1994; updated 2 Aug 1995 ** */ #ifdef apollo # define HASSETREUID 1 /* has setreuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(2) call */ # define IP_SRCROUTE 0 /* does not have */ # define SPT_TYPE SPT_NONE /* don't use setproctitle */ # define LA_TYPE LA_SUBR /* use getloadavg.c */ # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define TZ_TYPE TZ_TZNAME # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif # undef S_IFSOCK /* S_IFSOCK and S_IFIFO are the same */ # undef S_IFIFO # define S_IFIFO 0010000 # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # define RLIMIT_NEEDS_SYS_TIME_H 1 # if defined(NGROUPS_MAX) && !NGROUPS_MAX # undef NGROUPS_MAX # endif #endif /* ** UnixWare 2.x */ #ifdef UNIXWARE2 # define UNIXWARE 1 # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ #endif /* ** UnixWare 1.1.2. ** ** Updated by Petr Lampa . ** From Evan Champion . */ #ifdef UNIXWARE # include # define SYSTEM5 1 # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # define HASSETREUID 1 # define HASSETSID 1 # define HASINITGROUPS 1 # define GIDSET_T gid_t # define SLEEP_T unsigned # define SFS_TYPE SFS_STATVFS # define LA_TYPE LA_ZERO # undef WIFEXITED # undef WEXITSTATUS # define _PATH_UNIX "/unix" # define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" # endif # define SYSLOG_BUFSIZE 128 #endif /* ** Intergraph CLIX 3.1 ** ** From Paul Southworth */ #ifdef CLIX # define SYSTEM5 1 /* looks like System V */ # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # endif # define DEV_BSIZE 512 /* device block size not defined */ # define GIDSET_T gid_t # undef LOG /* syslog not available */ # define NEEDFSYNC 1 /* no fsync in system library */ # define GETSHORT _getshort #endif /* ** NCR MP-RAS 2.x (SysVr4) with Wollongong TCP/IP ** ** From Kevin Darcy . */ #ifdef NCR_MP_RAS2 # include # define __svr4__ # define IP_SRCROUTE 0 /* Something is broken with getsockopt() */ # define SYSLOG_BUFSIZE 1024 # define SPT_TYPE SPT_NONE #endif /* ** NCR MP-RAS 3.x (SysVr4) with STREAMware TCP/IP ** ** From Tom Moore */ #ifdef NCR_MP_RAS3 # define __svr4__ # define SYSLOG_BUFSIZE 1024 # define SPT_TYPE SPT_NONE #endif /* ** Tandem NonStop-UX SVR4 ** ** From Rick McCarty . */ #ifdef NonStop_UX_BXX # define __svr4__ #endif /* ** Hitachi 3050R & 3050RX Workstations running HI-UX/WE2. ** ** Tested for 1.04 and 1.03 ** From Akihiro Hashimoto ("Hash") . */ #ifdef __H3050R # define SYSTEM5 1 /* include all the System V defines */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define setreuid(r, e) setresuid(r, e, -1) # define LA_TYPE LA_FLOAT # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define HASSETVBUF /* HI-UX has no setlinebuf */ # ifndef GIDSET_T # define GIDSET_T gid_t # endif # ifndef _PATH_UNIX # define _PATH_UNIX "/HI-UX" # endif # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* getusershell(3) causes core dumps */ # endif /* avoid m_flags conflict between db.h & sys/sysmacros.h on HIUX 3050 */ # undef m_flags # ifdef __STDC__ extern int syslog(int, char *, ...); # endif #endif /* ** Amdahl UTS System V 2.1.5 (SVr3-based) ** ** From: Janet Jackson . */ #ifdef _UTS # include # undef HASLSTAT /* has symlinks, but they cause problems */ # define NEEDFSYNC 1 /* system fsync(2) fails on non-EFS filesys */ # define SYS5SIGNALS 1 /* System V signal semantics */ # define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ # define HASUNAME 1 /* use System V uname(2) system call */ # define HASINITGROUPS 1 /* has initgroups(3) function */ # define HASSETVBUF 1 /* has setvbuf(3) function */ # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* does not have getusershell(3) function */ # endif # define GIDSET_T gid_t /* type of 2nd arg to getgroups(2) isn't int */ # define LA_TYPE LA_ZERO /* doesn't have load average */ # define SFS_TYPE SFS_4ARGS /* use 4-arg statfs() */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define _PATH_UNIX "/unix" # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" #endif /* ** Cray Computer Corporation's CSOS ** ** From Scott Bolte . */ #ifdef _CRAYCOM # define SYSTEM5 1 /* include all the System V defines */ # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define NEEDFSYNC 1 /* no fsync in system library */ # define MAXPATHLEN PATHSIZE # define LA_TYPE LA_ZERO # define SFS_TYPE SFS_4ARGS /* four argument statfs() call */ # define SFS_BAVAIL f_bfree /* alternate field name */ # define _POSIX_CHOWN_RESTRICTED -1 extern struct group *getgrent(), *getgrnam(), *getgrgid(); #endif /* ** Sony NEWS-OS 4.2.1R and 6.0.3 ** ** From Motonori NAKAMURA . */ #ifdef sony_news # ifndef __svr4 /* NEWS-OS 4.2.1R */ # ifndef BSD # define BSD /* has BSD routines */ # endif # define HASUNSETENV 1 /* has unsetenv(2) call */ # undef HASSETVBUF /* don't actually have setvbuf(3) */ # define WAITUNION 1 /* use "union wait" as wait argument type */ # define LA_TYPE LA_INT # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # define setpgid setpgrp # undef WIFEXITED # undef WEXITSTATUS # define MODE_T int /* system include files have no mode_t */ typedef int pid_t; typedef int (*sigfunc_t)(); # define SIGFUNC_DEFINED # define SIGFUNC_RETURN (0) # define SIGFUNC_DECL int # else /* NEWS-OS 6.0.3 with /bin/cc */ # ifndef __svr4__ # define __svr4__ /* use all System V Releae 4 defines below */ # endif # define HASSETSID 1 /* has Posix setsid(2) call */ # define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */ # define LA_TYPE LA_READKSYM /* use MIOC_READKSYM ioctl */ # ifndef SPT_TYPE # define SPT_TYPE SPT_SYSMIPS /* use sysmips() (OS 6.0.2 or later) */ # endif # define GIDSET_T gid_t # undef WIFEXITED # undef WEXITSTATUS # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 1024 # endif # define _PATH_UNIX "/stand/unix" # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" # endif # endif #endif /* ** Omron LUNA/UNIOS-B 3.0, LUNA2/Mach and LUNA88K Mach ** ** From Motonori NAKAMURA . */ #ifdef luna # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif # define HASUNSETENV 1 /* has unsetenv(2) call */ # define NEEDPUTENV 1 /* need putenv(3) call */ # define NEEDGETOPT 1 /* need a replacement for getopt(3) */ # define NEEDSTRSTR 1 /* need emulation of the strstr(3) call */ # define WAITUNION 1 /* use "union wait" as wait argument type */ # ifdef uniosb # include # define NEEDVPRINTF 1 /* need a replacement for vprintf(3) */ # define LA_TYPE LA_INT # define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ # endif # ifdef luna2 # define LA_TYPE LA_SUBR # define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone */ # endif # ifdef luna88k # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # define LA_TYPE LA_INT # endif # define SFS_TYPE SFS_VFS /* use statfs() implementation */ # define setpgid setpgrp # undef WIFEXITED # undef WEXITSTATUS typedef int pid_t; typedef int (*sigfunc_t)(); # define SIGFUNC_DEFINED # define SIGFUNC_RETURN (0) # define SIGFUNC_DECL int extern char *getenv(); extern int errno; # define _PATH_VENDOR_CF "/usr/lib/sendmail.cf" #endif /* ** NEC EWS-UX/V 4.2 (with /usr/ucb/cc) ** ** From Motonori NAKAMURA . */ #if defined(nec_ews_svr4) || defined(_nec_ews_svr4) # ifndef __svr4__ # define __svr4__ /* use all System V Releae 4 defines below */ # endif # define SYS5SIGNALS 1 /* SysV signal semantics -- reset on each sig */ # define HASSETSID 1 /* has Posix setsid(2) call */ # define LA_TYPE LA_READKSYM /* use MIOC_READSYM ioctl */ # define SFS_TYPE SFS_USTAT /* use System V ustat(2) syscall */ # define GIDSET_T gid_t # undef WIFEXITED # undef WEXITSTATUS # define NAMELISTMASK 0x7fffffff /* mask for nlist() values */ # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */ # endif #endif /* ** Fujitsu/ICL UXP/DS (For the DS/90 Series) ** ** From Diego R. Lopez . ** Additional changes from Fumio Moriya and Toshiaki Nomura of the ** Fujitsu Fresoftware gruop . */ #ifdef __uxp__ # include # include # include # define __svr4__ # define HASGETUSERSHELL 0 # define HASFLOCK 0 # if UXPDS == 10 # define HASSNPRINTF 0 /* no snprintf(3) or vsnprintf(3) */ # else # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # endif # define _PATH_UNIX "/stand/unix" # define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" # endif #endif /* ** Pyramid DC/OSx ** ** From Earle Ake . */ #ifdef DCOSx # define GIDSET_T gid_t # ifndef IDENTPROTO # define IDENTPROTO 0 /* TCP/IP implementation is broken */ # endif #endif /* ** Concurrent Computer Corporation Maxion ** ** From Donald R. Laster Jr. . */ #ifdef __MAXION__ # include # define __svr4__ 1 /* SVR4.2MP */ # define HASSETREUID 1 /* have setreuid(2) */ # define HASLSTAT 1 /* have lstat(2) */ # define HASSETRLIMIT 1 /* have setrlimit(2) */ # define HASGETDTABLESIZE 1 /* have getdtablesize(2) */ # define HASSNPRINTF 1 /* have snprintf(3) */ # define HASGETUSERSHELL 1 /* have getusershell(3) */ # define NOFTRUNCATE 1 /* do not have ftruncate(2) */ # define SLEEP_T unsigned # define SFS_TYPE SFS_STATVFS # define SFS_BAVAIL f_bavail # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 256 /* Use 256 bytes */ # endif # undef WUNTRACED # undef WIFEXITED # undef WIFSIGNALED # undef WIFSTOPPED # undef WEXITSTATUS # undef WTERMSIG # undef WSTOPSIG #endif /* ** Harris Nighthawk PowerUX (nh6000 box) ** ** Contributed by Bob Miorelli, Pratt & Whitney */ #ifdef _PowerUX # ifndef __svr4__ # define __svr4__ # endif # define _PATH_VENDOR_CF "/etc/mail/sendmail.cf" # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" # endif # define SYSLOG_BUFSIZE 1024 # define HASSNPRINTF 1 /* has snprintf(3) and vsnprintf(3) */ # define LA_TYPE LA_ZERO typedef struct msgb mblk_t; # undef offsetof /* avoid stddefs.h and sys/sysmacros.h conflict */ #endif /********************************************************************** ** End of Per-Operating System defines **********************************************************************/ /********************************************************************** ** More general defines **********************************************************************/ /* general BSD defines */ #ifdef BSD # define HASGETDTABLESIZE 1 /* has getdtablesize(2) call */ # define HASSETREUID 1 /* has setreuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # ifndef IP_SRCROUTE # define IP_SRCROUTE 1 /* can check IP source routing */ # endif # ifndef HASSETRLIMIT # define HASSETRLIMIT 1 /* has setrlimit(2) call */ # endif # ifndef HASFLOCK # define HASFLOCK 1 /* has flock(2) call */ # endif # ifndef TZ_TYPE # define TZ_TYPE TZ_TM_ZONE /* use tm->tm_zone variable */ # endif #endif /* general System V Release 4 defines */ #ifdef __svr4__ # define SYSTEM5 1 # define USESETEUID 1 /* has useable seteuid(2) call */ # define HASINITGROUPS 1 /* has initgroups(3) call */ # define BSD_COMP 1 /* get BSD ioctl calls */ # ifndef HASSETRLIMIT # define HASSETRLIMIT 1 /* has setrlimit(2) call */ # endif # ifndef HASGETUSERSHELL # define HASGETUSERSHELL 0 /* does not have getusershell(3) call */ # endif # ifndef HASFCHMOD # define HASFCHMOD 1 /* most (all?) SVr4s seem to have fchmod(2) */ # endif # ifndef _PATH_UNIX # define _PATH_UNIX "/unix" # endif # ifndef _PATH_VENDOR_CF # define _PATH_VENDOR_CF "/usr/ucblib/sendmail.cf" # endif # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/usr/ucblib/sendmail.pid" # endif # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 128 # endif # ifndef SFS_TYPE # define SFS_TYPE SFS_STATVFS # endif # define USE_SIGLONGJMP 1 /* sigsetjmp needed for signal handling */ #endif /* general System V defines */ #ifdef SYSTEM5 # include # define HASUNAME 1 /* use System V uname(2) system call */ # define SYS5SETPGRP 1 /* use System V setpgrp(2) syscall */ # define HASSETVBUF 1 /* we have setvbuf(3) in libc */ # ifndef HASULIMIT # define HASULIMIT 1 /* has the ulimit(2) syscall */ # endif # ifndef LA_TYPE # ifdef MIOC_READKSYM # define LA_TYPE LA_READKSYM /* use MIOC_READKSYM ioctl */ # else # define LA_TYPE LA_INT /* assume integer load average */ # endif # endif # ifndef SFS_TYPE # define SFS_TYPE SFS_USTAT /* use System V ustat(2) syscall */ # endif # ifndef TZ_TYPE # define TZ_TYPE TZ_TZNAME /* use tzname[] vector */ # endif # define bcopy(s, d, l) (memmove((d), (s), (l))) # define bzero(d, l) (memset((d), '\0', (l))) # define bcmp(s, d, l) (memcmp((s), (d), (l))) #endif /* general POSIX defines */ #ifdef _POSIX_VERSION # define HASSETSID 1 /* has Posix setsid(2) call */ # define HASWAITPID 1 /* has Posix waitpid(2) call */ # if _POSIX_VERSION >= 199500 && !defined(USESETEUID) # define USESETEUID 1 /* has useable seteuid(2) call */ # endif #endif /* ** Tweaking for systems that (for example) claim to be BSD or POSIX ** but don't have all the standard BSD or POSIX routines (boo hiss). */ #ifdef titan # undef HASINITGROUPS /* doesn't have initgroups(3) call */ #endif #ifdef _CRAYCOM # undef HASSETSID /* despite POSIX claim, doesn't have setsid */ #endif #ifdef ISC_UNIX # undef bcopy /* despite SystemV claim, uses BSD bcopy */ #endif #ifdef ALTOS_SYSTEM_V # undef bcopy /* despite SystemV claim, uses BSD bcopy */ # undef bzero /* despite SystemV claim, uses BSD bzero */ # undef bcmp /* despite SystemV claim, uses BSD bcmp */ #endif /* ** Due to a "feature" in some operating systems such as Ultrix 4.3 and ** HPUX 8.0, if you receive a "No route to host" message (ICMP message ** ICMP_UNREACH_HOST) on _any_ connection, all connections to that host ** are closed. Some firewalls return this error if you try to connect ** to the IDENT port (113), so you can't receive email from these hosts ** on these systems. The firewall really should use a more specific -** message such as ICMP_UNREACH_PROTOCOL or _PORT or _NET_PROHIB. If +** message such as ICMP_UNREACH_PROTOCOL or _PORT or _FILTER_PROHIB. If ** not explicitly set to zero above, default it on. */ #ifndef IDENTPROTO # define IDENTPROTO 1 /* use IDENT proto (RFC 1413) */ #endif #ifndef IP_SRCROUTE # define IP_SRCROUTE 1 /* Detect IP source routing */ #endif #ifndef HASGETUSERSHELL # define HASGETUSERSHELL 1 /* libc has getusershell(3) call */ #endif #ifndef NETUNIX # define NETUNIX 1 /* include unix domain support */ #endif #ifndef HASFLOCK # define HASFLOCK 0 /* assume no flock(2) support */ #endif #ifndef HASSETREUID # define HASSETREUID 0 /* assume no setreuid(2) call */ #endif #ifndef HASFCHMOD # define HASFCHMOD 0 /* assume no fchmod(2) syscall */ #endif #ifndef USESETEUID # define USESETEUID 0 /* assume no seteuid(2) call or no saved ids */ #endif #ifndef HASSETRLIMIT # define HASSETRLIMIT 0 /* assume no setrlimit(2) support */ #endif #ifndef HASULIMIT # define HASULIMIT 0 /* assume no ulimit(2) support */ #endif #ifndef OLD_NEWDB # define OLD_NEWDB 0 /* assume newer version of newdb */ #endif #ifndef SECUREWARE # define SECUREWARE 0 /* assume no SecureWare C2 auditing hooks */ #endif #ifndef USE_SIGLONGJMP # define USE_SIGLONGJMP 0 /* assume setjmp handles signals properly */ #endif /* ** If no type for argument two of getgroups call is defined, assume ** it's an integer -- unfortunately, there seem to be several choices ** here. */ #ifndef GIDSET_T # define GIDSET_T int #endif #ifndef UID_T # define UID_T uid_t #endif #ifndef GID_T # define GID_T gid_t #endif #ifndef SIZE_T # define SIZE_T size_t #endif #ifndef MODE_T # define MODE_T mode_t #endif #ifndef ARGV_T # define ARGV_T char ** #endif /********************************************************************** ** Remaining definitions should never have to be changed. They are ** primarily to provide back compatibility for older systems -- for ** example, it includes some POSIX compatibility definitions **********************************************************************/ /* System 5 compatibility */ #ifndef S_ISREG # define S_ISREG(foo) ((foo & S_IFMT) == S_IFREG) #endif #ifndef S_ISDIR # define S_ISDIR(foo) ((foo & S_IFMT) == S_IFDIR) #endif #if !defined(S_ISLNK) && defined(S_IFLNK) # define S_ISLNK(foo) ((foo & S_IFMT) == S_IFLNK) #endif +#ifndef S_IRUSR +# define S_IRUSR 0400 +#endif #ifndef S_IWUSR # define S_IWUSR 0200 #endif +#ifndef S_IRGRP +# define S_IRGRP 0040 +#endif #ifndef S_IWGRP # define S_IWGRP 0020 #endif +#ifndef S_IROTH +# define S_IROTH 0004 +#endif #ifndef S_IWOTH # define S_IWOTH 0002 #endif /* ** Older systems don't have this error code -- it should be in ** /usr/include/sysexits.h. */ # ifndef EX_CONFIG # define EX_CONFIG 78 /* configuration error */ # endif /* pseudo-code used in server SMTP */ # define EX_QUIT 22 /* drop out of server immediately */ /* pseudo-code used for mci_setstat */ # define EX_NOTSTICKY -5 /* don't save persistent status */ /* +** An "impossible" file mode to indicate that the file does not exist. +*/ + +#define ST_MODE_NOFILE 0171147 /* unlikely to occur */ + + +/* ** These are used in a few cases where we need some special ** error codes, but where the system doesn't provide something ** reasonable. They are printed in errstring. */ #ifndef E_PSEUDOBASE # define E_PSEUDOBASE 256 #endif -#define EOPENTIMEOUT (E_PSEUDOBASE + 0) /* timeout on open */ +#define E_SM_OPENTIMEOUT (E_PSEUDOBASE + 0) /* Timeout on file open */ +#define E_SM_NOSLINK (E_PSEUDOBASE + 1) /* Symbolic links not allowed */ +#define E_SM_NOHLINK (E_PSEUDOBASE + 2) /* Hard links not allowed */ +#define E_SM_REGONLY (E_PSEUDOBASE + 3) /* Regular files only */ +#define E_SM_ISEXEC (E_PSEUDOBASE + 4) /* Executable files not allowed */ +#define E_SM_WWDIR (E_PSEUDOBASE + 5) /* World writable directory */ +#define E_SM_GWDIR (E_PSEUDOBASE + 6) /* Group writable directory */ +#define E_SM_FILECHANGE (E_PSEUDOBASE + 7) /* File changed after open */ +#define E_SM_WWFILE (E_PSEUDOBASE + 8) /* World writable file */ +#define E_SM_GWFILE (E_PSEUDOBASE + 9) /* Group writable file */ #define E_DNSBASE (E_PSEUDOBASE + 20) /* base for DNS h_errno */ /* type of arbitrary pointer */ #ifndef ARBPTR_T # define ARBPTR_T void * #endif #ifndef __P # include "cdefs.h" #endif -#if NAMED_BIND -# include -# ifdef __svr4__ -# ifdef NOERROR -# undef NOERROR /* avoid compiler conflict with stream.h */ -# endif -# endif -# ifndef __ksr__ -extern int h_errno; -# endif +#if NAMED_BIND && !defined(__ksr__) +extern int h_errno; #endif /* ** The size of an IP address -- can't use sizeof because of problems ** on Crays, where everything is 64 bits. This will break if/when ** IP addresses are expanded to eight bytes. */ #ifndef INADDRSZ # define INADDRSZ 4 #endif /* ** The size of various known types -- for reading network protocols. ** Again, we can't use sizeof because of compiler randomness. */ #ifndef INT16SZ # define INT16SZ 2 #endif #ifndef INT32SZ # define INT32SZ 4 #endif /* ** Do some required dependencies */ #if NETINET || NETISO # ifndef SMTP # define SMTP 1 /* enable user and server SMTP */ # endif # ifndef QUEUE # define QUEUE 1 /* enable queueing */ # endif # ifndef DAEMON # define DAEMON 1 /* include the daemon (requires IPC & SMTP) */ # endif #endif /* ** Arrange to use either varargs or stdargs */ # ifdef __STDC__ # include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap, f) # define VA_END va_end(ap) # else # include # define VA_LOCAL_DECL va_list ap; # define VA_START(f) va_start(ap) # define VA_END va_end(ap) # endif #ifdef HASUNAME # include # ifdef newstr # undef newstr # endif #else /* ! HASUNAME */ # define NODE_LENGTH 32 struct utsname { char nodename[NODE_LENGTH+1]; }; #endif /* HASUNAME */ #if !defined(MAXHOSTNAMELEN) && !defined(_SCO_unix_) && !defined(NonStop_UX_BXX) && !defined(ALTOS_SYSTEM_V) # define MAXHOSTNAMELEN 256 #endif #if !defined(SIGCHLD) && defined(SIGCLD) # define SIGCHLD SIGCLD #endif #ifndef STDIN_FILENO # define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO # define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO # define STDERR_FILENO 2 #endif #ifndef LOCK_SH # define LOCK_SH 0x01 /* shared lock */ # define LOCK_EX 0x02 /* exclusive lock */ # define LOCK_NB 0x04 /* non-blocking lock */ # define LOCK_UN 0x08 /* unlock */ #endif #ifndef SEEK_SET # define SEEK_SET 0 # define SEEK_CUR 1 # define SEEK_END 2 #endif #ifndef SIG_ERR # define SIG_ERR ((void (*)()) -1) #endif #ifndef WEXITSTATUS # define WEXITSTATUS(st) (((st) >> 8) & 0377) #endif #ifndef WIFEXITED # define WIFEXITED(st) (((st) & 0377) == 0) #endif #ifndef SIGFUNC_DEFINED typedef void (*sigfunc_t) __P((int)); #endif #ifndef SIGFUNC_RETURN # define SIGFUNC_RETURN #endif #ifndef SIGFUNC_DECL # define SIGFUNC_DECL void #endif /* size of syslog buffer */ #ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 1024 #endif /* ** Size of tobuf (deliver.c) ** Tweak this to match your syslog implementation. It will have to ** allow for the extra information printed. */ #ifndef TOBUFSIZE # if (SYSLOG_BUFSIZE) > 768 # define TOBUFSIZE (SYSLOG_BUFSIZE - 512) # else -# define TOBUFSIZE 256 +# define TOBUFSIZE (SYSLOG_BUFSIZE / 2) # endif #endif /* TOBUFSIZE must never be permitted to exceed MAXLINE - 128 */ #if TOBUFSIZE > (MAXLINE - 128) # undef TOBUFSIZE # define TOBUFSIZE (MAXLINE - 128) #endif /* ** Size of prescan buffer. ** Despite comments in the _sendmail_ book, this probably should ** not be changed; there are some hard-to-define dependencies. */ # define PSBUFSIZE (MAXNAME + MAXATOM) /* size of prescan buffer */ /* fork routine -- set above using #ifdef _osname_ or in Makefile */ # ifndef FORK # define FORK fork /* function to call to fork mailer */ # endif /* ** Default to using scanf in readcf. */ #ifndef SCANF # define SCANF 1 #endif /* ** SVr4 and similar systems use different routines for setjmp/longjmp ** with signal support */ #if USE_SIGLONGJMP /* Silly SCO /usr/include/setjmp.h file has #define setjmp(env) setjmp(env) */ # ifdef setjmp # undef setjmp # endif # define jmp_buf sigjmp_buf # define setjmp(env) sigsetjmp(env, 1) # define longjmp(env, val) siglongjmp(env, val) #endif #if !defined(NGROUPS_MAX) && defined(NGROUPS) # define NGROUPS_MAX NGROUPS /* POSIX naming convention */ +#endif + +/* +** If we don't have a system syslog, simulate it. +*/ + +#if !LOG +# define LOG_EMERG 0 /* system is unusable */ +# define LOG_ALERT 1 /* action must be taken immediately */ +# define LOG_CRIT 2 /* critical conditions */ +# define LOG_ERR 3 /* error conditions */ +# define LOG_WARNING 4 /* warning conditions */ +# define LOG_NOTICE 5 /* normal but significant condition */ +# define LOG_INFO 6 /* informational */ +# define LOG_DEBUG 7 /* debug-level messages */ #endif Index: head/usr.sbin/sendmail/src/daemon.c =================================================================== --- head/usr.sbin/sendmail/src/daemon.c (revision 26988) +++ head/usr.sbin/sendmail/src/daemon.c (revision 26989) @@ -1,1845 +1,1974 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "sendmail.h" #ifndef lint #ifdef DAEMON -static char sccsid[] = "@(#)daemon.c 8.159 (Berkeley) 1/14/97 (with daemon mode)"; +static char sccsid[] = "@(#)daemon.c 8.175 (Berkeley) 6/1/97 (with daemon mode)"; #else -static char sccsid[] = "@(#)daemon.c 8.159 (Berkeley) 1/14/97 (without daemon mode)"; +static char sccsid[] = "@(#)daemon.c 8.175 (Berkeley) 6/1/97 (without daemon mode)"; #endif #endif /* not lint */ -#if DAEMON || defined(SOCK_STREAM) +#if defined(SOCK_STREAM) || defined(__GNU_LIBRARY__) +# define USE_SOCK_STREAM 1 +#endif + +#if DAEMON || defined(USE_SOCK_STREAM) # include # if NAMED_BIND # include # ifndef NO_DATA # define NO_DATA NO_ADDRESS # endif # endif #endif #if DAEMON +# include + # if IP_SRCROUTE # include # include # include # endif /* ** DAEMON.C -- routines to use when running as a daemon. ** ** This entire file is highly dependent on the 4.2 BSD ** interprocess communication primitives. No attempt has ** been made to make this file portable to Version 7, ** Version 6, MPX files, etc. If you should try such a ** thing yourself, I recommend chucking the entire file ** and starting from scratch. Basic semantics are: ** ** getrequests(e) ** Opens a port and initiates a connection. ** Returns in a child. Must set InChannel and ** OutChannel appropriately. ** clrdaemon() ** Close any open files associated with getting ** the connection; this is used when running the queue, ** etc., to avoid having extra file descriptors during ** the queue run and to avoid confusing the network ** code (if it cares). ** makeconnection(host, port, outfile, infile, e) ** Make a connection to the named host on the given ** port. Set *outfile and *infile to the files ** appropriate for communication. Returns zero on ** success, else an exit status describing the ** error. ** host_map_lookup(map, hbuf, avp, pstat) ** Convert the entry in hbuf into a canonical form. */ /* ** GETREQUESTS -- open mail IPC port and get requests. ** ** Parameters: ** e -- the current envelope. ** ** Returns: ** TRUE -- if a "null server" should be used -- that is, one ** that rejects all commands. ** FALSE -- to use a normal server. ** ** Side Effects: ** Waits until some interesting activity occurs. When ** it does, a child is created to process it, and the ** parent waits for completion. Return from this ** routine is always in the child. The file pointers ** "InChannel" and "OutChannel" should be set to point ** to the communication channel. */ int DaemonSocket = -1; /* fd describing socket */ SOCKADDR DaemonAddr; /* socket for incoming */ int ListenQueueSize = 10; /* size of listen queue */ int TcpRcvBufferSize = 0; /* size of TCP receive buffer */ int TcpSndBufferSize = 0; /* size of TCP send buffer */ bool getrequests(e) ENVELOPE *e; { int t; bool refusingconnections = TRUE; FILE *pidf; int socksize; #if XDEBUG bool j_has_dot; #endif extern void reapchild(); extern int opendaemonsocket __P((bool)); /* ** Set up the address for the mailer. */ if (DaemonAddr.sin.sin_family == 0) DaemonAddr.sin.sin_family = AF_INET; if (DaemonAddr.sin.sin_addr.s_addr == 0) DaemonAddr.sin.sin_addr.s_addr = INADDR_ANY; if (DaemonAddr.sin.sin_port == 0) { register struct servent *sp; sp = getservbyname("smtp", "tcp"); if (sp == NULL) { syserr("554 service \"smtp\" unknown"); DaemonAddr.sin.sin_port = htons(25); } else DaemonAddr.sin.sin_port = sp->s_port; } /* ** Try to actually open the connection. */ if (tTd(15, 1)) printf("getrequests: port 0x%x\n", DaemonAddr.sin.sin_port); /* get a socket for the SMTP connection */ socksize = opendaemonsocket(TRUE); (void) setsignal(SIGCHLD, reapchild); /* write the pid to the log file for posterity */ pidf = safefopen(PidFile, O_WRONLY|O_CREAT|O_TRUNC, 0644, - SFF_NOSLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT); - if (pidf != NULL) + SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT); + if (pidf == NULL) { + sm_syslog(LOG_ERR, NOQID, "unable to write %s", PidFile); + } + else + { extern char *CommandLineArgs; /* write the process id on line 1 */ fprintf(pidf, "%ld\n", (long) getpid()); /* line 2 contains all command line flags */ fprintf(pidf, "%s\n", CommandLineArgs); /* flush and close */ fclose(pidf); } #if XDEBUG { char jbuf[MAXHOSTNAMELEN]; expand("\201j", jbuf, sizeof jbuf, e); j_has_dot = strchr(jbuf, '.') != NULL; } #endif if (tTd(15, 1)) printf("getrequests: %d\n", DaemonSocket); for (;;) { register pid_t pid; auto int lotherend; int savederrno; int pipefd[2]; extern bool refuseconnections(); - extern int getla(); /* see if we are rejecting connections */ (void) blocksignal(SIGALRM); if (refuseconnections(ntohs(DaemonAddr.sin.sin_port))) { if (DaemonSocket >= 0) { /* close socket so peer will fail quickly */ (void) close(DaemonSocket); DaemonSocket = -1; } refusingconnections = TRUE; sleep(15); continue; } /* arrange to (re)open the socket if necessary */ if (refusingconnections) { (void) opendaemonsocket(FALSE); refusingconnections = FALSE; } #if XDEBUG /* check for disaster */ { char jbuf[MAXHOSTNAMELEN]; extern void dumpstate __P((char *)); expand("\201j", jbuf, sizeof jbuf, e); if (!wordinclass(jbuf, 'w')) { dumpstate("daemon lost $j"); - syslog(LOG_ALERT, "daemon process doesn't have $j in $=w; see syslog"); + sm_syslog(LOG_ALERT, NOQID, + "daemon process doesn't have $j in $=w; see syslog"); abort(); } else if (j_has_dot && strchr(jbuf, '.') == NULL) { dumpstate("daemon $j lost dot"); - syslog(LOG_ALERT, "daemon process $j lost dot; see syslog"); + sm_syslog(LOG_ALERT, NOQID, + "daemon process $j lost dot; see syslog"); abort(); } } #endif /* wait for a connection */ setproctitle("accepting connections on port %d", ntohs(DaemonAddr.sin.sin_port)); #if 0 /* ** Andrew Sun claims that this will ** fix the SVr4 problem. But it seems to have gone away, ** so is it worth doing this? */ if (SetNonBlocking(DaemonSocket, FALSE) < 0) log an error here; #endif (void) releasesignal(SIGALRM); - do + for (;;) { + fd_set readfds; + struct timeval timeout; + + FD_ZERO(&readfds); + FD_SET(DaemonSocket, &readfds); + timeout.tv_sec = 60; + timeout.tv_usec = 0; + + t = select(DaemonSocket + 1, &readfds, NULL, NULL, &timeout); + if (DoQueueRun) + (void) runqueue(TRUE, FALSE); + if (t <= 0 || !FD_ISSET(DaemonSocket, &readfds)) + continue; + errno = 0; lotherend = socksize; t = accept(DaemonSocket, (struct sockaddr *)&RealHostAddr, &lotherend); - } while (t < 0 && errno == EINTR); + if (t >= 0 || errno != EINTR) + break; + } savederrno = errno; (void) blocksignal(SIGALRM); if (t < 0) { errno = savederrno; syserr("getrequests: accept"); /* arrange to re-open the socket next time around */ (void) close(DaemonSocket); DaemonSocket = -1; refusingconnections = TRUE; sleep(5); continue; } /* ** Create a subprocess to process the mail. */ if (tTd(15, 2)) printf("getrequests: forking (fd = %d)\n", t); /* ** Create a pipe to keep the child from writing to the ** socket until after the parent has closed it. Otherwise ** the parent may hang if the child has closed it first. */ if (pipe(pipefd) < 0) pipefd[0] = pipefd[1] = -1; blocksignal(SIGCHLD); pid = fork(); if (pid < 0) { syserr("daemon: cannot fork"); if (pipefd[0] != -1) { (void) close(pipefd[0]); (void) close(pipefd[1]); } (void) releasesignal(SIGCHLD); sleep(10); (void) close(t); continue; } if (pid == 0) { char *p; extern SIGFUNC_DECL intsig __P((int)); FILE *inchannel, *outchannel; bool nullconn; /* ** CHILD -- return to caller. ** Collect verified idea of sending host. ** Verify calling user id if possible here. */ (void) releasesignal(SIGALRM); (void) releasesignal(SIGCHLD); (void) setsignal(SIGCHLD, SIG_DFL); (void) setsignal(SIGHUP, intsig); (void) close(DaemonSocket); proc_list_clear(); /* don't schedule queue runs if we are told to ETRN */ QueueIntvl = 0; setproctitle("startup with %s", anynet_ntoa(&RealHostAddr)); if (pipefd[0] != -1) { auto char c; /* ** Wait for the parent to close the write end ** of the pipe, which we will see as an EOF. ** This guarantees that we won't write to the ** socket until after the parent has closed ** the pipe. */ /* close the write end of the pipe */ (void) close(pipefd[1]); /* we shouldn't be interrupted, but ... */ while (read(pipefd[0], &c, 1) < 0 && errno == EINTR) continue; (void) close(pipefd[0]); } /* determine host name */ p = hostnamebyanyaddr(&RealHostAddr); if (strlen(p) > (SIZE_T) MAXNAME) p[MAXNAME] = '\0'; RealHostName = newstr(p); setproctitle("startup with %s", p); if ((inchannel = fdopen(t, "r")) == NULL || (t = dup(t)) < 0 || (outchannel = fdopen(t, "w")) == NULL) { syserr("cannot open SMTP server channel, fd=%d", t); exit(0); } InChannel = inchannel; OutChannel = outchannel; DisConnected = FALSE; + /* open maps for check_relay ruleset */ + initmaps(FALSE, e); + /* validate the connection */ HoldErrs = TRUE; nullconn = !validate_connection(&RealHostAddr, RealHostName, e); HoldErrs = FALSE; if (nullconn) break; #ifdef XLA if (!xla_host_ok(RealHostName)) { message("421 Too many SMTP sessions for this host"); exit(0); } #endif if (tTd(15, 2)) printf("getreq: returning (normal server)\n"); return FALSE; } /* parent -- keep track of children */ proc_list_add(pid); (void) releasesignal(SIGCHLD); /* close the read end of the synchronization pipe */ if (pipefd[0] != -1) (void) close(pipefd[0]); /* close the port so that others will hang (for a while) */ (void) close(t); /* release the child by closing the read end of the sync pipe */ if (pipefd[1] != -1) (void) close(pipefd[1]); } if (tTd(15, 2)) printf("getreq: returning (null server)\n"); return TRUE; } /* ** OPENDAEMONSOCKET -- open the SMTP socket ** ** Deals with setting all appropriate options. DaemonAddr must ** be set up in advance. ** ** Parameters: ** firsttime -- set if this is the initial open. ** ** Returns: ** Size in bytes of the daemon socket addr. ** ** Side Effects: ** Leaves DaemonSocket set to the open socket. ** Exits if the socket cannot be created. */ #define MAXOPENTRIES 10 /* maximum number of tries to open connection */ int opendaemonsocket(firsttime) bool firsttime; { int on = 1; int socksize = 0; int ntries = 0; int saveerrno; if (tTd(15, 2)) printf("opendaemonsocket()\n"); do { if (ntries > 0) sleep(5); if (firsttime || DaemonSocket < 0) { DaemonSocket = socket(DaemonAddr.sa.sa_family, SOCK_STREAM, 0); if (DaemonSocket < 0) { saveerrno = errno; syserr("opendaemonsocket: can't create server SMTP socket"); severe: -# ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, "problem creating SMTP socket"); -# endif /* LOG */ + sm_syslog(LOG_ALERT, NOQID, + "problem creating SMTP socket"); DaemonSocket = -1; continue; } /* turn on network debugging? */ if (tTd(15, 101)) (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on); (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on); (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on); #ifdef SO_RCVBUF if (TcpRcvBufferSize > 0) { if (setsockopt(DaemonSocket, SOL_SOCKET, SO_RCVBUF, (char *) &TcpRcvBufferSize, sizeof(TcpRcvBufferSize)) < 0) syserr("opendaemonsocket: setsockopt(SO_RCVBUF)"); } #endif switch (DaemonAddr.sa.sa_family) { # if NETINET case AF_INET: socksize = sizeof DaemonAddr.sin; break; # endif # if NETISO case AF_ISO: socksize = sizeof DaemonAddr.siso; break; # endif default: socksize = sizeof DaemonAddr; break; } if (bind(DaemonSocket, &DaemonAddr.sa, socksize) < 0) { /* probably another daemon already */ saveerrno = errno; syserr("opendaemonsocket: cannot bind"); (void) close(DaemonSocket); goto severe; } } if (!firsttime && listen(DaemonSocket, ListenQueueSize) < 0) { saveerrno = errno; syserr("opendaemonsocket: cannot listen"); (void) close(DaemonSocket); goto severe; } return socksize; } while (ntries++ < MAXOPENTRIES && transienterror(saveerrno)); syserr("!opendaemonsocket: server SMTP socket wedged: exiting"); finis(); return -1; /* avoid compiler warning on IRIX */ } /* ** CLRDAEMON -- reset the daemon connection ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** releases any resources used by the passive daemon. */ void clrdaemon() { if (DaemonSocket >= 0) (void) close(DaemonSocket); DaemonSocket = -1; } /* ** SETDAEMONOPTIONS -- set options for running the daemon ** ** Parameters: ** p -- the options line. ** ** Returns: ** none. */ void setdaemonoptions(p) register char *p; { if (DaemonAddr.sa.sa_family == AF_UNSPEC) DaemonAddr.sa.sa_family = AF_INET; while (p != NULL) { register char *f; register char *v; while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; f = p; p = strchr(p, ','); if (p != NULL) *p++ = '\0'; v = strchr(f, '='); if (v == NULL) continue; while (isascii(*++v) && isspace(*v)) continue; if (isascii(*f) && islower(*f)) *f = toupper(*f); switch (*f) { case 'F': /* address family */ if (isascii(*v) && isdigit(*v)) DaemonAddr.sa.sa_family = atoi(v); #if NETINET else if (strcasecmp(v, "inet") == 0) DaemonAddr.sa.sa_family = AF_INET; #endif #if NETISO else if (strcasecmp(v, "iso") == 0) DaemonAddr.sa.sa_family = AF_ISO; #endif #if NETNS else if (strcasecmp(v, "ns") == 0) DaemonAddr.sa.sa_family = AF_NS; #endif #if NETX25 else if (strcasecmp(v, "x.25") == 0) DaemonAddr.sa.sa_family = AF_CCITT; #endif else syserr("554 Unknown address family %s in Family=option", v); break; case 'A': /* address */ switch (DaemonAddr.sa.sa_family) { #if NETINET case AF_INET: if (isascii(*v) && isdigit(*v)) DaemonAddr.sin.sin_addr.s_addr = inet_addr(v); else { register struct hostent *hp; hp = sm_gethostbyname(v); if (hp == NULL) syserr("554 host \"%s\" unknown", v); else bcopy(hp->h_addr, &DaemonAddr.sin.sin_addr, INADDRSZ); } break; #endif default: syserr("554 Address= option unsupported for family %d", DaemonAddr.sa.sa_family); break; } break; case 'P': /* port */ switch (DaemonAddr.sa.sa_family) { #if NETISO short port; #endif #if NETINET case AF_INET: if (isascii(*v) && isdigit(*v)) DaemonAddr.sin.sin_port = htons(atoi(v)); else { register struct servent *sp; sp = getservbyname(v, "tcp"); if (sp == NULL) syserr("554 service \"%s\" unknown", v); else DaemonAddr.sin.sin_port = sp->s_port; } break; #endif #if NETISO case AF_ISO: /* assume two byte transport selector */ if (isascii(*v) && isdigit(*v)) port = htons(atoi(v)); else { register struct servent *sp; sp = getservbyname(v, "tcp"); if (sp == NULL) syserr("554 service \"%s\" unknown", v); else port = sp->s_port; } bcopy((char *) &port, TSEL(&DaemonAddr.siso), 2); break; #endif default: syserr("554 Port= option unsupported for family %d", DaemonAddr.sa.sa_family); break; } break; case 'L': /* listen queue size */ ListenQueueSize = atoi(v); break; case 'S': /* send buffer size */ TcpSndBufferSize = atoi(v); break; case 'R': /* receive buffer size */ TcpRcvBufferSize = atoi(v); break; default: syserr("554 DaemonPortOptions parameter \"%s\" unknown", f); } } } /* ** MAKECONNECTION -- make a connection to an SMTP socket on another machine. ** ** Parameters: ** host -- the name of the host. ** port -- the port number to connect to. ** mci -- a pointer to the mail connection information ** structure to be filled in. ** e -- the current envelope. ** ** Returns: ** An exit code telling whether the connection could be ** made and if not why not. ** ** Side Effects: ** none. */ static jmp_buf CtxConnectTimeout; static void connecttimeout() { errno = ETIMEDOUT; longjmp(CtxConnectTimeout, 1); } SOCKADDR CurHostAddr; /* address of current host */ int makeconnection(host, port, mci, e) char *host; u_short port; register MCI *mci; ENVELOPE *e; { - register volatile int i = 0; + register volatile int addrno = 0; register volatile int s; register struct hostent *volatile hp = (struct hostent *)NULL; SOCKADDR addr; int sav_errno; volatile int addrlen; volatile bool firstconnect; EVENT *volatile ev = NULL; /* ** Set up the address for the mailer. ** Accept "[a.b.c.d]" syntax for host name. */ #if NAMED_BIND h_errno = 0; #endif errno = 0; bzero(&CurHostAddr, sizeof CurHostAddr); SmtpPhase = mci->mci_phase = "initial connection"; CurHostName = host; if (host[0] == '[') { long hid; register char *p = strchr(host, ']'); if (p != NULL) { *p = '\0'; #if NETINET hid = inet_addr(&host[1]); if (hid == INADDR_NONE) #endif { /* try it as a host name (avoid MX lookup) */ hp = sm_gethostbyname(&host[1]); if (hp == NULL && p[-1] == '.') { #if NAMED_BIND int oldopts = _res.options; _res.options &= ~(RES_DEFNAMES|RES_DNSRCH); #endif p[-1] = '\0'; hp = sm_gethostbyname(&host[1]); p[-1] = '.'; #if NAMED_BIND _res.options = oldopts; #endif } *p = ']'; goto gothostent; } *p = ']'; } if (p == NULL) { extern char MsgBuf[]; usrerr("553 Invalid numeric domain spec \"%s\"", host); mci_setstat(mci, EX_NOHOST, "5.1.2", MsgBuf); return EX_NOHOST; } #if NETINET addr.sin.sin_family = AF_INET; /*XXX*/ addr.sin.sin_addr.s_addr = hid; #endif } else { /* contortion to get around SGI cc complaints */ { register char *p = &host[strlen(host) - 1]; hp = sm_gethostbyname(host); if (hp == NULL && *p == '.') { #if NAMED_BIND int oldopts = _res.options; _res.options &= ~(RES_DEFNAMES|RES_DNSRCH); #endif *p = '\0'; hp = sm_gethostbyname(host); *p = '.'; #if NAMED_BIND _res.options = oldopts; #endif } } gothostent: if (hp == NULL) { #if NAMED_BIND /* check for name server timeouts */ if (errno == ETIMEDOUT || h_errno == TRY_AGAIN || (errno == ECONNREFUSED && UseNameServer)) { mci_setstat(mci, EX_TEMPFAIL, "4.4.3", NULL); return EX_TEMPFAIL; } #endif mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); return (EX_NOHOST); } addr.sa.sa_family = hp->h_addrtype; switch (hp->h_addrtype) { #if NETINET case AF_INET: bcopy(hp->h_addr, &addr.sin.sin_addr, INADDRSZ); break; #endif default: bcopy(hp->h_addr, addr.sa.sa_data, hp->h_length); break; } - i = 1; + addrno = 1; } /* ** Determine the port number. */ if (port == 0) { register struct servent *sp = getservbyname("smtp", "tcp"); if (sp == NULL) { -#ifdef LOG if (LogLevel > 2) - syslog(LOG_ERR, "makeconnection: service \"smtp\" unknown"); -#endif + sm_syslog(LOG_ERR, NOQID, + "makeconnection: service \"smtp\" unknown"); port = htons(25); } else port = sp->s_port; } switch (addr.sa.sa_family) { #if NETINET case AF_INET: addr.sin.sin_port = port; addrlen = sizeof (struct sockaddr_in); break; #endif #if NETISO case AF_ISO: /* assume two byte transport selector */ bcopy((char *) &port, TSEL((struct sockaddr_iso *) &addr), 2); addrlen = sizeof (struct sockaddr_iso); break; #endif default: syserr("Can't connect to address family %d", addr.sa.sa_family); mci_setstat(mci, EX_NOHOST, "5.1.2", NULL); return (EX_NOHOST); } /* ** Try to actually open the connection. */ #ifdef XLA /* if too many connections, don't bother trying */ if (!xla_noqueue_ok(host)) return EX_TEMPFAIL; #endif firstconnect = TRUE; for (;;) { if (tTd(16, 1)) printf("makeconnection (%s [%s])\n", host, anynet_ntoa(&addr)); /* save for logging */ CurHostAddr = addr; if (bitnset(M_SECURE_PORT, mci->mci_mailer->m_flags)) { int rport = IPPORT_RESERVED - 1; s = rresvport(&rport); } else { s = socket(AF_INET, SOCK_STREAM, 0); } if (s < 0) { sav_errno = errno; syserr("makeconnection: cannot create socket"); #ifdef XLA xla_host_end(host); #endif mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); return EX_TEMPFAIL; } #ifdef SO_SNDBUF if (TcpSndBufferSize > 0) { if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &TcpSndBufferSize, sizeof(TcpSndBufferSize)) < 0) syserr("makeconnection: setsockopt(SO_SNDBUF)"); } #endif if (tTd(16, 1)) printf("makeconnection: fd=%d\n", s); /* turn on network debugging? */ if (tTd(16, 101)) { int on = 1; (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on); } if (e->e_xfp != NULL) (void) fflush(e->e_xfp); /* for debugging */ errno = 0; /* for debugging */ /* ** Linux seems to hang in connect for 90 minutes (!!!). ** Time out the connect to avoid this problem. */ if (setjmp(CtxConnectTimeout) == 0) { + int i; + if (e->e_ntries <= 0 && TimeOuts.to_iconnect != 0) ev = setevent(TimeOuts.to_iconnect, connecttimeout, 0); else if (TimeOuts.to_connect != 0) ev = setevent(TimeOuts.to_connect, connecttimeout, 0); else ev = NULL; - if (connect(s, (struct sockaddr *) &addr, addrlen) >= 0) - { - if (ev != NULL) - clrevent(ev); + i = connect(s, (struct sockaddr *) &addr, addrlen); + sav_errno = errno; + if (ev != NULL) + clrevent(ev); + if (i >= 0) break; - } } - sav_errno = errno; - if (ev != NULL) - clrevent(ev); + else + sav_errno = errno; /* if running demand-dialed connection, try again */ if (DialDelay > 0 && firstconnect) { if (tTd(16, 1)) printf("Connect failed (%s); trying again...\n", errstring(sav_errno)); firstconnect = FALSE; sleep(DialDelay); continue; } /* couldn't connect.... figure out why */ (void) close(s); - if (hp != NULL && hp->h_addr_list[i]) + if (hp != NULL && hp->h_addr_list[addrno]) { if (tTd(16, 1)) printf("Connect failed (%s); trying new address....\n", errstring(sav_errno)); switch (addr.sa.sa_family) { #if NETINET case AF_INET: - bcopy(hp->h_addr_list[i++], + bcopy(hp->h_addr_list[addrno++], &addr.sin.sin_addr, INADDRSZ); break; #endif default: - bcopy(hp->h_addr_list[i++], + bcopy(hp->h_addr_list[addrno++], addr.sa.sa_data, hp->h_length); break; } continue; } /* couldn't open connection */ #ifdef XLA xla_host_end(host); #endif mci_setstat(mci, EX_TEMPFAIL, "4.4.1", NULL); return EX_TEMPFAIL; } /* connection ok, put it into canonical form */ if ((mci->mci_out = fdopen(s, "w")) == NULL || (s = dup(s)) < 0 || (mci->mci_in = fdopen(s, "r")) == NULL) { syserr("cannot open SMTP client channel, fd=%d", s); mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); return EX_TEMPFAIL; } mci_setstat(mci, EX_OK, NULL, NULL); return (EX_OK); } /* ** MYHOSTNAME -- return the name of this host. ** ** Parameters: ** hostbuf -- a place to return the name of this host. ** size -- the size of hostbuf. ** ** Returns: ** A list of aliases for this host. ** ** Side Effects: ** Adds numeric codes to $=w. */ struct hostent * myhostname(hostbuf, size) char hostbuf[]; int size; { register struct hostent *hp; if (gethostname(hostbuf, size) < 0) { (void) strcpy(hostbuf, "localhost"); } hp = sm_gethostbyname(hostbuf); if (hp == NULL) return NULL; if (strchr(hp->h_name, '.') != NULL || strchr(hostbuf, '.') == NULL) { (void) strncpy(hostbuf, hp->h_name, size - 1); hostbuf[size - 1] = '\0'; } /* ** If there is still no dot in the name, try looking for a ** dotted alias. */ if (strchr(hostbuf, '.') == NULL) { char **ha; for (ha = hp->h_aliases; *ha != NULL; ha++) { if (strchr(*ha, '.') != NULL) { (void) strncpy(hostbuf, *ha, size - 1); hostbuf[size - 1] = '\0'; break; } } } /* ** If _still_ no dot, wait for a while and try again -- it is ** possible that some service is starting up. This can result ** in excessive delays if the system is badly configured, but ** there really isn't a way around that, particularly given that ** the config file hasn't been read at this point. ** All in all, a bit of a mess. */ if (strchr(hostbuf, '.') == NULL && !getcanonname(hostbuf, size, TRUE)) { -#ifdef LOG - syslog(LOG_CRIT, "My unqualified host name (%s) unknown; sleeping for retry", + sm_syslog(LOG_CRIT, NOQID, + "My unqualified host name (%s) unknown; sleeping for retry", hostbuf); -#endif message("My unqualified host name (%s) unknown; sleeping for retry", hostbuf); sleep(60); if (!getcanonname(hostbuf, size, TRUE)) { -#ifdef LOG - syslog(LOG_ALERT, "unable to qualify my own domain name (%s) -- using short name", + sm_syslog(LOG_ALERT, NOQID, + "unable to qualify my own domain name (%s) -- using short name", hostbuf); -#endif message("WARNING: unable to qualify my own domain name (%s) -- using short name", hostbuf); } } return (hp); } /* ** GETAUTHINFO -- get the real host name asociated with a file descriptor ** ** Uses RFC1413 protocol to try to get info from the other end. ** ** Parameters: ** fd -- the descriptor ** ** Returns: ** The user@host information associated with this descriptor. */ static jmp_buf CtxAuthTimeout; static void authtimeout() { longjmp(CtxAuthTimeout, 1); } char * getauthinfo(fd) int fd; { int falen; register char *volatile p = NULL; SOCKADDR la; int lalen; register struct servent *sp; volatile int s; int i; EVENT *ev; int nleft; + struct hostent *hp; + char **ha; + bool may_be_forged; char ibuf[MAXNAME + 1]; static char hbuf[MAXNAME * 2 + 2]; falen = sizeof RealHostAddr; if (isatty(fd) || getpeername(fd, &RealHostAddr.sa, &falen) < 0 || falen <= 0 || RealHostAddr.sa.sa_family == 0) { (void) snprintf(hbuf, sizeof hbuf, "%s@localhost", RealUserName); if (tTd(9, 1)) printf("getauthinfo: %s\n", hbuf); return hbuf; } if (RealHostName == NULL) { /* translate that to a host name */ RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr)); if (strlen(RealHostName) > MAXNAME) RealHostName[MAXNAME - 1] = '\0'; } + /* cross check RealHostName with forward DNS lookup */ + if (anynet_ntoa(&RealHostAddr)[0] == '[') + { + /* address is not a socket */ + may_be_forged = FALSE; + } + else + { + /* try to match the reverse against the forward lookup */ + hp = gethostbyname(RealHostName); + + if (hp == NULL) + may_be_forged = TRUE; + else + { + for (ha = hp->h_addr_list; *ha != NULL; ha++) + if (bcmp(*ha, + (char *) &RealHostAddr.sin.sin_addr, + hp->h_length) == 0) + break; + may_be_forged = *ha == NULL; + } + } + if (TimeOuts.to_ident == 0) goto noident; lalen = sizeof la; if (RealHostAddr.sa.sa_family != AF_INET || getsockname(fd, &la.sa, &lalen) < 0 || lalen <= 0 || la.sa.sa_family != AF_INET) { /* no ident info */ goto noident; } /* create ident query */ (void) snprintf(ibuf, sizeof ibuf, "%d,%d\r\n", ntohs(RealHostAddr.sin.sin_port), ntohs(la.sin.sin_port)); /* create local address */ la.sin.sin_port = 0; /* create foreign address */ sp = getservbyname("auth", "tcp"); if (sp != NULL) RealHostAddr.sin.sin_port = sp->s_port; else RealHostAddr.sin.sin_port = htons(113); s = -1; if (setjmp(CtxAuthTimeout) != 0) { if (s >= 0) (void) close(s); goto noident; } /* put a timeout around the whole thing */ ev = setevent(TimeOuts.to_ident, authtimeout, 0); /* connect to foreign IDENT server using same address as SMTP socket */ s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { clrevent(ev); goto noident; } if (bind(s, &la.sa, sizeof la.sin) < 0 || connect(s, &RealHostAddr.sa, sizeof RealHostAddr.sin) < 0) { goto closeident; } if (tTd(9, 10)) printf("getauthinfo: sent %s", ibuf); /* send query */ if (write(s, ibuf, strlen(ibuf)) < 0) goto closeident; /* get result */ p = &ibuf[0]; nleft = sizeof ibuf - 1; while ((i = read(s, p, nleft)) > 0) { p += i; nleft -= i; *p = '\0'; if (strchr(ibuf, '\n') != NULL) break; } (void) close(s); clrevent(ev); if (i < 0 || p == &ibuf[0]) goto noident; if (*--p == '\n' && *--p == '\r') p--; *++p = '\0'; if (tTd(9, 3)) printf("getauthinfo: got %s\n", ibuf); /* parse result */ p = strchr(ibuf, ':'); if (p == NULL) { /* malformed response */ goto noident; } while (isascii(*++p) && isspace(*p)) continue; if (strncasecmp(p, "userid", 6) != 0) { /* presumably an error string */ goto noident; } p += 6; while (isascii(*p) && isspace(*p)) p++; if (*p++ != ':') { /* either useridxx or malformed response */ goto noident; } /* p now points to the OSTYPE field */ p = strchr(p, ':'); if (p == NULL) { /* malformed response */ goto noident; } /* 1413 says don't do this -- but it's broken otherwise */ while (isascii(*++p) && isspace(*p)) continue; /* p now points to the authenticated name -- copy carefully */ cleanstrcpy(hbuf, p, MAXNAME); i = strlen(hbuf); snprintf(&hbuf[i], sizeof hbuf - i, "@%s", RealHostName == NULL ? "localhost" : RealHostName); goto postident; closeident: (void) close(s); clrevent(ev); noident: if (RealHostName == NULL) { if (tTd(9, 1)) printf("getauthinfo: NULL\n"); return NULL; } snprintf(hbuf, sizeof hbuf, "%s", RealHostName); postident: #if IP_SRCROUTE +# ifndef GET_IPOPT_DST +# define GET_IPOPT_DST(dst) (dst) +# endif /* ** Extract IP source routing information. ** ** Format of output for a connection from site a through b ** through c to d: ** loose: @site-c@site-b:site-a ** strict: !@site-c@site-b:site-a ** ** o - pointer within ipopt_list structure. ** q - pointer within ls/ss rr route data ** p - pointer to hbuf */ if (RealHostAddr.sa.sa_family == AF_INET) { int ipoptlen, j; u_char *q; u_char *o; int l; struct in_addr addr; struct ipoption ipopt; ipoptlen = sizeof ipopt; if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS, (char *) &ipopt, &ipoptlen) < 0) goto noipsr; if (ipoptlen == 0) goto noipsr; o = (u_char *) ipopt.ipopt_list; while (o != NULL && o < (u_char *) &ipopt + ipoptlen) { switch (*o) { case IPOPT_EOL: o = NULL; break; case IPOPT_NOP: o++; break; case IPOPT_SSRR: case IPOPT_LSRR: + /* + ** Source routing. + ** o[0] is the option type (loose/strict). + ** o[1] is the length of this option, + ** including option type and + ** length. + ** o[2] is the pointer into the route + ** data. + ** o[3] begins the route data. + */ + p = &hbuf[strlen(hbuf)]; l = sizeof hbuf - (hbuf - p) - 6; snprintf(p, SPACELEFT(hbuf, p), " [%s@%.*s", *o == IPOPT_SSRR ? "!" : "", l > 240 ? 120 : l / 2, - inet_ntoa(ipopt.ipopt_dst)); + inet_ntoa(GET_IPOPT_DST(ipopt.ipopt_dst))); i = strlen(p); p += i; l -= strlen(p); - /* o[1] is option length */ - j = *++o / sizeof(struct in_addr) - 1; + j = o[1] / sizeof(struct in_addr) - 1; /* q skips length and router pointer to data */ - q = o + 2; + q = &o[3]; for ( ; j >= 0; j--) { memcpy(&addr, q, sizeof(addr)); snprintf(p, SPACELEFT(hbuf, p), "%c%.*s", j != 0 ? '@' : ':', l > 240 ? 120 : j == 0 ? l : l / 2, inet_ntoa(addr)); i = strlen(p); p += i; l -= i + 1; q += sizeof(struct in_addr); } - o += *o; + o += o[1]; break; default: /* Skip over option */ o += o[1]; break; } } snprintf(p, SPACELEFT(hbuf, p), "]"); goto postipsr; } #endif noipsr: if (RealHostName != NULL && RealHostName[0] != '[') { p = &hbuf[strlen(hbuf)]; (void) snprintf(p, SPACELEFT(hbuf, p), " [%.100s]", anynet_ntoa(&RealHostAddr)); } + if (may_be_forged) + { + p = &hbuf[strlen(hbuf)]; + (void) snprintf(p, SPACELEFT(hbuf, p), " (may be forged)"); + } postipsr: if (tTd(9, 1)) printf("getauthinfo: %s\n", hbuf); return hbuf; } /* ** HOST_MAP_LOOKUP -- turn a hostname into canonical form ** ** Parameters: -** map -- a pointer to this map (unused). +** map -- a pointer to this map. ** name -- the (presumably unqualified) hostname. ** av -- unused -- for compatibility with other mapping ** functions. ** statp -- an exit status (out parameter) -- set to ** EX_TEMPFAIL if the name server is unavailable. ** ** Returns: ** The mapping, if found. ** NULL if no mapping found. ** ** Side Effects: ** Looks up the host specified in hbuf. If it is not ** the canonical name for that host, return the canonical -** name. +** name (unless MF_MATCHONLY is set, which will cause the +** status only to be returned). */ char * host_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { register struct hostent *hp; struct in_addr in_addr; char *cp; register STAB *s; char hbuf[MAXNAME + 1]; /* ** See if we have already looked up this name. If so, just ** return it. */ s = stab(name, ST_NAMECANON, ST_ENTER); if (bitset(NCF_VALID, s->s_namecanon.nc_flags)) { if (tTd(9, 1)) printf("host_map_lookup(%s) => CACHE %s\n", name, s->s_namecanon.nc_cname == NULL ? "NULL" : s->s_namecanon.nc_cname); errno = s->s_namecanon.nc_errno; #if NAMED_BIND h_errno = s->s_namecanon.nc_herrno; #endif *statp = s->s_namecanon.nc_stat; if (*statp == EX_TEMPFAIL) { CurEnv->e_status = "4.4.3"; message("851 %s: Name server timeout", shortenstring(name, 33)); } - return s->s_namecanon.nc_cname; + if (*statp != EX_OK) + return NULL; + if (bitset(MF_MATCHONLY, map->map_mflags)) + cp = map_rewrite(map, name, strlen(name), NULL); + else + cp = map_rewrite(map, + s->s_namecanon.nc_cname, + strlen(s->s_namecanon.nc_cname), + av); + return cp; } /* ** If we are running without a regular network connection (usually ** dial-on-demand) and we are just queueing, we want to avoid DNS ** lookups because those could try to connect to a server. */ if (CurEnv->e_sendmode == SM_DEFER) { if (tTd(9, 1)) printf("host_map_lookup(%s) => DEFERRED\n", name); *statp = EX_TEMPFAIL; return NULL; } /* ** If first character is a bracket, then it is an address ** lookup. Address is copied into a temporary buffer to ** strip the brackets and to preserve name if address is ** unknown. */ if (*name != '[') { if (tTd(9, 1)) printf("host_map_lookup(%s) => ", name); s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */ snprintf(hbuf, sizeof hbuf, "%s", name); if (getcanonname(hbuf, sizeof hbuf - 1, !HasWildcardMX)) { if (tTd(9, 1)) printf("%s\n", hbuf); + s->s_namecanon.nc_stat = EX_OK; + s->s_namecanon.nc_cname = newstr(hbuf); if (bitset(MF_MATCHONLY, map->map_mflags)) - { - cp = map_rewrite(map, name, strlen(name), av); - s->s_namecanon.nc_cname = newstr(hbuf); - } + cp = map_rewrite(map, name, strlen(name), NULL); else - { cp = map_rewrite(map, hbuf, strlen(hbuf), av); - s->s_namecanon.nc_cname = newstr(cp); - } return cp; } else { s->s_namecanon.nc_errno = errno; #if NAMED_BIND s->s_namecanon.nc_herrno = h_errno; if (tTd(9, 1)) printf("FAIL (%d)\n", h_errno); switch (h_errno) { case TRY_AGAIN: if (UseNameServer) { CurEnv->e_status = "4.4.3"; message("851 %s: Name server timeout", shortenstring(name, 33)); } *statp = EX_TEMPFAIL; break; case HOST_NOT_FOUND: case NO_DATA: *statp = EX_NOHOST; break; case NO_RECOVERY: *statp = EX_SOFTWARE; break; default: *statp = EX_UNAVAILABLE; break; } #else if (tTd(9, 1)) printf("FAIL\n"); *statp = EX_NOHOST; #endif s->s_namecanon.nc_stat = *statp; return NULL; } } if ((cp = strchr(name, ']')) == NULL) return (NULL); *cp = '\0'; in_addr.s_addr = inet_addr(&name[1]); + *cp = ']'; /* nope -- ask the name server */ hp = sm_gethostbyaddr((char *)&in_addr, INADDRSZ, AF_INET); s->s_namecanon.nc_errno = errno; #if NAMED_BIND s->s_namecanon.nc_herrno = h_errno; #endif s->s_namecanon.nc_flags |= NCF_VALID; /* will be soon */ if (hp == NULL) { s->s_namecanon.nc_stat = *statp = EX_NOHOST; return (NULL); } /* found a match -- copy out */ - cp = map_rewrite(map, (char *) hp->h_name, strlen(hp->h_name), av); s->s_namecanon.nc_stat = *statp = EX_OK; - s->s_namecanon.nc_cname = newstr(cp); + s->s_namecanon.nc_cname = newstr(hp->h_name); + if (bitset(MF_MATCHONLY, map->map_mflags)) + cp = map_rewrite(map, name, strlen(name), NULL); + else + cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av); return cp; } # else /* DAEMON */ /* code for systems without sophisticated networking */ /* ** MYHOSTNAME -- stub version for case of no daemon code. ** ** Can't convert to upper case here because might be a UUCP name. ** ** Mark, you can change this to be anything you want...... */ char ** myhostname(hostbuf, size) char hostbuf[]; int size; { register FILE *f; hostbuf[0] = '\0'; f = fopen("/usr/include/whoami", "r"); if (f != NULL) { (void) fgets(hostbuf, size, f); fixcrlf(hostbuf, TRUE); (void) fclose(f); } return (NULL); } /* ** GETAUTHINFO -- get the real host name asociated with a file descriptor ** ** Parameters: ** fd -- the descriptor ** ** Returns: ** The host name associated with this descriptor, if it can ** be determined. ** NULL otherwise. ** ** Side Effects: ** none */ char * getauthinfo(fd) int fd; { return NULL; } /* ** MAPHOSTNAME -- turn a hostname into canonical form ** ** Parameters: ** map -- a pointer to the database map. ** name -- a buffer containing a hostname. ** avp -- a pointer to a (cf file defined) argument vector. ** statp -- an exit status (out parameter). ** ** Returns: ** mapped host name ** FALSE otherwise. ** ** Side Effects: ** Looks up the host specified in name. If it is not ** the canonical name for that host, replace it with ** the canonical name. If the name is unknown, or it ** is already the canonical name, leave it unchanged. */ /*ARGSUSED*/ char * host_map_lookup(map, name, avp, statp) MAP *map; char *name; char **avp; char *statp; { register struct hostent *hp; + char *cp; hp = sm_gethostbyname(name); - if (hp != NULL) - return hp->h_name; - *statp = EX_NOHOST; - return NULL; + if (hp == NULL) + { + *statp = EX_NOHOST; + return NULL; + } + if (bitset(MF_MATCHONLY, map->map_mflags)) + cp = map_rewrite(map, name, strlen(name), NULL); + else + cp = map_rewrite(map, hp->h_name, strlen(hp->h_name), av); + return cp; } #endif /* DAEMON */ /* +** HOST_MAP_INIT -- initialize host class structures +*/ + +bool +host_map_init(map, args) + MAP *map; + char *args; +{ + register char *p = args; + + for (;;) + { + while (isascii(*p) && isspace(*p)) + p++; + if (*p != '-') + break; + switch (*++p) + { + case 'a': + map->map_app = ++p; + break; + + case 'm': + map->map_mflags |= MF_MATCHONLY; + break; + + case 't': + map->map_mflags |= MF_NODEFER; + break; + } + while (*p != '\0' && !(isascii(*p) && isspace(*p))) + p++; + if (*p != '\0') + *p++ = '\0'; + } + if (map->map_app != NULL) + map->map_app = newstr(map->map_app); + return TRUE; +} + /* ** ANYNET_NTOA -- convert a network address to printable form. ** ** Parameters: ** sap -- a pointer to a sockaddr structure. ** ** Returns: ** A printable version of that sockaddr. */ -#ifdef SOCK_STREAM +#ifdef USE_SOCK_STREAM #if NETLINK # include #endif char * anynet_ntoa(sap) register SOCKADDR *sap; { register char *bp; register char *ap; int l; static char buf[100]; /* check for null/zero family */ if (sap == NULL) return "NULLADDR"; if (sap->sa.sa_family == 0) return "0"; switch (sap->sa.sa_family) { #if NETUNIX case AF_UNIX: if (sap->sunix.sun_path[0] != '\0') snprintf(buf, sizeof buf, "[UNIX: %.64s]", sap->sunix.sun_path); else snprintf(buf, sizeof buf, "[UNIX: localhost]"); return buf; #endif #if NETINET case AF_INET: return inet_ntoa(sap->sin.sin_addr); #endif #if NETLINK case AF_LINK: snprintf(buf, sizeof buf, "[LINK: %s]", link_ntoa((struct sockaddr_dl *) &sap->sa)); return buf; #endif default: /* this case is needed when nothing is #defined */ /* in order to keep the switch syntactically correct */ break; } /* unknown family -- just dump bytes */ (void) snprintf(buf, sizeof buf, "Family %d: ", sap->sa.sa_family); bp = &buf[strlen(buf)]; ap = sap->sa.sa_data; for (l = sizeof sap->sa.sa_data; --l >= 0; ) { (void) snprintf(bp, SPACELEFT(buf, bp), "%02x:", *ap++ & 0377); bp += 3; } *--bp = '\0'; return buf; } /* ** HOSTNAMEBYANYADDR -- return name of host based on address ** ** Parameters: ** sap -- SOCKADDR pointer ** ** Returns: ** text representation of host name. ** ** Side Effects: ** none. */ char * hostnamebyanyaddr(sap) register SOCKADDR *sap; { register struct hostent *hp; int saveretry; #if NAMED_BIND /* shorten name server timeout to avoid higher level timeouts */ saveretry = _res.retry; _res.retry = 3; #endif /* NAMED_BIND */ switch (sap->sa.sa_family) { #if NETINET case AF_INET: hp = sm_gethostbyaddr((char *) &sap->sin.sin_addr, INADDRSZ, AF_INET); break; #endif #if NETISO case AF_ISO: hp = sm_gethostbyaddr((char *) &sap->siso.siso_addr, sizeof sap->siso.siso_addr, AF_ISO); break; #endif #if NETUNIX case AF_UNIX: hp = NULL; break; #endif default: hp = sm_gethostbyaddr(sap->sa.sa_data, sizeof sap->sa.sa_data, sap->sa.sa_family); break; } #if NAMED_BIND _res.retry = saveretry; #endif /* NAMED_BIND */ if (hp != NULL) return (char *) hp->h_name; else { /* produce a dotted quad */ static char buf[203]; (void) snprintf(buf, sizeof buf, "[%.200s]", anynet_ntoa(sap)); return buf; } } #endif /* SOCK_STREAM */ Index: head/usr.sbin/sendmail/src/deliver.c =================================================================== --- head/usr.sbin/sendmail/src/deliver.c (revision 26988) +++ head/usr.sbin/sendmail/src/deliver.c (revision 26989) @@ -1,3411 +1,3465 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)deliver.c 8.266 (Berkeley) 1/17/97"; +static char sccsid[] = "@(#)deliver.c 8.282 (Berkeley) 6/11/97"; #endif /* not lint */ #include "sendmail.h" #include #if NAMED_BIND #include extern int h_errno; #endif #if SMTP extern char SmtpError[]; #endif /* ** SENDALL -- actually send all the messages. ** ** Parameters: ** e -- the envelope to send. ** mode -- the delivery mode to use. If SM_DEFAULT, use ** the current e->e_sendmode. ** ** Returns: ** none. ** ** Side Effects: ** Scans the send lists and sends everything it finds. ** Delivers any appropriate error messages. ** If we are running in a non-interactive mode, takes the ** appropriate action. */ void sendall(e, mode) ENVELOPE *e; int mode; { register ADDRESS *q; char *owner; int otherowners; register ENVELOPE *ee; ENVELOPE *splitenv = NULL; - bool oldverbose = Verbose; + int oldverbose = Verbose; bool somedeliveries = FALSE; pid_t pid; extern void sendenvelope(); /* ** If we have had global, fatal errors, don't bother sending ** the message at all if we are in SMTP mode. Local errors ** (e.g., a single address failing) will still cause the other ** addresses to be sent. */ if (bitset(EF_FATALERRS, e->e_flags) && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) { e->e_flags |= EF_CLRQUEUE; return; } /* determine actual delivery mode */ - CurrentLA = getla(); if (mode == SM_DEFAULT) { mode = e->e_sendmode; if (mode != SM_VERIFY && mode != SM_DEFER && shouldqueue(e->e_msgpriority, e->e_ctime)) mode = SM_QUEUE; } if (tTd(13, 1)) { extern void printenvflags(); printf("\n===== SENDALL: mode %c, id %s, e_from ", mode, e->e_id); printaddr(&e->e_from, FALSE); printf("\te_flags = "); printenvflags(e); printf("sendqueue:\n"); printaddr(e->e_sendqueue, TRUE); } /* ** Do any preprocessing necessary for the mode we are running. ** Check to make sure the hop count is reasonable. ** Delete sends to the sender in mailing lists. */ CurEnv = e; if (tTd(62, 1)) checkfds(NULL); if (e->e_hopcount > MaxHopCount) { errno = 0; #if QUEUE queueup(e, mode == SM_QUEUE || mode == SM_DEFER); #endif e->e_flags |= EF_FATALERRS|EF_PM_NOTIFY|EF_CLRQUEUE; syserr("554 Too many hops %d (%d max): from %s via %s, to %s", e->e_hopcount, MaxHopCount, e->e_from.q_paddr, RealHostName == NULL ? "localhost" : RealHostName, e->e_sendqueue->q_paddr); e->e_sendqueue->q_status = "5.4.6"; return; } /* ** Do sender deletion. ** ** If the sender has the QQUEUEUP flag set, skip this. ** This can happen if the name server is hosed when you ** are trying to send mail. The result is that the sender ** is instantiated in the queue as a recipient. */ if (!bitset(EF_METOO, e->e_flags) && !bitset(QQUEUEUP, e->e_from.q_flags)) { if (tTd(13, 5)) { printf("sendall: QDONTSEND "); printaddr(&e->e_from, FALSE); } e->e_from.q_flags |= QDONTSEND; (void) recipient(&e->e_from, &e->e_sendqueue, 0, e); } /* ** Handle alias owners. ** ** We scan up the q_alias chain looking for owners. ** We discard owners that are the same as the return path. */ for (q = e->e_sendqueue; q != NULL; q = q->q_next) { register struct address *a; for (a = q; a != NULL && a->q_owner == NULL; a = a->q_alias) continue; if (a != NULL) q->q_owner = a->q_owner; if (q->q_owner != NULL && !bitset(QDONTSEND, q->q_flags) && strcmp(q->q_owner, e->e_from.q_paddr) == 0) q->q_owner = NULL; } if (tTd(13, 25)) { printf("\nAfter first owner pass, sendq =\n"); printaddr(e->e_sendqueue, TRUE); } owner = ""; otherowners = 1; while (owner != NULL && otherowners > 0) { if (tTd(13, 28)) printf("owner = \"%s\", otherowners = %d\n", owner, otherowners); owner = NULL; otherowners = bitset(EF_SENDRECEIPT, e->e_flags) ? 1 : 0; for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (tTd(13, 30)) { printf("Checking "); printaddr(q, FALSE); } if (bitset(QDONTSEND, q->q_flags)) { if (tTd(13, 30)) printf(" ... QDONTSEND\n"); continue; } if (tTd(13, 29) && !tTd(13, 30)) { printf("Checking "); printaddr(q, FALSE); } if (q->q_owner != NULL) { if (owner == NULL) { if (tTd(13, 40)) printf(" ... First owner = \"%s\"\n", q->q_owner); owner = q->q_owner; } else if (owner != q->q_owner) { if (strcmp(owner, q->q_owner) == 0) { if (tTd(13, 40)) printf(" ... Same owner = \"%s\"\n", owner); /* make future comparisons cheap */ q->q_owner = owner; } else { if (tTd(13, 40)) printf(" ... Another owner \"%s\"\n", q->q_owner); otherowners++; } owner = q->q_owner; } else if (tTd(13, 40)) printf(" ... Same owner = \"%s\"\n", owner); } else { if (tTd(13, 40)) printf(" ... Null owner\n"); otherowners++; } /* ** If this mailer is expensive, and if we don't ** want to make connections now, just mark these ** addresses and return. This is useful if we ** want to batch connections to reduce load. This ** will cause the messages to be queued up, and a ** daemon will come along to send the messages later. */ if (bitset(QBADADDR|QQUEUEUP, q->q_flags)) { if (tTd(13, 30)) printf(" ... QBADADDR|QQUEUEUP\n"); continue; } if (NoConnect && !Verbose && bitnset(M_EXPENSIVE, q->q_mailer->m_flags)) { if (tTd(13, 30)) printf(" ... expensive\n"); q->q_flags |= QQUEUEUP; } else { if (tTd(13, 30)) printf(" ... deliverable\n"); somedeliveries = TRUE; } } if (owner != NULL && otherowners > 0) { extern HDR *copyheader(); extern ADDRESS *copyqueue(); + extern void dup_queue_file __P((ENVELOPE *, ENVELOPE *, int)); /* ** Split this envelope into two. */ ee = (ENVELOPE *) xalloc(sizeof(ENVELOPE)); *ee = *e; ee->e_id = NULL; (void) queuename(ee, '\0'); if (tTd(13, 1)) printf("sendall: split %s into %s, owner = \"%s\", otherowners = %d\n", e->e_id, ee->e_id, owner, otherowners); ee->e_header = copyheader(e->e_header); ee->e_sendqueue = copyqueue(e->e_sendqueue); ee->e_errorqueue = copyqueue(e->e_errorqueue); ee->e_flags = e->e_flags & ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS|EF_SENDRECEIPT|EF_RET_PARAM); ee->e_flags |= EF_NORECEIPT; setsender(owner, ee, NULL, '\0', TRUE); if (tTd(13, 5)) { printf("sendall(split): QDONTSEND "); printaddr(&ee->e_from, FALSE); } ee->e_from.q_flags |= QDONTSEND; ee->e_dfp = NULL; ee->e_xfp = NULL; ee->e_errormode = EM_MAIL; ee->e_sibling = splitenv; splitenv = ee; for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (q->q_owner == owner) { q->q_flags |= QDONTSEND; q->q_flags &= ~(QQUEUEUP|QBADADDR); if (tTd(13, 6)) printf("\t... stripping %s from original envelope\n", q->q_paddr); } } for (q = ee->e_sendqueue; q != NULL; q = q->q_next) { if (q->q_owner != owner) { q->q_flags |= QDONTSEND; q->q_flags &= ~(QQUEUEUP|QBADADDR); if (tTd(13, 6)) printf("\t... dropping %s from cloned envelope\n", q->q_paddr); } else { /* clear DSN parameters */ q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); q->q_flags |= DefaultNotify & ~QPINGONSUCCESS; if (tTd(13, 6)) printf("\t... moving %s to cloned envelope\n", q->q_paddr); } } if (mode != SM_VERIFY && bitset(EF_HAS_DF, e->e_flags)) - { - char df1buf[20], df2buf[20]; - - ee->e_dfp = NULL; - snprintf(df1buf, sizeof df1buf, "%s", - queuename(e, 'd')); - snprintf(df2buf, sizeof df2buf, "%s", - queuename(ee, 'd')); - if (link(df1buf, df2buf) < 0) - { - int saverrno = errno; - - syserr("sendall: link(%s, %s)", - df1buf, df2buf); - if (saverrno == EEXIST) - { - if (unlink(df2buf) < 0) - { - syserr("!sendall: unlink(%s): permanent", - df2buf); - /*NOTREACHED*/ - } - if (link(df1buf, df2buf) < 0) - { - syserr("!sendall: link(%s, %s): permanent", - df1buf, df2buf); - /*NOTREACHED*/ - } - } - } - } -#ifdef LOG + dup_queue_file(e, ee, 'd'); + openxscript(ee); if (LogLevel > 4) - syslog(LOG_INFO, "%s: clone %s, owner=%s", - ee->e_id, e->e_id, owner); -#endif + sm_syslog(LOG_INFO, ee->e_id, + "clone %s, owner=%s", + e->e_id, owner); } } if (owner != NULL) { setsender(owner, e, NULL, '\0', TRUE); if (tTd(13, 5)) { printf("sendall(owner): QDONTSEND "); printaddr(&e->e_from, FALSE); } e->e_from.q_flags |= QDONTSEND; e->e_errormode = EM_MAIL; e->e_flags |= EF_NORECEIPT; e->e_flags &= ~EF_FATALERRS; } /* if nothing to be delivered, just queue up everything */ if (!somedeliveries && mode != SM_QUEUE && mode != SM_DEFER && mode != SM_VERIFY) { if (tTd(13, 29)) printf("No deliveries: auto-queuing\n"); mode = SM_QUEUE; + + /* treat this as a delivery in terms of counting tries */ + e->e_dtime = curtime(); + e->e_ntries++; + for (ee = splitenv; ee != NULL; ee = ee->e_sibling) + { + ee->e_dtime = curtime(); + ee->e_ntries++; + } } # if QUEUE if ((mode == SM_QUEUE || mode == SM_DEFER || mode == SM_FORK || (mode != SM_VERIFY && SuperSafe)) && (!bitset(EF_INQUEUE, e->e_flags) || splitenv != NULL)) { /* be sure everything is instantiated in the queue */ queueup(e, mode == SM_QUEUE || mode == SM_DEFER); for (ee = splitenv; ee != NULL; ee = ee->e_sibling) queueup(ee, mode == SM_QUEUE || mode == SM_DEFER); } #endif /* QUEUE */ if (tTd(62, 10)) checkfds("after envelope splitting"); /* ** If we belong in background, fork now. */ if (tTd(13, 20)) { printf("sendall: final mode = %c\n", mode); if (tTd(13, 21)) { printf("\n================ Final Send Queue(s) =====================\n"); printf("\n *** Envelope %s, e_from=%s ***\n", e->e_id, e->e_from.q_paddr); printaddr(e->e_sendqueue, TRUE); for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { printf("\n *** Envelope %s, e_from=%s ***\n", ee->e_id, ee->e_from.q_paddr); printaddr(ee->e_sendqueue, TRUE); } printf("==========================================================\n\n"); } } switch (mode) { case SM_VERIFY: - Verbose = TRUE; + Verbose = 2; break; case SM_QUEUE: case SM_DEFER: queueonly: if (e->e_nrcpts > 0) e->e_flags |= EF_INQUEUE; dropenvelope(e, FALSE); for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { if (ee->e_nrcpts > 0) ee->e_flags |= EF_INQUEUE; dropenvelope(ee, FALSE); } return; case SM_FORK: if (e->e_xfp != NULL) (void) fflush(e->e_xfp); # if !HASFLOCK /* ** Since fcntl locking has the interesting semantic that ** the lock is owned by a process, not by an open file ** descriptor, we have to flush this to the queue, and ** then restart from scratch in the child. */ { /* save id for future use */ char *qid = e->e_id; /* now drop the envelope in the parent */ e->e_flags |= EF_INQUEUE; dropenvelope(e, FALSE); /* arrange to reacquire lock after fork */ e->e_id = qid; } for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { /* save id for future use */ char *qid = ee->e_id; /* drop envelope in parent */ ee->e_flags |= EF_INQUEUE; dropenvelope(ee, FALSE); /* and save qid for reacquisition */ ee->e_id = qid; } # endif /* !HASFLOCK */ pid = fork(); if (pid < 0) { goto queueonly; } else if (pid > 0) { # if HASFLOCK /* be sure we leave the temp files to our child */ /* can't call unlockqueue to avoid unlink of xfp */ if (e->e_lockfp != NULL) (void) xfclose(e->e_lockfp, "sendenvelope lockfp", e->e_id); e->e_lockfp = NULL; /* close any random open files in the envelope */ closexscript(e); if (e->e_dfp != NULL) (void) xfclose(e->e_dfp, "sendenvelope dfp", e->e_id); e->e_dfp = NULL; e->e_flags &= ~EF_HAS_DF; # endif /* make sure the parent doesn't own the envelope */ e->e_id = NULL; /* catch intermediate zombie */ (void) waitfor(pid); return; } /* double fork to avoid zombies */ pid = fork(); if (pid > 0) exit(EX_OK); /* be sure we are immune from the terminal */ disconnect(2, e); /* prevent parent from waiting if there was an error */ if (pid < 0) { e->e_flags |= EF_INQUEUE; finis(); } + /* be sure to give error messages in child */ + QuickAbort = OnlyOneError = FALSE; + /* ** Close any cached connections. ** ** We don't send the QUIT protocol because the parent ** still knows about the connection. ** ** This should only happen when delivering an error ** message. */ mci_flush(FALSE, NULL); # if HASFLOCK break; # else /* ** Now reacquire and run the various queue files. */ for (ee = splitenv; ee != NULL; ee = e->e_sibling) (void) dowork(ee->e_id, FALSE, FALSE, ee); (void) dowork(e->e_id, FALSE, FALSE, e); finis(); # endif /* !HASFLOCK */ } sendenvelope(e, mode); dropenvelope(e, TRUE); for (ee = splitenv; ee != NULL; ee = ee->e_sibling) { CurEnv = ee; if (mode != SM_VERIFY) openxscript(ee); sendenvelope(ee, mode); dropenvelope(ee, TRUE); } CurEnv = e; Verbose = oldverbose; if (mode == SM_FORK) finis(); } void sendenvelope(e, mode) register ENVELOPE *e; char mode; { register ADDRESS *q; bool didany; if (tTd(13, 10)) printf("sendenvelope(%s) e_flags=0x%lx\n", e->e_id == NULL ? "[NOQUEUE]" : e->e_id, e->e_flags); -#ifdef LOG if (LogLevel > 80) - syslog(LOG_DEBUG, "%s: sendenvelope, flags=0x%x", - e->e_id == NULL ? "[NOQUEUE]" : e->e_id, + sm_syslog(LOG_DEBUG, e->e_id, + "sendenvelope, flags=0x%x", e->e_flags); -#endif /* ** If we have had global, fatal errors, don't bother sending ** the message at all if we are in SMTP mode. Local errors ** (e.g., a single address failing) will still cause the other ** addresses to be sent. */ if (bitset(EF_FATALERRS, e->e_flags) && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) { e->e_flags |= EF_CLRQUEUE; return; } /* ** Run through the list and send everything. ** ** Set EF_GLOBALERRS so that error messages during delivery ** result in returned mail. */ e->e_nsent = 0; e->e_flags |= EF_GLOBALERRS; define(macid("{envid}", NULL), e->e_envid, e); define(macid("{bodytype}", NULL), e->e_bodytype, e); didany = FALSE; /* now run through the queue */ for (q = e->e_sendqueue; q != NULL; q = q->q_next) { #if XDEBUG char wbuf[MAXNAME + 20]; (void) snprintf(wbuf, sizeof wbuf, "sendall(%.*s)", MAXNAME, q->q_paddr); checkfd012(wbuf); #endif if (mode == SM_VERIFY) { e->e_to = q->q_paddr; if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) { if (q->q_host != NULL && q->q_host[0] != '\0') message("deliverable: mailer %s, host %s, user %s", q->q_mailer->m_name, q->q_host, q->q_user); else message("deliverable: mailer %s, user %s", q->q_mailer->m_name, q->q_user); } } else if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) { extern int deliver __P((ENVELOPE *, ADDRESS *)); # if QUEUE /* ** Checkpoint the send list every few addresses */ if (e->e_nsent >= CheckpointInterval) { queueup(e, FALSE); e->e_nsent = 0; } # endif /* QUEUE */ (void) deliver(e, q); didany = TRUE; } } if (didany) { e->e_dtime = curtime(); e->e_ntries++; } #if XDEBUG checkfd012("end of sendenvelope"); #endif } /* +** DUP_QUEUE_FILE -- duplicate a queue file into a split queue +** +** Parameters: +** e -- the existing envelope +** ee -- the new envelope +** type -- the queue file type (e.g., 'd') +** +** Returns: +** none +*/ + +void +dup_queue_file(e, ee, type) + struct envelope *e, *ee; + int type; +{ + char f1buf[MAXQFNAME], f2buf[MAXQFNAME]; + + ee->e_dfp = NULL; + ee->e_xfp = NULL; + snprintf(f1buf, sizeof f1buf, "%s", queuename(e, type)); + snprintf(f2buf, sizeof f2buf, "%s", queuename(ee, type)); + if (link(f1buf, f2buf) < 0) + { + int saverrno = errno; + + syserr("sendall: link(%s, %s)", f1buf, f2buf); + if (saverrno == EEXIST) + { + if (unlink(f2buf) < 0) + { + syserr("!sendall: unlink(%s): permanent", + f2buf); + /*NOTREACHED*/ + } + if (link(f1buf, f2buf) < 0) + { + syserr("!sendall: link(%s, %s): permanent", + f1buf, f2buf); + /*NOTREACHED*/ + } + } + } +} + /* ** DOFORK -- do a fork, retrying a couple of times on failure. ** ** This MUST be a macro, since after a vfork we are running ** two processes on the same stack!!! ** ** Parameters: ** none. ** ** Returns: ** From a macro??? You've got to be kidding! ** ** Side Effects: ** Modifies the ==> LOCAL <== variable 'pid', leaving: ** pid of child in parent, zero in child. ** -1 on unrecoverable error. ** ** Notes: ** I'm awfully sorry this looks so awful. That's ** vfork for you..... */ # define NFORKTRIES 5 # ifndef FORK # define FORK fork # endif # define DOFORK(fORKfN) \ {\ register int i;\ \ for (i = NFORKTRIES; --i >= 0; )\ {\ pid = fORKfN();\ if (pid >= 0)\ break;\ if (i > 0)\ sleep((unsigned) NFORKTRIES - i);\ }\ } /* ** DOFORK -- simple fork interface to DOFORK. ** ** Parameters: ** none. ** ** Returns: ** pid of child in parent. ** zero in child. ** -1 on error. ** ** Side Effects: ** returns twice, once in parent and once in child. */ int dofork() { register pid_t pid = -1; DOFORK(fork); return (pid); } /* ** DELIVER -- Deliver a message to a list of addresses. ** ** This routine delivers to everyone on the same host as the ** user on the head of the list. It is clever about mailers ** that don't handle multiple users. It is NOT guaranteed ** that it will deliver to all these addresses however -- so ** deliver should be called once for each address on the ** list. ** ** Parameters: ** e -- the envelope to deliver. ** firstto -- head of the address list to deliver to. ** ** Returns: ** zero -- successfully delivered. ** else -- some failure, see ExitStat for more info. ** ** Side Effects: ** The standard input is passed off to someone. */ #ifndef NO_UID # define NO_UID -1 #endif #ifndef NO_GID # define NO_GID -1 #endif int deliver(e, firstto) register ENVELOPE *e; ADDRESS *firstto; { char *host; /* host being sent to */ char *user; /* user being sent to */ char **pvp; register char **mvp; register char *p; register MAILER *m; /* mailer for this recipient */ ADDRESS *volatile ctladdr; ADDRESS *volatile contextaddr = NULL; register MCI *volatile mci; register ADDRESS *to = firstto; volatile bool clever = FALSE; /* running user smtp to this mailer */ ADDRESS *volatile tochain = NULL; /* users chain in this mailer call */ int rcode; /* response code */ char *firstsig; /* signature of firstto */ pid_t pid = -1; char *volatile curhost; register volatile u_short port = 0; time_t xstart; bool suidwarn; bool anyok; /* at least one address was OK */ + bool goodmxfound = FALSE; /* at least one MX was OK */ int mpvect[2]; int rpvect[2]; char *pv[MAXPV+1]; char tobuf[TOBUFSIZE]; /* text line of to people */ char buf[MAXNAME + 1]; char rpathbuf[MAXNAME + 1]; /* translated return path */ extern int checkcompat(); extern void markfailure __P((ENVELOPE *, ADDRESS *, MCI *, int)); errno = 0; if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags)) return (0); suidwarn = geteuid() == 0; #if NAMED_BIND /* unless interactive, try twice, over a minute */ if (OpMode == MD_DAEMON || OpMode == MD_SMTP) { _res.retrans = 30; _res.retry = 2; } #endif m = to->q_mailer; host = to->q_host; CurEnv = e; /* just in case */ e->e_statmsg = NULL; #if SMTP SmtpError[0] = '\0'; #endif xstart = curtime(); if (tTd(10, 1)) printf("\n--deliver, id=%s, mailer=%s, host=`%s', first user=`%s'\n", e->e_id, m->m_name, host, to->q_user); if (tTd(10, 100)) printopenfds(FALSE); /* ** Do initial argv setup. ** Insert the mailer name. Notice that $x expansion is ** NOT done on the mailer name. Then, if the mailer has ** a picky -f flag, we insert it as appropriate. This ** code does not check for 'pv' overflow; this places a ** manifest lower limit of 4 for MAXPV. ** The from address rewrite is expected to make ** the address relative to the other end. */ /* rewrite from address, using rewriting rules */ rcode = EX_OK; if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) p = e->e_sender; else p = e->e_from.q_paddr; p = remotename(p, m, RF_SENDERADDR|RF_CANONICAL, &rcode, e); if (strlen(p) >= (SIZE_T) sizeof rpathbuf) { p = shortenstring(p, 203); syserr("remotename: huge return %s", p); } snprintf(rpathbuf, sizeof rpathbuf, "%s", p); define('g', rpathbuf, e); /* translated return path */ define('h', host, e); /* to host */ Errors = 0; pvp = pv; *pvp++ = m->m_argv[0]; /* insert -f or -r flag as appropriate */ if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags))) { if (bitnset(M_FOPT, m->m_flags)) *pvp++ = "-f"; else *pvp++ = "-r"; *pvp++ = newstr(rpathbuf); } /* ** Append the other fixed parts of the argv. These run ** up to the first entry containing "$u". There can only ** be one of these, and there are only a few more slots ** in the pv after it. */ for (mvp = m->m_argv; (p = *++mvp) != NULL; ) { /* can't use strchr here because of sign extension problems */ while (*p != '\0') { if ((*p++ & 0377) == MACROEXPAND) { if (*p == 'u') break; } } if (*p != '\0') break; /* this entry is safe -- go ahead and process it */ expand(*mvp, buf, sizeof buf, e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV - 3]) { syserr("554 Too many parameters to %s before $u", pv[0]); return (-1); } } /* ** If we have no substitution for the user name in the argument ** list, we know that we must supply the names otherwise -- and ** SMTP is the answer!! */ if (*mvp == NULL) { /* running SMTP */ # if SMTP clever = TRUE; *pvp = NULL; # else /* SMTP */ /* oops! we don't implement SMTP */ syserr("554 SMTP style mailer not implemented"); return (EX_SOFTWARE); # endif /* SMTP */ } /* ** At this point *mvp points to the argument with $u. We ** run through our address list and append all the addresses ** we can. If we run out of space, do not fret! We can ** always send another copy later. */ tobuf[0] = '\0'; e->e_to = tobuf; ctladdr = NULL; firstsig = hostsignature(firstto->q_mailer, firstto->q_host, e); for (; to != NULL; to = to->q_next) { /* avoid sending multiple recipients to dumb mailers */ if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags)) break; /* if already sent or not for this host, don't send */ if (bitset(QDONTSEND|QBADADDR|QQUEUEUP, to->q_flags) || to->q_mailer != firstto->q_mailer || strcmp(hostsignature(to->q_mailer, to->q_host, e), firstsig) != 0) continue; /* avoid overflowing tobuf */ if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2)) break; if (tTd(10, 1)) { printf("\nsend to "); printaddr(to, FALSE); } /* compute effective uid/gid when sending */ if (bitnset(M_RUNASRCPT, to->q_mailer->m_flags)) contextaddr = ctladdr = getctladdr(to); if (tTd(10, 2)) { printf("ctladdr="); printaddr(ctladdr, FALSE); } user = to->q_user; e->e_to = to->q_paddr; if (tTd(10, 5)) { printf("deliver: QDONTSEND "); printaddr(to, FALSE); } to->q_flags |= QDONTSEND; /* ** Check to see that these people are allowed to ** talk to each other. */ if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize) { e->e_flags |= EF_NO_BODY_RETN; to->q_status = "5.2.3"; usrerr("552 Message is too large; %ld bytes max", m->m_maxsize); + markfailure(e, to, NULL, EX_UNAVAILABLE); giveresponse(EX_UNAVAILABLE, m, NULL, ctladdr, xstart, e); continue; } #if NAMED_BIND h_errno = 0; #endif /* do config file checking of compatibility */ rcode = rscheck("check_compat", e->e_from.q_paddr, to->q_paddr, e); if (rcode == EX_OK) { /* do in-code checking */ rcode = checkcompat(to, e); } if (rcode != EX_OK) { markfailure(e, to, NULL, rcode); giveresponse(rcode, m, NULL, ctladdr, xstart, e); continue; } /* ** Strip quote bits from names if the mailer is dumb ** about them. */ if (bitnset(M_STRIPQ, m->m_flags)) { stripquotes(user); stripquotes(host); } /* hack attack -- delivermail compatibility */ if (m == ProgMailer && *user == '|') user++; /* ** If an error message has already been given, don't ** bother to send to this address. ** ** >>>>>>>>>> This clause assumes that the local mailer ** >> NOTE >> cannot do any further aliasing; that ** >>>>>>>>>> function is subsumed by sendmail. */ if (bitset(QBADADDR|QQUEUEUP, to->q_flags)) continue; /* ** See if this user name is "special". ** If the user name has a slash in it, assume that this ** is a file -- send it off without further ado. Note ** that this type of addresses is not processed along ** with the others, so we fudge on the To person. */ if (strcmp(m->m_mailer, "[FILE]") == 0) { rcode = mailfile(user, ctladdr, SFF_CREAT, e); giveresponse(rcode, m, NULL, ctladdr, xstart, e); e->e_nsent++; if (rcode == EX_OK) { to->q_flags |= QSENT; markstats(e, to); if (bitnset(M_LOCALMAILER, m->m_flags) && bitset(QPINGONSUCCESS, to->q_flags)) { to->q_flags |= QDELIVERED; to->q_status = "2.1.5"; fprintf(e->e_xfp, "%s... Successfully delivered\n", to->q_paddr); } } to->q_statdate = curtime(); continue; } /* ** Address is verified -- add this user to mailer ** argv, and add it to the print list of recipients. */ /* link together the chain of recipients */ to->q_tchain = tochain; tochain = to; /* create list of users for error messages */ (void) strcat(tobuf, ","); (void) strcat(tobuf, to->q_paddr); define('u', user, e); /* to user */ p = to->q_home; if (p == NULL && ctladdr != NULL) p = ctladdr->q_home; define('z', p, e); /* user's home */ /* ** Expand out this user into argument list. */ if (!clever) { expand(*mvp, buf, sizeof buf, e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV - 2]) { /* allow some space for trailing parms */ break; } } } /* see if any addresses still exist */ if (tobuf[0] == '\0') { define('g', (char *) NULL, e); return (0); } /* print out messages as full list */ e->e_to = tobuf + 1; /* ** Fill out any parameters after the $u parameter. */ while (!clever && *++mvp != NULL) { expand(*mvp, buf, sizeof buf, e); *pvp++ = newstr(buf); if (pvp >= &pv[MAXPV]) syserr("554 deliver: pv overflow after $u for %s", pv[0]); } *pvp++ = NULL; /* ** Call the mailer. ** The argument vector gets built, pipes ** are created as necessary, and we fork & exec as ** appropriate. ** If we are running SMTP, we just need to clean up. */ /*XXX this seems a bit wierd */ if (ctladdr == NULL && m != ProgMailer && m != FileMailer && bitset(QGOODUID, e->e_from.q_flags)) ctladdr = &e->e_from; #if NAMED_BIND if (ConfigLevel < 2) _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ #endif if (tTd(11, 1)) { printf("openmailer:"); printav(pv); } errno = 0; #if NAMED_BIND h_errno = 0; #endif CurHostName = NULL; /* ** Deal with the special case of mail handled through an IPC ** connection. ** In this case we don't actually fork. We must be ** running SMTP for this to work. We will return a ** zero pid to indicate that we are running IPC. ** We also handle a debug version that just talks to stdin/out. */ curhost = NULL; SmtpPhase = NULL; mci = NULL; #if XDEBUG { char wbuf[MAXLINE]; /* make absolutely certain 0, 1, and 2 are in use */ snprintf(wbuf, sizeof wbuf, "%s... openmailer(%s)", shortenstring(e->e_to, 203), m->m_name); checkfd012(wbuf); } #endif /* check for 8-bit available */ if (bitset(EF_HAS8BIT, e->e_flags) && bitnset(M_7BITS, m->m_flags) && (bitset(EF_DONT_MIME, e->e_flags) || !(bitset(MM_MIME8BIT, MimeMode) || (bitset(EF_IS_MIME, e->e_flags) && bitset(MM_CVTMIME, MimeMode))))) { usrerr("554 Cannot send 8-bit data to 7-bit destination"); rcode = EX_DATAERR; e->e_status = "5.6.3"; goto give_up; } if (tTd(62, 8)) checkfds("before delivery"); /* check for Local Person Communication -- not for mortals!!! */ if (strcmp(m->m_mailer, "[LPC]") == 0) { mci = (MCI *) xalloc(sizeof *mci); bzero((char *) mci, sizeof *mci); mci->mci_in = stdin; mci->mci_out = stdout; mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; mci->mci_mailer = m; } else if (strcmp(m->m_mailer, "[IPC]") == 0 || strcmp(m->m_mailer, "[TCP]") == 0) { #if DAEMON register int i; if (pv[0] == NULL || pv[1] == NULL || pv[1][0] == '\0') { syserr("null host name for %s mailer", m->m_mailer); rcode = EX_CONFIG; goto give_up; } CurHostName = pv[1]; curhost = hostsignature(m, pv[1], e); if (curhost == NULL || curhost[0] == '\0') { syserr("null host signature for %s", pv[1]); rcode = EX_CONFIG; goto give_up; } if (!clever) { syserr("554 non-clever IPC"); rcode = EX_CONFIG; goto give_up; } if (pv[2] != NULL) { port = htons(atoi(pv[2])); if (port == 0) { struct servent *sp = getservbyname(pv[2], "tcp"); if (sp == NULL) syserr("Service %s unknown", pv[2]); else port = sp->s_port; } } tryhost: while (*curhost != '\0') { register char *p; static char hostbuf[MAXNAME + 1]; extern int makeconnection __P((char *, u_short, MCI *, ENVELOPE *)); /* pull the next host from the signature */ p = strchr(curhost, ':'); if (p == NULL) p = (char *) &curhost[strlen(curhost)]; if (p == curhost) { syserr("deliver: null host name in signature"); curhost++; continue; } - strncpy(hostbuf, curhost, p - curhost); - hostbuf[p - curhost] = '\0'; + i = p - curhost; + if (i >= sizeof hostbuf) + i = sizeof hostbuf - 1; + strncpy(hostbuf, curhost, i); + hostbuf[i] = '\0'; if (*p != '\0') p++; curhost = p; /* see if we already know that this host is fried */ CurHostName = hostbuf; mci = mci_get(hostbuf, m); if (mci->mci_state != MCIS_CLOSED) { if (tTd(11, 1)) { printf("openmailer: "); mci_dump(mci, FALSE); } CurHostName = mci->mci_host; message("Using cached %sSMTP connection to %s via %s...", bitset(MCIF_ESMTP, mci->mci_flags) ? "E" : "", hostbuf, m->m_name); break; } mci->mci_mailer = m; if (mci->mci_exitstat != EX_OK) + { + if (mci->mci_exitstat == EX_TEMPFAIL) + goodmxfound = TRUE; continue; + } if (mci_lock_host(mci) != EX_OK) { mci_setstat(mci, EX_TEMPFAIL, "4.4.5", NULL); + goodmxfound = TRUE; continue; } /* try the connection */ setproctitle("%s %s: %s", e->e_id, hostbuf, "user open"); if (port == 0) message("Connecting to %s via %s...", hostbuf, m->m_name); else message("Connecting to %s port %d via %s...", hostbuf, port, m->m_name); i = makeconnection(hostbuf, port, mci, e); mci->mci_lastuse = curtime(); mci->mci_exitstat = i; mci->mci_errno = errno; #if NAMED_BIND mci->mci_herrno = h_errno; #endif if (i == EX_OK) { + goodmxfound = TRUE; mci->mci_state = MCIS_OPENING; mci_cache(mci); if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d === CONNECT %s\n", (int) getpid(), hostbuf); break; } else { if (tTd(11, 1)) printf("openmailer: makeconnection => stat=%d, errno=%d\n", i, errno); + if (i == EX_TEMPFAIL) + goodmxfound = TRUE; mci_unlock_host(mci); } /* enter status of this host */ setstat(i); /* should print some message here for -v mode */ } if (mci == NULL) { syserr("deliver: no host name"); rcode = EX_SOFTWARE; goto give_up; } mci->mci_pid = 0; #else /* no DAEMON */ syserr("554 openmailer: no IPC"); if (tTd(11, 1)) printf("openmailer: NULL\n"); rcode = EX_UNAVAILABLE; goto give_up; #endif /* DAEMON */ } else { /* flush any expired connections */ (void) mci_scan(NULL); /* announce the connection to verbose listeners */ if (host == NULL || host[0] == '\0') message("Connecting to %s...", m->m_name); else message("Connecting to %s via %s...", host, m->m_name); if (TrafficLogFile != NULL) { char **av; fprintf(TrafficLogFile, "%05d === EXEC", (int) getpid()); for (av = pv; *av != NULL; av++) fprintf(TrafficLogFile, " %s", *av); fprintf(TrafficLogFile, "\n"); } #if XDEBUG checkfd012("before creating mail pipe"); #endif /* create a pipe to shove the mail through */ if (pipe(mpvect) < 0) { syserr("%s... openmailer(%s): pipe (to mailer)", shortenstring(e->e_to, 203), m->m_name); if (tTd(11, 1)) printf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } #if XDEBUG /* make sure we didn't get one of the standard I/O files */ if (mpvect[0] < 3 || mpvect[1] < 3) { syserr("%s... openmailer(%s): bogus mpvect %d %d", shortenstring(e->e_to, 203), m->m_name, mpvect[0], mpvect[1]); printopenfds(TRUE); if (tTd(11, 1)) printf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } /* make sure system call isn't dead meat */ checkfdopen(mpvect[0], "mpvect[0]"); checkfdopen(mpvect[1], "mpvect[1]"); if (mpvect[0] == mpvect[1] || (e->e_lockfp != NULL && (mpvect[0] == fileno(e->e_lockfp) || mpvect[1] == fileno(e->e_lockfp)))) { if (e->e_lockfp == NULL) syserr("%s... openmailer(%s): overlapping mpvect %d %d", shortenstring(e->e_to, 203), m->m_name, mpvect[0], mpvect[1]); else syserr("%s... openmailer(%s): overlapping mpvect %d %d, lockfp = %d", shortenstring(e->e_to, 203), m->m_name, mpvect[0], mpvect[1], fileno(e->e_lockfp)); } #endif /* if this mailer speaks smtp, create a return pipe */ #if SMTP if (clever) { if (pipe(rpvect) < 0) { syserr("%s... openmailer(%s): pipe (from mailer)", shortenstring(e->e_to, 203), m->m_name); (void) close(mpvect[0]); (void) close(mpvect[1]); if (tTd(11, 1)) printf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } # if XDEBUG checkfdopen(rpvect[0], "rpvect[0]"); checkfdopen(rpvect[1], "rpvect[1]"); # endif } #endif /* ** Actually fork the mailer process. ** DOFORK is clever about retrying. ** ** Dispose of SIGCHLD signal catchers that may be laying ** around so that endmail will get it. */ if (e->e_xfp != NULL) (void) fflush(e->e_xfp); /* for debugging */ (void) fflush(stdout); # ifdef SIGCHLD (void) setsignal(SIGCHLD, SIG_DFL); # endif /* SIGCHLD */ DOFORK(FORK); /* pid is set by DOFORK */ if (pid < 0) { /* failure */ syserr("%s... openmailer(%s): cannot fork", shortenstring(e->e_to, 203), m->m_name); (void) close(mpvect[0]); (void) close(mpvect[1]); #if SMTP if (clever) { (void) close(rpvect[0]); (void) close(rpvect[1]); } #endif if (tTd(11, 1)) printf("openmailer: NULL\n"); rcode = EX_OSERR; goto give_up; } else if (pid == 0) { int i; int saveerrno; int new_euid = NO_UID; int new_ruid = NO_UID; int new_gid = NO_GID; struct stat stb; extern int DtableSize; if (e->e_lockfp != NULL) (void) close(fileno(e->e_lockfp)); /* child -- set up input & exec mailer */ (void) setsignal(SIGINT, SIG_IGN); (void) setsignal(SIGHUP, SIG_IGN); (void) setsignal(SIGTERM, SIG_DFL); if (m != FileMailer || stat(tochain->q_user, &stb) < 0) stb.st_mode = 0; #if HASSETUSERCONTEXT /* ** Set user resources. */ if (contextaddr != NULL) { struct passwd *pwd; if (contextaddr->q_ruser != NULL) pwd = sm_getpwnam(contextaddr->q_ruser); else pwd = sm_getpwnam(contextaddr->q_user); if (pwd != NULL) (void) setusercontext(NULL, pwd, pwd->pw_uid, LOGIN_SETRESOURCES|LOGIN_SETPRIORITY); } #endif /* tweak niceness */ if (m->m_nice != 0) nice(m->m_nice); /* reset group id */ if (bitnset(M_SPECIFIC_UID, m->m_flags)) new_gid = m->m_gid; else if (bitset(S_ISGID, stb.st_mode)) new_gid = stb.st_gid; else if (ctladdr != NULL && ctladdr->q_gid != 0) { if (!DontInitGroups) (void) initgroups(ctladdr->q_ruser != NULL ? ctladdr->q_ruser : ctladdr->q_user, ctladdr->q_gid); new_gid = ctladdr->q_gid; } else { if (!DontInitGroups) (void) initgroups(DefUser, DefGid); if (m->m_gid == 0) new_gid = DefGid; else new_gid = m->m_gid; } if (new_gid != NO_GID && setgid(new_gid) < 0 && suidwarn) syserr("openmailer: setgid(%ld) failed", (long) new_gid); /* reset user id */ endpwent(); if (bitnset(M_SPECIFIC_UID, m->m_flags)) new_euid = m->m_uid; if (bitset(S_ISUID, stb.st_mode)) new_ruid = stb.st_uid; else if (ctladdr != NULL && ctladdr->q_uid != 0) new_ruid = ctladdr->q_uid; else if (m->m_uid != 0) new_ruid = m->m_uid; else if (!bitnset(M_SPECIFIC_UID, m->m_flags)) new_ruid = DefUid; if (new_euid != NO_UID) { vendor_set_uid(new_euid); #if USESETEUID if (seteuid(new_euid) < 0 && suidwarn) syserr("openmailer: seteuid(%ld) failed", (long) new_euid); #else # if HASSETREUID if (setreuid(new_ruid, new_euid) < 0 && suidwarn) syserr("openmailer: setreuid(%ld, %ld) failed", (long) new_ruid, (long) new_euid); # else if (new_euid != geteuid() && setuid(new_euid) < 0 && suidwarn) syserr("openmailer: setuid(%ld) failed", (long) new_euid); # endif #endif } else if (new_ruid != NO_UID) { vendor_set_uid(new_ruid); if (setuid(new_ruid) < 0 && suidwarn) syserr("openmailer: setuid(%ld) failed", (long) new_ruid); } if (tTd(11, 2)) printf("openmailer: running as r/euid=%d/%d\n", (int) getuid(), (int) geteuid()); /* move into some "safe" directory */ if (m->m_execdir != NULL) { char *p, *q; char buf[MAXLINE + 1]; for (p = m->m_execdir; p != NULL; p = q) { q = strchr(p, ':'); if (q != NULL) *q = '\0'; expand(p, buf, sizeof buf, e); if (q != NULL) *q++ = ':'; if (tTd(11, 20)) printf("openmailer: trydir %s\n", buf); if (buf[0] != '\0' && chdir(buf) >= 0) break; } } /* arrange to filter std & diag output of command */ #if SMTP if (clever) { (void) close(rpvect[0]); if (dup2(rpvect[1], STDOUT_FILENO) < 0) { syserr("%s... openmailer(%s): cannot dup pipe %d for stdout", shortenstring(e->e_to, 203), m->m_name, rpvect[1]); _exit(EX_OSERR); } (void) close(rpvect[1]); } else if (OpMode == MD_SMTP || OpMode == MD_DAEMON || HoldErrs || DisConnected) { /* put mailer output in transcript */ if (dup2(fileno(e->e_xfp), STDOUT_FILENO) < 0) { syserr("%s... openmailer(%s): cannot dup xscript %d for stdout", shortenstring(e->e_to, 203), m->m_name, fileno(e->e_xfp)); _exit(EX_OSERR); } } #endif if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0) { syserr("%s... openmailer(%s): cannot dup stdout for stderr", shortenstring(e->e_to, 203), m->m_name); _exit(EX_OSERR); } /* arrange to get standard input */ (void) close(mpvect[1]); if (dup2(mpvect[0], STDIN_FILENO) < 0) { syserr("%s... openmailer(%s): cannot dup pipe %d for stdin", shortenstring(e->e_to, 203), m->m_name, mpvect[0]); _exit(EX_OSERR); } (void) close(mpvect[0]); /* arrange for all the files to be closed */ for (i = 3; i < DtableSize; i++) { register int j; if ((j = fcntl(i, F_GETFD, 0)) != -1) (void) fcntl(i, F_SETFD, j | 1); } /* run disconnected from terminal */ (void) setsid(); /* try to execute the mailer */ execve(m->m_mailer, (ARGV_T) pv, (ARGV_T) UserEnviron); saveerrno = errno; syserr("Cannot exec %s", m->m_mailer); if (bitnset(M_LOCALMAILER, m->m_flags) || transienterror(saveerrno)) _exit(EX_OSERR); _exit(EX_UNAVAILABLE); } /* ** Set up return value. */ mci = (MCI *) xalloc(sizeof *mci); bzero((char *) mci, sizeof *mci); mci->mci_mailer = m; mci->mci_state = clever ? MCIS_OPENING : MCIS_OPEN; mci->mci_pid = pid; (void) close(mpvect[0]); mci->mci_out = fdopen(mpvect[1], "w"); if (mci->mci_out == NULL) { syserr("deliver: cannot create mailer output channel, fd=%d", mpvect[1]); (void) close(mpvect[1]); #if SMTP if (clever) { (void) close(rpvect[0]); (void) close(rpvect[1]); } #endif rcode = EX_OSERR; goto give_up; } #if SMTP if (clever) { (void) close(rpvect[1]); mci->mci_in = fdopen(rpvect[0], "r"); if (mci->mci_in == NULL) { syserr("deliver: cannot create mailer input channel, fd=%d", mpvect[1]); (void) close(rpvect[0]); fclose(mci->mci_out); mci->mci_out = NULL; rcode = EX_OSERR; goto give_up; } } else #endif { mci->mci_flags |= MCIF_TEMP; mci->mci_in = NULL; } } /* ** If we are in SMTP opening state, send initial protocol. */ if (bitnset(M_7BITS, m->m_flags) && (!clever || mci->mci_state == MCIS_OPENING)) mci->mci_flags |= MCIF_7BIT; #if SMTP if (clever && mci->mci_state != MCIS_CLOSED) { extern void smtpinit __P((MAILER *, MCI *, ENVELOPE *)); smtpinit(m, mci, e); } #endif /* clear out per-message flags from connection structure */ mci->mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7); if (bitset(EF_HAS8BIT, e->e_flags) && !bitset(EF_DONT_MIME, e->e_flags) && bitnset(M_7BITS, m->m_flags)) mci->mci_flags |= MCIF_CVT8TO7; #if MIME7TO8 if (bitnset(M_MAKE8BIT, m->m_flags) && !bitset(MCIF_7BIT, mci->mci_flags) && (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL && (strcasecmp(p, "quoted-printable") == 0 || strcasecmp(p, "base64") == 0) && (p = hvalue("Content-Type", e->e_header)) != NULL) { /* may want to convert 7 -> 8 */ /* XXX should really parse it here -- and use a class XXX */ if (strncasecmp(p, "text/plain", 10) == 0 && (p[10] == '\0' || p[10] == ' ' || p[10] == ';')) mci->mci_flags |= MCIF_CVT7TO8; } #endif if (tTd(11, 1)) { printf("openmailer: "); mci_dump(mci, FALSE); } if (mci->mci_state != MCIS_OPEN) { /* couldn't open the mailer */ rcode = mci->mci_exitstat; errno = mci->mci_errno; #if NAMED_BIND h_errno = mci->mci_herrno; #endif if (rcode == EX_OK) { /* shouldn't happen */ syserr("554 deliver: mci=%lx rcode=%d errno=%d state=%d sig=%s", (long) mci, rcode, errno, mci->mci_state, firstsig); mci_dump_all(TRUE); rcode = EX_SOFTWARE; } #if DAEMON else if (curhost != NULL && *curhost != '\0') { /* try next MX site */ goto tryhost; } #endif } else if (!clever) { /* ** Format and send message. */ putfromline(mci, e); (*e->e_puthdr)(mci, e->e_header, e); (*e->e_putbody)(mci, e, NULL); /* get the exit status */ rcode = endmailer(mci, e, pv); } else #if SMTP { extern int smtpmailfrom __P((MAILER *, MCI *, ENVELOPE *)); extern int smtprcpt __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *)); extern int smtpdata __P((MAILER *, MCI *, ENVELOPE *)); extern int smtpgetstat __P((MAILER *, MCI *, ENVELOPE *)); /* ** Send the MAIL FROM: protocol */ rcode = smtpmailfrom(m, mci, e); if (rcode == EX_OK) { register char *t = tobuf; register int i; /* send the recipient list */ tobuf[0] = '\0'; for (to = tochain; to != NULL; to = to->q_tchain) { e->e_to = to->q_paddr; - if ((i = smtprcpt(to, m, mci, e)) != EX_OK) + if (strlen(to->q_paddr) + (t - tobuf) + 2 >= sizeof tobuf) { + /* not enough room */ + continue; + } + else if ((i = smtprcpt(to, m, mci, e)) != EX_OK) + { markfailure(e, to, mci, i); giveresponse(i, m, mci, ctladdr, xstart, e); } else { *t++ = ','; for (p = to->q_paddr; *p; *t++ = *p++) continue; *t = '\0'; } } /* now send the data */ if (tobuf[0] == '\0') { rcode = EX_OK; e->e_to = NULL; if (bitset(MCIF_CACHED, mci->mci_flags)) smtprset(m, mci, e); } else { e->e_to = tobuf + 1; rcode = smtpdata(m, mci, e); } } # if DAEMON if (rcode == EX_TEMPFAIL && curhost != NULL && *curhost != '\0') { /* try next MX site */ goto tryhost; } # endif } #else /* not SMTP */ { syserr("554 deliver: need SMTP compiled to use clever mailer"); rcode = EX_CONFIG; goto give_up; } #endif /* SMTP */ #if NAMED_BIND if (ConfigLevel < 2) _res.options |= RES_DEFNAMES | RES_DNSRCH; /* XXX */ #endif if (tTd(62, 1)) checkfds("after delivery"); /* ** Do final status disposal. ** We check for something in tobuf for the SMTP case. ** If we got a temporary failure, arrange to queue the ** addressees. */ give_up: #if SMTP # if _FFR_LMTP if (bitnset(M_LMTP, m->m_flags)) { tobuf[0] = '\0'; anyok = FALSE; } else # endif #endif anyok = rcode == EX_OK; for (to = tochain; to != NULL; to = to->q_tchain) { /* see if address already marked */ if (bitset(QBADADDR|QQUEUEUP, to->q_flags)) continue; #if SMTP # if _FFR_LMTP /* if running LMTP, get the status for each address */ if (bitnset(M_LMTP, m->m_flags)) { rcode = smtpgetstat(m, mci, e); if (rcode == EX_OK) { - strcat(tobuf, ","); - strcat(tobuf, to->q_paddr); + if (strlen(to->q_paddr) + strlen(tobuf) + 2 >= sizeof tobuf) + { + syserr("LMTP tobuf overflow"); + } + else + { + strcat(tobuf, ","); + strcat(tobuf, to->q_paddr); + } anyok = TRUE; } else { e->e_to = to->q_paddr; markfailure(e, to, mci, rcode); giveresponse(rcode, m, mci, ctladdr, xstart, e); e->e_to = tobuf + 1; continue; } } else # endif #endif { /* mark bad addresses */ if (rcode != EX_OK) { + if (goodmxfound && rcode == EX_NOHOST) + rcode = EX_TEMPFAIL; markfailure(e, to, mci, rcode); continue; } } /* successful delivery */ to->q_flags |= QSENT; to->q_statdate = curtime(); e->e_nsent++; if (bitnset(M_LOCALMAILER, m->m_flags) && bitset(QPINGONSUCCESS, to->q_flags)) { to->q_flags |= QDELIVERED; to->q_status = "2.1.5"; fprintf(e->e_xfp, "%s... Successfully delivered\n", to->q_paddr); } else if (bitset(QPINGONSUCCESS, to->q_flags) && bitset(QPRIMARY, to->q_flags) && !bitset(MCIF_DSN, mci->mci_flags)) { to->q_flags |= QRELAYED; fprintf(e->e_xfp, "%s... relayed; expect no further notifications\n", to->q_paddr); } } #if SMTP # if _FFR_LMTP if (bitnset(M_LMTP, m->m_flags)) { /* ** Global information applies to the last recipient only; ** clear it out to avoid bogus errors. */ rcode = EX_OK; e->e_statmsg = NULL; /* reset the mci state for the next transaction */ if (mci != NULL && mci->mci_state == MCIS_ACTIVE) mci->mci_state = MCIS_OPEN; } # endif #endif if (tobuf[0] != '\0') giveresponse(rcode, m, mci, ctladdr, xstart, e); if (anyok) markstats(e, tochain); mci_store_persistent(mci); #if SMTP /* now close the connection */ if (clever && mci != NULL && mci->mci_state != MCIS_CLOSED && !bitset(MCIF_CACHED, mci->mci_flags)) smtpquit(m, mci, e); #endif /* ** Restore state and return. */ #if XDEBUG { char wbuf[MAXLINE]; /* make absolutely certain 0, 1, and 2 are in use */ snprintf(wbuf, sizeof wbuf, "%s... end of deliver(%s)", e->e_to == NULL ? "NO-TO-LIST" : shortenstring(e->e_to, 203), m->m_name); checkfd012(wbuf); } #endif errno = 0; define('g', (char *) NULL, e); return (rcode); } /* ** MARKFAILURE -- mark a failure on a specific address. ** ** Parameters: ** e -- the envelope we are sending. ** q -- the address to mark. ** mci -- mailer connection information. ** rcode -- the code signifying the particular failure. ** ** Returns: ** none. ** ** Side Effects: ** marks the address (and possibly the envelope) with the ** failure so that an error will be returned or ** the message will be queued, as appropriate. */ void markfailure(e, q, mci, rcode) register ENVELOPE *e; register ADDRESS *q; register MCI *mci; int rcode; { char *stat = NULL; switch (rcode) { case EX_OK: break; case EX_TEMPFAIL: case EX_IOERR: case EX_OSERR: q->q_flags |= QQUEUEUP; + q->q_flags &= ~QDONTSEND; break; default: q->q_flags |= QBADADDR; break; } /* find most specific error code possible */ if (mci != NULL && mci->mci_status != NULL) { q->q_status = mci->mci_status; q->q_rstatus = mci->mci_rstatus; } else if (e->e_status != NULL) { q->q_status = e->e_status; q->q_rstatus = NULL; } else { switch (rcode) { case EX_USAGE: stat = "5.5.4"; break; case EX_DATAERR: stat = "5.5.2"; break; case EX_NOUSER: stat = "5.1.1"; break; case EX_NOHOST: stat = "5.1.2"; break; case EX_NOINPUT: case EX_CANTCREAT: case EX_NOPERM: stat = "5.3.0"; break; case EX_UNAVAILABLE: case EX_SOFTWARE: case EX_OSFILE: case EX_PROTOCOL: case EX_CONFIG: stat = "5.5.0"; break; case EX_OSERR: case EX_IOERR: stat = "4.5.0"; break; case EX_TEMPFAIL: stat = "4.2.0"; break; } if (stat != NULL) q->q_status = stat; } q->q_statdate = curtime(); if (CurHostName != NULL && CurHostName[0] != '\0') q->q_statmta = newstr(CurHostName); if (rcode != EX_OK && q->q_rstatus == NULL && q->q_mailer != NULL && q->q_mailer->m_diagtype != NULL && strcasecmp(q->q_mailer->m_diagtype, "UNIX") == 0) { char buf[30]; (void) snprintf(buf, sizeof buf, "%d", rcode); q->q_rstatus = newstr(buf); } } /* ** ENDMAILER -- Wait for mailer to terminate. ** ** We should never get fatal errors (e.g., segmentation ** violation), so we report those specially. For other ** errors, we choose a status message (into statmsg), ** and if it represents an error, we print it. ** ** Parameters: ** pid -- pid of mailer. ** e -- the current envelope. ** pv -- the parameter vector that invoked the mailer ** (for error messages). ** ** Returns: ** exit code of mailer. ** ** Side Effects: ** none. */ int endmailer(mci, e, pv) register MCI *mci; register ENVELOPE *e; char **pv; { int st; /* close any connections */ if (mci->mci_in != NULL) (void) xfclose(mci->mci_in, mci->mci_mailer->m_name, "mci_in"); if (mci->mci_out != NULL) (void) xfclose(mci->mci_out, mci->mci_mailer->m_name, "mci_out"); mci->mci_in = mci->mci_out = NULL; mci->mci_state = MCIS_CLOSED; /* in the IPC case there is nothing to wait for */ if (mci->mci_pid == 0) return (EX_OK); -#ifdef _FFR_TIMEOUT_WAIT +#if _FFR_TIMEOUT_WAIT put a timeout around the wait #endif /* wait for the mailer process to die and collect status */ st = waitfor(mci->mci_pid); if (st == -1) { syserr("endmailer %s: wait", mci->mci_mailer->m_name); return (EX_SOFTWARE); } if (WIFEXITED(st)) { /* normal death -- return status */ return (WEXITSTATUS(st)); } /* it died a horrid death */ syserr("451 mailer %s died with signal %o", mci->mci_mailer->m_name, st); /* log the arguments */ if (pv != NULL && e->e_xfp != NULL) { register char **av; fprintf(e->e_xfp, "Arguments:"); for (av = pv; *av != NULL; av++) fprintf(e->e_xfp, " %s", *av); fprintf(e->e_xfp, "\n"); } ExitStat = EX_TEMPFAIL; return (EX_TEMPFAIL); } /* ** GIVERESPONSE -- Interpret an error response from a mailer ** ** Parameters: ** stat -- the status code from the mailer (high byte ** only; core dumps must have been taken care of ** already). ** m -- the mailer info for this mailer. ** mci -- the mailer connection info -- can be NULL if the ** response is given before the connection is made. ** ctladdr -- the controlling address for the recipient ** address(es). ** xstart -- the transaction start time, for computing ** transaction delays. ** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** Errors may be incremented. ** ExitStat may be set. */ void giveresponse(stat, m, mci, ctladdr, xstart, e) int stat; register MAILER *m; register MCI *mci; ADDRESS *ctladdr; time_t xstart; ENVELOPE *e; { register const char *statmsg; extern char *SysExMsg[]; register int i; extern int N_SysEx; char buf[MAXLINE]; /* ** Compute status message from code. */ i = stat - EX__BASE; if (stat == 0) { statmsg = "250 Sent"; if (e->e_statmsg != NULL) { (void) snprintf(buf, sizeof buf, "%s (%s)", statmsg, shortenstring(e->e_statmsg, 403)); statmsg = buf; } } else if (i < 0 || i > N_SysEx) { (void) snprintf(buf, sizeof buf, "554 unknown mailer error %d", stat); stat = EX_UNAVAILABLE; statmsg = buf; } else if (stat == EX_TEMPFAIL) { char *bp = buf; snprintf(bp, SPACELEFT(buf, bp), "%s", SysExMsg[i] + 1); bp += strlen(bp); #if NAMED_BIND if (h_errno == TRY_AGAIN) statmsg = errstring(h_errno+E_DNSBASE); else #endif { if (errno != 0) statmsg = errstring(errno); else { #if SMTP statmsg = SmtpError; #else /* SMTP */ statmsg = NULL; #endif /* SMTP */ } } if (statmsg != NULL && statmsg[0] != '\0') snprintf(bp, SPACELEFT(buf, bp), ": %s", statmsg); statmsg = buf; } #if NAMED_BIND else if (stat == EX_NOHOST && h_errno != 0) { statmsg = errstring(h_errno + E_DNSBASE); (void) snprintf(buf, sizeof buf, "%s (%s)", SysExMsg[i] + 1, statmsg); statmsg = buf; } #endif else { statmsg = SysExMsg[i]; if (*statmsg++ == ':') { (void) snprintf(buf, sizeof buf, "%s: %s", statmsg, errstring(errno)); statmsg = buf; } } /* ** Print the message as appropriate */ if (stat == EX_OK || stat == EX_TEMPFAIL) { extern char MsgBuf[]; message("%s", &statmsg[4]); if (stat == EX_TEMPFAIL && e->e_xfp != NULL) fprintf(e->e_xfp, "%s\n", &MsgBuf[4]); } else { char mbuf[8]; Errors++; snprintf(mbuf, sizeof mbuf, "%.3s %%s", statmsg); usrerr(mbuf, &statmsg[4]); } /* ** Final cleanup. ** Log a record of the transaction. Compute the new ** ExitStat -- if we already had an error, stick with ** that. */ if (LogLevel > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6)) logdelivery(m, mci, &statmsg[4], ctladdr, xstart, e); if (tTd(11, 2)) printf("giveresponse: stat=%d, e->e_message=%s\n", stat, e->e_message == NULL ? "" : e->e_message); if (stat != EX_TEMPFAIL) setstat(stat); if (stat != EX_OK && (stat != EX_TEMPFAIL || e->e_message == NULL)) { if (e->e_message != NULL) free(e->e_message); e->e_message = newstr(&statmsg[4]); } errno = 0; #if NAMED_BIND h_errno = 0; #endif } /* ** LOGDELIVERY -- log the delivery in the system log ** ** Care is taken to avoid logging lines that are too long, because ** some versions of syslog have an unfortunate proclivity for core ** dumping. This is a hack, to be sure, that is at best empirical. ** ** Parameters: ** m -- the mailer info. Can be NULL for initial queue. ** mci -- the mailer connection info -- can be NULL if the ** log is occuring when no connection is active. ** stat -- the message to print for the status. ** ctladdr -- the controlling address for the to list. ** xstart -- the transaction start time, used for ** computing transaction delay. ** e -- the current envelope. ** ** Returns: ** none ** ** Side Effects: ** none */ void logdelivery(m, mci, stat, ctladdr, xstart, e) MAILER *m; register MCI *mci; const char *stat; ADDRESS *ctladdr; time_t xstart; register ENVELOPE *e; { -# ifdef LOG register char *bp; register char *p; int l; char buf[1024]; # if (SYSLOG_BUFSIZE) >= 256 /* ctladdr: max 106 bytes */ bp = buf; if (ctladdr != NULL) { snprintf(bp, SPACELEFT(buf, bp), ", ctladdr=%s", shortenstring(ctladdr->q_paddr, 83)); bp += strlen(bp); if (bitset(QGOODUID, ctladdr->q_flags)) { (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", ctladdr->q_uid, ctladdr->q_gid); bp += strlen(bp); } } /* delay & xdelay: max 41 bytes */ snprintf(bp, SPACELEFT(buf, bp), ", delay=%s", pintvl(curtime() - e->e_ctime, TRUE)); bp += strlen(bp); if (xstart != (time_t) 0) { snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s", pintvl(curtime() - xstart, TRUE)); bp += strlen(bp); } /* mailer: assume about 19 bytes (max 10 byte mailer name) */ if (m != NULL) { snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name); bp += strlen(bp); } /* relay: max 66 bytes for IPv4 addresses */ if (mci != NULL && mci->mci_host != NULL) { # if DAEMON extern SOCKADDR CurHostAddr; # endif snprintf(bp, SPACELEFT(buf, bp), ", relay=%s", shortenstring(mci->mci_host, 40)); bp += strlen(bp); # if DAEMON if (CurHostAddr.sa.sa_family != 0) { snprintf(bp, SPACELEFT(buf, bp), " [%s]", anynet_ntoa(&CurHostAddr)); } # endif } else if (strcmp(stat, "queued") != 0) { char *p = macvalue('h', e); if (p != NULL && p[0] != '\0') { snprintf(bp, SPACELEFT(buf, bp), ", relay=%s", shortenstring(p, 40)); } } bp += strlen(bp); #define STATLEN (((SYSLOG_BUFSIZE) - 100) / 4) #if (STATLEN) < 63 # undef STATLEN # define STATLEN 63 #endif #if (STATLEN) > 203 # undef STATLEN # define STATLEN 203 #endif /* stat: max 210 bytes */ if ((bp - buf) > (sizeof buf - ((STATLEN) + 20))) { /* desperation move -- truncate data */ bp = buf + sizeof buf - ((STATLEN) + 17); strcpy(bp, "..."); bp += 3; } (void) strcpy(bp, ", stat="); bp += strlen(bp); (void) strcpy(bp, shortenstring(stat, (STATLEN))); /* id, to: max 13 + TOBUFSIZE bytes */ l = SYSLOG_BUFSIZE - 100 - strlen(buf); p = e->e_to; while (strlen(p) >= (SIZE_T) l) { register char *q = strchr(p + l, ','); if (q == NULL) break; - syslog(LOG_INFO, "%s: to=%.*s [more]%s", - e->e_id, ++q - p, p, buf); + sm_syslog(LOG_INFO, e->e_id, + "to=%.*s [more]%s", + ++q - p, p, buf); p = q; } - syslog(LOG_INFO, "%s: to=%s%s", e->e_id, p, buf); + sm_syslog(LOG_INFO, e->e_id, "to=%s%s", p, buf); # else /* we have a very short log buffer size */ l = SYSLOG_BUFSIZE - 85; p = e->e_to; while (strlen(p) >= (SIZE_T) l) { register char *q = strchr(p + l, ','); if (q == NULL) break; - syslog(LOG_INFO, "%s: to=%.*s [more]", - e->e_id, ++q - p, p); + sm_syslog(LOG_INFO, e->e_id, + "to=%.*s [more]", + ++q - p, p); p = q; } - syslog(LOG_INFO, "%s: to=%s", e->e_id, p); + sm_syslog(LOG_INFO, e->e_id, "to=%s", p); if (ctladdr != NULL) { bp = buf; snprintf(bp, SPACELEFT(buf, bp), "ctladdr=%s", shortenstring(ctladdr->q_paddr, 83)); bp += strlen(bp); if (bitset(QGOODUID, ctladdr->q_flags)) { (void) snprintf(bp, SPACELEFT(buf, bp), " (%d/%d)", ctladdr->q_uid, ctladdr->q_gid); bp += strlen(bp); } - syslog(LOG_INFO, "%s: %s", e->e_id, buf); + sm_syslog(LOG_INFO, e->e_id, "%s", buf); } bp = buf; snprintf(bp, SPACELEFT(buf, bp), "delay=%s", pintvl(curtime() - e->e_ctime, TRUE)); bp += strlen(bp); if (xstart != (time_t) 0) { snprintf(bp, SPACELEFT(buf, bp), ", xdelay=%s", pintvl(curtime() - xstart, TRUE)); bp += strlen(bp); } if (m != NULL) { snprintf(bp, SPACELEFT(buf, bp), ", mailer=%s", m->m_name); bp += strlen(bp); } - syslog(LOG_INFO, "%s: %.1000s", e->e_id, buf); + sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); buf[0] = '\0'; bp = buf; if (mci != NULL && mci->mci_host != NULL) { # if DAEMON extern SOCKADDR CurHostAddr; # endif snprintf(bp, SPACELEFT(buf, bp), "relay=%.100s", mci->mci_host); bp += strlen(bp); # if DAEMON if (CurHostAddr.sa.sa_family != 0) snprintf(bp, SPACELEFT(buf, bp), " [%.100s]", anynet_ntoa(&CurHostAddr)); # endif } else if (strcmp(stat, "queued") != 0) { char *p = macvalue('h', e); if (p != NULL && p[0] != '\0') snprintf(buf, sizeof buf, "relay=%.100s", p); } if (buf[0] != '\0') - syslog(LOG_INFO, "%s: %.1000s", e->e_id, buf); + sm_syslog(LOG_INFO, e->e_id, "%.1000s", buf); - syslog(LOG_INFO, "%s: stat=%s", e->e_id, shortenstring(stat, 63)); + sm_syslog(LOG_INFO, e->e_id, "stat=%s", shortenstring(stat, 63)); # endif /* short log buffer */ -# endif /* LOG */ } /* ** PUTFROMLINE -- output a UNIX-style from line (or whatever) ** ** This can be made an arbitrary message separator by changing $l ** ** One of the ugliest hacks seen by human eyes is contained herein: ** UUCP wants those stupid "remote from " lines. Why oh why ** does a well-meaning programmer such as myself have to deal with ** this kind of antique garbage???? ** ** Parameters: ** mci -- the connection information. ** e -- the envelope. ** ** Returns: ** none ** ** Side Effects: ** outputs some text to fp. */ void putfromline(mci, e) register MCI *mci; ENVELOPE *e; { char *template = UnixFromLine; char buf[MAXLINE]; char xbuf[MAXLINE]; if (bitnset(M_NHDR, mci->mci_mailer->m_flags)) return; if (bitnset(M_UGLYUUCP, mci->mci_mailer->m_flags)) { char *bang; expand("\201g", buf, sizeof buf, e); bang = strchr(buf, '!'); if (bang == NULL) { char *at; char hname[MAXNAME]; /* ** If we can construct a UUCP path, do so */ at = strrchr(buf, '@'); if (at == NULL) { expand( "\201k", hname, sizeof hname, e); at = hname; } else *at++ = '\0'; (void) snprintf(xbuf, sizeof xbuf, "From %.800s \201d remote from %.100s\n", buf, at); } else { *bang++ = '\0'; (void) snprintf(xbuf, sizeof xbuf, "From %.800s \201d remote from %.100s\n", bang, buf); template = xbuf; } } expand(template, buf, sizeof buf, e); - putxline(buf, mci, PXLF_NOTHINGSPECIAL); + putxline(buf, strlen(buf), mci, PXLF_NOTHINGSPECIAL); } /* ** PUTBODY -- put the body of a message. ** ** Parameters: ** mci -- the connection information. ** e -- the envelope to put out. ** separator -- if non-NULL, a message separator that must ** not be permitted in the resulting message. ** ** Returns: ** none. ** ** Side Effects: ** The message is written onto fp. */ /* values for output state variable */ #define OS_HEAD 0 /* at beginning of line */ #define OS_CR 1 /* read a carriage return */ #define OS_INLINE 2 /* putting rest of line */ void putbody(mci, e, separator) register MCI *mci; register ENVELOPE *e; char *separator; { char buf[MAXLINE]; /* ** Output the body of the message */ if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) { char *df = queuename(e, 'd'); e->e_dfp = fopen(df, "r"); if (e->e_dfp == NULL) syserr("putbody: Cannot open %s for %s from %s", df, e->e_to, e->e_from.q_paddr); } if (e->e_dfp == NULL) { if (bitset(MCIF_INHEADER, mci->mci_flags)) { putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; } putline("<<< No Message Collected >>>", mci); goto endofmessage; } if (e->e_dfino == (ino_t) 0) { struct stat stbuf; if (fstat(fileno(e->e_dfp), &stbuf) < 0) e->e_dfino = -1; else { e->e_dfdev = stbuf.st_dev; e->e_dfino = stbuf.st_ino; } } rewind(e->e_dfp); #if MIME8TO7 if (bitset(MCIF_CVT8TO7, mci->mci_flags)) { char *boundaries[MAXMIMENESTING + 1]; /* ** Do 8 to 7 bit MIME conversion. */ /* make sure it looks like a MIME message */ if (hvalue("MIME-Version", e->e_header) == NULL) putline("MIME-Version: 1.0", mci); if (hvalue("Content-Type", e->e_header) == NULL) { snprintf(buf, sizeof buf, "Content-Type: text/plain; charset=%s", defcharset(e)); putline(buf, mci); } /* now do the hard work */ boundaries[0] = NULL; mime8to7(mci, e->e_header, e, boundaries, M87F_OUTER); } # if MIME7TO8 else if (bitset(MCIF_CVT7TO8, mci->mci_flags)) { mime7to8(mci, e->e_header, e); } # endif else #endif { int ostate; register char *bp; register char *pbp; register int c; register char *xp; int padc; char *buflim; int pos = 0; char peekbuf[10]; /* we can pass it through unmodified */ if (bitset(MCIF_INHEADER, mci->mci_flags)) { putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; } /* determine end of buffer; allow for short mailer lines */ buflim = &buf[sizeof buf - 1]; if (mci->mci_mailer->m_linelimit > 0 && mci->mci_mailer->m_linelimit < sizeof buf - 1) buflim = &buf[mci->mci_mailer->m_linelimit - 1]; /* copy temp file to output with mapping */ ostate = OS_HEAD; bp = buf; pbp = peekbuf; while (!ferror(mci->mci_out)) { if (pbp > peekbuf) c = *--pbp; else if ((c = getc(e->e_dfp)) == EOF) break; if (bitset(MCIF_7BIT, mci->mci_flags)) c &= 0x7f; switch (ostate) { case OS_HEAD: -#ifdef _FFR_NONULLS +#if _FFR_NONULLS if (c == '\0' && bitnset(M_NONULLS, mci->mci_mailer->m_flags)) break; #endif if (c != '\r' && c != '\n' && bp < buflim) { *bp++ = c; break; } /* check beginning of line for special cases */ *bp = '\0'; pos = 0; padc = EOF; if (buf[0] == 'F' && bitnset(M_ESCFROM, mci->mci_mailer->m_flags) && strncmp(buf, "From ", 5) == 0) { padc = '>'; } if (buf[0] == '-' && buf[1] == '-' && separator != NULL) { /* possible separator */ int sl = strlen(separator); if (strncmp(&buf[2], separator, sl) == 0) padc = ' '; } if (buf[0] == '.' && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { padc = '.'; } /* now copy out saved line */ if (TrafficLogFile != NULL) { fprintf(TrafficLogFile, "%05d >>> ", (int) getpid()); if (padc != EOF) putc(padc, TrafficLogFile); for (xp = buf; xp < bp; xp++) putc(*xp, TrafficLogFile); if (c == '\n') fputs(mci->mci_mailer->m_eol, TrafficLogFile); } if (padc != EOF) { putc(padc, mci->mci_out); pos++; } for (xp = buf; xp < bp; xp++) putc(*xp, mci->mci_out); if (c == '\n') { fputs(mci->mci_mailer->m_eol, mci->mci_out); pos = 0; } else { pos += bp - buf; if (c != '\r') *pbp++ = c; } bp = buf; /* determine next state */ if (c == '\n') ostate = OS_HEAD; else if (c == '\r') ostate = OS_CR; else ostate = OS_INLINE; continue; case OS_CR: if (c == '\n') { /* got CRLF */ fputs(mci->mci_mailer->m_eol, mci->mci_out); if (TrafficLogFile != NULL) { fputs(mci->mci_mailer->m_eol, TrafficLogFile); } ostate = OS_HEAD; continue; } /* had a naked carriage return */ *pbp++ = c; c = '\r'; ostate = OS_INLINE; goto putch; case OS_INLINE: if (c == '\r') { ostate = OS_CR; continue; } -#ifdef _FFR_NONULLS +#if _FFR_NONULLS if (c == '\0' && bitnset(M_NONULLS, mci->mci_mailer->m_flags)) break; #endif putch: if (mci->mci_mailer->m_linelimit > 0 && pos > mci->mci_mailer->m_linelimit && c != '\n') { putc('!', mci->mci_out); fputs(mci->mci_mailer->m_eol, mci->mci_out); if (TrafficLogFile != NULL) { fprintf(TrafficLogFile, "!%s", mci->mci_mailer->m_eol); } ostate = OS_HEAD; *pbp++ = c; continue; } if (c == '\n') { if (TrafficLogFile != NULL) fputs(mci->mci_mailer->m_eol, TrafficLogFile); fputs(mci->mci_mailer->m_eol, mci->mci_out); pos = 0; ostate = OS_HEAD; } else { if (TrafficLogFile != NULL) putc(c, TrafficLogFile); putc(c, mci->mci_out); pos++; ostate = OS_INLINE; } break; } } /* make sure we are at the beginning of a line */ if (bp > buf) { if (TrafficLogFile != NULL) { for (xp = buf; xp < bp; xp++) putc(*xp, TrafficLogFile); } for (xp = buf; xp < bp; xp++) putc(*xp, mci->mci_out); pos += bp - buf; } if (pos > 0) { if (TrafficLogFile != NULL) fputs(mci->mci_mailer->m_eol, TrafficLogFile); fputs(mci->mci_mailer->m_eol, mci->mci_out); } } if (ferror(e->e_dfp)) { syserr("putbody: df%s: read error", e->e_id); ExitStat = EX_IOERR; } endofmessage: /* some mailers want extra blank line at end of message */ if (bitnset(M_BLANKEND, mci->mci_mailer->m_flags) && buf[0] != '\0' && buf[0] != '\n') putline("", mci); (void) fflush(mci->mci_out); if (ferror(mci->mci_out) && errno != EPIPE) { syserr("putbody: write error"); ExitStat = EX_IOERR; } errno = 0; } /* ** MAILFILE -- Send a message to a file. ** ** If the file has the setuid/setgid bits set, but NO execute ** bits, sendmail will try to become the owner of that file ** rather than the real user. Obviously, this only works if ** sendmail runs as root. ** ** This could be done as a subordinate mailer, except that it ** is used implicitly to save messages in ~/dead.letter. We ** view this as being sufficiently important as to include it ** here. For example, if the system is dying, we shouldn't have ** to create another process plus some pipes to save the message. ** ** Parameters: ** filename -- the name of the file to send to. ** ctladdr -- the controlling address header -- includes ** the userid/groupid to be when sending. ** sfflags -- flags for opening. ** e -- the current envelope. ** ** Returns: ** The exit code associated with the operation. ** ** Side Effects: ** none. */ int mailfile(filename, ctladdr, sfflags, e) char *filename; ADDRESS *ctladdr; int sfflags; register ENVELOPE *e; { register FILE *f; register pid_t pid = -1; int mode; bool suidwarn = geteuid() == 0; if (tTd(11, 1)) { printf("mailfile %s\n ctladdr=", filename); printaddr(ctladdr, FALSE); } if (e->e_xfp != NULL) fflush(e->e_xfp); /* ** Special case /dev/null. This allows us to restrict file ** delivery to regular files only. */ if (strcmp(filename, "/dev/null") == 0) return EX_OK; /* ** Fork so we can change permissions here. ** Note that we MUST use fork, not vfork, because of ** the complications of calling subroutines, etc. */ DOFORK(fork); if (pid < 0) return (EX_OSERR); else if (pid == 0) { /* child -- actually write to file */ struct stat stb; MCI mcibuf; int oflags = O_WRONLY|O_APPEND; if (e->e_lockfp != NULL) (void) close(fileno(e->e_lockfp)); (void) setsignal(SIGINT, SIG_DFL); (void) setsignal(SIGHUP, SIG_DFL); (void) setsignal(SIGTERM, SIG_DFL); (void) umask(OldUmask); e->e_to = filename; ExitStat = EX_OK; #ifdef HASLSTAT if ((SafeFileEnv != NULL ? lstat(filename, &stb) : stat(filename, &stb)) < 0) #else if (stat(filename, &stb) < 0) #endif { stb.st_mode = FileMode; oflags |= O_CREAT|O_EXCL; } else if (bitset(0111, stb.st_mode) || stb.st_nlink != 1 || (SafeFileEnv != NULL && !S_ISREG(stb.st_mode))) exit(EX_CANTCREAT); mode = stb.st_mode; /* limit the errors to those actually caused in the child */ errno = 0; ExitStat = EX_OK; if (ctladdr != NULL || bitset(SFF_RUNASREALUID, sfflags)) { /* ignore setuid and setgid bits */ mode &= ~(S_ISGID|S_ISUID); } /* we have to open the dfile BEFORE setuid */ if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags)) { char *df = queuename(e, 'd'); e->e_dfp = fopen(df, "r"); if (e->e_dfp == NULL) { syserr("mailfile: Cannot open %s for %s from %s", df, e->e_to, e->e_from.q_paddr); } } /* select a new user to run as */ if (!bitset(SFF_RUNASREALUID, sfflags)) { if (bitset(S_ISUID, mode)) { RealUserName = NULL; RealUid = stb.st_uid; } else if (ctladdr != NULL && ctladdr->q_uid != 0) { if (ctladdr->q_ruser != NULL) RealUserName = ctladdr->q_ruser; else RealUserName = ctladdr->q_user; RealUid = ctladdr->q_uid; } else if (FileMailer != NULL && FileMailer->m_uid != 0) { RealUserName = DefUser; RealUid = FileMailer->m_uid; } else { RealUserName = DefUser; RealUid = DefUid; } /* select a new group to run as */ if (bitset(S_ISGID, mode)) RealGid = stb.st_gid; else if (ctladdr != NULL && ctladdr->q_uid != 0) RealGid = ctladdr->q_gid; else if (FileMailer != NULL && FileMailer->m_gid != 0) RealGid = FileMailer->m_gid; else RealGid = DefGid; } /* last ditch */ if (!bitset(SFF_ROOTOK, sfflags)) { if (RealUid == 0) RealUid = DefUid; if (RealGid == 0) RealGid = DefGid; } /* set group id list (needs /etc/group access) */ if (RealUserName != NULL && !DontInitGroups) (void) initgroups(RealUserName, RealGid); /* if you have a safe environment, go into it */ if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') { int i; if (chroot(SafeFileEnv) < 0) { syserr("mailfile: Cannot chroot(%s)", SafeFileEnv); exit(EX_CANTCREAT); } i = strlen(SafeFileEnv); if (strncmp(SafeFileEnv, filename, i) == 0) filename += i; } if (chdir("/") < 0) syserr("mailfile: cannot chdir(/)"); /* now reset the group and user ids */ endpwent(); if (setgid(RealGid) < 0 && suidwarn) syserr("mailfile: setgid(%ld) failed", (long) RealGid); vendor_set_uid(RealUid); if (setuid(RealUid) < 0 && suidwarn) syserr("mailfile: setuid(%ld) failed", (long) RealUid); - sfflags |= SFF_NOPATHCHECK; + sfflags |= SFF_NOPATHCHECK|SFF_NOLINK; sfflags &= ~SFF_OPENASROOT; f = safefopen(filename, oflags, FileMode, sfflags); if (f == NULL) { message("554 cannot open: %s", errstring(errno)); exit(EX_CANTCREAT); } if (fstat(fileno(f), &stb) < 0) { message("554 cannot fstat %s", errstring(errno)); exit(EX_CANTCREAT); } bzero(&mcibuf, sizeof mcibuf); mcibuf.mci_mailer = FileMailer; mcibuf.mci_out = f; if (bitnset(M_7BITS, FileMailer->m_flags)) mcibuf.mci_flags |= MCIF_7BIT; putfromline(&mcibuf, e); (*e->e_puthdr)(&mcibuf, e->e_header, e); (*e->e_putbody)(&mcibuf, e, NULL); putline("\n", &mcibuf); if (fflush(f) < 0 || ferror(f)) { message("451 I/O error: %s", errstring(errno)); setstat(EX_IOERR); } /* reset ISUID & ISGID bits for paranoid systems */ #if HASFCHMOD (void) fchmod(fileno(f), (MODE_T) stb.st_mode); #else (void) chmod(filename, (MODE_T) stb.st_mode); #endif (void) xfclose(f, "mailfile", filename); (void) fflush(stdout); + setuid(RealUid); exit(ExitStat); /*NOTREACHED*/ } else { /* parent -- wait for exit status */ int st; st = waitfor(pid); if (WIFEXITED(st)) return (WEXITSTATUS(st)); else { syserr("child died on signal %d", st); return (EX_UNAVAILABLE); } /*NOTREACHED*/ } return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */ } /* ** HOSTSIGNATURE -- return the "signature" for a host. ** ** The signature describes how we are going to send this -- it ** can be just the hostname (for non-Internet hosts) or can be ** an ordered list of MX hosts. ** ** Parameters: ** m -- the mailer describing this host. ** host -- the host name. ** e -- the current envelope. ** ** Returns: ** The signature for this host. ** ** Side Effects: ** Can tweak the symbol table. */ char * hostsignature(m, host, e) register MAILER *m; char *host; ENVELOPE *e; { register char *p; register STAB *s; int i; int len; #if NAMED_BIND int nmx; char *hp; char *endp; int oldoptions = _res.options; char *mxhosts[MAXMXHOSTS + 1]; #endif /* ** Check to see if this uses IPC -- if not, it can't have MX records. */ p = m->m_mailer; if (strcmp(p, "[IPC]") != 0 && strcmp(p, "[TCP]") != 0) { /* just an ordinary mailer */ return host; } /* ** Look it up in the symbol table. */ s = stab(host, ST_HOSTSIG, ST_ENTER); if (s->s_hostsig != NULL) return s->s_hostsig; /* ** Not already there -- create a signature. */ #if NAMED_BIND if (ConfigLevel < 2) _res.options &= ~(RES_DEFNAMES | RES_DNSRCH); /* XXX */ for (hp = host; hp != NULL; hp = endp) { endp = strchr(hp, ':'); if (endp != NULL) *endp = '\0'; if (bitnset(M_NOMX, m->m_flags)) { /* skip MX lookups */ nmx = 1; mxhosts[0] = hp; } else { auto int rcode; nmx = getmxrr(hp, mxhosts, TRUE, &rcode); if (nmx <= 0) { register MCI *mci; /* update the connection info for this host */ mci = mci_get(hp, m); mci->mci_errno = errno; mci->mci_herrno = h_errno; mci->mci_lastuse = curtime(); mci_setstat(mci, rcode, NULL, NULL); /* use the original host name as signature */ nmx = 1; mxhosts[0] = hp; } } len = 0; for (i = 0; i < nmx; i++) { len += strlen(mxhosts[i]) + 1; } if (s->s_hostsig != NULL) len += strlen(s->s_hostsig) + 1; p = xalloc(len); if (s->s_hostsig != NULL) { (void) strcpy(p, s->s_hostsig); free(s->s_hostsig); s->s_hostsig = p; p += strlen(p); *p++ = ':'; } else s->s_hostsig = p; for (i = 0; i < nmx; i++) { if (i != 0) *p++ = ':'; strcpy(p, mxhosts[i]); p += strlen(p); } if (endp != NULL) *endp++ = ':'; } makelower(s->s_hostsig); if (ConfigLevel < 2) _res.options = oldoptions; #else /* not using BIND -- the signature is just the host name */ s->s_hostsig = host; #endif if (tTd(17, 1)) printf("hostsignature(%s) = %s\n", host, s->s_hostsig); return s->s_hostsig; } Index: head/usr.sbin/sendmail/src/domain.c =================================================================== --- head/usr.sbin/sendmail/src/domain.c (revision 26988) +++ head/usr.sbin/sendmail/src/domain.c (revision 26989) @@ -1,896 +1,898 @@ /* - * Copyright (c) 1986, 1995, 1996 Eric P. Allman + * Copyright (c) 1986, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "sendmail.h" #ifndef lint #if NAMED_BIND -static char sccsid[] = "@(#)domain.c 8.64 (Berkeley) 10/30/96 (with name server)"; +static char sccsid[] = "@(#)domain.c 8.67 (Berkeley) 4/9/97 (with name server)"; #else -static char sccsid[] = "@(#)domain.c 8.64 (Berkeley) 10/30/96 (without name server)"; +static char sccsid[] = "@(#)domain.c 8.67 (Berkeley) 4/9/97 (without name server)"; #endif #endif /* not lint */ #if NAMED_BIND #include #include #include /* ** The standard udp packet size PACKETSZ (512) is not sufficient for some ** nameserver answers containing very many resource records. The resolver ** may switch to tcp and retry if it detects udp packet overflow. ** Also note that the resolver routines res_query and res_search return ** the size of the *un*truncated answer in case the supplied answer buffer ** it not big enough to accommodate the entire answer. */ #ifndef MAXPACKET # define MAXPACKET 8192 /* max packet size used internally by BIND */ #endif typedef union { HEADER qb1; u_char qb2[MAXPACKET]; } querybuf; #ifndef MXHOSTBUFSIZE # define MXHOSTBUFSIZE (128 * MAXMXHOSTS) #endif static char MXHostBuf[MXHOSTBUFSIZE]; #ifndef MAXDNSRCH # define MAXDNSRCH 6 /* number of possible domains to search */ #endif #ifndef MAX # define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifndef NO_DATA # define NO_DATA NO_ADDRESS #endif #ifndef HFIXEDSZ # define HFIXEDSZ 12 /* sizeof(HEADER) */ #endif #define MAXCNAMEDEPTH 10 /* maximum depth of CNAME recursion */ #if defined(__RES) && (__RES >= 19940415) # define RES_UNC_T char * #else # define RES_UNC_T u_char * #endif /* ** GETMXRR -- get MX resource records for a domain ** ** Parameters: ** host -- the name of the host to MX. ** mxhosts -- a pointer to a return buffer of MX records. ** droplocalhost -- If TRUE, all MX records less preferred ** than the local host (as determined by $=w) will ** be discarded. ** rcode -- a pointer to an EX_ status code. ** ** Returns: ** The number of MX records found. ** -1 if there is an internal failure. ** If no MX records are found, mxhosts[0] is set to host ** and 1 is returned. */ int getmxrr(host, mxhosts, droplocalhost, rcode) char *host; char **mxhosts; bool droplocalhost; int *rcode; { register u_char *eom, *cp; register int i, j, n; int nmx = 0; register char *bp; HEADER *hp; querybuf answer; int ancount, qdcount, buflen; bool seenlocal = FALSE; u_short pref, type; u_short localpref = 256; char *fallbackMX = FallBackMX; bool trycanon = FALSE; int (*resfunc)(); extern int res_query(), res_search(); u_short prefer[MAXMXHOSTS]; int weight[MAXMXHOSTS]; extern int mxrand __P((char *)); if (tTd(8, 2)) printf("getmxrr(%s, droplocalhost=%d)\n", host, droplocalhost); if (fallbackMX != NULL && droplocalhost && wordinclass(fallbackMX, 'w')) { /* don't use fallback for this pass */ fallbackMX = NULL; } *rcode = EX_OK; /* efficiency hack -- numeric or non-MX lookups */ if (host[0] == '[') goto punt; /* ** If we don't have MX records in our host switch, don't ** try for MX records. Note that this really isn't "right", ** since we might be set up to try NIS first and then DNS; ** if the host is found in NIS we really shouldn't be doing ** MX lookups. However, that should be a degenerate case. */ if (!UseNameServer) goto punt; if (HasWildcardMX && ConfigLevel >= 6) resfunc = res_query; else resfunc = res_search; errno = 0; n = (*resfunc)(host, C_IN, T_MX, (u_char *) &answer, sizeof(answer)); if (n < 0) { if (tTd(8, 1)) printf("getmxrr: res_search(%s) failed (errno=%d, h_errno=%d)\n", (host == NULL) ? "" : host, errno, h_errno); switch (h_errno) { case NO_DATA: trycanon = TRUE; /* fall through */ case NO_RECOVERY: /* no MX data on this host */ goto punt; case HOST_NOT_FOUND: #if BROKEN_RES_SEARCH case 0: /* Ultrix resolver retns failure w/ h_errno=0 */ #endif /* host doesn't exist in DNS; might be in /etc/hosts */ trycanon = TRUE; *rcode = EX_NOHOST; goto punt; case TRY_AGAIN: + case -1: /* couldn't connect to the name server */ if (fallbackMX != NULL) { /* name server is hosed -- push to fallback */ mxhosts[nmx++] = fallbackMX; return nmx; } /* it might come up later; better queue it up */ *rcode = EX_TEMPFAIL; break; default: syserr("getmxrr: res_search (%s) failed with impossible h_errno (%d)\n", host, h_errno); *rcode = EX_OSERR; break; } /* irreconcilable differences */ return (-1); } /* avoid problems after truncation in tcp packets */ if (n > sizeof(answer)) n = sizeof(answer); /* find first satisfactory answer */ hp = (HEADER *)&answer; cp = (u_char *)&answer + HFIXEDSZ; eom = (u_char *)&answer + n; for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ) if ((n = dn_skipname(cp, eom)) < 0) goto punt; buflen = sizeof(MXHostBuf) - 1; bp = MXHostBuf; ancount = ntohs(hp->ancount); while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS - 1) { if ((n = dn_expand((u_char *)&answer, eom, cp, (RES_UNC_T) bp, buflen)) < 0) break; cp += n; GETSHORT(type, cp); cp += INT16SZ + INT32SZ; GETSHORT(n, cp); if (type != T_MX) { if (tTd(8, 8) || _res.options & RES_DEBUG) printf("unexpected answer type %d, size %d\n", type, n); cp += n; continue; } GETSHORT(pref, cp); if ((n = dn_expand((u_char *)&answer, eom, cp, (RES_UNC_T) bp, buflen)) < 0) break; cp += n; if (wordinclass(bp, 'w')) { if (tTd(8, 3)) printf("found localhost (%s) in MX list, pref=%d\n", bp, pref); if (droplocalhost) { if (!seenlocal || pref < localpref) localpref = pref; seenlocal = TRUE; continue; } weight[nmx] = 0; } else weight[nmx] = mxrand(bp); prefer[nmx] = pref; mxhosts[nmx++] = bp; n = strlen(bp); bp += n; if (bp[-1] != '.') { *bp++ = '.'; n++; } *bp++ = '\0'; buflen -= n + 1; } /* sort the records */ for (i = 0; i < nmx; i++) { for (j = i + 1; j < nmx; j++) { if (prefer[i] > prefer[j] || (prefer[i] == prefer[j] && weight[i] > weight[j])) { register int temp; register char *temp1; temp = prefer[i]; prefer[i] = prefer[j]; prefer[j] = temp; temp1 = mxhosts[i]; mxhosts[i] = mxhosts[j]; mxhosts[j] = temp1; temp = weight[i]; weight[i] = weight[j]; weight[j] = temp; } } if (seenlocal && prefer[i] >= localpref) { /* truncate higher preference part of list */ nmx = i; } } /* delete duplicates from list (yes, some bozos have duplicates) */ for (i = 0; i < nmx - 1; ) { if (strcasecmp(mxhosts[i], mxhosts[i + 1]) != 0) i++; else { /* compress out duplicate */ for (j = i + 1; j < nmx; j++) mxhosts[j] = mxhosts[j + 1]; nmx--; } } if (nmx == 0) { punt: if (seenlocal && (!TryNullMXList || sm_gethostbyname(host) == NULL)) { /* ** If we have deleted all MX entries, this is ** an error -- we should NEVER send to a host that ** has an MX, and this should have been caught ** earlier in the config file. ** ** Some sites prefer to go ahead and try the ** A record anyway; that case is handled by ** setting TryNullMXList. I believe this is a ** bad idea, but it's up to you.... */ *rcode = EX_CONFIG; syserr("MX list for %s points back to %s", host, MyHostName); return -1; } if (strlen(host) >= (SIZE_T) sizeof MXHostBuf) { *rcode = EX_CONFIG; syserr("Host name %s too long", shortenstring(host, 203)); return -1; } snprintf(MXHostBuf, sizeof MXHostBuf, "%s", host); mxhosts[0] = MXHostBuf; if (host[0] == '[') { register char *p; /* this may be an MX suppression-style address */ p = strchr(MXHostBuf, ']'); if (p != NULL) { *p = '\0'; if (inet_addr(&MXHostBuf[1]) != INADDR_NONE) { nmx++; *p = ']'; } else { trycanon = TRUE; mxhosts[0]++; } } } if (trycanon && getcanonname(mxhosts[0], sizeof MXHostBuf - 2, FALSE)) { bp = &MXHostBuf[strlen(MXHostBuf)]; if (bp[-1] != '.') { *bp++ = '.'; *bp = '\0'; } nmx = 1; } } /* if we have a default lowest preference, include that */ if (fallbackMX != NULL && !seenlocal) mxhosts[nmx++] = fallbackMX; return (nmx); } /* ** MXRAND -- create a randomizer for equal MX preferences ** ** If two MX hosts have equal preferences we want to randomize ** the selection. But in order for signatures to be the same, ** we need to randomize the same way each time. This function ** computes a pseudo-random hash function from the host name. ** ** Parameters: ** host -- the name of the host. ** ** Returns: ** A random but repeatable value based on the host name. ** ** Side Effects: ** none. */ int mxrand(host) register char *host; { int hfunc; static unsigned int seed; if (seed == 0) { seed = (int) curtime() & 0xffff; if (seed == 0) seed++; } if (tTd(17, 9)) printf("mxrand(%s)", host); hfunc = seed; while (*host != '\0') { int c = *host++; if (isascii(c) && isupper(c)) c = tolower(c); hfunc = ((hfunc << 1) ^ c) % 2003; } hfunc &= 0xff; hfunc++; if (tTd(17, 9)) printf(" = %d\n", hfunc); return hfunc; } /* ** BESTMX -- find the best MX for a name ** ** This is really a hack, but I don't see any obvious way ** to generalize it at the moment. */ char * bestmx_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { int nmx; auto int rcode; int saveopts = _res.options; char *mxhosts[MAXMXHOSTS + 1]; _res.options &= ~(RES_DNSRCH|RES_DEFNAMES); nmx = getmxrr(name, mxhosts, FALSE, &rcode); _res.options = saveopts; if (nmx <= 0) return NULL; if (bitset(MF_MATCHONLY, map->map_mflags)) return map_rewrite(map, name, strlen(name), NULL); else return map_rewrite(map, mxhosts[0], strlen(mxhosts[0]), av); } /* ** DNS_GETCANONNAME -- get the canonical name for named host using DNS ** ** This algorithm tries to be smart about wildcard MX records. ** This is hard to do because DNS doesn't tell is if we matched ** against a wildcard or a specific MX. ** ** We always prefer A & CNAME records, since these are presumed ** to be specific. ** ** If we match an MX in one pass and lose it in the next, we use ** the old one. For example, consider an MX matching *.FOO.BAR.COM. ** A hostname bletch.foo.bar.com will match against this MX, but ** will stop matching when we try bletch.bar.com -- so we know ** that bletch.foo.bar.com must have been right. This fails if ** there was also an MX record matching *.BAR.COM, but there are ** some things that just can't be fixed. ** ** Parameters: ** host -- a buffer containing the name of the host. ** This is a value-result parameter. ** hbsize -- the size of the host buffer. ** trymx -- if set, try MX records as well as A and CNAME. ** statp -- pointer to place to store status. ** ** Returns: ** TRUE -- if the host matched. ** FALSE -- otherwise. */ bool dns_getcanonname(host, hbsize, trymx, statp) char *host; int hbsize; bool trymx; int *statp; { register u_char *eom, *ap; register char *cp; register int n; HEADER *hp; querybuf answer; int ancount, qdcount; int ret; char **domain; int type; char **dp; char *mxmatch; bool amatch; bool gotmx = FALSE; int qtype; int loopcnt; char *xp; char nbuf[MAX(MAXPACKET, MAXDNAME*2+2)]; char *searchlist[MAXDNSRCH+2]; extern char *gethostalias(); if (tTd(8, 2)) printf("dns_getcanonname(%s, trymx=%d)\n", host, trymx); if ((_res.options & RES_INIT) == 0 && res_init() == -1) { *statp = EX_UNAVAILABLE; return FALSE; } /* ** Initialize domain search list. If there is at least one ** dot in the name, search the unmodified name first so we ** find "vse.CS" in Czechoslovakia instead of in the local ** domain (e.g., vse.CS.Berkeley.EDU). ** ** Older versions of the resolver could create this ** list by tearing apart the host name. */ loopcnt = 0; cnameloop: /* Check for dots in the name */ for (cp = host, n = 0; *cp != '\0'; cp++) if (*cp == '.') n++; /* ** If this is a simple name, determine whether it matches an ** alias in the file defined by the environment variable HOSTALIASES. */ if (n == 0 && (xp = gethostalias(host)) != NULL) { if (loopcnt++ > MAXCNAMEDEPTH) { syserr("loop in ${HOSTALIASES} file"); } else { strncpy(host, xp, hbsize); host[hbsize - 1] = '\0'; goto cnameloop; } } /* ** Build the search list. ** If there is at least one dot in name, start with a null ** domain to search the unmodified name first. ** If name does not end with a dot and search up local domain ** tree desired, append each local domain component to the ** search list; if name contains no dots and default domain ** name is desired, append default domain name to search list; ** else if name ends in a dot, remove that dot. */ dp = searchlist; if (n > 0) *dp++ = ""; if (n >= 0 && *--cp != '.' && bitset(RES_DNSRCH, _res.options)) { for (domain = _res.dnsrch; *domain != NULL; ) *dp++ = *domain++; } else if (n == 0 && bitset(RES_DEFNAMES, _res.options)) { *dp++ = _res.defdname; } else if (*cp == '.') { *cp = '\0'; } *dp = NULL; /* ** Now loop through the search list, appending each domain in turn ** name and searching for a match. */ mxmatch = NULL; qtype = T_ANY; for (dp = searchlist; *dp != NULL; ) { if (qtype == T_ANY) gotmx = FALSE; if (tTd(8, 5)) printf("dns_getcanonname: trying %s.%s (%s)\n", host, *dp, qtype == T_ANY ? "ANY" : qtype == T_A ? "A" : qtype == T_MX ? "MX" : "???"); ret = res_querydomain(host, *dp, C_IN, qtype, answer.qb2, sizeof(answer.qb2)); if (ret <= 0) { if (tTd(8, 7)) printf("\tNO: errno=%d, h_errno=%d\n", errno, h_errno); if (errno == ECONNREFUSED || h_errno == TRY_AGAIN) { /* the name server seems to be down */ h_errno = TRY_AGAIN; *statp = EX_TEMPFAIL; return FALSE; } if (h_errno != HOST_NOT_FOUND) { /* might have another type of interest */ if (qtype == T_ANY) { qtype = T_A; continue; } else if (qtype == T_A && !gotmx && trymx) { qtype = T_MX; continue; } } /* definite no -- try the next domain */ dp++; qtype = T_ANY; continue; } else if (tTd(8, 7)) printf("\tYES\n"); /* avoid problems after truncation in tcp packets */ if (ret > sizeof(answer)) ret = sizeof(answer); /* ** Appear to have a match. Confirm it by searching for A or ** CNAME records. If we don't have a local domain ** wild card MX record, we will accept MX as well. */ hp = (HEADER *) &answer; ap = (u_char *) &answer + HFIXEDSZ; eom = (u_char *) &answer + ret; /* skip question part of response -- we know what we asked */ for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ) { if ((ret = dn_skipname(ap, eom)) < 0) { if (tTd(8, 20)) printf("qdcount failure (%d)\n", ntohs(hp->qdcount)); *statp = EX_SOFTWARE; return FALSE; /* ???XXX??? */ } } amatch = FALSE; for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n) { n = dn_expand((u_char *) &answer, eom, ap, (RES_UNC_T) nbuf, sizeof nbuf); if (n < 0) break; ap += n; GETSHORT(type, ap); ap += INT16SZ + INT32SZ; GETSHORT(n, ap); switch (type) { case T_MX: gotmx = TRUE; if (**dp != '\0' && HasWildcardMX) { /* ** If we are using MX matches and have ** not yet gotten one, save this one ** but keep searching for an A or ** CNAME match. */ if (trymx && mxmatch == NULL) mxmatch = *dp; continue; } /* ** If we did not append a domain name, this ** must have been a canonical name to start ** with. Even if we did append a domain name, ** in the absence of a wildcard MX this must ** still be a real MX match. ** Such MX matches are as good as an A match, ** fall through. */ case T_A: /* Flag that a good match was found */ amatch = TRUE; /* continue in case a CNAME also exists */ continue; case T_CNAME: if (DontExpandCnames) { /* got CNAME -- guaranteed canonical */ amatch = TRUE; break; } if (loopcnt++ > MAXCNAMEDEPTH) { /*XXX should notify postmaster XXX*/ message("DNS failure: CNAME loop for %s", host); if (CurEnv->e_message == NULL) { char ebuf[MAXLINE]; snprintf(ebuf, sizeof ebuf, "Deferred: DNS failure: CNAME loop for %.100s", host); CurEnv->e_message = newstr(ebuf); } h_errno = NO_RECOVERY; *statp = EX_CONFIG; return FALSE; } /* value points at name */ if ((ret = dn_expand((u_char *)&answer, eom, ap, (RES_UNC_T) nbuf, sizeof(nbuf))) < 0) break; (void)strncpy(host, nbuf, hbsize); /* XXX */ host[hbsize - 1] = '\0'; /* ** RFC 1034 section 3.6 specifies that CNAME ** should point at the canonical name -- but ** urges software to try again anyway. */ goto cnameloop; default: /* not a record of interest */ continue; } } if (amatch) { /* ** Got a good match -- either an A, CNAME, or an ** exact MX record. Save it and get out of here. */ mxmatch = *dp; break; } /* ** Nothing definitive yet. ** If this was a T_ANY query, we don't really know what ** was returned -- it might have been a T_NS, ** for example. Try T_A to be more specific ** during the next pass. ** If this was a T_A query and we haven't yet found a MX ** match, try T_MX if allowed to do so. ** Otherwise, try the next domain. */ if (qtype == T_ANY) qtype = T_A; else if (qtype == T_A && !gotmx && trymx) qtype = T_MX; else { qtype = T_ANY; dp++; } } /* if nothing was found, we are done */ if (mxmatch == NULL) { *statp = EX_NOHOST; return FALSE; } /* ** Create canonical name and return. ** If saved domain name is null, name was already canonical. ** Otherwise append the saved domain name. */ (void) snprintf(nbuf, sizeof nbuf, "%.*s%s%.*s", MAXDNAME, host, *mxmatch == '\0' ? "" : ".", MAXDNAME, mxmatch); strncpy(host, nbuf, hbsize); host[hbsize - 1] = '\0'; if (tTd(8, 5)) printf("dns_getcanonname: %s\n", host); *statp = EX_OK; return TRUE; } char * gethostalias(host) char *host; { char *fname; FILE *fp; register char *p = NULL; char buf[MAXLINE]; static char hbuf[MAXDNAME]; fname = getenv("HOSTALIASES"); if (fname == NULL || (fp = safefopen(fname, O_RDONLY, 0, SFF_REGONLY)) == NULL) return NULL; while (fgets(buf, sizeof buf, fp) != NULL) { for (p = buf; p != '\0' && !(isascii(*p) && isspace(*p)); p++) continue; if (*p == 0) { /* syntax error */ continue; } *p++ = '\0'; if (strcasecmp(buf, host) == 0) break; } if (feof(fp)) { /* no match */ fclose(fp); return NULL; } + fclose(fp); /* got a match; extract the equivalent name */ while (*p != '\0' && isascii(*p) && isspace(*p)) p++; host = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; *p = '\0'; strncpy(hbuf, host, sizeof hbuf - 1); hbuf[sizeof hbuf - 1] = '\0'; return hbuf; } #endif /* NAMED_BIND */ Index: head/usr.sbin/sendmail/src/headers.c =================================================================== --- head/usr.sbin/sendmail/src/headers.c (revision 26988) +++ head/usr.sbin/sendmail/src/headers.c (revision 26989) @@ -1,1499 +1,1556 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)headers.c 8.103 (Berkeley) 12/11/96"; +static char sccsid[] = "@(#)headers.c 8.110 (Berkeley) 6/14/97"; #endif /* not lint */ # include # include "sendmail.h" /* +** SETUPHEADERS -- initialize headers in symbol table +** +** Parameters: +** none +** +** Returns: +** none +*/ + +void +setupheaders() +{ + struct hdrinfo *hi; + STAB *s; + + for (hi = HdrInfo; hi->hi_field != NULL; hi++) + { + s = stab(hi->hi_field, ST_HEADER, ST_ENTER); + s->s_header.hi_flags = hi->hi_flags; + s->s_header.hi_ruleset = NULL; + } +} + /* ** CHOMPHEADER -- process and save a header line. ** ** Called by collect and by readcf to deal with header lines. ** ** Parameters: ** line -- header as a text line. ** def -- if set, this is a default value. ** hdrp -- a pointer to the place to save the header. ** e -- the envelope including this header. ** ** Returns: ** flags for this header. ** ** Side Effects: ** The header is saved on the header list. ** Contents of 'line' are destroyed. */ +struct hdrinfo NormalHeader = { NULL, 0, NULL }; + int chompheader(line, def, hdrp, e) char *line; bool def; HDR **hdrp; register ENVELOPE *e; { register char *p; register HDR *h; HDR **hp; char *fname; char *fvalue; - struct hdrinfo *hi; bool cond = FALSE; bool headeronly; + STAB *s; + struct hdrinfo *hi; BITMAP mopts; if (tTd(31, 6)) { printf("chompheader: "); xputs(line); printf("\n"); } headeronly = hdrp != NULL; if (!headeronly) hdrp = &e->e_header; /* strip off options */ clrbitmap(mopts); p = line; if (*p == '?') { /* have some */ register char *q = strchr(p + 1, *p); if (q != NULL) { *q++ = '\0'; while (*++p != '\0') setbitn(*p, mopts); p = q; } else syserr("553 header syntax error, line \"%s\"", line); cond = TRUE; } /* find canonical name */ fname = p; while (isascii(*p) && isgraph(*p) && *p != ':') p++; fvalue = p; while (isascii(*p) && isspace(*p)) p++; if (*p++ != ':' || fname == fvalue) { syserr("553 header syntax error, line \"%s\"", line); - return (0); + return 0; } *fvalue = '\0'; fvalue = p; /* strip field value on front */ if (*fvalue == ' ') fvalue++; /* security scan: long field names are end-of-header */ if (strlen(fname) > 100) return H_EOH; - /* see if it is a known type */ - for (hi = HdrInfo; hi->hi_field != NULL; hi++) +#if _FFR_HEADER_RSCHECK + /* check to see if it represents a ruleset call */ + if (def) { - if (strcasecmp(hi->hi_field, fname) == 0) - break; + char hbuf[50]; + + (void) expand(fvalue, hbuf, sizeof hbuf, e); + for (p = hbuf; isascii(*p) && isspace(*p); ) + p++; + if ((*p++ & 0377) == CALLSUBR) + { + auto char *endp; + + if (strtorwset(p, &endp, ST_ENTER) > 0) + { + *endp = '\0'; + s = stab(fname, ST_HEADER, ST_ENTER); + s->s_header.hi_ruleset = newstr(p); + } + return 0; + } } +#endif + /* see if it is a known type */ + s = stab(fname, ST_HEADER, ST_FIND); + if (s != NULL) + hi = &s->s_header; + else + hi = &NormalHeader; + if (tTd(31, 9)) { - if (hi->hi_field == NULL) - printf("no header match\n"); + if (s == NULL) + printf("no header flags match\n"); else - printf("header match, hi_flags=%x\n", hi->hi_flags); + printf("header match, flags=%x, ruleset=%s\n", + hi->hi_flags, hi->hi_ruleset); } /* see if this is a resent message */ if (!def && !headeronly && bitset(H_RESENT, hi->hi_flags)) e->e_flags |= EF_RESENT; /* if this is an Errors-To: header keep track of it now */ if (UseErrorsTo && !def && !headeronly && bitset(H_ERRORSTO, hi->hi_flags)) (void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e); /* if this means "end of header" quit now */ if (bitset(H_EOH, hi->hi_flags)) - return (hi->hi_flags); + return hi->hi_flags; /* ** Horrible hack to work around problem with Lotus Notes SMTP ** mail gateway, which generates From: headers with newlines in ** them and the
on the second line. Although this is ** legal RFC 822, many MUAs don't handle this properly and thus ** never find the actual address. */ if (bitset(H_FROM, hi->hi_flags) && SingleLineFromHeader) { while ((p = strchr(fvalue, '\n')) != NULL) *p = ' '; } /* + ** If there is a check ruleset, verify it against the header. + */ + + if (!def && hi->hi_ruleset != NULL) + (void) rscheck(hi->hi_ruleset, fvalue, NULL, e); + + /* ** Drop explicit From: if same as what we would generate. ** This is to make MH (which doesn't always give a full name) ** insert the full name information in all circumstances. */ p = "resent-from"; if (!bitset(EF_RESENT, e->e_flags)) p += 7; if (!def && !headeronly && !bitset(EF_QUEUERUN, e->e_flags) && strcasecmp(fname, p) == 0) { if (tTd(31, 2)) { printf("comparing header from (%s) against default (%s or %s)\n", fvalue, e->e_from.q_paddr, e->e_from.q_user); } if (e->e_from.q_paddr != NULL && (strcmp(fvalue, e->e_from.q_paddr) == 0 || strcmp(fvalue, e->e_from.q_user) == 0)) - return (hi->hi_flags); + return hi->hi_flags; } /* delete default value for this header */ for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link) { if (strcasecmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags) && !bitset(H_FORCE, h->h_flags)) { h->h_value = NULL; if (!cond) { /* copy conditions from default case */ bcopy((char *)h->h_mflags, (char *)mopts, sizeof mopts); } } } /* create a new node */ h = (HDR *) xalloc(sizeof *h); h->h_field = newstr(fname); h->h_value = newstr(fvalue); h->h_link = NULL; bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts); *hp = h; h->h_flags = hi->hi_flags; if (def) h->h_flags |= H_DEFAULT; if (cond) h->h_flags |= H_CHECK; /* hack to see if this is a new format message */ if (!def && !headeronly && bitset(H_RCPT|H_FROM, h->h_flags) && (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL || strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL)) { e->e_flags &= ~EF_OLDSTYLE; } - return (h->h_flags); + return h->h_flags; } /* ** ADDHEADER -- add a header entry to the end of the queue. ** ** This bypasses the special checking of chompheader. ** ** Parameters: ** field -- the name of the header field. ** value -- the value of the field. ** hp -- an indirect pointer to the header structure list. ** ** Returns: ** none. ** ** Side Effects: ** adds the field on the list of headers for this envelope. */ void addheader(field, value, hdrlist) char *field; char *value; HDR **hdrlist; { register HDR *h; - register struct hdrinfo *hi; + STAB *s; HDR **hp; /* find info struct */ - for (hi = HdrInfo; hi->hi_field != NULL; hi++) - { - if (strcasecmp(field, hi->hi_field) == 0) - break; - } + s = stab(field, ST_HEADER, ST_FIND); /* find current place in list -- keep back pointer? */ for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link) { if (strcasecmp(field, h->h_field) == 0) break; } /* allocate space for new header */ h = (HDR *) xalloc(sizeof *h); h->h_field = field; h->h_value = newstr(value); h->h_link = *hp; - h->h_flags = hi->hi_flags | H_DEFAULT; + h->h_flags = H_DEFAULT; + if (s != NULL) + h->h_flags |= s->s_header.hi_flags; clrbitmap(h->h_mflags); *hp = h; } /* ** HVALUE -- return value of a header. ** ** Only "real" fields (i.e., ones that have not been supplied ** as a default) are used. ** ** Parameters: ** field -- the field name. ** header -- the header list. ** ** Returns: ** pointer to the value part. ** NULL if not found. ** ** Side Effects: ** none. */ char * hvalue(field, header) char *field; HDR *header; { register HDR *h; for (h = header; h != NULL; h = h->h_link) { if (!bitset(H_DEFAULT, h->h_flags) && strcasecmp(h->h_field, field) == 0) return (h->h_value); } return (NULL); } /* ** ISHEADER -- predicate telling if argument is a header. ** ** A line is a header if it has a single word followed by ** optional white space followed by a colon. ** ** Header fields beginning with two dashes, although technically ** permitted by RFC822, are automatically rejected in order ** to make MIME work out. Without this we could have a technically ** legal header such as ``--"foo:bar"'' that would also be a legal ** MIME separator. ** ** Parameters: ** h -- string to check for possible headerness. ** ** Returns: ** TRUE if h is a header. ** FALSE otherwise. ** ** Side Effects: ** none. */ bool isheader(h) char *h; { register char *s = h; if (s[0] == '-' && s[1] == '-') return FALSE; while (*s > ' ' && *s != ':' && *s != '\0') s++; if (h == s) return FALSE; /* following technically violates RFC822 */ while (isascii(*s) && isspace(*s)) s++; return (*s == ':'); } /* ** EATHEADER -- run through the stored header and extract info. ** ** Parameters: ** e -- the envelope to process. ** full -- if set, do full processing (e.g., compute ** message priority). This should not be set ** when reading a queue file because some info ** needed to compute the priority is wrong. ** ** Returns: ** none. ** ** Side Effects: ** Sets a bunch of global variables from information ** in the collected header. ** Aborts the message if the hop count is exceeded. */ void eatheader(e, full) register ENVELOPE *e; bool full; { register HDR *h; register char *p; int hopcnt = 0; char *msgid; char buf[MAXLINE]; extern int priencode __P((char *)); /* ** Set up macros for possible expansion in headers. */ define('f', e->e_sender, e); define('g', e->e_sender, e); if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0') define('u', e->e_origrcpt, e); else define('u', NULL, e); /* full name of from person */ p = hvalue("full-name", e->e_header); if (p != NULL) define('x', p, e); if (tTd(32, 1)) printf("----- collected header -----\n"); msgid = NULL; for (h = e->e_header; h != NULL; h = h->h_link) { if (tTd(32, 1)) printf("%s: ", h->h_field); if (h->h_value == NULL) { if (tTd(32, 1)) printf("\n"); continue; } /* do early binding */ if (bitset(H_DEFAULT, h->h_flags)) { if (tTd(32, 1)) { printf("("); xputs(h->h_value); printf(") "); } expand(h->h_value, buf, sizeof buf, e); if (buf[0] != '\0') { if (bitset(H_FROM, h->h_flags)) { extern char *crackaddr(); expand(crackaddr(buf), buf, sizeof buf, e); } h->h_value = newstr(buf); h->h_flags &= ~H_DEFAULT; } } if (tTd(32, 1)) { xputs(h->h_value); printf("\n"); } /* count the number of times it has been processed */ if (bitset(H_TRACE, h->h_flags)) hopcnt++; /* send to this person if we so desire */ if (GrabTo && bitset(H_RCPT, h->h_flags) && !bitset(H_DEFAULT, h->h_flags) && (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags))) { int saveflags = e->e_flags; (void) sendtolist(h->h_value, NULLADDR, &e->e_sendqueue, 0, e); /* delete fatal errors generated by this address */ if (!GrabTo && !bitset(EF_FATALERRS, saveflags)) e->e_flags &= ~EF_FATALERRS; } /* save the message-id for logging */ p = "resent-message-id"; if (!bitset(EF_RESENT, e->e_flags)) p += 7; if (strcasecmp(h->h_field, p) == 0) { msgid = h->h_value; while (isascii(*msgid) && isspace(*msgid)) msgid++; } } if (tTd(32, 1)) printf("----------------------------\n"); /* if we are just verifying (that is, sendmail -t -bv), drop out now */ if (OpMode == MD_VERIFY) return; /* store hop count */ if (hopcnt > e->e_hopcount) e->e_hopcount = hopcnt; /* message priority */ p = hvalue("precedence", e->e_header); if (p != NULL) e->e_class = priencode(p); if (e->e_class < 0) e->e_timeoutclass = TOC_NONURGENT; else if (e->e_class > 0) e->e_timeoutclass = TOC_URGENT; if (full) { e->e_msgpriority = e->e_msgsize - e->e_class * WkClassFact + e->e_nrcpts * WkRecipFact; } /* message timeout priority */ p = hvalue("priority", e->e_header); if (p != NULL) { /* (this should be in the configuration file) */ if (strcasecmp(p, "urgent") == 0) e->e_timeoutclass = TOC_URGENT; else if (strcasecmp(p, "normal") == 0) e->e_timeoutclass = TOC_NORMAL; else if (strcasecmp(p, "non-urgent") == 0) e->e_timeoutclass = TOC_NONURGENT; } /* date message originated */ p = hvalue("posted-date", e->e_header); if (p == NULL) p = hvalue("date", e->e_header); if (p != NULL) define('a', p, e); /* check to see if this is a MIME message */ if ((e->e_bodytype != NULL && strcasecmp(e->e_bodytype, "8BITMIME") == 0) || hvalue("MIME-Version", e->e_header) != NULL) { e->e_flags |= EF_IS_MIME; if (HasEightBits) e->e_bodytype = "8BITMIME"; } else if ((p = hvalue("Content-Type", e->e_header)) != NULL) { /* this may be an RFC 1049 message */ p = strpbrk(p, ";/"); if (p == NULL || *p == ';') { /* yep, it is */ e->e_flags |= EF_DONT_MIME; } } /* ** From person in antiquated ARPANET mode ** required by UK Grey Book e-mail gateways (sigh) */ if (OpMode == MD_ARPAFTP) { register struct hdrinfo *hi; for (hi = HdrInfo; hi->hi_field != NULL; hi++) { if (bitset(H_FROM, hi->hi_flags) && (!bitset(H_RESENT, hi->hi_flags) || bitset(EF_RESENT, e->e_flags)) && (p = hvalue(hi->hi_field, e->e_header)) != NULL) break; } if (hi->hi_field != NULL) { if (tTd(32, 2)) printf("eatheader: setsender(*%s == %s)\n", hi->hi_field, p); setsender(p, e, NULL, '\0', TRUE); } } /* ** Log collection information. */ -# ifdef LOG if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) logsender(e, msgid); -# endif /* LOG */ e->e_flags &= ~EF_LOGSENDER; } /* ** LOGSENDER -- log sender information ** ** Parameters: ** e -- the envelope to log ** msgid -- the message id ** ** Returns: ** none */ void logsender(e, msgid) register ENVELOPE *e; char *msgid; { -# ifdef LOG char *name; register char *sbp; register char *p; int l; char hbuf[MAXNAME + 1]; char sbuf[MAXLINE + 1]; char mbuf[MAXNAME + 1]; /* don't allow newlines in the message-id */ if (msgid != NULL) { l = strlen(msgid); if (l > sizeof mbuf - 1) l = sizeof mbuf - 1; bcopy(msgid, mbuf, l); mbuf[l] = '\0'; p = mbuf; while ((p = strchr(p, '\n')) != NULL) *p++ = ' '; } if (bitset(EF_RESPONSE, e->e_flags)) name = "[RESPONSE]"; else if ((name = macvalue('_', e)) != NULL) ; else if (RealHostName == NULL) name = "localhost"; else if (RealHostName[0] == '[') name = RealHostName; else { name = hbuf; (void) snprintf(hbuf, sizeof hbuf, "%.80s", RealHostName); if (RealHostAddr.sa.sa_family != 0) { p = &hbuf[strlen(hbuf)]; (void) snprintf(p, SPACELEFT(hbuf, p), " (%.100s)", anynet_ntoa(&RealHostAddr)); } } /* some versions of syslog only take 5 printf args */ # if (SYSLOG_BUFSIZE) >= 256 sbp = sbuf; snprintf(sbp, SPACELEFT(sbuf, sbp), "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d", e->e_from.q_paddr == NULL ? "" : e->e_from.q_paddr, e->e_msgsize, e->e_class, e->e_msgpriority, e->e_nrcpts); sbp += strlen(sbp); if (msgid != NULL) { snprintf(sbp, SPACELEFT(sbuf, sbp), ", msgid=%.100s", mbuf); sbp += strlen(sbp); } if (e->e_bodytype != NULL) { (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", bodytype=%.20s", e->e_bodytype); sbp += strlen(sbp); } p = macvalue('r', e); if (p != NULL) (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", proto=%.20s", p); - syslog(LOG_INFO, "%s: %.850s, relay=%.100s", - e->e_id, sbuf, name); + sm_syslog(LOG_INFO, e->e_id, + "%.850s, relay=%.100s", + sbuf, name); # else /* short syslog buffer */ - syslog(LOG_INFO, "%s: from=%s", - e->e_id, e->e_from.q_paddr == NULL ? "" : - shortenstring(e->e_from.q_paddr, 83)); - syslog(LOG_INFO, "%s: size=%ld, class=%ld, pri=%ld, nrcpts=%d", - e->e_id, e->e_msgsize, e->e_class, - e->e_msgpriority, e->e_nrcpts); + sm_syslog(LOG_INFO, e->e_id, + "from=%s", + e->e_from.q_paddr == NULL ? "" + : shortenstring(e->e_from.q_paddr, 83)); + sm_syslog(LOG_INFO, e->e_id, + "size=%ld, class=%ld, pri=%ld, nrcpts=%d", + e->e_msgsize, e->e_class, e->e_msgpriority, e->e_nrcpts); if (msgid != NULL) - syslog(LOG_INFO, "%s: msgid=%s", - e->e_id, shortenstring(mbuf, 83)); + sm_syslog(LOG_INFO, e->e_id, + "msgid=%s", + shortenstring(mbuf, 83)); sbp = sbuf; - snprintf(sbp, SPACELEFT(sbuf, sbp), "%s:", e->e_id); - sbp += strlen(sbp); + *sbp = '\0'; if (e->e_bodytype != NULL) { - snprintf(sbp, SPACELEFT(sbuf, sbp), " bodytype=%.20s,", e->e_bodytype); + snprintf(sbp, SPACELEFT(sbuf, sbp), "bodytype=%.20s, ", e->e_bodytype); sbp += strlen(sbp); } p = macvalue('r', e); if (p != NULL) { - snprintf(sbp, SPACELEFT(sbuf, sbp), " proto=%.20s,", p); + snprintf(sbp, SPACELEFT(sbuf, sbp), "proto=%.20s, ", p); sbp += strlen(sbp); } - syslog(LOG_INFO, "%.400s relay=%.100s", sbuf, name); + sm_syslog(LOG_INFO, e->e_id, + "%.400srelay=%.100s", sbuf, name); # endif -# endif } /* ** PRIENCODE -- encode external priority names into internal values. ** ** Parameters: ** p -- priority in ascii. ** ** Returns: ** priority as a numeric level. ** ** Side Effects: ** none. */ int priencode(p) char *p; { register int i; for (i = 0; i < NumPriorities; i++) { if (!strcasecmp(p, Priorities[i].pri_name)) return (Priorities[i].pri_val); } /* unknown priority */ return (0); } /* ** CRACKADDR -- parse an address and turn it into a macro ** ** This doesn't actually parse the address -- it just extracts ** it and replaces it with "$g". The parse is totally ad hoc ** and isn't even guaranteed to leave something syntactically ** identical to what it started with. However, it does leave ** something semantically identical. ** ** This algorithm has been cleaned up to handle a wider range ** of cases -- notably quoted and backslash escaped strings. ** This modification makes it substantially better at preserving ** the original syntax. ** ** Parameters: ** addr -- the address to be cracked. ** ** Returns: ** a pointer to the new version. ** ** Side Effects: ** none. ** ** Warning: ** The return value is saved in local storage and should ** be copied if it is to be reused. */ char * crackaddr(addr) register char *addr; { register char *p; register char c; int cmtlev; int realcmtlev; int anglelev, realanglelev; int copylev; int bracklev; bool qmode; bool realqmode; bool skipping; bool putgmac = FALSE; bool quoteit = FALSE; bool gotangle = FALSE; bool gotcolon = FALSE; register char *bp; char *buflim; char *bufhead; char *addrhead; static char buf[MAXNAME + 1]; if (tTd(33, 1)) printf("crackaddr(%s)\n", addr); /* strip leading spaces */ while (*addr != '\0' && isascii(*addr) && isspace(*addr)) addr++; /* ** Start by assuming we have no angle brackets. This will be ** adjusted later if we find them. */ bp = bufhead = buf; - buflim = &buf[sizeof buf - 6]; + buflim = &buf[sizeof buf - 7]; p = addrhead = addr; copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0; bracklev = 0; qmode = realqmode = FALSE; while ((c = *p++) != '\0') { /* ** If the buffer is overful, go into a special "skipping" ** mode that tries to keep legal syntax but doesn't actually ** output things. */ skipping = bp >= buflim; if (copylev > 0 && !skipping) *bp++ = c; /* check for backslash escapes */ if (c == '\\') { /* arrange to quote the address */ if (cmtlev <= 0 && !qmode) quoteit = TRUE; if ((c = *p++) == '\0') { /* too far */ p--; goto putg; } if (copylev > 0 && !skipping) *bp++ = c; goto putg; } /* check for quoted strings */ if (c == '"' && cmtlev <= 0) { qmode = !qmode; if (copylev > 0 && !skipping) realqmode = !realqmode; continue; } if (qmode) goto putg; /* check for comments */ if (c == '(') { cmtlev++; /* allow space for closing paren */ if (!skipping) { buflim--; realcmtlev++; if (copylev++ <= 0) { if (bp != bufhead) *bp++ = ' '; *bp++ = c; } } } if (cmtlev > 0) { if (c == ')') { cmtlev--; copylev--; if (!skipping) { realcmtlev--; buflim++; } } continue; } else if (c == ')') { /* syntax error: unmatched ) */ if (copylev > 0 && !skipping) bp--; } /* count nesting on [ ... ] (for IPv6 domain literals) */ if (c == '[') bracklev++; else if (c == ']') bracklev--; /* check for group: list; syntax */ if (c == ':' && anglelev <= 0 && bracklev <= 0 && !gotcolon && !ColonOkInAddr) { register char *q; /* ** Check for DECnet phase IV ``::'' (host::user) ** or ** DECnet phase V ``:.'' syntaxes. The latter ** covers ``user@DEC:.tay.myhost'' and ** ``DEC:.tay.myhost::user'' syntaxes (bletch). */ if (*p == ':' || *p == '.') { if (cmtlev <= 0 && !qmode) quoteit = TRUE; if (copylev > 0 && !skipping) { *bp++ = c; *bp++ = *p; } p++; goto putg; } gotcolon = TRUE; bp = bufhead; if (quoteit) { *bp++ = '"'; /* back up over the ':' and any spaces */ --p; while (isascii(*--p) && isspace(*p)) continue; p++; } for (q = addrhead; q < p; ) { c = *q++; if (bp < buflim) { if (quoteit && c == '"') *bp++ = '\\'; *bp++ = c; } } if (quoteit) { if (bp == &bufhead[1]) bp--; else *bp++ = '"'; while ((c = *p++) != ':') { if (bp < buflim) *bp++ = c; } *bp++ = c; } /* any trailing white space is part of group: */ while (isascii(*p) && isspace(*p) && bp < buflim) *bp++ = *p++; copylev = 0; putgmac = quoteit = FALSE; bufhead = bp; addrhead = p; continue; } if (c == ';' && copylev <= 0 && !ColonOkInAddr) { if (bp < buflim) *bp++ = c; } /* check for characters that may have to be quoted */ if (strchr(MustQuoteChars, c) != NULL) { /* ** If these occur as the phrase part of a <> ** construct, but are not inside of () or already ** quoted, they will have to be quoted. Note that ** now (but don't actually do the quoting). */ if (cmtlev <= 0 && !qmode) quoteit = TRUE; } /* check for angle brackets */ if (c == '<') { register char *q; /* assume first of two angles is bogus */ if (gotangle) quoteit = TRUE; gotangle = TRUE; /* oops -- have to change our mind */ anglelev = 1; if (!skipping) realanglelev = 1; bp = bufhead; if (quoteit) { *bp++ = '"'; /* back up over the '<' and any spaces */ --p; while (isascii(*--p) && isspace(*p)) continue; p++; } for (q = addrhead; q < p; ) { c = *q++; if (bp < buflim) { if (quoteit && c == '"') *bp++ = '\\'; *bp++ = c; } } if (quoteit) { if (bp == &buf[1]) bp--; else *bp++ = '"'; while ((c = *p++) != '<') { if (bp < buflim) *bp++ = c; } *bp++ = c; } copylev = 0; putgmac = quoteit = FALSE; continue; } if (c == '>') { if (anglelev > 0) { anglelev--; if (!skipping) { realanglelev--; buflim++; } } else if (!skipping) { /* syntax error: unmatched > */ if (copylev > 0) bp--; quoteit = TRUE; continue; } if (copylev++ <= 0) *bp++ = c; continue; } /* must be a real address character */ putg: if (copylev <= 0 && !putgmac) { if (bp > bufhead && bp[-1] == ')') *bp++ = ' '; *bp++ = MACROEXPAND; *bp++ = 'g'; putgmac = TRUE; } } /* repair any syntactic damage */ if (realqmode) *bp++ = '"'; while (realcmtlev-- > 0) *bp++ = ')'; while (realanglelev-- > 0) *bp++ = '>'; *bp++ = '\0'; if (tTd(33, 1)) { printf("crackaddr=>`"); xputs(buf); printf("'\n"); } return (buf); } /* ** PUTHEADER -- put the header part of a message from the in-core copy ** ** Parameters: ** mci -- the connection information. ** h -- the header to put. ** e -- envelope to use. ** ** Returns: ** none. ** ** Side Effects: ** none. */ /* * Macro for fast max (not available in e.g. DG/UX, 386/ix). */ #ifndef MAX # define MAX(a,b) (((a)>(b))?(a):(b)) #endif void putheader(mci, hdr, e) register MCI *mci; HDR *hdr; register ENVELOPE *e; { register HDR *h; char buf[MAX(MAXLINE,BUFSIZ)]; char obuf[MAXLINE]; if (tTd(34, 1)) printf("--- putheader, mailer = %s ---\n", mci->mci_mailer->m_name); mci->mci_flags |= MCIF_INHEADER; for (h = hdr; h != NULL; h = h->h_link) { register char *p = h->h_value; extern bool bitintersect(); if (tTd(34, 11)) { printf(" %s: ", h->h_field); xputs(p); } /* suppress Content-Transfer-Encoding: if we are MIMEing */ if (bitset(H_CTE, h->h_flags) && bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags)) { if (tTd(34, 11)) printf(" (skipped (content-transfer-encoding))\n"); continue; } if (bitset(MCIF_INMIME, mci->mci_flags)) { if (tTd(34, 11)) printf("\n"); put_vanilla_header(h, p, mci); continue; } if (bitset(H_CHECK|H_ACHECK, h->h_flags) && !bitintersect(h->h_mflags, mci->mci_mailer->m_flags)) { if (tTd(34, 11)) printf(" (skipped)\n"); continue; } /* handle Resent-... headers specially */ if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) { if (tTd(34, 11)) printf(" (skipped (resent))\n"); continue; } /* suppress return receipts if requested */ if (bitset(H_RECEIPTTO, h->h_flags) && -#if _FFR_DSN_RRT +#if _FFR_DSN_RRT_OPTION (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags))) #else bitset(EF_NORECEIPT, e->e_flags)) #endif { if (tTd(34, 11)) printf(" (skipped (receipt))\n"); continue; } /* macro expand value if generated internally */ if (bitset(H_DEFAULT, h->h_flags)) { expand(p, buf, sizeof buf, e); p = buf; - if (p == NULL || *p == '\0') + if (*p == '\0') { if (tTd(34, 11)) printf(" (skipped -- null value)\n"); continue; } } if (bitset(H_BCC, h->h_flags)) { /* Bcc: field -- either truncate or delete */ if (bitset(EF_DELETE_BCC, e->e_flags)) { if (tTd(34, 11)) printf(" (skipped -- bcc)\n"); } else { /* no other recipient headers: truncate value */ (void) snprintf(obuf, sizeof obuf, "%s:", h->h_field); putline(obuf, mci); } continue; } if (tTd(34, 11)) printf("\n"); if (bitset(H_FROM|H_RCPT, h->h_flags)) { /* address field */ bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); if (bitset(H_FROM, h->h_flags)) oldstyle = FALSE; commaize(h, p, oldstyle, mci, e); } else { put_vanilla_header(h, p, mci); } } /* ** If we are converting this to a MIME message, add the ** MIME headers. */ #if MIME8TO7 if (bitset(MM_MIME8BIT, MimeMode) && bitset(EF_HAS8BIT, e->e_flags) && !bitset(EF_DONT_MIME, e->e_flags) && !bitnset(M_8BITS, mci->mci_mailer->m_flags) && !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8, mci->mci_flags)) { if (hvalue("MIME-Version", e->e_header) == NULL) putline("MIME-Version: 1.0", mci); if (hvalue("Content-Type", e->e_header) == NULL) { snprintf(obuf, sizeof obuf, "Content-Type: text/plain; charset=%s", defcharset(e)); putline(obuf, mci); } if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL) putline("Content-Transfer-Encoding: 8bit", mci); } #endif } /* ** PUT_VANILLA_HEADER -- output a fairly ordinary header ** ** Parameters: ** h -- the structure describing this header ** v -- the value of this header ** mci -- the connection info for output ** ** Returns: ** none. */ void put_vanilla_header(h, v, mci) HDR *h; char *v; MCI *mci; { register char *nlp; register char *obp; int putflags; char obuf[MAXLINE]; putflags = 0; -#ifdef _FFR_7BITHDRS +#if _FFR_7BITHDRS if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) putflags |= PXLF_STRIP8BIT; #endif (void) snprintf(obuf, sizeof obuf, "%.200s: ", h->h_field); obp = obuf + strlen(obuf); while ((nlp = strchr(v, '\n')) != NULL) { int l; l = nlp - v; if (sizeof obuf - (obp - obuf) < l) l = sizeof obuf - (obp - obuf); snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v); - putxline(obuf, mci, putflags); + putxline(obuf, strlen(obuf), mci, putflags); v += l + 1; obp = obuf; if (*v != ' ' && *v != '\t') *obp++ = ' '; } snprintf(obp, SPACELEFT(obuf, obp), "%.*s", sizeof obuf - (obp - obuf) - 1, v); - putxline(obuf, mci, putflags); + putxline(obuf, strlen(obuf), mci, putflags); } /* ** COMMAIZE -- output a header field, making a comma-translated list. ** ** Parameters: ** h -- the header field to output. ** p -- the value to put in it. ** oldstyle -- TRUE if this is an old style header. ** mci -- the connection information. ** e -- the envelope containing the message. ** ** Returns: ** none. ** ** Side Effects: ** outputs "p" to file "fp". */ void commaize(h, p, oldstyle, mci, e) register HDR *h; register char *p; bool oldstyle; register MCI *mci; register ENVELOPE *e; { register char *obp; int opos; int omax; bool firstone = TRUE; int putflags = 0; char obuf[MAXLINE + 3]; /* ** Output the address list translated by the ** mailer and with commas. */ if (tTd(14, 2)) printf("commaize(%s: %s)\n", h->h_field, p); -#ifdef _FFR_7BITHDRS +#if _FFR_7BITHDRS if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) putflags |= PXLF_STRIP8BIT; #endif obp = obuf; (void) snprintf(obp, SPACELEFT(obuf, obp), "%.200s: ", h->h_field); opos = strlen(h->h_field) + 2; + if (opos > 202) + opos = 202; obp += opos; omax = mci->mci_mailer->m_linelimit - 2; if (omax < 0 || omax > 78) omax = 78; /* ** Run through the list of values. */ while (*p != '\0') { register char *name; register int c; char savechar; int flags; auto int stat; /* ** Find the end of the name. New style names ** end with a comma, old style names end with ** a space character. However, spaces do not ** necessarily delimit an old-style name -- at ** signs mean keep going. */ /* find end of name */ while ((isascii(*p) && isspace(*p)) || *p == ',') p++; name = p; for (;;) { auto char *oldp; char pvpbuf[PSBUFSIZE]; (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf, sizeof pvpbuf, &oldp, NULL); p = oldp; /* look to see if we have an at sign */ while (*p != '\0' && isascii(*p) && isspace(*p)) p++; if (*p != '@') { p = oldp; break; } p += *p == '@' ? 1 : 2; while (*p != '\0' && isascii(*p) && isspace(*p)) p++; } /* at the end of one complete name */ /* strip off trailing white space */ while (p >= name && ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0')) p--; if (++p == name) continue; savechar = *p; *p = '\0'; /* translate the name to be relative */ flags = RF_HEADERADDR|RF_ADDDOMAIN; if (bitset(H_FROM, h->h_flags)) flags |= RF_SENDERADDR; #if USERDB else if (e->e_from.q_mailer != NULL && bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags)) { extern char *udbsender(); char *q; q = udbsender(name); if (q != NULL) name = q; } #endif stat = EX_OK; name = remotename(name, mci->mci_mailer, flags, &stat, e); if (*name == '\0') { *p = savechar; continue; } /* output the name with nice formatting */ opos += strlen(name); if (!firstone) opos += 2; if (opos > omax && !firstone) { snprintf(obp, SPACELEFT(obuf, obp), ",\n"); - putxline(obuf, mci, putflags); + putxline(obuf, strlen(obuf), mci, putflags); obp = obuf; (void) strcpy(obp, " "); opos = strlen(obp); obp += opos; opos += strlen(name); } else if (!firstone) { snprintf(obp, SPACELEFT(obuf, obp), ", "); obp += 2; } while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) *obp++ = c; firstone = FALSE; *p = savechar; } *obp = '\0'; - putxline(obuf, mci, putflags); + putxline(obuf, strlen(obuf), mci, putflags); } /* ** COPYHEADER -- copy header list ** ** This routine is the equivalent of newstr for header lists ** ** Parameters: ** header -- list of header structures to copy. ** ** Returns: ** a copy of 'header'. ** ** Side Effects: ** none. */ HDR * copyheader(header) register HDR *header; { register HDR *newhdr; HDR *ret; register HDR **tail = &ret; while (header != NULL) { newhdr = (HDR *) xalloc(sizeof(HDR)); STRUCTCOPY(*header, *newhdr); *tail = newhdr; tail = &newhdr->h_link; header = header->h_link; } *tail = NULL; return ret; } Index: head/usr.sbin/sendmail/src/main.c =================================================================== --- head/usr.sbin/sendmail/src/main.c (revision 26988) +++ head/usr.sbin/sendmail/src/main.c (revision 26989) @@ -1,2445 +1,2484 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)main.c 8.230 (Berkeley) 1/17/97"; +static char sccsid[] = "@(#)main.c 8.246 (Berkeley) 6/11/97"; #endif /* not lint */ #define _DEFINE #include "sendmail.h" #include #if NAMED_BIND #include #endif # ifdef lint char edata, end; # endif /* lint */ /* ** SENDMAIL -- Post mail to a set of destinations. ** ** This is the basic mail router. All user mail programs should ** call this routine to actually deliver mail. Sendmail in ** turn calls a bunch of mail servers that do the real work of ** delivering the mail. ** -** Sendmail is driven by tables read in from /usr/lib/sendmail.cf -** (read by readcf.c). Some more static configuration info, -** including some code that you may want to tailor for your -** installation, is in conf.c. You may also want to touch -** daemon.c (if you have some other IPC mechanism), acct.c -** (to change your accounting), names.c (to adjust the name -** server mechanism). +** Sendmail is driven by settings read in from /etc/sendmail.cf +** (read by readcf.c). ** ** Usage: ** /usr/lib/sendmail [flags] addr ... ** ** See the associated documentation for details. ** ** Author: ** Eric Allman, UCB/INGRES (until 10/81). ** Britton-Lee, Inc., purveyors of fine ** database computers (11/81 - 10/88). ** International Computer Science Institute ** (11/88 - 9/89). ** UCB/Mammoth Project (10/89 - 7/95). -** InReference, Inc. (8/95 - present). +** InReference, Inc. (8/95 - 1/97). ** The support of the my employers is gratefully acknowledged. ** Few of them (Britton-Lee in particular) have had ** anything to gain from my involvement in this project. */ int NextMailer; /* "free" index into Mailer struct */ char *FullName; /* sender's full name */ ENVELOPE BlankEnvelope; /* a "blank" envelope */ ENVELOPE MainEnvelope; /* the envelope around the basic letter */ ADDRESS NullAddress = /* a null address */ { "", "", NULL, "" }; char *CommandLineArgs; /* command line args for pid file */ bool Warn_Q_option = FALSE; /* warn about Q option use */ char **SaveArgv; /* argument vector for re-execing */ +#ifdef NGROUPS_MAX +GIDSET_T InitialGidSet[NGROUPS_MAX]; +#endif + static void obsolete(); extern void printmailer __P((MAILER *)); extern void tTflag __P((char *)); #if DAEMON && !SMTP ERROR %%%% Cannot have DAEMON mode without SMTP %%%% ERROR #endif /* DAEMON && !SMTP */ #if SMTP && !QUEUE ERROR %%%% Cannot have SMTP mode without QUEUE %%%% ERROR #endif /* DAEMON && !SMTP */ #define MAXCONFIGLEVEL 7 /* highest config version level known */ int main(argc, argv, envp) int argc; char **argv; char **envp; { register char *p; char **av; extern char Version[]; char *ep, *from; typedef int (*fnptr)(); STAB *st; register int i; int j; bool queuemode = FALSE; /* process queue requests */ bool safecf = TRUE; bool warn_C_flag = FALSE; char warn_f_flag = '\0'; bool run_in_foreground = FALSE; /* -bD mode */ static bool reenter = FALSE; struct passwd *pw; struct stat stb; struct hostent *hp; bool nullserver = FALSE; char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */ static char rnamebuf[MAXNAME]; /* holds RealUserName */ char *emptyenviron[1]; extern int DtableSize; extern int optind; extern int opterr; extern char *optarg; extern char **environ; extern time_t convtime(); extern SIGFUNC_DECL intsig __P((int)); extern struct hostent *myhostname(); extern char *getauthinfo(); extern char *getcfname(); extern SIGFUNC_DECL sigusr1 __P((int)); extern SIGFUNC_DECL sighup __P((int)); extern void initmacros __P((ENVELOPE *)); extern void init_md __P((int, char **)); extern int getdtsize __P((void)); extern void tTsetup __P((u_char *, int, char *)); extern void setdefaults __P((ENVELOPE *)); extern void initsetproctitle __P((int, char **, char **)); extern void init_vendor_macros __P((ENVELOPE *)); extern void load_if_names __P((void)); extern void vendor_pre_defaults __P((ENVELOPE *)); extern void vendor_post_defaults __P((ENVELOPE *)); extern void readcf __P((char *, bool, ENVELOPE *)); extern void printqueue __P((void)); extern void sendtoargv __P((char **, ENVELOPE *)); extern void resetlimits __P((void)); extern void drop_privileges __P((void)); /* ** Check to see if we reentered. ** This would normally happen if e_putheader or e_putbody ** were NULL when invoked. */ if (reenter) { syserr("main: reentered!"); abort(); } reenter = TRUE; /* avoid null pointer dereferences */ TermEscape.te_rv_on = TermEscape.te_rv_off = ""; /* do machine-dependent initializations */ init_md(argc, argv); #ifdef SIGUSR1 /* arrange to dump state on user-1 signal */ setsignal(SIGUSR1, sigusr1); #endif /* in 4.4BSD, the table can be huge; impose a reasonable limit */ DtableSize = getdtsize(); if (DtableSize > 256) DtableSize = 256; /* ** Be sure we have enough file descriptors. ** But also be sure that 0, 1, & 2 are open. */ i = open("/dev/null", O_RDWR, 0); if (fstat(STDIN_FILENO, &stb) < 0 && errno != EOPNOTSUPP) (void) dup2(i, STDIN_FILENO); if (fstat(STDOUT_FILENO, &stb) < 0 && errno != EOPNOTSUPP) (void) dup2(i, STDOUT_FILENO); if (fstat(STDERR_FILENO, &stb) < 0 && errno != EOPNOTSUPP) (void) dup2(i, STDERR_FILENO); if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) (void) close(i); i = DtableSize; while (--i > 0) { if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) (void) close(i); } errno = 0; -#ifdef LOG +#if LOG # ifdef LOG_MAIL openlog("sendmail", LOG_PID, LOG_MAIL); # else openlog("sendmail", LOG_PID); # endif #endif tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); +#ifdef NGROUPS_MAX + /* save initial group set for future checks */ + i = getgroups(NGROUPS_MAX, InitialGidSet); + if (i == 0) + InitialGidSet[0] = (GID_T) -1; + while (i < NGROUPS_MAX) + InitialGidSet[i++] = InitialGidSet[0]; +#endif + /* drop group id privileges (RunAsUser not yet set) */ drop_privileges(); /* Handle any non-getoptable constructions. */ obsolete(argv); /* ** Do a quick prescan of the argument list. */ #if defined(__osf__) || defined(_AIX3) # define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x" #endif #if defined(sony_news) # define OPTIONS "B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:" #endif #ifndef OPTIONS # define OPTIONS "B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:" #endif opterr = 0; while ((j = getopt(argc, argv, OPTIONS)) != -1) { switch (j) { case 'd': /* hack attack -- see if should use ANSI mode */ if (strcmp(optarg, "ANSI") == 0) { TermEscape.te_rv_on = "\033[7m"; TermEscape.te_rv_off = "\033[0m"; break; } tTflag(optarg); setbuf(stdout, (char *) NULL); break; } } opterr = 1; /* set up the blank envelope */ BlankEnvelope.e_puthdr = putheader; BlankEnvelope.e_putbody = putbody; BlankEnvelope.e_xfp = NULL; STRUCTCOPY(NullAddress, BlankEnvelope.e_from); CurEnv = &BlankEnvelope; STRUCTCOPY(NullAddress, MainEnvelope.e_from); /* ** Set default values for variables. ** These cannot be in initialized data space. */ setdefaults(&BlankEnvelope); RealUid = getuid(); RealGid = getgid(); pw = sm_getpwuid(RealUid); if (pw != NULL) (void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name); else (void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid); RealUserName = rnamebuf; /* save command line arguments */ i = 0; for (av = argv; *av != NULL; ) i += strlen(*av++) + 1; SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1)); CommandLineArgs = xalloc(i); p = CommandLineArgs; for (av = argv, i = 0; *av != NULL; ) { SaveArgv[i++] = newstr(*av); if (av != argv) *p++ = ' '; strcpy(p, *av++); p += strlen(p); } SaveArgv[i] = NULL; if (tTd(0, 1)) { int ll; extern char *CompileOptions[]; printf("Version %s\n Compiled with:", Version); av = CompileOptions; ll = 7; while (*av != NULL) { if (ll + strlen(*av) > 63) { putchar('\n'); ll = 0; } if (ll == 0) { putchar('\t'); putchar('\t'); } else putchar(' '); printf("%s", *av); ll += strlen(*av++) + 1; } putchar('\n'); } if (tTd(0, 10)) { int ll; extern char *OsCompileOptions[]; printf(" OS Defines:"); av = OsCompileOptions; ll = 7; while (*av != NULL) { if (ll + strlen(*av) > 63) { putchar('\n'); ll = 0; } if (ll == 0) { putchar('\t'); putchar('\t'); } else putchar(' '); printf("%s", *av); ll += strlen(*av++) + 1; } putchar('\n'); #ifdef _PATH_UNIX printf("Kernel symbols:\t%s\n", _PATH_UNIX); #endif printf(" Def Conf file:\t%s\n", getcfname()); printf(" Pid file:\t%s\n", PidFile); } InChannel = stdin; OutChannel = stdout; /* initialize for setproctitle */ initsetproctitle(argc, argv, envp); /* clear sendmail's environment */ ExternalEnviron = environ; emptyenviron[0] = NULL; environ = emptyenviron; /* prime the child environment */ setuserenv("AGENT", "sendmail"); if (setsignal(SIGINT, SIG_IGN) != SIG_IGN) (void) setsignal(SIGINT, intsig); (void) setsignal(SIGTERM, intsig); (void) setsignal(SIGPIPE, SIG_IGN); OldUmask = umask(022); OpMode = MD_DELIVER; FullName = getextenv("NAME"); /* ** Initialize name server if it is going to be used. */ #if NAMED_BIND if (!bitset(RES_INIT, _res.options)) res_init(); if (tTd(8, 8)) _res.options |= RES_DEBUG; # ifdef RES_NOALIASES _res.options |= RES_NOALIASES; # endif #endif errno = 0; from = NULL; /* initialize some macros, etc. */ initmacros(CurEnv); init_vendor_macros(CurEnv); /* version */ define('v', Version, CurEnv); /* hostname */ hp = myhostname(jbuf, sizeof jbuf); if (jbuf[0] != '\0') { struct utsname utsname; if (tTd(0, 4)) printf("canonical name: %s\n", jbuf); define('w', newstr(jbuf), CurEnv); /* must be new string */ define('j', newstr(jbuf), CurEnv); setclass('w', jbuf); p = strchr(jbuf, '.'); if (p != NULL) { if (p[1] != '\0') { define('m', newstr(&p[1]), CurEnv); } while (p != NULL && strchr(&p[1], '.') != NULL) { *p = '\0'; if (tTd(0, 4)) printf("\ta.k.a.: %s\n", jbuf); setclass('w', jbuf); *p++ = '.'; p = strchr(p, '.'); } } if (uname(&utsname) >= 0) p = utsname.nodename; else { if (tTd(0, 22)) printf("uname failed (%s)\n", errstring(errno)); makelower(jbuf); p = jbuf; } if (tTd(0, 4)) printf(" UUCP nodename: %s\n", p); p = newstr(p); define('k', p, CurEnv); setclass('k', p); setclass('w', p); } if (hp != NULL) { for (av = hp->h_aliases; av != NULL && *av != NULL; av++) { if (tTd(0, 4)) printf("\ta.k.a.: %s\n", *av); setclass('w', *av); } #if NETINET if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ) { register int i; for (i = 0; hp->h_addr_list[i] != NULL; i++) { char ipbuf[103]; snprintf(ipbuf, sizeof ipbuf, "[%.100s]", inet_ntoa(*((struct in_addr *) hp->h_addr_list[i]))); if (tTd(0, 4)) printf("\ta.k.a.: %s\n", ipbuf); setclass('w', ipbuf); } } #endif } - /* probe interfaces and locate any additional names */ - load_if_names(); - /* current time */ define('b', arpadate((char *) NULL), CurEnv); /* ** Crack argv. */ av = argv; p = strrchr(*av, '/'); if (p++ == NULL) p = *av; if (strcmp(p, "newaliases") == 0) OpMode = MD_INITALIAS; else if (strcmp(p, "mailq") == 0) OpMode = MD_PRINT; else if (strcmp(p, "smtpd") == 0) OpMode = MD_DAEMON; else if (strcmp(p, "hoststat") == 0) OpMode = MD_HOSTSTAT; else if (strcmp(p, "purgestat") == 0) OpMode = MD_PURGESTAT; optind = 1; while ((j = getopt(argc, argv, OPTIONS)) != -1) { switch (j) { case 'b': /* operations mode */ switch (j = *optarg) { case MD_DAEMON: case MD_FGDAEMON: # if !DAEMON usrerr("Daemon mode not implemented"); ExitStat = EX_USAGE; break; # endif /* DAEMON */ case MD_SMTP: # if !SMTP usrerr("I don't speak SMTP"); ExitStat = EX_USAGE; break; # endif /* SMTP */ case MD_INITALIAS: case MD_DELIVER: case MD_VERIFY: case MD_TEST: case MD_PRINT: case MD_HOSTSTAT: case MD_PURGESTAT: case MD_ARPAFTP: OpMode = j; break; case MD_FREEZE: usrerr("Frozen configurations unsupported"); ExitStat = EX_USAGE; break; default: usrerr("Invalid operation mode %c", j); ExitStat = EX_USAGE; break; } break; case 'B': /* body type */ CurEnv->e_bodytype = optarg; break; case 'C': /* select configuration file (already done) */ if (RealUid != 0) warn_C_flag = TRUE; ConfFile = optarg; endpwent(); (void) setgid(RealGid); (void) setuid(RealUid); safecf = FALSE; break; case 'd': /* debugging -- already done */ break; case 'f': /* from address */ case 'r': /* obsolete -f flag */ if (from != NULL) { usrerr("More than one \"from\" person"); ExitStat = EX_USAGE; break; } from = newstr(denlstring(optarg, TRUE, TRUE)); if (strcmp(RealUserName, from) != 0) warn_f_flag = j; break; case 'F': /* set full name */ FullName = newstr(optarg); break; case 'h': /* hop count */ CurEnv->e_hopcount = strtol(optarg, &ep, 10); if (*ep) { usrerr("Bad hop count (%s)", optarg); ExitStat = EX_USAGE; } break; case 'n': /* don't alias */ NoAlias = TRUE; break; case 'N': /* delivery status notifications */ DefaultNotify |= QHASNOTIFY; if (strcasecmp(optarg, "never") == 0) break; for (p = optarg; p != NULL; optarg = p) { p = strchr(p, ','); if (p != NULL) *p++ = '\0'; if (strcasecmp(optarg, "success") == 0) DefaultNotify |= QPINGONSUCCESS; else if (strcasecmp(optarg, "failure") == 0) DefaultNotify |= QPINGONFAILURE; else if (strcasecmp(optarg, "delay") == 0) DefaultNotify |= QPINGONDELAY; else { usrerr("Invalid -N argument"); ExitStat = EX_USAGE; } } break; case 'o': /* set option */ setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv); break; case 'O': /* set option (long form) */ setoption(' ', optarg, FALSE, TRUE, CurEnv); break; case 'p': /* set protocol */ p = strchr(optarg, ':'); if (p != NULL) { *p++ = '\0'; if (*p != '\0') { ep = xalloc(strlen(p) + 1); cleanstrcpy(ep, p, MAXNAME); define('s', ep, CurEnv); } } if (*optarg != '\0') { ep = xalloc(strlen(optarg) + 1); cleanstrcpy(ep, optarg, MAXNAME); define('r', ep, CurEnv); } break; case 'q': /* run queue files at intervals */ # if QUEUE FullName = NULL; queuemode = TRUE; switch (optarg[0]) { case 'I': QueueLimitId = newstr(&optarg[1]); break; case 'R': QueueLimitRecipient = newstr(&optarg[1]); break; case 'S': QueueLimitSender = newstr(&optarg[1]); break; default: QueueIntvl = convtime(optarg, 'm'); break; } # else /* QUEUE */ usrerr("I don't know about queues"); ExitStat = EX_USAGE; # endif /* QUEUE */ break; case 'R': /* DSN RET: what to return */ if (bitset(EF_RET_PARAM, CurEnv->e_flags)) { usrerr("Duplicate -R flag"); ExitStat = EX_USAGE; break; } CurEnv->e_flags |= EF_RET_PARAM; if (strcasecmp(optarg, "hdrs") == 0) CurEnv->e_flags |= EF_NO_BODY_RETN; else if (strcasecmp(optarg, "full") != 0) { usrerr("Invalid -R value"); ExitStat = EX_USAGE; } break; case 't': /* read recipients from message */ GrabTo = TRUE; break; case 'U': /* initial (user) submission */ UserSubmission = TRUE; break; case 'V': /* DSN ENVID: set "original" envelope id */ if (!xtextok(optarg)) { usrerr("Invalid syntax in -V flag"); ExitStat = EX_USAGE; } else CurEnv->e_envid = newstr(optarg); break; case 'X': /* traffic log file */ endpwent(); setgid(RealGid); setuid(RealUid); TrafficLogFile = fopen(optarg, "a"); if (TrafficLogFile == NULL) { syserr("cannot open %s", optarg); ExitStat = EX_CANTCREAT; break; } #ifdef HASSETVBUF setvbuf(TrafficLogFile, NULL, _IOLBF, 0); #else setlinebuf(TrafficLogFile); #endif break; /* compatibility flags */ case 'c': /* connect to non-local mailers */ case 'i': /* don't let dot stop me */ case 'm': /* send to me too */ case 'T': /* set timeout interval */ case 'v': /* give blow-by-blow description */ setoption(j, "T", FALSE, TRUE, CurEnv); break; case 'e': /* error message disposition */ case 'M': /* define macro */ setoption(j, optarg, FALSE, TRUE, CurEnv); break; case 's': /* save From lines in headers */ setoption('f', "T", FALSE, TRUE, CurEnv); break; # ifdef DBM case 'I': /* initialize alias DBM file */ OpMode = MD_INITALIAS; break; # endif /* DBM */ # if defined(__osf__) || defined(_AIX3) case 'x': /* random flag that OSF/1 & AIX mailx passes */ break; # endif # if defined(sony_news) case 'E': case 'J': /* ignore flags for Japanese code conversion impremented on Sony NEWS */ break; # endif default: ExitStat = EX_USAGE; finis(); break; } } av += optind; /* ** Do basic initialization. ** Read system control file. ** Extract special fields for local use. */ /* set up ${opMode} for use in config file */ { char mbuf[2]; mbuf[0] = OpMode; mbuf[1] = '\0'; define(MID_OPMODE, newstr(mbuf), CurEnv); } #if XDEBUG checkfd012("before readcf"); #endif vendor_pre_defaults(CurEnv); readcf(getcfname(), safecf, CurEnv); ConfigFileRead = TRUE; vendor_post_defaults(CurEnv); /* avoid denial-of-service attacks */ resetlimits(); if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON) { /* drop privileges -- daemon mode done after socket/bind */ drop_privileges(); } /* ** Find our real host name for future logging. */ p = getauthinfo(STDIN_FILENO); define('_', p, CurEnv); /* suppress error printing if errors mailed back or whatever */ if (CurEnv->e_errormode != EM_PRINT) HoldErrs = TRUE; /* set up the $=m class now, after .cf has a chance to redefine $m */ expand("\201m", jbuf, sizeof jbuf, CurEnv); setclass('m', jbuf); + /* probe interfaces and locate any additional names */ + if (!DontProbeInterfaces) + load_if_names(); + if (tTd(0, 1)) { printf("\n============ SYSTEM IDENTITY (after readcf) ============"); printf("\n (short domain name) $w = "); xputs(macvalue('w', CurEnv)); printf("\n (canonical domain name) $j = "); xputs(macvalue('j', CurEnv)); printf("\n (subdomain name) $m = "); xputs(macvalue('m', CurEnv)); printf("\n (node name) $k = "); xputs(macvalue('k', CurEnv)); printf("\n========================================================\n\n"); } /* ** Do more command line checking -- these are things that ** have to modify the results of reading the config file. */ /* process authorization warnings from command line */ if (warn_C_flag) auth_warning(CurEnv, "Processed by %s with -C %s", RealUserName, ConfFile); if (Warn_Q_option) auth_warning(CurEnv, "Processed from queue %s", QueueDir); /* check body type for legality */ if (CurEnv->e_bodytype == NULL) /* nothing */ ; else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0) SevenBitInput = TRUE; else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0) SevenBitInput = FALSE; else { usrerr("Illegal body type %s", CurEnv->e_bodytype); CurEnv->e_bodytype = NULL; } /* tweak default DSN notifications */ if (DefaultNotify == 0) DefaultNotify = QPINGONFAILURE|QPINGONDELAY; /* Enforce use of local time (null string overrides this) */ if (TimeZoneSpec == NULL) unsetenv("TZ"); else if (TimeZoneSpec[0] != '\0') setuserenv("TZ", TimeZoneSpec); else setuserenv("TZ", NULL); tzset(); + /* be sure we don't pick up bogus HOSTALIASES environment variable */ + if (queuemode && RealUid != 0) + (void) unsetenv("HOSTALIASES"); + /* check for sane configuration level */ if (ConfigLevel > MAXCONFIGLEVEL) { syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)", ConfigLevel, Version, MAXCONFIGLEVEL); } /* need MCI cache to have persistence */ if (HostStatDir != NULL && MaxMciCache == 0) { HostStatDir = NULL; printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n"); } /* need HostStatusDir in order to have SingleThreadDelivery */ if (SingleThreadDelivery && HostStatDir == NULL) { SingleThreadDelivery = FALSE; printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n"); } /* check for permissions */ if ((OpMode == MD_DAEMON || OpMode == MD_PURGESTAT) && RealUid != 0) { -#ifdef LOG if (LogLevel > 1) - syslog(LOG_ALERT, "user %d attempted to %s", + sm_syslog(LOG_ALERT, NOQID, + "user %d attempted to %s", RealUid, OpMode == MD_DAEMON ? "run daemon" : "purge host status"); -#endif usrerr("Permission denied"); exit(EX_USAGE); } if (MeToo) BlankEnvelope.e_flags |= EF_METOO; switch (OpMode) { case MD_TEST: /* don't have persistent host status in test mode */ HostStatDir = NULL; + Verbose = 2; + CurEnv->e_errormode = EM_PRINT; break; case MD_FGDAEMON: run_in_foreground = TRUE; OpMode = MD_DAEMON; /* fall through ... */ case MD_DAEMON: vendor_daemon_setup(CurEnv); /* remove things that don't make sense in daemon mode */ FullName = NULL; GrabTo = FALSE; /* arrange to restart on hangup signal */ -#ifdef LOG if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/') - syslog(LOG_WARNING, "daemon invoked without full pathname; kill -1 won't work"); -#endif + sm_syslog(LOG_WARNING, NOQID, + "daemon invoked without full pathname; kill -1 won't work"); setsignal(SIGHUP, sighup); /* workaround: can't seem to release the signal in the parent */ releasesignal(SIGHUP); break; case MD_INITALIAS: - Verbose = TRUE; + Verbose = 2; + CurEnv->e_errormode = EM_PRINT; /* fall through... */ case MD_PRINT: /* to handle sendmail -bp -qSfoobar properly */ queuemode = FALSE; /* fall through... */ default: /* arrange to exit cleanly on hangup signal */ if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL) setsignal(SIGHUP, intsig); break; } /* full names can't have newlines */ if (FullName != NULL && strchr(FullName, '\n') != NULL) FullName = newstr(denlstring(FullName, TRUE, TRUE)); /* do heuristic mode adjustment */ if (Verbose) { /* turn off noconnect option */ setoption('c', "F", TRUE, FALSE, CurEnv); /* turn on interactive delivery */ setoption('d', "", TRUE, FALSE, CurEnv); } if (ConfigLevel < 3) { UseErrorsTo = TRUE; } /* set options that were previous macros */ if (SmtpGreeting == NULL) { if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL) SmtpGreeting = newstr(p); else SmtpGreeting = "\201j Sendmail \201v ready at \201b"; } if (UnixFromLine == NULL) { if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL) UnixFromLine = newstr(p); else UnixFromLine = "From \201g \201d"; } /* our name for SMTP codes */ expand("\201j", jbuf, sizeof jbuf, CurEnv); MyHostName = jbuf; if (strchr(jbuf, '.') == NULL) message("WARNING: local host name (%s) is not qualified; fix $j in config file", jbuf); /* make certain that this name is part of the $=w class */ setclass('w', MyHostName); /* the indices of built-in mailers */ st = stab("local", ST_MAILER, ST_FIND); if (st != NULL) LocalMailer = st->s_mailer; else if (OpMode != MD_TEST || !warn_C_flag) syserr("No local mailer defined"); st = stab("prog", ST_MAILER, ST_FIND); if (st == NULL) syserr("No prog mailer defined"); else { ProgMailer = st->s_mailer; clrbitn(M_MUSER, ProgMailer->m_flags); } st = stab("*file*", ST_MAILER, ST_FIND); if (st == NULL) syserr("No *file* mailer defined"); else { FileMailer = st->s_mailer; clrbitn(M_MUSER, FileMailer->m_flags); } st = stab("*include*", ST_MAILER, ST_FIND); if (st == NULL) syserr("No *include* mailer defined"); else InclMailer = st->s_mailer; if (ConfigLevel < 6) { /* heuristic tweaking of local mailer for back compat */ if (LocalMailer != NULL) { setbitn(M_ALIASABLE, LocalMailer->m_flags); setbitn(M_HASPWENT, LocalMailer->m_flags); setbitn(M_TRYRULESET5, LocalMailer->m_flags); setbitn(M_CHECKINCLUDE, LocalMailer->m_flags); setbitn(M_CHECKPROG, LocalMailer->m_flags); setbitn(M_CHECKFILE, LocalMailer->m_flags); setbitn(M_CHECKUDB, LocalMailer->m_flags); } if (ProgMailer != NULL) setbitn(M_RUNASRCPT, ProgMailer->m_flags); if (FileMailer != NULL) setbitn(M_RUNASRCPT, FileMailer->m_flags); } if (ConfigLevel < 7) { if (LocalMailer != NULL) setbitn(M_VRFY250, LocalMailer->m_flags); if (ProgMailer != NULL) setbitn(M_VRFY250, ProgMailer->m_flags); if (FileMailer != NULL) setbitn(M_VRFY250, FileMailer->m_flags); } /* MIME Content-Types that cannot be transfer encoded */ setclass('n', "multipart/signed"); /* MIME message/xxx subtypes that can be treated as messages */ setclass('s', "rfc822"); /* MIME Content-Transfer-Encodings that can be encoded */ setclass('e', "7bit"); setclass('e', "8bit"); setclass('e', "binary"); #ifdef USE_B_CLASS /* MIME Content-Types that should be treated as binary */ setclass('b', "image"); setclass('b', "audio"); setclass('b', "video"); setclass('b', "application/octet-stream"); #endif /* operate in queue directory */ - if (OpMode == MD_TEST) - /* nothing -- just avoid further if clauses */ ; - else if (QueueDir == NULL) + if (QueueDir == NULL) { - syserr("QueueDirectory (Q) option must be set"); - ExitStat = EX_CONFIG; + if (OpMode != MD_TEST) + { + syserr("QueueDirectory (Q) option must be set"); + ExitStat = EX_CONFIG; + } } - else if (chdir(QueueDir) < 0) + else { - syserr("cannot chdir(%s)", QueueDir); - ExitStat = EX_CONFIG; + /* test path to get warning messages */ + (void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE); + if (OpMode != MD_TEST && chdir(QueueDir) < 0) + { + syserr("cannot chdir(%s)", QueueDir); + ExitStat = EX_CONFIG; + } } /* check host status directory for validity */ if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE)) { /* cannot use this value */ if (tTd(0, 2)) printf("Cannot use HostStatusDirectory = %s: %s\n", HostStatDir, errstring(errno)); HostStatDir = NULL; } # if QUEUE if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags)) { struct stat stbuf; /* check to see if we own the queue directory */ if (stat(".", &stbuf) < 0) syserr("main: cannot stat %s", QueueDir); if (stbuf.st_uid != RealUid) { /* nope, really a botch */ usrerr("You do not have permission to process the queue"); exit (EX_NOPERM); } } # endif /* QUEUE */ /* if we've had errors so far, exit now */ if (ExitStat != EX_OK && OpMode != MD_TEST) { endpwent(); setuid(RealUid); exit(ExitStat); } #if XDEBUG checkfd012("before main() initmaps"); #endif /* ** Do operation-mode-dependent initialization. */ switch (OpMode) { case MD_PRINT: /* print the queue */ #if QUEUE dropenvelope(CurEnv, TRUE); printqueue(); endpwent(); setuid(RealUid); exit(EX_OK); #else /* QUEUE */ usrerr("No queue to print"); finis(); #endif /* QUEUE */ case MD_HOSTSTAT: mci_traverse_persistent(mci_print_persistent, NULL); exit(EX_OK); break; case MD_PURGESTAT: mci_traverse_persistent(mci_purge_persistent, NULL); exit(EX_OK); break; case MD_INITALIAS: /* initialize alias database */ initmaps(TRUE, CurEnv); endpwent(); setuid(RealUid); exit(ExitStat); case MD_SMTP: nullserver = FALSE; /* fall through... */ case MD_DAEMON: /* reset DSN parameters */ DefaultNotify = QPINGONFAILURE|QPINGONDELAY; CurEnv->e_envid = NULL; CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN); /* don't open alias database -- done in srvrsmtp */ break; default: /* open the alias database */ initmaps(FALSE, CurEnv); break; } if (tTd(0, 15)) { extern void printrules __P((void)); /* print configuration table (or at least part of it) */ if (tTd(0, 90)) printrules(); for (i = 0; i < MAXMAILERS; i++) { if (Mailer[i] != NULL) printmailer(Mailer[i]); } } /* ** Switch to the main envelope. */ CurEnv = newenvelope(&MainEnvelope, CurEnv); MainEnvelope.e_flags = BlankEnvelope.e_flags; /* ** If test mode, read addresses from stdin and process. */ if (OpMode == MD_TEST) { char buf[MAXLINE]; SIGFUNC_DECL intindebug __P((int)); if (isatty(fileno(stdin))) - Verbose = TRUE; + Verbose = 2; if (Verbose) { printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n"); printf("Enter
\n"); } if (setjmp(TopFrame) > 0) printf("\n"); (void) setsignal(SIGINT, intindebug); for (;;) { extern void testmodeline __P((char *, ENVELOPE *)); if (Verbose) printf("> "); (void) fflush(stdout); if (fgets(buf, sizeof buf, stdin) == NULL) finis(); p = strchr(buf, '\n'); if (p != NULL) *p = '\0'; if (!Verbose) printf("> %s\n", buf); testmodeline(buf, CurEnv); } } # if QUEUE /* ** If collecting stuff from the queue, go start doing that. */ if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0) { - (void) unsetenv("HOSTALIASES"); (void) runqueue(FALSE, Verbose); finis(); } # endif /* QUEUE */ /* ** If a daemon, wait for a request. ** getrequests will always return in a child. ** If we should also be processing the queue, start ** doing it in background. ** We check for any errors that might have happened ** during startup. */ if (OpMode == MD_DAEMON || QueueIntvl != 0) { char dtype[200]; extern bool getrequests __P((ENVELOPE *)); if (!run_in_foreground && !tTd(99, 100)) { /* put us in background */ i = fork(); if (i < 0) syserr("daemon: cannot fork"); if (i != 0) exit(0); /* disconnect from our controlling tty */ disconnect(2, CurEnv); } dtype[0] = '\0'; if (OpMode == MD_DAEMON) strcat(dtype, "+SMTP"); if (QueueIntvl != 0) { strcat(dtype, "+queueing@"); strcat(dtype, pintvl(QueueIntvl, TRUE)); } if (tTd(0, 1)) strcat(dtype, "+debugging"); -#ifdef LOG - syslog(LOG_INFO, "starting daemon (%s): %s", Version, dtype + 1); -#endif + sm_syslog(LOG_INFO, NOQID, + "starting daemon (%s): %s", Version, dtype + 1); #ifdef XLA xla_create_file(); #endif # if QUEUE if (queuemode) { (void) runqueue(TRUE, FALSE); if (OpMode != MD_DAEMON) + { for (;;) + { pause(); + if (DoQueueRun) + (void) runqueue(TRUE, FALSE); + } + } } # endif /* QUEUE */ dropenvelope(CurEnv, TRUE); #if DAEMON nullserver = getrequests(CurEnv); /* drop privileges */ drop_privileges(); /* at this point we are in a child: reset state */ (void) newenvelope(CurEnv, CurEnv); /* ** Get authentication data */ p = getauthinfo(fileno(InChannel)); define('_', p, &BlankEnvelope); #endif /* DAEMON */ } # if SMTP /* ** If running SMTP protocol, start collecting and executing ** commands. This will never return. */ if (OpMode == MD_SMTP || OpMode == MD_DAEMON) { char pbuf[20]; extern void smtp __P((bool, ENVELOPE *)); /* ** Save some macros for check_* rulesets. */ define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope); define(macid("{client_addr}", NULL), newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope); if (RealHostAddr.sa.sa_family == AF_INET) snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port); else snprintf(pbuf, sizeof pbuf, "0"); define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope); smtp(nullserver, CurEnv); } # endif /* SMTP */ clearenvelope(CurEnv, FALSE); if (OpMode == MD_VERIFY) { CurEnv->e_sendmode = SM_VERIFY; CurEnv->e_errormode = EM_PRINT; PostMasterCopy = NULL; HoldErrs = FALSE; } else { /* interactive -- all errors are global */ CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER; } /* ** Do basic system initialization and set the sender */ initsys(CurEnv); if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't')) auth_warning(CurEnv, "%s set sender to %s using -%c", RealUserName, from, warn_f_flag); setsender(from, CurEnv, NULL, '\0', FALSE); if (macvalue('s', CurEnv) == NULL) define('s', RealHostName, CurEnv); if (*av == NULL && !GrabTo) { CurEnv->e_flags |= EF_GLOBALERRS; usrerr("Recipient names must be specified"); /* collect body for UUCP return */ if (OpMode != MD_VERIFY) - collect(InChannel, FALSE, FALSE, NULL, CurEnv); + collect(InChannel, FALSE, NULL, CurEnv); finis(); } /* ** Scan argv and deliver the message to everyone. */ sendtoargv(av, CurEnv); /* if we have had errors sofar, arrange a meaningful exit stat */ if (Errors > 0 && ExitStat == EX_OK) ExitStat = EX_USAGE; /* ** Read the input mail. */ CurEnv->e_to = NULL; if (OpMode != MD_VERIFY || GrabTo) { + long savedflags = CurEnv->e_flags & EF_FATALERRS; + CurEnv->e_flags |= EF_GLOBALERRS; - collect(InChannel, FALSE, FALSE, NULL, CurEnv); + CurEnv->e_flags &= ~EF_FATALERRS; + collect(InChannel, FALSE, NULL, CurEnv); + + /* bail out if there were fatal errors in collect */ + if (OpMode != MD_VERIFY && bitset(EF_FATALERRS, CurEnv->e_flags)) + { + CurEnv->e_flags |= EF_CLRQUEUE; + finis(); + /*NOTREACHED*/ + return -1; + } + CurEnv->e_flags |= savedflags; } errno = 0; if (tTd(1, 1)) printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr); /* ** Actually send everything. ** If verifying, just ack. */ CurEnv->e_from.q_flags |= QDONTSEND; if (tTd(1, 5)) { printf("main: QDONTSEND "); printaddr(&CurEnv->e_from, FALSE); } CurEnv->e_to = NULL; + CurrentLA = getla(); sendall(CurEnv, SM_DEFAULT); /* ** All done. ** Don't send return error message if in VERIFY mode. */ finis(); /*NOTREACHED*/ return -1; } SIGFUNC_DECL intindebug(sig) int sig; { longjmp(TopFrame, 1); return SIGFUNC_RETURN; } /* ** FINIS -- Clean up and exit. ** ** Parameters: ** none ** ** Returns: ** never ** ** Side Effects: ** exits sendmail */ void finis() { if (tTd(2, 1)) { extern void printenvflags(); printf("\n====finis: stat %d e_id=%s e_flags=", ExitStat, CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id); printenvflags(CurEnv); } if (tTd(2, 9)) printopenfds(FALSE); + /* if we fail in finis(), just exit */ + if (setjmp(TopFrame) != 0) + { + /* failed -- just give it up */ + goto forceexit; + } + /* clean up temp files */ CurEnv->e_to = NULL; if (CurEnv->e_id != NULL) dropenvelope(CurEnv, TRUE); /* flush any cached connections */ mci_flush(TRUE, NULL); # ifdef XLA /* clean up extended load average stuff */ xla_all_end(); # endif /* and exit */ -# ifdef LOG + forceexit: if (LogLevel > 78) - syslog(LOG_DEBUG, "finis, pid=%d", getpid()); -# endif /* LOG */ + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "finis, pid=%d", + getpid()); if (ExitStat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET) ExitStat = EX_OK; /* reset uid for process accounting */ endpwent(); setuid(RealUid); exit(ExitStat); } /* ** INTSIG -- clean up on interrupt ** ** This just arranges to exit. It pessimises in that it ** may resend a message. ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Unlocks the current job. */ SIGFUNC_DECL intsig(sig) int sig; { -#ifdef LOG if (LogLevel > 79) - syslog(LOG_DEBUG, "%s: interrupt", - CurEnv->e_id == NULL ? "[NOQUEUE]" : CurEnv->e_id); -#endif + sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt"); FileName = NULL; unlockqueue(CurEnv); #ifdef XLA xla_all_end(); #endif /* reset uid for process accounting */ endpwent(); setuid(RealUid); exit(EX_OK); } /* ** INITMACROS -- initialize the macro system ** ** This just involves defining some macros that are actually ** used internally as metasymbols to be themselves. ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** initializes several macros to be themselves. */ struct metamac MetaMacros[] = { /* LHS pattern matching characters */ { '*', MATCHZANY }, { '+', MATCHANY }, { '-', MATCHONE }, { '=', MATCHCLASS }, { '~', MATCHNCLASS }, /* these are RHS metasymbols */ { '#', CANONNET }, { '@', CANONHOST }, { ':', CANONUSER }, { '>', CALLSUBR }, /* the conditional operations */ { '?', CONDIF }, { '|', CONDELSE }, { '.', CONDFI }, /* the hostname lookup characters */ { '[', HOSTBEGIN }, { ']', HOSTEND }, { '(', LOOKUPBEGIN }, { ')', LOOKUPEND }, /* miscellaneous control characters */ { '&', MACRODEXPAND }, { '\0' } }; #define MACBINDING(name, mid) \ stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \ MacroName[mid] = name; void initmacros(e) register ENVELOPE *e; { register struct metamac *m; register int c; char buf[5]; extern char *MacroName[256]; for (m = MetaMacros; m->metaname != '\0'; m++) { buf[0] = m->metaval; buf[1] = '\0'; define(m->metaname, newstr(buf), e); } buf[0] = MATCHREPL; buf[2] = '\0'; for (c = '0'; c <= '9'; c++) { buf[1] = c; define(c, newstr(buf), e); } /* set defaults for some macros sendmail will use later */ define('n', "MAILER-DAEMON", e); /* set up external names for some internal macros */ MACBINDING("opMode", MID_OPMODE); /*XXX should probably add equivalents for all short macros here XXX*/ } /* ** DISCONNECT -- remove our connection with any foreground process ** ** Parameters: ** droplev -- how "deeply" we should drop the line. ** 0 -- ignore signals, mail back errors, make sure ** output goes to stdout. ** 1 -- also, make stdout go to transcript. ** 2 -- also, disconnect from controlling terminal ** (only for daemon mode). ** e -- the current envelope. ** ** Returns: ** none ** ** Side Effects: ** Trys to insure that we are immune to vagaries of ** the controlling tty. */ void disconnect(droplev, e) int droplev; register ENVELOPE *e; { int fd; if (tTd(52, 1)) printf("disconnect: In %d Out %d, e=%lx\n", fileno(InChannel), fileno(OutChannel), (u_long) e); if (tTd(52, 100)) { printf("don't\n"); return; } -#ifdef LOG if (LogLevel > 93) - syslog(LOG_DEBUG, "%s: disconnect level %d", - e->e_id == NULL ? "[NOQUEUE]" : e->e_id, droplev); -#endif + sm_syslog(LOG_DEBUG, e->e_id, + "disconnect level %d", + droplev); /* be sure we don't get nasty signals */ (void) setsignal(SIGINT, SIG_IGN); (void) setsignal(SIGQUIT, SIG_IGN); /* we can't communicate with our caller, so.... */ HoldErrs = TRUE; CurEnv->e_errormode = EM_MAIL; - Verbose = FALSE; + Verbose = 0; DisConnected = TRUE; /* all input from /dev/null */ if (InChannel != stdin) { (void) fclose(InChannel); InChannel = stdin; } (void) freopen("/dev/null", "r", stdin); /* output to the transcript */ if (OutChannel != stdout) { (void) fclose(OutChannel); OutChannel = stdout; } if (droplev > 0) { if (e->e_xfp == NULL) fd = open("/dev/null", O_WRONLY, 0666); else fd = fileno(e->e_xfp); (void) fflush(stdout); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); if (e->e_xfp == NULL) close(fd); } /* drop our controlling TTY completely if possible */ if (droplev > 1) { (void) setsid(); errno = 0; } #if XDEBUG checkfd012("disconnect"); #endif -# ifdef LOG if (LogLevel > 71) - syslog(LOG_DEBUG, "in background, pid=%d", getpid()); -# endif /* LOG */ + sm_syslog(LOG_DEBUG, e->e_id, + "in background, pid=%d", + getpid()); errno = 0; } static void obsolete(argv) char *argv[]; { register char *ap; register char *op; while ((ap = *++argv) != NULL) { /* Return if "--" or not an option of any form. */ if (ap[0] != '-' || ap[1] == '-') return; /* skip over options that do have a value */ op = strchr(OPTIONS, ap[1]); if (op != NULL && *++op == ':' && ap[2] == '\0' && ap[1] != 'd' && #if defined(sony_news) ap[1] != 'E' && ap[1] != 'J' && #endif argv[1] != NULL && argv[1][0] != '-') { argv++; continue; } /* If -C doesn't have an argument, use sendmail.cf. */ #define __DEFPATH "sendmail.cf" if (ap[1] == 'C' && ap[2] == '\0') { *argv = xalloc(sizeof(__DEFPATH) + 2); argv[0][0] = '-'; argv[0][1] = 'C'; (void)strcpy(&argv[0][2], __DEFPATH); } /* If -q doesn't have an argument, run it once. */ if (ap[1] == 'q' && ap[2] == '\0') *argv = "-q0"; /* if -d doesn't have an argument, use 0-99.1 */ if (ap[1] == 'd' && ap[2] == '\0') *argv = "-d0-99.1"; # if defined(sony_news) /* if -E doesn't have an argument, use -EC */ if (ap[1] == 'E' && ap[2] == '\0') *argv = "-EC"; /* if -J doesn't have an argument, use -JJ */ if (ap[1] == 'J' && ap[2] == '\0') *argv = "-JJ"; # endif } } /* ** AUTH_WARNING -- specify authorization warning ** ** Parameters: ** e -- the current envelope. ** msg -- the text of the message. ** args -- arguments to the message. ** ** Returns: ** none. */ void #ifdef __STDC__ auth_warning(register ENVELOPE *e, const char *msg, ...) #else auth_warning(e, msg, va_alist) register ENVELOPE *e; const char *msg; va_dcl #endif { char buf[MAXLINE]; VA_LOCAL_DECL if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags)) { register char *p; static char hostbuf[48]; extern struct hostent *myhostname(); if (hostbuf[0] == '\0') (void) myhostname(hostbuf, sizeof hostbuf); (void) snprintf(buf, sizeof buf, "%s: ", hostbuf); p = &buf[strlen(buf)]; VA_START(msg); vsnprintf(p, SPACELEFT(buf, p), msg, ap); VA_END; addheader("X-Authentication-Warning", buf, &e->e_header); -#ifdef LOG if (LogLevel > 3) - syslog(LOG_INFO, "%s: Authentication-Warning: %.400s", - e->e_id == NULL ? "[NOQUEUE]" : e->e_id, buf); -#endif + sm_syslog(LOG_INFO, e->e_id, + "Authentication-Warning: %.400s", + buf); } } /* ** GETEXTENV -- get from external environment ** ** Parameters: ** envar -- the name of the variable to retrieve ** ** Returns: ** The value, if any. */ char * getextenv(envar) const char *envar; { char **envp; int l; l = strlen(envar); for (envp = ExternalEnviron; *envp != NULL; envp++) { if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=') return &(*envp)[l + 1]; } return NULL; } /* ** SETUSERENV -- set an environment in the propogated environment ** ** Parameters: ** envar -- the name of the environment variable. ** value -- the value to which it should be set. If ** null, this is extracted from the incoming ** environment. If that is not set, the call ** to setuserenv is ignored. ** ** Returns: ** none. */ void setuserenv(envar, value) const char *envar; const char *value; { int i; char **evp = UserEnviron; char *p; if (value == NULL) { value = getextenv(envar); if (value == NULL) return; } i = strlen(envar); p = (char *) xalloc(strlen(value) + i + 2); strcpy(p, envar); p[i++] = '='; strcpy(&p[i], value); while (*evp != NULL && strncmp(*evp, p, i) != 0) evp++; if (*evp != NULL) { *evp++ = p; } else if (evp < &UserEnviron[MAXUSERENVIRON]) { *evp++ = p; *evp = NULL; } /* make sure it is in our environment as well */ if (putenv(p) < 0) syserr("setuserenv: putenv(%s) failed", p); } /* ** DUMPSTATE -- dump state ** ** For debugging. */ void dumpstate(when) char *when; { -#ifdef LOG register char *j = macvalue('j', CurEnv); int rs; - syslog(LOG_DEBUG, "--- dumping state on %s: $j = %s ---", + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "--- dumping state on %s: $j = %s ---", when, j == NULL ? "" : j); if (j != NULL) { if (!wordinclass(j, 'w')) - syslog(LOG_DEBUG, "*** $j not in $=w ***"); + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "*** $j not in $=w ***"); } - syslog(LOG_DEBUG, "CurChildren = %d", CurChildren); - syslog(LOG_DEBUG, "--- open file descriptors: ---"); + sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren); + sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---"); printopenfds(TRUE); - syslog(LOG_DEBUG, "--- connection cache: ---"); + sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---"); mci_dump_all(TRUE); rs = strtorwset("debug_dumpstate", NULL, ST_FIND); if (rs > 0) { int stat; register char **pvp; char *pv[MAXATOM + 1]; pv[0] = NULL; stat = rewrite(pv, rs, 0, CurEnv); - syslog(LOG_DEBUG, + sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- ruleset debug_dumpstate returns stat %d, pv: ---", stat); for (pvp = pv; *pvp != NULL; pvp++) - syslog(LOG_DEBUG, "%s", *pvp); + sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp); } - syslog(LOG_DEBUG, "--- end of state dump ---"); -#endif + sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---"); } SIGFUNC_DECL sigusr1(sig) int sig; { dumpstate("user signal"); return SIGFUNC_RETURN; } SIGFUNC_DECL sighup(sig) int sig; { if (SaveArgv[0][0] != '/') { -#ifdef LOG if (LogLevel > 3) - syslog(LOG_INFO, "could not restart: need full path"); -#endif + sm_syslog(LOG_INFO, NOQID, "could not restart: need full path"); exit(EX_OSFILE); } -#ifdef LOG if (LogLevel > 3) - syslog(LOG_INFO, "restarting %s on signal", SaveArgv[0]); -#endif + sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]); + alarm(0); releasesignal(SIGHUP); if (setgid(RealGid) < 0 || setuid(RealUid) < 0) { -#ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, "could not set[ug]id(%d, %d): %m", + sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m", RealUid, RealGid); -#endif exit(EX_OSERR); } execv(SaveArgv[0], (ARGV_T) SaveArgv); -#ifdef LOG if (LogLevel > 0) - syslog(LOG_ALERT, "could not exec %s: %m", SaveArgv[0]); -#endif + sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]); exit(EX_OSFILE); } /* ** DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option ** ** Parameters: ** none. ** ** Returns: ** none. */ void drop_privileges() { #ifdef NGROUPS_MAX /* reset group permissions; these can be set later */ GIDSET_T emptygidset[NGROUPS_MAX]; emptygidset[0] = RunAsGid == 0 ? getegid() : RunAsGid; (void) setgroups(1, emptygidset); #endif if (RunAsGid != 0) (void) setgid(RunAsGid); if (RunAsUid != 0) (void) setuid(RunAsUid); } /* ** TESTMODELINE -- process a test mode input line ** ** Parameters: ** line -- the input line. ** e -- the current environment. ** Syntax: ** # a comment ** .X process X as a configuration line ** =X dump a configuration item (such as mailers) ** $X dump a macro or class ** /X try an activity ** X normal process through rule set X */ void testmodeline(line, e) char *line; ENVELOPE *e; { register char *p; char *q; auto char *delimptr; int mid; int i, rs; STAB *map; char **s; struct rewrite *rw; ADDRESS a; static int tryflags = RF_COPYNONE; char exbuf[MAXLINE]; extern bool invalidaddr __P((char *, char *)); extern char *crackaddr __P((char *)); extern void dump_class __P((STAB *, int)); extern void translate_dollars __P((char *)); extern void help __P((char *)); switch (line[0]) { case '#': case 0: return; case '?': help("-bt"); return; case '.': /* config-style settings */ switch (line[1]) { case 'D': mid = macid(&line[2], &delimptr); if (mid == '\0') return; translate_dollars(delimptr); define(mid, newstr(delimptr), e); break; case 'C': if (line[2] == '\0') /* not to call syserr() */ return; mid = macid(&line[2], &delimptr); if (mid == '\0') return; translate_dollars(delimptr); expand(delimptr, exbuf, sizeof exbuf, e); p = exbuf; while (*p != '\0') { register char *wd; char delim; while (*p != '\0' && isascii(*p) && isspace(*p)) p++; wd = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; delim = *p; *p = '\0'; if (wd[0] != '\0') setclass(mid, wd); *p = delim; } break; case '\0': printf("Usage: .[DC]macro value(s)\n"); break; default: printf("Unknown \".\" command %s\n", line); break; } return; case '=': /* config-style settings */ switch (line[1]) { case 'S': /* dump rule set */ rs = strtorwset(&line[2], NULL, ST_FIND); if (rs < 0) { printf("Undefined ruleset %s\n", &line[2]); return; } rw = RewriteRules[rs]; if (rw == NULL) return; do { putchar('R'); s = rw->r_lhs; while (*s != NULL) { xputs(*s++); putchar(' '); } putchar('\t'); putchar('\t'); s = rw->r_rhs; while (*s != NULL) { xputs(*s++); putchar(' '); } putchar('\n'); } while ((rw = rw->r_next) != NULL); break; case 'M': for (i = 0; i < MAXMAILERS; i++) { if (Mailer[i] != NULL) printmailer(Mailer[i]); } break; case '\0': printf("Usage: =Sruleset or =M\n"); break; default: printf("Unknown \"=\" command %s\n", line); break; } return; case '-': /* set command-line-like opts */ switch (line[1]) { case 'd': tTflag(&line[2]); break; case '\0': printf("Usage: -d{debug arguments}\n"); break; default: printf("Unknown \"-\" command %s\n", line); break; } return; case '$': if (line[1] == '=') { mid = macid(&line[2], NULL); if (mid != '\0') stabapply(dump_class, mid); return; } mid = macid(&line[1], NULL); if (mid == '\0') return; p = macvalue(mid, e); if (p == NULL) printf("Undefined\n"); else { xputs(p); printf("\n"); } return; case '/': /* miscellaneous commands */ p = &line[strlen(line)]; while (--p >= line && isascii(*p) && isspace(*p)) *p = '\0'; p = strpbrk(line, " \t"); if (p != NULL) { while (isascii(*p) && isspace(*p)) *p++ = '\0'; } else p = ""; if (line[1] == '\0') { printf("Usage: /[canon|map|mx|parse|try|tryflags]\n"); return; } if (strcasecmp(&line[1], "mx") == 0) { #if NAMED_BIND /* look up MX records */ int nmx; auto int rcode; char *mxhosts[MAXMXHOSTS + 1]; if (*p == '\0') { printf("Usage: /mx address\n"); return; } nmx = getmxrr(p, mxhosts, FALSE, &rcode); printf("getmxrr(%s) returns %d value(s):\n", p, nmx); for (i = 0; i < nmx; i++) printf("\t%s\n", mxhosts[i]); #else printf("No MX code compiled in\n"); #endif } else if (strcasecmp(&line[1], "canon") == 0) { char host[MAXHOSTNAMELEN]; if (*p == '\0') { printf("Usage: /canon address\n"); return; } else if (strlen(p) >= sizeof host) { printf("Name too long\n"); return; } strcpy(host, p); (void) getcanonname(host, sizeof(host), HasWildcardMX); printf("getcanonname(%s) returns %s\n", p, host); } else if (strcasecmp(&line[1], "map") == 0) { auto int rcode = EX_OK; if (*p == '\0') { printf("Usage: /map mapname key\n"); return; } for (q = p; *q != '\0' && !isspace(*q); q++) continue; if (*q == '\0') { printf("No key specified\n"); return; } *q++ = '\0'; map = stab(p, ST_MAP, ST_FIND); if (map == NULL) { printf("Map named \"%s\" not found\n", p); + return; + } + if (!bitset(MF_OPEN, map->s_map.map_mflags)) + { + printf("Map named \"%s\" not open\n", p); return; } printf("map_lookup: %s (%s) ", p, q); p = (*map->s_map.map_class->map_lookup) (&map->s_map, q, NULL, &rcode); if (p == NULL) printf("no match (%d)\n", rcode); else printf("returns %s (%d)\n", p, rcode); } else if (strcasecmp(&line[1], "try") == 0) { MAILER *m; STAB *s; auto int rcode = EX_OK; q = strpbrk(p, " \t"); if (q != NULL) { while (isascii(*q) && isspace(*q)) *q++ = '\0'; } if (q == NULL || *q == '\0') { printf("Usage: /try mailer address\n"); return; } s = stab(p, ST_MAILER, ST_FIND); if (s == NULL) { printf("Unknown mailer %s\n", p); return; } m = s->s_mailer; printf("Trying %s %s address %s for mailer %s\n", bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient", q, p); p = remotename(q, m, tryflags, &rcode, CurEnv); printf("Rcode = %d, addr = %s\n", rcode, p == NULL ? "" : p); e->e_to = NULL; } else if (strcasecmp(&line[1], "tryflags") == 0) { if (*p == '\0') { printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n"); return; } for (; *p != '\0'; p++) { switch (*p) { case 'H': case 'h': tryflags |= RF_HEADERADDR; break; case 'E': case 'e': tryflags &= ~RF_HEADERADDR; break; case 'S': case 's': tryflags |= RF_SENDERADDR; break; case 'R': case 'r': tryflags &= ~RF_SENDERADDR; break; } } } else if (strcasecmp(&line[1], "parse") == 0) { if (*p == '\0') { printf("Usage: /parse address\n"); return; } q = crackaddr(p); printf("Cracked address = "); xputs(q); printf("\nParsing %s %s address\n", bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope", bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient"); if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL) printf("Cannot parse\n"); else if (a.q_host != NULL && a.q_host[0] != '\0') printf("mailer %s, host %s, user %s\n", a.q_mailer->m_name, a.q_host, a.q_user); else printf("mailer %s, user %s\n", a.q_mailer->m_name, a.q_user); e->e_to = NULL; } else { printf("Unknown \"/\" command %s\n", line); } return; } for (p = line; isascii(*p) && isspace(*p); p++) continue; q = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p == '\0') { printf("No address!\n"); return; } *p = '\0'; if (invalidaddr(p + 1, NULL)) return; do { register char **pvp; char pvpbuf[PSBUFSIZE]; pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf, &delimptr, NULL); if (pvp == NULL) continue; p = q; while (*p != '\0') { int stat; int rs = strtorwset(p, NULL, ST_FIND); if (rs < 0) { printf("Undefined ruleset %s\n", p); break; } stat = rewrite(pvp, rs, 0, e); if (stat != EX_OK) printf("== Ruleset %s (%d) status %d\n", p, rs, stat); while (*p != '\0' && *p++ != ',') continue; } } while (*(p = delimptr) != '\0'); } void dump_class(s, id) register STAB *s; int id; { if (s->s_type != ST_CLASS) return; if (bitnset(id & 0xff, s->s_class)) printf("%s\n", s->s_name); } Index: head/usr.sbin/sendmail/src/mime.c =================================================================== --- head/usr.sbin/sendmail/src/mime.c (revision 26988) +++ head/usr.sbin/sendmail/src/mime.c (revision 26989) @@ -1,1185 +1,1187 @@ /* - * Copyright (c) 1994, 1996 Eric P. Allman + * Copyright (c) 1994, 1996-1997 Eric P. Allman * Copyright (c) 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ # include "sendmail.h" # include #ifndef lint -static char sccsid[] = "@(#)mime.c 8.54 (Berkeley) 1/14/97"; +static char sccsid[] = "@(#)mime.c 8.59 (Berkeley) 5/6/97"; #endif /* not lint */ /* ** MIME support. ** ** I am indebted to John Beck of Hewlett-Packard, who contributed ** his code to me for inclusion. As it turns out, I did not use ** his code since he used a "minimum change" approach that used ** several temp files, and I wanted a "minimum impact" approach ** that would avoid copying. However, looking over his code ** helped me cement my understanding of the problem. ** ** I also looked at, but did not directly use, Nathaniel ** Borenstein's "code.c" module. Again, it functioned as ** a file-to-file translator, which did not fit within my ** design bounds, but it was a useful base for understanding ** the problem. */ #if MIME8TO7 /* character set for hex and base64 encoding */ char Base16Code[] = "0123456789ABCDEF"; char Base64Code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* types of MIME boundaries */ #define MBT_SYNTAX 0 /* syntax error */ #define MBT_NOTSEP 1 /* not a boundary */ #define MBT_INTERMED 2 /* intermediate boundary (no trailing --) */ #define MBT_FINAL 3 /* final boundary (trailing -- included) */ static char *MimeBoundaryNames[] = { "SYNTAX", "NOTSEP", "INTERMED", "FINAL" }; bool MapNLtoCRLF; extern int mimeboundary __P((char *, char **)); /* ** MIME8TO7 -- output 8 bit body in 7 bit format ** ** The header has already been output -- this has to do the ** 8 to 7 bit conversion. It would be easy if we didn't have ** to deal with nested formats (multipart/xxx and message/rfc822). ** ** We won't be called if we don't have to do a conversion, and ** appropriate MIME-Version: and Content-Type: fields have been ** output. Any Content-Transfer-Encoding: field has not been ** output, and we can add it here. ** ** Parameters: ** mci -- mailer connection information. ** header -- the header for this body part. ** e -- envelope. ** boundaries -- the currently pending message boundaries. ** NULL if we are processing the outer portion. ** flags -- to tweak processing. ** ** Returns: ** An indicator of what terminated the message part: ** MBT_FINAL -- the final boundary ** MBT_INTERMED -- an intermediate boundary ** MBT_NOTSEP -- an end of file */ struct args { char *field; /* name of field */ char *value; /* value of that field */ }; int mime8to7(mci, header, e, boundaries, flags) register MCI *mci; HDR *header; register ENVELOPE *e; char **boundaries; int flags; { register char *p; int linelen; int bt; off_t offset; size_t sectionsize, sectionhighbits; int i; char *type; char *subtype; char *cte; char **pvp; int argc = 0; char *bp; bool use_qp = FALSE; struct args argv[MAXMIMEARGS]; char bbuf[128]; char buf[MAXLINE]; char pvpbuf[MAXLINE]; extern u_char MimeTokenTab[256]; extern int mime_getchar __P((FILE *, char **, int *)); extern int mime_getchar_crlf __P((FILE *, char **, int *)); if (tTd(43, 1)) { printf("mime8to7: flags = %x, boundaries =", flags); if (boundaries[0] == NULL) printf(" "); else { for (i = 0; boundaries[i] != NULL; i++) printf(" %s", boundaries[i]); } printf("\n"); } MapNLtoCRLF = TRUE; p = hvalue("Content-Transfer-Encoding", header); if (p == NULL || (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, MimeTokenTab)) == NULL || pvp[0] == NULL) { cte = NULL; } else { cataddr(pvp, NULL, buf, sizeof buf, '\0'); cte = newstr(buf); } type = subtype = NULL; p = hvalue("Content-Type", header); if (p == NULL) { if (bitset(M87F_DIGEST, flags)) p = "message/rfc822"; else p = "text/plain"; } if (p != NULL && (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, MimeTokenTab)) != NULL && pvp[0] != NULL) { if (tTd(43, 40)) { for (i = 0; pvp[i] != NULL; i++) printf("pvp[%d] = \"%s\"\n", i, pvp[i]); } type = *pvp++; if (*pvp != NULL && strcmp(*pvp, "/") == 0 && *++pvp != NULL) { subtype = *pvp++; } /* break out parameters */ while (*pvp != NULL && argc < MAXMIMEARGS) { /* skip to semicolon separator */ while (*pvp != NULL && strcmp(*pvp, ";") != 0) pvp++; if (*pvp++ == NULL || *pvp == NULL) break; /* extract field name */ argv[argc].field = *pvp++; /* see if there is a value */ if (*pvp != NULL && strcmp(*pvp, "=") == 0 && (*++pvp == NULL || strcmp(*pvp, ";") != 0)) { argv[argc].value = *pvp; argc++; } } } /* check for disaster cases */ if (type == NULL) type = "-none-"; if (subtype == NULL) subtype = "-none-"; /* don't propogate some flags more than one level into the message */ flags &= ~M87F_DIGEST; /* ** Check for cases that can not be encoded. ** ** For example, you can't encode certain kinds of types ** or already-encoded messages. If we find this case, ** just copy it through. */ snprintf(buf, sizeof buf, "%.100s/%.100s", type, subtype); if (wordinclass(buf, 'n') || (cte != NULL && !wordinclass(cte, 'e'))) flags |= M87F_NO8BIT; #ifdef USE_B_CLASS if (wordinclass(buf, 'b') || wordinclass(type, 'b')) MapNLtoCRLF = FALSE; #endif if (wordinclass(buf, 'q') || wordinclass(type, 'q')) use_qp = TRUE; /* ** Multipart requires special processing. ** ** Do a recursive descent into the message. */ if (strcasecmp(type, "multipart") == 0 && !bitset(M87F_NO8BIT, flags)) { int blen; if (strcasecmp(subtype, "digest") == 0) flags |= M87F_DIGEST; for (i = 0; i < argc; i++) { if (strcasecmp(argv[i].field, "boundary") == 0) break; } - if (i >= argc) + if (i >= argc || argv[i].value == NULL) { - syserr("mime8to7: Content-Type: \"%s\": missing boundary", - p); + syserr("mime8to7: Content-Type: \"%s\": %s boundary", + i >= argc ? "missing" : "bogus", p); p = "---"; /* avoid bounce loops */ e->e_flags |= EF_DONT_MIME; } else { p = argv[i].value; stripquotes(p); } blen = strlen(p); if (blen > sizeof bbuf - 1) { syserr("mime8to7: multipart boundary \"%s\" too long", p); blen = sizeof bbuf - 1; /* avoid bounce loops */ e->e_flags |= EF_DONT_MIME; } strncpy(bbuf, p, blen); bbuf[blen] = '\0'; if (tTd(43, 1)) printf("mime8to7: multipart boundary \"%s\"\n", bbuf); for (i = 0; i < MAXMIMENESTING; i++) if (boundaries[i] == NULL) break; if (i >= MAXMIMENESTING) { syserr("mime8to7: multipart nesting boundary too deep"); /* avoid bounce loops */ e->e_flags |= EF_DONT_MIME; } else { boundaries[i] = bbuf; boundaries[i + 1] = NULL; } mci->mci_flags |= MCIF_INMIME; /* skip the early "comment" prologue */ putline("", mci); while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { bt = mimeboundary(buf, boundaries); if (bt != MBT_NOTSEP) break; - putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); + putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT); if (tTd(43, 99)) printf(" ...%s", buf); } if (feof(e->e_dfp)) bt = MBT_FINAL; while (bt != MBT_FINAL) { auto HDR *hdr = NULL; snprintf(buf, sizeof buf, "--%s", bbuf); putline(buf, mci); if (tTd(43, 35)) printf(" ...%s\n", buf); - collect(e->e_dfp, FALSE, FALSE, &hdr, e); + collect(e->e_dfp, FALSE, &hdr, e); if (tTd(43, 101)) putline("+++after collect", mci); putheader(mci, hdr, e); if (tTd(43, 101)) putline("+++after putheader", mci); bt = mime8to7(mci, hdr, e, boundaries, flags); } snprintf(buf, sizeof buf, "--%s--", bbuf); putline(buf, mci); if (tTd(43, 35)) printf(" ...%s\n", buf); boundaries[i] = NULL; mci->mci_flags &= ~MCIF_INMIME; /* skip the late "comment" epilogue */ while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { bt = mimeboundary(buf, boundaries); if (bt != MBT_NOTSEP) break; - putxline(buf, mci, PXLF_MAPFROM|PXLF_STRIP8BIT); + putxline(buf, strlen(buf), mci, PXLF_MAPFROM|PXLF_STRIP8BIT); if (tTd(43, 99)) printf(" ...%s", buf); } if (feof(e->e_dfp)) bt = MBT_FINAL; if (tTd(43, 3)) printf("\t\t\tmime8to7=>%s (multipart)\n", MimeBoundaryNames[bt]); return bt; } /* ** Message/xxx types -- recurse exactly once. ** ** Class 's' is predefined to have "rfc822" only. */ if (strcasecmp(type, "message") == 0) { if (!wordinclass(subtype, 's')) { flags |= M87F_NO8BIT; } else { auto HDR *hdr = NULL; putline("", mci); mci->mci_flags |= MCIF_INMIME; - collect(e->e_dfp, FALSE, FALSE, &hdr, e); + collect(e->e_dfp, FALSE, &hdr, e); if (tTd(43, 101)) putline("+++after collect", mci); putheader(mci, hdr, e); if (tTd(43, 101)) putline("+++after putheader", mci); if (hvalue("MIME-Version", hdr) == NULL) putline("MIME-Version: 1.0", mci); bt = mime8to7(mci, hdr, e, boundaries, flags); mci->mci_flags &= ~MCIF_INMIME; return bt; } } /* ** Non-compound body type ** ** Compute the ratio of seven to eight bit characters; ** use that as a heuristic to decide how to do the ** encoding. */ sectionsize = sectionhighbits = 0; if (!bitset(M87F_NO8BIT, flags)) { /* remember where we were */ offset = ftell(e->e_dfp); if (offset == -1) syserr("mime8to7: cannot ftell on df%s", e->e_id); /* do a scan of this body type to count character types */ while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { if (mimeboundary(buf, boundaries) != MBT_NOTSEP) break; for (p = buf; *p != '\0'; p++) { /* count bytes with the high bit set */ sectionsize++; if (bitset(0200, *p)) sectionhighbits++; } /* ** Heuristic: if 1/4 of the first 4K bytes are 8-bit, ** assume base64. This heuristic avoids double-reading ** large graphics or video files. */ if (sectionsize >= 4096 && sectionhighbits > sectionsize / 4) break; } /* return to the original offset for processing */ /* XXX use relative seeks to handle >31 bit file sizes? */ if (fseek(e->e_dfp, offset, SEEK_SET) < 0) syserr("mime8to7: cannot fseek on df%s", e->e_id); else clearerr(e->e_dfp); } /* ** Heuristically determine encoding method. ** If more than 1/8 of the total characters have the ** eighth bit set, use base64; else use quoted-printable. ** However, only encode binary encoded data as base64, ** since otherwise the NL=>CRLF mapping will be a problem. */ if (tTd(43, 8)) { printf("mime8to7: %ld high bit(s) in %ld byte(s), cte=%s, type=%s/%s\n", (long) sectionhighbits, (long) sectionsize, cte == NULL ? "[none]" : cte, type == NULL ? "[none]" : type, subtype == NULL ? "[none]" : subtype); } if (cte != NULL && strcasecmp(cte, "binary") == 0) sectionsize = sectionhighbits; linelen = 0; bp = buf; if (sectionhighbits == 0) { /* no encoding necessary */ if (cte != NULL) { snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %.200s", cte); putline(buf, mci); if (tTd(43, 36)) printf(" ...%s\n", buf); } putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { bt = mimeboundary(buf, boundaries); if (bt != MBT_NOTSEP) break; putline(buf, mci); } if (feof(e->e_dfp)) bt = MBT_FINAL; } else if (!MapNLtoCRLF || (sectionsize / 8 < sectionhighbits && !use_qp)) { /* use base64 encoding */ int c1, c2; if (tTd(43, 36)) printf(" ...Content-Transfer-Encoding: base64\n"); putline("Content-Transfer-Encoding: base64", mci); snprintf(buf, sizeof buf, "X-MIME-Autoconverted: from 8bit to base64 by %s id %s", MyHostName, e->e_id); putline(buf, mci); putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; while ((c1 = mime_getchar_crlf(e->e_dfp, boundaries, &bt)) != EOF) { if (linelen > 71) { *bp = '\0'; putline(buf, mci); linelen = 0; bp = buf; } linelen += 4; *bp++ = Base64Code[(c1 >> 2)]; c1 = (c1 & 0x03) << 4; c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); if (c2 == EOF) { *bp++ = Base64Code[c1]; *bp++ = '='; *bp++ = '='; break; } c1 |= (c2 >> 4) & 0x0f; *bp++ = Base64Code[c1]; c1 = (c2 & 0x0f) << 2; c2 = mime_getchar_crlf(e->e_dfp, boundaries, &bt); if (c2 == EOF) { *bp++ = Base64Code[c1]; *bp++ = '='; break; } c1 |= (c2 >> 6) & 0x03; *bp++ = Base64Code[c1]; *bp++ = Base64Code[c2 & 0x3f]; } *bp = '\0'; putline(buf, mci); } else { /* use quoted-printable encoding */ int c1, c2; int fromstate; BITMAP badchars; /* set up map of characters that must be mapped */ clrbitmap(badchars); for (c1 = 0x00; c1 < 0x20; c1++) setbitn(c1, badchars); clrbitn('\t', badchars); for (c1 = 0x7f; c1 < 0x100; c1++) setbitn(c1, badchars); setbitn('=', badchars); if (bitnset(M_EBCDIC, mci->mci_mailer->m_flags)) for (p = "!\"#$@[\\]^`{|}~"; *p != '\0'; p++) setbitn(*p, badchars); if (tTd(43, 36)) printf(" ...Content-Transfer-Encoding: quoted-printable\n"); putline("Content-Transfer-Encoding: quoted-printable", mci); snprintf(buf, sizeof buf, "X-MIME-Autoconverted: from 8bit to quoted-printable by %s id %s", MyHostName, e->e_id); putline(buf, mci); putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; fromstate = 0; c2 = '\n'; while ((c1 = mime_getchar(e->e_dfp, boundaries, &bt)) != EOF) { if (c1 == '\n') { if (c2 == ' ' || c2 == '\t') { *bp++ = '='; *bp++ = Base16Code[(c2 >> 4) & 0x0f]; *bp++ = Base16Code[c2 & 0x0f]; } if (buf[0] == '.' && bp == &buf[1]) { buf[0] = '='; *bp++ = Base16Code[('.' >> 4) & 0x0f]; *bp++ = Base16Code['.' & 0x0f]; } *bp = '\0'; putline(buf, mci); linelen = fromstate = 0; bp = buf; c2 = c1; continue; } if (c2 == ' ' && linelen == 4 && fromstate == 4 && bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) { *bp++ = '='; *bp++ = '2'; *bp++ = '0'; linelen += 3; } else if (c2 == ' ' || c2 == '\t') { *bp++ = c2; linelen++; } if (linelen > 72 && (linelen > 75 || c1 != '.' || (linelen > 73 && c2 == '.'))) { if (linelen > 73 && c2 == '.') bp--; else c2 = '\n'; *bp++ = '='; *bp = '\0'; putline(buf, mci); linelen = fromstate = 0; bp = buf; if (c2 == '.') { *bp++ = '.'; linelen++; } } if (bitnset(c1 & 0xff, badchars)) { *bp++ = '='; *bp++ = Base16Code[(c1 >> 4) & 0x0f]; *bp++ = Base16Code[c1 & 0x0f]; linelen += 3; } else if (c1 != ' ' && c1 != '\t') { if (linelen < 4 && c1 == "From"[linelen]) fromstate++; *bp++ = c1; linelen++; } c2 = c1; } /* output any saved character */ if (c2 == ' ' || c2 == '\t') { *bp++ = '='; *bp++ = Base16Code[(c2 >> 4) & 0x0f]; *bp++ = Base16Code[c2 & 0x0f]; linelen += 3; } if (linelen > 0 || boundaries[0] != NULL) { *bp = '\0'; putline(buf, mci); } } if (tTd(43, 3)) printf("\t\t\tmime8to7=>%s (basic)\n", MimeBoundaryNames[bt]); return bt; } /* ** MIME_GETCHAR -- get a character for MIME processing ** ** Treats boundaries as EOF. ** ** Parameters: ** fp -- the input file. ** boundaries -- the current MIME boundaries. ** btp -- if the return value is EOF, *btp is set to ** the type of the boundary. ** ** Returns: ** The next character in the input stream. */ int mime_getchar(fp, boundaries, btp) register FILE *fp; char **boundaries; int *btp; { int c; static u_char *bp = NULL; static int buflen = 0; static bool atbol = TRUE; /* at beginning of line */ static int bt = MBT_SYNTAX; /* boundary type of next EOF */ static u_char buf[128]; /* need not be a full line */ if (buflen > 0) { buflen--; return *bp++; } bp = buf; buflen = 0; c = getc(fp); if (c == '\n') { /* might be part of a MIME boundary */ *bp++ = c; atbol = TRUE; c = getc(fp); if (c == '\n') { ungetc(c, fp); return c; } } if (c != EOF) *bp++ = c; else bt = MBT_FINAL; if (atbol && c == '-') { /* check for a message boundary */ c = getc(fp); if (c != '-') { if (c != EOF) *bp++ = c; else bt = MBT_FINAL; buflen = bp - buf - 1; bp = buf; return *bp++; } /* got "--", now check for rest of separator */ *bp++ = '-'; while (bp < &buf[sizeof buf - 2] && (c = getc(fp)) != EOF && c != '\n') { *bp++ = c; } *bp = '\0'; bt = mimeboundary((char *) &buf[1], boundaries); switch (bt) { case MBT_FINAL: case MBT_INTERMED: /* we have a message boundary */ buflen = 0; *btp = bt; return EOF; } atbol = c == '\n'; if (c != EOF) *bp++ = c; } buflen = bp - buf - 1; if (buflen < 0) { *btp = bt; return EOF; } bp = buf; return *bp++; } /* ** MIME_GETCHAR_CRLF -- do mime_getchar, but translate NL => CRLF ** ** Parameters: ** fp -- the input file. ** boundaries -- the current MIME boundaries. ** btp -- if the return value is EOF, *btp is set to ** the type of the boundary. ** ** Returns: ** The next character in the input stream. */ int mime_getchar_crlf(fp, boundaries, btp) register FILE *fp; char **boundaries; int *btp; { static bool sendlf = FALSE; int c; if (sendlf) { sendlf = FALSE; return '\n'; } c = mime_getchar(fp, boundaries, btp); if (c == '\n' && MapNLtoCRLF) { sendlf = TRUE; return '\r'; } return c; } /* ** MIMEBOUNDARY -- determine if this line is a MIME boundary & its type ** ** Parameters: ** line -- the input line. ** boundaries -- the set of currently pending boundaries. ** ** Returns: ** MBT_NOTSEP -- if this is not a separator line ** MBT_INTERMED -- if this is an intermediate separator ** MBT_FINAL -- if this is a final boundary ** MBT_SYNTAX -- if this is a boundary for the wrong ** enclosure -- i.e., a syntax error. */ int mimeboundary(line, boundaries) register char *line; char **boundaries; { int type = MBT_NOTSEP; int i; int savec; extern int isboundary __P((char *, char **)); if (line[0] != '-' || line[1] != '-' || boundaries == NULL) return MBT_NOTSEP; i = strlen(line); if (line[i - 1] == '\n') i--; /* strip off trailing whitespace */ while (line[i - 1] == ' ' || line[i - 1] == '\t') i--; savec = line[i]; line[i] = '\0'; if (tTd(43, 5)) printf("mimeboundary: line=\"%s\"... ", line); /* check for this as an intermediate boundary */ if (isboundary(&line[2], boundaries) >= 0) type = MBT_INTERMED; else if (i > 2 && strncmp(&line[i - 2], "--", 2) == 0) { /* check for a final boundary */ line[i - 2] = '\0'; if (isboundary(&line[2], boundaries) >= 0) type = MBT_FINAL; line[i - 2] = '-'; } line[i] = savec; if (tTd(43, 5)) printf("%s\n", MimeBoundaryNames[type]); return type; } /* ** DEFCHARSET -- return default character set for message ** ** The first choice for character set is for the mailer ** corresponding to the envelope sender. If neither that ** nor the global configuration file has a default character ** set defined, return "unknown-8bit" as recommended by ** RFC 1428 section 3. ** ** Parameters: ** e -- the envelope for this message. ** ** Returns: ** The default character set for that mailer. */ char * defcharset(e) register ENVELOPE *e; { if (e != NULL && e->e_from.q_mailer != NULL && e->e_from.q_mailer->m_defcharset != NULL) return e->e_from.q_mailer->m_defcharset; if (DefaultCharSet != NULL) return DefaultCharSet; return "unknown-8bit"; } /* ** ISBOUNDARY -- is a given string a currently valid boundary? ** ** Parameters: ** line -- the current input line. ** boundaries -- the list of valid boundaries. ** ** Returns: ** The index number in boundaries if the line is found. ** -1 -- otherwise. ** */ int isboundary(line, boundaries) char *line; char **boundaries; { register int i; for (i = 0; boundaries[i] != NULL; i++) { if (strcmp(line, boundaries[i]) == 0) return i; } return -1; } #endif /* MIME8TO7 */ #if MIME7TO8 /* ** MIME7TO8 -- output 7 bit encoded MIME body in 8 bit format ** ** This is a hack. Supports translating the two 7-bit body-encodings ** (quoted-printable and base64) to 8-bit coded bodies. ** ** There is not much point in supporting multipart here, as the UA ** will be able to deal with encoded MIME bodies if it can parse MIME ** multipart messages. ** ** Note also that we wont be called unless it is a text/plain MIME ** message, encoded base64 or QP and mailer flag '9' has been defined ** on mailer. ** ** Contributed by Marius Olaffson . ** ** Parameters: ** mci -- mailer connection information. ** header -- the header for this body part. ** e -- envelope. ** ** Returns: ** none. */ extern int mime_fromqp __P((u_char *, u_char **, int, int)); static char index_64[128] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 }; #define CHAR64(c) (((c) < 0 || (c) > 127) ? -1 : index_64[(c)]) void mime7to8(mci, header, e) register MCI *mci; HDR *header; register ENVELOPE *e; { register char *p; char *cte; char **pvp; u_char *fbufp; char buf[MAXLINE]; u_char fbuf[MAXLINE + 1]; char pvpbuf[MAXLINE]; extern u_char MimeTokenTab[256]; p = hvalue("Content-Transfer-Encoding", header); if (p == NULL || (pvp = prescan(p, '\0', pvpbuf, sizeof pvpbuf, NULL, MimeTokenTab)) == NULL || pvp[0] == NULL) { /* "can't happen" -- upper level should have caught this */ syserr("mime7to8: unparsable CTE %s", p == NULL ? "" : p); /* avoid bounce loops */ e->e_flags |= EF_DONT_MIME; /* cheap failsafe algorithm -- should work on text/plain */ if (p != NULL) { snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %s", p); putline(buf, mci); } putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; while (fgets(buf, sizeof buf, e->e_dfp) != NULL) putline(buf, mci); return; } cataddr(pvp, NULL, buf, sizeof buf, '\0'); cte = newstr(buf); putline("Content-Transfer-Encoding: 8bit", mci); snprintf(buf, sizeof buf, "X-MIME-Autoconverted: from %.200s to 8bit by %s id %s", cte, MyHostName, e->e_id); putline(buf, mci); putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; /* ** Translate body encoding to 8-bit. Supports two types of ** encodings; "base64" and "quoted-printable". Assume qp if ** it is not base64. */ if (strcasecmp(cte, "base64") == 0) { int c1, c2, c3, c4; fbufp = fbuf; while ((c1 = fgetc(e->e_dfp)) != EOF) { if (isascii(c1) && isspace(c1)) continue; do { c2 = fgetc(e->e_dfp); } while (isascii(c2) && isspace(c2)); if (c2 == EOF) break; do { c3 = fgetc(e->e_dfp); } while (isascii(c3) && isspace(c3)); if (c3 == EOF) break; do { c4 = fgetc(e->e_dfp); } while (isascii(c4) && isspace(c4)); if (c4 == EOF) break; if (c1 == '=' || c2 == '=') continue; c1 = CHAR64(c1); c2 = CHAR64(c2); *fbufp = (c1 << 2) | ((c2 & 0x30) >> 4); if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE]) { if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) fbufp++; - *fbufp = '\0'; - putline((char *) fbuf, mci); + putxline((char *) fbuf, fbufp - fbuf, + mci, PXLF_MAPFROM); fbufp = fbuf; } if (c3 == '=') continue; c3 = CHAR64(c3); *fbufp = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2); if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE]) { if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) fbufp++; - *fbufp = '\0'; - putline((char *) fbuf, mci); + putxline((char *) fbuf, fbufp - fbuf, + mci, PXLF_MAPFROM); fbufp = fbuf; } if (c4 == '=') continue; c4 = CHAR64(c4); *fbufp = ((c3 & 0x03) << 6) | c4; if (*fbufp++ == '\n' || fbufp >= &fbuf[MAXLINE]) { if (*--fbufp != '\n' || (fbufp > fbuf && *--fbufp != '\r')) fbufp++; - *fbufp = '\0'; - putline((char *) fbuf, mci); + putxline((char *) fbuf, fbufp - fbuf, + mci, PXLF_MAPFROM); fbufp = fbuf; } } } else { /* quoted-printable */ fbufp = fbuf; while (fgets(buf, sizeof buf, e->e_dfp) != NULL) { if (mime_fromqp((u_char *) buf, &fbufp, 0, &fbuf[MAXLINE] - fbufp) == 0) continue; - putline((char *) fbuf, mci); + if (fbufp - fbuf > 0) + putxline((char *) fbuf, fbufp - fbuf - 1, mci, + PXLF_MAPFROM); fbufp = fbuf; } } /* force out partial last line */ if (fbufp > fbuf) { *fbufp = '\0'; - putline((char *) fbuf, mci); + putxline((char *) fbuf, fbufp - fbuf, mci, PXLF_MAPFROM); } if (tTd(43, 3)) printf("\t\t\tmime7to8 => %s to 8bit done\n", cte); } /* ** The following is based on Borenstein's "codes.c" module, with simplifying ** changes as we do not deal with multipart, and to do the translation in-core, ** with an attempt to prevent overrun of output buffers. ** ** What is needed here are changes to defned this code better against ** bad encodings. Questionable to always return 0xFF for bad mappings. */ static char index_hex[128] = { -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 }; #define HEXCHAR(c) (((c) < 0 || (c) > 127) ? -1 : index_hex[(c)]) int mime_fromqp(infile, outfile, state, maxlen) u_char *infile; u_char **outfile; int state; /* Decoding body (0) or header (1) */ int maxlen; /* Max # of chars allowed in outfile */ { int c1, c2; int nchar = 0; while ((c1 = *infile++) != '\0') { if (c1 == '=') { if ((c1 = *infile++) == 0) break; if (c1 == '\n') /* ignore it */ { if (state == 0) return 0; } else { if ((c2 = *infile++) == '\0') break; c1 = HEXCHAR(c1); c2 = HEXCHAR(c2); if (++nchar > maxlen) break; *(*outfile)++ = c1 << 4 | c2; } } else { if (state == 1 && c1 == '_') c1 = ' '; if (++nchar > maxlen) break; *(*outfile)++ = c1; if (c1 == '\n') break; } } *(*outfile)++ = '\0'; return 1; } #endif /* MIME7TO8 */ Index: head/usr.sbin/sendmail/src/parseaddr.c =================================================================== --- head/usr.sbin/sendmail/src/parseaddr.c (revision 26988) +++ head/usr.sbin/sendmail/src/parseaddr.c (revision 26989) @@ -1,2403 +1,2437 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)parseaddr.c 8.115 (Berkeley) 11/24/96"; +static char sccsid[] = "@(#)parseaddr.c 8.128 (Berkeley) 6/14/97"; #endif /* not lint */ # include "sendmail.h" /* ** PARSEADDR -- Parse an address ** ** Parses an address and breaks it up into three parts: a ** net to transmit the message on, the host to transmit it ** to, and a user on that host. These are loaded into an ** ADDRESS header with the values squirreled away if necessary. ** The "user" part may not be a real user; the process may ** just reoccur on that machine. For example, on a machine ** with an arpanet connection, the address ** csvax.bill@berkeley ** will break up to a "user" of 'csvax.bill' and a host ** of 'berkeley' -- to be transmitted over the arpanet. ** ** Parameters: ** addr -- the address to parse. ** a -- a pointer to the address descriptor buffer. ** If NULL, a header will be created. ** flags -- describe detail for parsing. See RF_ definitions ** in sendmail.h. ** delim -- the character to terminate the address, passed ** to prescan. ** delimptr -- if non-NULL, set to the location of the ** delim character that was found. ** e -- the envelope that will contain this address. ** ** Returns: ** A pointer to the address descriptor header (`a' if ** `a' is non-NULL). ** NULL on error. ** ** Side Effects: ** none */ /* following delimiters are inherent to the internal algorithms */ # define DELIMCHARS "()<>,;\r\n" /* default word delimiters */ ADDRESS * parseaddr(addr, a, flags, delim, delimptr, e) char *addr; register ADDRESS *a; int flags; int delim; char **delimptr; register ENVELOPE *e; { register char **pvp; auto char *delimptrbuf; bool queueup; char pvpbuf[PSBUFSIZE]; extern ADDRESS *buildaddr(); extern bool invalidaddr(); extern void allocaddr __P((ADDRESS *, int, char *)); /* ** Initialize and prescan address. */ e->e_to = addr; if (tTd(20, 1)) printf("\n--parseaddr(%s)\n", addr); if (delimptr == NULL) delimptr = &delimptrbuf; pvp = prescan(addr, delim, pvpbuf, sizeof pvpbuf, delimptr, NULL); if (pvp == NULL) { if (tTd(20, 1)) printf("parseaddr-->NULL\n"); return (NULL); } if (invalidaddr(addr, delim == '\0' ? NULL : *delimptr)) { if (tTd(20, 1)) printf("parseaddr-->bad address\n"); return NULL; } /* ** Save addr if we are going to have to. ** ** We have to do this early because there is a chance that ** the map lookups in the rewriting rules could clobber ** static memory somewhere. */ if (bitset(RF_COPYPADDR, flags) && addr != NULL) { char savec = **delimptr; if (savec != '\0') **delimptr = '\0'; e->e_to = addr = newstr(addr); if (savec != '\0') **delimptr = savec; } /* ** Apply rewriting rules. ** Ruleset 0 does basic parsing. It must resolve. */ queueup = FALSE; if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) queueup = TRUE; if (rewrite(pvp, 0, 0, e) == EX_TEMPFAIL) queueup = TRUE; /* ** Build canonical address from pvp. */ a = buildaddr(pvp, a, flags, e); /* ** Make local copies of the host & user and then ** transport them out. */ allocaddr(a, flags, addr); if (bitset(QBADADDR, a->q_flags)) return a; /* ** If there was a parsing failure, mark it for queueing. */ if (queueup && OpMode != MD_INITALIAS) { char *msg = "Transient parse error -- message queued for future delivery"; if (e->e_sendmode == SM_DEFER) msg = "Deferring message until queue run"; if (tTd(20, 1)) printf("parseaddr: queuing message\n"); message(msg); if (e->e_message == NULL && e->e_sendmode != SM_DEFER) e->e_message = newstr(msg); a->q_flags |= QQUEUEUP; a->q_status = "4.4.3"; } /* ** Compute return value. */ if (tTd(20, 1)) { printf("parseaddr-->"); printaddr(a, FALSE); } return (a); } /* ** INVALIDADDR -- check for address containing meta-characters ** ** Parameters: ** addr -- the address to check. ** ** Returns: ** TRUE -- if the address has any "wierd" characters ** FALSE -- otherwise. */ bool invalidaddr(addr, delimptr) register char *addr; char *delimptr; { char savedelim = '\0'; if (delimptr != NULL) { savedelim = *delimptr; if (savedelim != '\0') *delimptr = '\0'; } + if (strlen(addr) > TOBUFSIZE - 2) + { + usrerr("553 Address too long (%d bytes max)", TOBUFSIZE - 2); + goto failure; + } for (; *addr != '\0'; addr++) { if ((*addr & 0340) == 0200) break; } if (*addr == '\0') { if (delimptr != NULL && savedelim != '\0') *delimptr = savedelim; return FALSE; } setstat(EX_USAGE); usrerr("553 Address contained invalid control characters"); +failure: if (delimptr != NULL && savedelim != '\0') *delimptr = savedelim; return TRUE; } /* ** ALLOCADDR -- do local allocations of address on demand. ** ** Also lowercases the host name if requested. ** ** Parameters: ** a -- the address to reallocate. ** flags -- the copy flag (see RF_ definitions in sendmail.h ** for a description). ** paddr -- the printname of the address. ** ** Returns: ** none. ** ** Side Effects: ** Copies portions of a into local buffers as requested. */ void allocaddr(a, flags, paddr) register ADDRESS *a; int flags; char *paddr; { if (tTd(24, 4)) printf("allocaddr(flags=%x, paddr=%s)\n", flags, paddr); a->q_paddr = paddr; if (a->q_user == NULL) a->q_user = ""; if (a->q_host == NULL) a->q_host = ""; if (bitset(RF_COPYPARSE, flags)) { a->q_host = newstr(a->q_host); if (a->q_user != a->q_paddr) a->q_user = newstr(a->q_user); } if (a->q_paddr == NULL) a->q_paddr = a->q_user; } /* ** PRESCAN -- Prescan name and make it canonical ** ** Scans a name and turns it into a set of tokens. This process ** deletes blanks and comments (in parentheses). ** ** This routine knows about quoted strings and angle brackets. ** ** There are certain subtleties to this routine. The one that ** comes to mind now is that backslashes on the ends of names ** are silently stripped off; this is intentional. The problem ** is that some versions of sndmsg (like at LBL) set the kill ** character to something other than @ when reading addresses; ** so people type "csvax.eric\@berkeley" -- which screws up the ** berknet mailer. ** ** Parameters: ** addr -- the name to chomp. ** delim -- the delimiter for the address, normally ** '\0' or ','; \0 is accepted in any case. ** If '\t' then we are reading the .cf file. ** pvpbuf -- place to put the saved text -- note that ** the pointers are static. ** pvpbsize -- size of pvpbuf. ** delimptr -- if non-NULL, set to the location of the ** terminating delimiter. ** toktab -- if set, a token table to use for parsing. ** If NULL, use the default table. ** ** Returns: ** A pointer to a vector of tokens. ** NULL on error. */ /* states and character types */ # define OPR 0 /* operator */ # define ATM 1 /* atom */ # define QST 2 /* in quoted string */ # define SPC 3 /* chewing up spaces */ # define ONE 4 /* pick up one character */ # define ILL 5 /* illegal character */ # define NSTATES 6 /* number of states */ # define TYPE 017 /* mask to select state type */ /* meta bits for table */ # define M 020 /* meta character; don't pass through */ # define B 040 /* cause a break */ # define MB M|B /* meta-break */ static short StateTab[NSTATES][NSTATES] = { /* oldst chtype> OPR ATM QST SPC ONE ILL */ /*OPR*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|MB }, /*ATM*/ { OPR|B, ATM, QST|B, SPC|MB, ONE|B, ILL|MB }, /*QST*/ { QST, QST, OPR, QST, QST, QST }, /*SPC*/ { OPR, ATM, QST, SPC|M, ONE, ILL|MB }, /*ONE*/ { OPR, OPR, OPR, OPR, OPR, ILL|MB }, /*ILL*/ { OPR|B, ATM|B, QST|B, SPC|MB, ONE|B, ILL|M }, }; /* token type table -- it gets modified with $o characters */ static u_char TokTypeTab[256] = { /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,SPC,SPC,SPC,SPC,SPC,ATM,ATM, /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* sp ! " # $ % & ' ( ) * + , - . / */ SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,ATM,ATM,ATM,ATM, /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* @ A B C D E F G H I J K L M N O */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* P Q R S T U V W X Y Z [ \ ] ^ _ */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* ` a b c d e f g h i j k l m n o */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* p q r s t u v w x y z { | } ~ del */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ OPR,OPR,ONE,OPR,OPR,OPR,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ OPR,OPR,OPR,ONE,ONE,ONE,OPR,OPR, OPR,OPR,OPR,OPR,OPR,OPR,OPR,OPR, /* sp ! " # $ % & ' ( ) * + , - . / */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* @ A B C D E F G H I J K L M N O */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* P Q R S T U V W X Y Z [ \ ] ^ _ */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* ` a b c d e f g h i j k l m n o */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* p q r s t u v w x y z { | } ~ del */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, }; /* token type table for MIME parsing */ u_char MimeTokenTab[256] = { /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,SPC,SPC,SPC,SPC,SPC,ILL,ILL, /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, /* sp ! " # $ % & ' ( ) * + , - . / */ SPC,ATM,QST,ATM,ATM,ATM,ATM,ATM, ATM,SPC,ATM,ATM,OPR,ATM,ATM,OPR, /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,OPR,OPR,OPR,OPR,OPR,OPR, /* @ A B C D E F G H I J K L M N O */ OPR,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* P Q R S T U V W X Y Z [ \ ] ^ _ */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,OPR,OPR,OPR,ATM,ATM, /* ` a b c d e f g h i j k l m n o */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* p q r s t u v w x y z { | } ~ del */ ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, ATM,ATM,ATM,ATM,ATM,ATM,ATM,ATM, /* nul soh stx etx eot enq ack bel bs ht nl vt np cr so si */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, /* dle dc1 dc2 dc3 dc4 nak syn etb can em sub esc fs gs rs us */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, /* sp ! " # $ % & ' ( ) * + , - . / */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, /* @ A B C D E F G H I J K L M N O */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, /* P Q R S T U V W X Y Z [ \ ] ^ _ */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, /* ` a b c d e f g h i j k l m n o */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, /* p q r s t u v w x y z { | } ~ del */ ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, ILL,ILL,ILL,ILL,ILL,ILL,ILL,ILL, }; # define NOCHAR -1 /* signal nothing in lookahead token */ char ** prescan(addr, delim, pvpbuf, pvpbsize, delimptr, toktab) char *addr; int delim; char pvpbuf[]; int pvpbsize; char **delimptr; u_char *toktab; { register char *p; register char *q; register int c; char **avp; bool bslashmode; bool route_syntax; int cmntcnt; int anglecnt; char *tok; int state; int newstate; char *saveto = CurEnv->e_to; static char *av[MAXATOM+1]; static char firsttime = TRUE; extern int errno; if (firsttime) { /* initialize the token type table */ char obuf[50]; firsttime = FALSE; if (OperatorChars == NULL) { if (ConfigLevel < 7) OperatorChars = macvalue('o', CurEnv); if (OperatorChars == NULL) OperatorChars = ".:@[]"; } expand(OperatorChars, obuf, sizeof obuf - sizeof DELIMCHARS, CurEnv); strcat(obuf, DELIMCHARS); for (p = obuf; *p != '\0'; p++) { if (TokTypeTab[*p & 0xff] == ATM) TokTypeTab[*p & 0xff] = OPR; } } if (toktab == NULL) toktab = TokTypeTab; /* make sure error messages don't have garbage on them */ errno = 0; q = pvpbuf; bslashmode = FALSE; route_syntax = FALSE; cmntcnt = 0; anglecnt = 0; avp = av; state = ATM; c = NOCHAR; p = addr; CurEnv->e_to = p; if (tTd(22, 11)) { printf("prescan: "); xputs(p); (void) putchar('\n'); } do { /* read a token */ tok = q; for (;;) { /* store away any old lookahead character */ if (c != NOCHAR && !bslashmode) { /* see if there is room */ if (q >= &pvpbuf[pvpbsize - 5]) { usrerr("553 Address too long"); if (strlen(addr) > (SIZE_T) MAXNAME) addr[MAXNAME] = '\0'; returnnull: if (delimptr != NULL) *delimptr = p; CurEnv->e_to = saveto; return (NULL); } /* squirrel it away */ *q++ = c; } /* read a new input character */ c = *p++; if (c == '\0') { /* diagnose and patch up bad syntax */ if (state == QST) { usrerr("653 Unbalanced '\"'"); c = '"'; } else if (cmntcnt > 0) { usrerr("653 Unbalanced '('"); c = ')'; } else if (anglecnt > 0) { c = '>'; usrerr("653 Unbalanced '<'"); } else break; p--; } else if (c == delim && cmntcnt <= 0 && state != QST) { if (anglecnt <= 0) break; /* special case for better error management */ if (delim == ',' && !route_syntax) { usrerr("653 Unbalanced '<'"); c = '>'; p--; } } if (tTd(22, 101)) printf("c=%c, s=%d; ", c, state); /* chew up special characters */ *q = '\0'; if (bslashmode) { bslashmode = FALSE; /* kludge \! for naive users */ if (cmntcnt > 0) { c = NOCHAR; continue; } else if (c != '!' || state == QST) { *q++ = '\\'; continue; } } if (c == '\\') { bslashmode = TRUE; } else if (state == QST) { /* do nothing, just avoid next clauses */ } else if (c == '(') { cmntcnt++; c = NOCHAR; } else if (c == ')') { if (cmntcnt <= 0) { usrerr("653 Unbalanced ')'"); c = NOCHAR; } else cmntcnt--; } else if (cmntcnt > 0) c = NOCHAR; else if (c == '<') { char *q = p; anglecnt++; while (isascii(*q) && isspace(*q)) q++; if (*q == '@') route_syntax = TRUE; } else if (c == '>') { if (anglecnt <= 0) { usrerr("653 Unbalanced '>'"); c = NOCHAR; } else anglecnt--; route_syntax = FALSE; } else if (delim == ' ' && isascii(c) && isspace(c)) c = ' '; if (c == NOCHAR) continue; /* see if this is end of input */ if (c == delim && anglecnt <= 0 && state != QST) break; newstate = StateTab[state][toktab[c & 0xff]]; if (tTd(22, 101)) printf("ns=%02o\n", newstate); state = newstate & TYPE; if (state == ILL) { if (isascii(c) && isprint(c)) usrerr("653 Illegal character %c", c); else usrerr("653 Illegal character 0x%02x", c); } if (bitset(M, newstate)) c = NOCHAR; if (bitset(B, newstate)) break; } /* new token */ if (tok != q) { *q++ = '\0'; if (tTd(22, 36)) { printf("tok="); xputs(tok); (void) putchar('\n'); } if (avp >= &av[MAXATOM]) { syserr("553 prescan: too many tokens"); goto returnnull; } if (q - tok > MAXNAME) { syserr("553 prescan: token too long"); goto returnnull; } *avp++ = tok; } } while (c != '\0' && (c != delim || anglecnt > 0)); *avp = NULL; p--; if (delimptr != NULL) *delimptr = p; if (tTd(22, 12)) { printf("prescan==>"); printav(av); } CurEnv->e_to = saveto; if (av[0] == NULL) { if (tTd(22, 1)) printf("prescan: null leading token\n"); return (NULL); } return (av); } /* ** REWRITE -- apply rewrite rules to token vector. ** ** This routine is an ordered production system. Each rewrite ** rule has a LHS (called the pattern) and a RHS (called the ** rewrite); 'rwr' points the the current rewrite rule. ** ** For each rewrite rule, 'avp' points the address vector we ** are trying to match against, and 'pvp' points to the pattern. ** If pvp points to a special match value (MATCHZANY, MATCHANY, ** MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp ** matched is saved away in the match vector (pointed to by 'mvp'). ** ** When a match between avp & pvp does not match, we try to ** back out. If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS ** we must also back out the match in mvp. If we reach a ** MATCHANY or MATCHZANY we just extend the match and start ** over again. ** ** When we finally match, we rewrite the address vector ** and try over again. ** ** Parameters: ** pvp -- pointer to token vector. ** ruleset -- the ruleset to use for rewriting. ** reclevel -- recursion level (to catch loops). ** e -- the current envelope. ** ** Returns: ** A status code. If EX_TEMPFAIL, higher level code should ** attempt recovery. ** ** Side Effects: ** pvp is modified. */ struct match { char **first; /* first token matched */ char **last; /* last token matched */ char **pattern; /* pointer to pattern */ }; # define MAXMATCH 9 /* max params per rewrite */ int rewrite(pvp, ruleset, reclevel, e) char **pvp; int ruleset; int reclevel; register ENVELOPE *e; { register char *ap; /* address pointer */ register char *rp; /* rewrite pointer */ register char **avp; /* address vector pointer */ register char **rvp; /* rewrite vector pointer */ register struct match *mlp; /* cur ptr into mlist */ register struct rewrite *rwr; /* pointer to current rewrite rule */ int ruleno; /* current rule number */ int rstat = EX_OK; /* return status */ int loopcount; struct match mlist[MAXMATCH]; /* stores match on LHS */ char *npvp[MAXATOM+1]; /* temporary space for rebuild */ extern int callsubr __P((char**, int, ENVELOPE *)); + extern int sm_strcasecmp __P((char *, char *)); if (OpMode == MD_TEST || tTd(21, 1)) { printf("rewrite: ruleset %3d input:", ruleset); printav(pvp); } if (ruleset < 0 || ruleset >= MAXRWSETS) { syserr("554 rewrite: illegal ruleset number %d", ruleset); return EX_CONFIG; } if (reclevel++ > MaxRuleRecursion) { syserr("rewrite: excessive recursion (max %d), ruleset %d", MaxRuleRecursion, ruleset); return EX_CONFIG; } if (pvp == NULL) return EX_USAGE; /* ** Run through the list of rewrite rules, applying ** any that match. */ ruleno = 1; loopcount = 0; for (rwr = RewriteRules[ruleset]; rwr != NULL; ) { int stat; /* if already canonical, quit now */ if (pvp[0] != NULL && (pvp[0][0] & 0377) == CANONNET) break; if (tTd(21, 12)) { printf("-----trying rule:"); printav(rwr->r_lhs); } /* try to match on this rule */ mlp = mlist; rvp = rwr->r_lhs; avp = pvp; if (++loopcount > 100) { syserr("554 Infinite loop in ruleset %d, rule %d", ruleset, ruleno); if (tTd(21, 1)) { printf("workspace: "); printav(pvp); } break; } while ((ap = *avp) != NULL || *rvp != NULL) { rp = *rvp; if (tTd(21, 35)) { printf("ADVANCE rp="); xputs(rp); printf(", ap="); xputs(ap); printf("\n"); } if (rp == NULL) { /* end-of-pattern before end-of-address */ goto backup; } if (ap == NULL && (*rp & 0377) != MATCHZANY && (*rp & 0377) != MATCHZERO) { /* end-of-input with patterns left */ goto backup; } switch (*rp & 0377) { char buf[MAXLINE]; case MATCHCLASS: /* match any phrase in a class */ mlp->pattern = rvp; mlp->first = avp; extendclass: ap = *avp; if (ap == NULL) goto backup; mlp->last = avp++; cataddr(mlp->first, mlp->last, buf, sizeof buf, '\0'); if (!wordinclass(buf, rp[1])) { if (tTd(21, 36)) { printf("EXTEND rp="); xputs(rp); printf(", ap="); xputs(ap); printf("\n"); } goto extendclass; } if (tTd(21, 36)) printf("CLMATCH\n"); mlp++; break; case MATCHNCLASS: /* match any token not in a class */ if (wordinclass(ap, rp[1])) goto backup; /* fall through */ case MATCHONE: case MATCHANY: /* match exactly one token */ mlp->pattern = rvp; mlp->first = avp; mlp->last = avp++; mlp++; break; case MATCHZANY: /* match zero or more tokens */ mlp->pattern = rvp; mlp->first = avp; mlp->last = avp - 1; mlp++; break; case MATCHZERO: /* match zero tokens */ break; case MACRODEXPAND: /* ** Match against run-time macro. ** This algorithm is broken for the ** general case (no recursive macros, ** improper tokenization) but should ** work for the usual cases. */ ap = macvalue(rp[1], e); mlp->first = avp; if (tTd(21, 2)) printf("rewrite: LHS $&%s => \"%s\"\n", macname(rp[1]), ap == NULL ? "(NULL)" : ap); if (ap == NULL) break; while (*ap != '\0') { if (*avp == NULL || strncasecmp(ap, *avp, strlen(*avp)) != 0) { /* no match */ avp = mlp->first; goto backup; } ap += strlen(*avp++); } /* match */ break; default: /* must have exact match */ - if (strcasecmp(rp, ap)) + if (sm_strcasecmp(rp, ap)) goto backup; avp++; break; } /* successful match on this token */ rvp++; continue; backup: /* match failed -- back up */ while (--mlp >= mlist) { rvp = mlp->pattern; rp = *rvp; avp = mlp->last + 1; ap = *avp; if (tTd(21, 36)) { printf("BACKUP rp="); xputs(rp); printf(", ap="); xputs(ap); printf("\n"); } if (ap == NULL) { /* run off the end -- back up again */ continue; } if ((*rp & 0377) == MATCHANY || (*rp & 0377) == MATCHZANY) { /* extend binding and continue */ mlp->last = avp++; rvp++; mlp++; break; } if ((*rp & 0377) == MATCHCLASS) { /* extend binding and try again */ mlp->last = avp; goto extendclass; } } if (mlp < mlist) { /* total failure to match */ break; } } /* ** See if we successfully matched */ if (mlp < mlist || *rvp != NULL) { if (tTd(21, 10)) printf("----- rule fails\n"); rwr = rwr->r_next; ruleno++; loopcount = 0; continue; } rvp = rwr->r_rhs; if (tTd(21, 12)) { printf("-----rule matches:"); printav(rvp); } rp = *rvp; if ((*rp & 0377) == CANONUSER) { rvp++; rwr = rwr->r_next; ruleno++; loopcount = 0; } else if ((*rp & 0377) == CANONHOST) { rvp++; rwr = NULL; } /* substitute */ for (avp = npvp; *rvp != NULL; rvp++) { register struct match *m; register char **pp; rp = *rvp; if ((*rp & 0377) == MATCHREPL) { /* substitute from LHS */ m = &mlist[rp[1] - '1']; if (m < mlist || m >= mlp) { syserr("554 rewrite: ruleset %d: replacement $%c out of bounds", ruleset, rp[1]); return EX_CONFIG; } if (tTd(21, 15)) { printf("$%c:", rp[1]); pp = m->first; while (pp <= m->last) { printf(" %lx=\"", (u_long) *pp); (void) fflush(stdout); printf("%s\"", *pp++); } printf("\n"); } pp = m->first; while (pp <= m->last) { if (avp >= &npvp[MAXATOM]) { syserr("554 rewrite: expansion too long"); return EX_DATAERR; } *avp++ = *pp++; } } else { /* vanilla replacement */ if (avp >= &npvp[MAXATOM]) { toolong: syserr("554 rewrite: expansion too long"); return EX_DATAERR; } if ((*rp & 0377) != MACRODEXPAND) *avp++ = rp; else { *avp = macvalue(rp[1], e); if (tTd(21, 2)) printf("rewrite: RHS $&%s => \"%s\"\n", macname(rp[1]), *avp == NULL ? "(NULL)" : *avp); if (*avp != NULL) avp++; } } } *avp++ = NULL; /* ** Check for any hostname/keyword lookups. */ for (rvp = npvp; *rvp != NULL; rvp++) { char **hbrvp; char **xpvp; int trsize; char *replac; int endtoken; STAB *map; char *mapname; char **key_rvp; char **arg_rvp; char **default_rvp; char buf[MAXNAME + 1]; char *pvpb1[MAXATOM + 1]; char *argvect[10]; char pvpbuf[PSBUFSIZE]; char *nullpvp[1]; extern char *map_lookup __P((STAB *, char *, char **, int *, ENVELOPE *)); if ((**rvp & 0377) != HOSTBEGIN && (**rvp & 0377) != LOOKUPBEGIN) continue; /* ** Got a hostname/keyword lookup. ** ** This could be optimized fairly easily. */ hbrvp = rvp; if ((**rvp & 0377) == HOSTBEGIN) { endtoken = HOSTEND; mapname = "host"; } else { endtoken = LOOKUPEND; mapname = *++rvp; } map = stab(mapname, ST_MAP, ST_FIND); if (map == NULL) syserr("554 rewrite: map %s not found", mapname); /* extract the match part */ key_rvp = ++rvp; default_rvp = NULL; arg_rvp = argvect; xpvp = NULL; replac = pvpbuf; while (*rvp != NULL && (**rvp & 0377) != endtoken) { int nodetype = **rvp & 0377; if (nodetype != CANONHOST && nodetype != CANONUSER) { rvp++; continue; } *rvp++ = NULL; if (xpvp != NULL) { cataddr(xpvp, NULL, replac, &pvpbuf[sizeof pvpbuf] - replac, '\0'); *++arg_rvp = replac; replac += strlen(replac) + 1; xpvp = NULL; } switch (nodetype) { case CANONHOST: xpvp = rvp; break; case CANONUSER: default_rvp = rvp; break; } } if (*rvp != NULL) *rvp++ = NULL; if (xpvp != NULL) { cataddr(xpvp, NULL, replac, &pvpbuf[sizeof pvpbuf] - replac, '\0'); *++arg_rvp = replac; } *++arg_rvp = NULL; /* save the remainder of the input string */ trsize = (int) (avp - rvp + 1) * sizeof *rvp; bcopy((char *) rvp, (char *) pvpb1, trsize); /* look it up */ cataddr(key_rvp, NULL, buf, sizeof buf, '\0'); argvect[0] = buf; replac = map_lookup(map, buf, argvect, &rstat, e); /* if no replacement, use default */ if (replac == NULL && default_rvp != NULL) { /* create the default */ cataddr(default_rvp, NULL, buf, sizeof buf, '\0'); replac = buf; } if (replac == NULL) { xpvp = key_rvp; } else if (*replac == '\0') { /* null replacement */ nullpvp[0] = NULL; xpvp = nullpvp; } else { /* scan the new replacement */ xpvp = prescan(replac, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL); if (xpvp == NULL) { /* prescan already printed error */ return EX_DATAERR; } } /* append it to the token list */ for (avp = hbrvp; *xpvp != NULL; xpvp++) { *avp++ = newstr(*xpvp); if (avp >= &npvp[MAXATOM]) goto toolong; } /* restore the old trailing information */ for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; ) if (avp >= &npvp[MAXATOM]) goto toolong; break; } /* ** Check for subroutine calls. */ stat = callsubr(npvp, reclevel, e); if (rstat == EX_OK || stat == EX_TEMPFAIL) rstat = stat; /* copy vector back into original space. */ for (avp = npvp; *avp++ != NULL;) continue; bcopy((char *) npvp, (char *) pvp, (int) (avp - npvp) * sizeof *avp); if (tTd(21, 4)) { printf("rewritten as:"); printav(pvp); } } if (OpMode == MD_TEST || tTd(21, 1)) { printf("rewrite: ruleset %3d returns:", ruleset); printav(pvp); } return rstat; } /* ** CALLSUBR -- call subroutines in rewrite vector ** ** Parameters: ** pvp -- pointer to token vector. ** reclevel -- the current recursion level. ** e -- the current envelope. ** ** Returns: ** The status from the subroutine call. ** ** Side Effects: ** pvp is modified. */ int callsubr(pvp, reclevel, e) char **pvp; int reclevel; ENVELOPE *e; { char **avp; char **rvp; register int i; int subr; int stat; int rstat = EX_OK; char *tpvp[MAXATOM + 1]; for (avp = pvp; *avp != NULL; avp++) { if ((**avp & 0377) == CALLSUBR && avp[1] != NULL) { subr = strtorwset(avp[1], NULL, ST_FIND); if (subr < 0) { syserr("Unknown ruleset %s", avp[1]); return EX_CONFIG; } if (tTd(21, 3)) printf("-----callsubr %s (%d)\n", avp[1], subr); /* ** Take care of possible inner calls first. ** use a full size temporary buffer to avoid ** overflows in rewrite, but strip off the ** subroutine call. */ for (i = 2; avp[i] != NULL; i++) tpvp[i - 2] = avp[i]; tpvp[i - 2] = NULL; stat = callsubr(tpvp, reclevel, e); if (rstat == EX_OK || stat == EX_TEMPFAIL) rstat = stat; /* ** Now we need to call the ruleset specified for ** the subroutine. we can do this with the ** temporary buffer that we set up earlier, ** since it has all the data we want to rewrite. */ stat = rewrite(tpvp, subr, reclevel, e); if (rstat == EX_OK || stat == EX_TEMPFAIL) rstat = stat; /* ** Find length of tpvp and current offset into ** pvp, if the total is greater than MAXATOM, ** then it would overflow the buffer if we copied ** it back in to pvp, in which case we throw a ** fit. */ for (rvp = tpvp; *rvp != NULL; rvp++) continue; if (((rvp - tpvp) + (avp - pvp)) > MAXATOM) { syserr("554 callsubr: expansion too long"); return EX_DATAERR; } /* ** Now we can copy the rewritten code over ** the initial subroutine call in the buffer. */ for (i = 0; tpvp[i] != NULL; i++) avp[i] = tpvp[i]; avp[i] = NULL; /* ** If we got this far, we've processed the left ** most subroutine, and recursively called ourselves ** to handle any other subroutines. We're done. */ break; } } return rstat; } /* ** MAP_LOOKUP -- do lookup in map ** ** Parameters: ** map -- the map to use for the lookup. ** key -- the key to look up. ** argvect -- arguments to pass to the map lookup. ** pstat -- a pointer to an integer in which to store the ** status from the lookup. ** e -- the current envelope. ** ** Returns: ** The result of the lookup. ** NULL -- if there was no data for the given key. */ char * map_lookup(map, key, argvect, pstat, e) STAB *map; char key[]; char **argvect; int *pstat; ENVELOPE *e; { auto int stat = EX_OK; char *replac; if (e->e_sendmode == SM_DEFER) { /* don't do any map lookups */ if (tTd(60, 1)) printf("map_lookup(%s, %s) => DEFERRED\n", map->s_name, key); *pstat = EX_TEMPFAIL; return NULL; } if (map == NULL || !bitset(MF_OPEN, map->s_map.map_mflags)) return NULL; if (!bitset(MF_KEEPQUOTES, map->s_map.map_mflags)) stripquotes(key); /* XXX should try to auto-open the map here */ if (tTd(60, 1)) printf("map_lookup(%s, %s) => ", map->s_name, key); replac = (*map->s_map.map_class->map_lookup)(&map->s_map, key, argvect, &stat); if (tTd(60, 1)) printf("%s (%d)\n", replac != NULL ? replac : "NOT FOUND", stat); /* should recover if stat == EX_TEMPFAIL */ if (stat == EX_TEMPFAIL && !bitset(MF_NODEFER, map->s_map.map_mflags)) { *pstat = EX_TEMPFAIL; if (tTd(60, 1)) printf("map_lookup(%s, %s) tempfail: errno=%d\n", map->s_name, key, errno); if (e->e_message == NULL) { - char mbuf[300]; + char mbuf[320]; snprintf(mbuf, sizeof mbuf, "%.80s map: lookup (%s): deferred", map->s_name, shortenstring(key, 203)); e->e_message = newstr(mbuf); } } return replac; } /* ** BUILDADDR -- build address from token vector. ** ** Parameters: ** tv -- token vector. ** a -- pointer to address descriptor to fill. ** If NULL, one will be allocated. ** flags -- info regarding whether this is a sender or ** a recipient. ** e -- the current envelope. ** ** Returns: ** NULL if there was an error. ** 'a' otherwise. ** ** Side Effects: ** fills in 'a' */ struct errcodes { char *ec_name; /* name of error code */ int ec_code; /* numeric code */ } ErrorCodes[] = { { "usage", EX_USAGE }, { "nouser", EX_NOUSER }, { "nohost", EX_NOHOST }, { "unavailable", EX_UNAVAILABLE }, { "software", EX_SOFTWARE }, { "tempfail", EX_TEMPFAIL }, { "protocol", EX_PROTOCOL }, #ifdef EX_CONFIG { "config", EX_CONFIG }, #endif { NULL, EX_UNAVAILABLE } }; ADDRESS * buildaddr(tv, a, flags, e) register char **tv; register ADDRESS *a; int flags; register ENVELOPE *e; { struct mailer **mp; register struct mailer *m; register char *p; char *mname; char **hostp; char hbuf[MAXNAME + 1]; static MAILER errormailer; static char *errorargv[] = { "ERROR", NULL }; static char ubuf[MAXNAME + 1]; if (tTd(24, 5)) { printf("buildaddr, flags=%x, tv=", flags); printav(tv); } if (a == NULL) a = (ADDRESS *) xalloc(sizeof *a); bzero((char *) a, sizeof *a); /* set up default error return flags */ a->q_flags |= DefaultNotify; /* figure out what net/mailer to use */ if (*tv == NULL || (**tv & 0377) != CANONNET) { syserr("554 buildaddr: no mailer in parsed address"); badaddr: a->q_flags |= QBADADDR; a->q_mailer = &errormailer; if (errormailer.m_name == NULL) { /* initialize the bogus mailer */ errormailer.m_name = "*error*"; errormailer.m_mailer = "ERROR"; errormailer.m_argv = errorargv; } return a; } mname = *++tv; /* extract host and user portions */ if (*++tv != NULL && (**tv & 0377) == CANONHOST) hostp = ++tv; else hostp = NULL; while (*tv != NULL && (**tv & 0377) != CANONUSER) tv++; if (*tv == NULL) { syserr("554 buildaddr: no user"); goto badaddr; } if (tv == hostp) hostp = NULL; else if (hostp != NULL) cataddr(hostp, tv - 1, hbuf, sizeof hbuf, '\0'); cataddr(++tv, NULL, ubuf, sizeof ubuf, ' '); /* save away the host name */ if (strcasecmp(mname, "error") == 0) { if (hostp != NULL) { register struct errcodes *ep; if (strchr(hbuf, '.') != NULL) { extern int dsntoexitstat __P((char *)); a->q_status = newstr(hbuf); setstat(dsntoexitstat(hbuf)); } else if (isascii(hbuf[0]) && isdigit(hbuf[0])) { setstat(atoi(hbuf)); } else { for (ep = ErrorCodes; ep->ec_name != NULL; ep++) if (strcasecmp(ep->ec_name, hbuf) == 0) break; setstat(ep->ec_code); } } else setstat(EX_UNAVAILABLE); stripquotes(ubuf); if (isascii(ubuf[0]) && isdigit(ubuf[0]) && isascii(ubuf[1]) && isdigit(ubuf[1]) && isascii(ubuf[2]) && isdigit(ubuf[2]) && ubuf[3] == ' ') { char fmt[10]; strncpy(fmt, ubuf, 3); strcpy(&fmt[3], " %s"); usrerr(fmt, ubuf + 4); /* ** If this is a 4xx code and we aren't running ** SMTP on our input, bounce this message; ** otherwise it disappears without a trace. */ if (fmt[0] == '4' && OpMode != MD_SMTP && OpMode != MD_DAEMON) { e->e_flags |= EF_FATALERRS; } } else { usrerr("553 %s", ubuf); } goto badaddr; } for (mp = Mailer; (m = *mp++) != NULL; ) { if (strcasecmp(m->m_name, mname) == 0) break; } if (m == NULL) { syserr("554 buildaddr: unknown mailer %s", mname); goto badaddr; } a->q_mailer = m; /* figure out what host (if any) */ if (hostp == NULL) { if (!bitnset(M_LOCALMAILER, m->m_flags)) { syserr("554 buildaddr: no host"); goto badaddr; } a->q_host = NULL; } else a->q_host = newstr(hbuf); /* figure out the user */ p = ubuf; if (bitnset(M_CHECKUDB, m->m_flags) && *p == '@') { p++; tv++; a->q_flags |= QNOTREMOTE; } /* do special mapping for local mailer */ if (*p == '"') p++; if (*p == '|' && bitnset(M_CHECKPROG, m->m_flags)) a->q_mailer = m = ProgMailer; else if (*p == '/' && bitnset(M_CHECKFILE, m->m_flags)) a->q_mailer = m = FileMailer; else if (*p == ':' && bitnset(M_CHECKINCLUDE, m->m_flags)) { /* may be :include: */ stripquotes(ubuf); if (strncasecmp(ubuf, ":include:", 9) == 0) { /* if :include:, don't need further rewriting */ a->q_mailer = m = InclMailer; a->q_user = newstr(&ubuf[9]); return a; } } /* rewrite according recipient mailer rewriting rules */ define('h', a->q_host, e); if (!bitset(RF_SENDERADDR|RF_HEADERADDR, flags)) { /* sender addresses done later */ (void) rewrite(tv, 2, 0, e); if (m->m_re_rwset > 0) (void) rewrite(tv, m->m_re_rwset, 0, e); } (void) rewrite(tv, 4, 0, e); /* save the result for the command line/RCPT argument */ cataddr(tv, NULL, ubuf, sizeof ubuf, '\0'); a->q_user = ubuf; /* ** Do mapping to lower case as requested by mailer */ if (a->q_host != NULL && !bitnset(M_HST_UPPER, m->m_flags)) makelower(a->q_host); if (!bitnset(M_USR_UPPER, m->m_flags)) makelower(a->q_user); if (tTd(24, 6)) { printf("buildaddr => "); printaddr(a, FALSE); } return a; } /* ** CATADDR -- concatenate pieces of addresses (putting in subs) ** ** Parameters: ** pvp -- parameter vector to rebuild. ** evp -- last parameter to include. Can be NULL to ** use entire pvp. ** buf -- buffer to build the string into. ** sz -- size of buf. ** spacesub -- the space separator character; if null, ** use SpaceSub. ** ** Returns: ** none. ** ** Side Effects: ** Destroys buf. */ void cataddr(pvp, evp, buf, sz, spacesub) char **pvp; char **evp; char *buf; register int sz; int spacesub; { bool oatomtok = FALSE; bool natomtok = FALSE; register int i; register char *p; if (spacesub == '\0') spacesub = SpaceSub; if (pvp == NULL) { (void) strcpy(buf, ""); return; } p = buf; sz -= 2; while (*pvp != NULL && (i = strlen(*pvp)) < sz) { natomtok = (TokTypeTab[**pvp & 0xff] == ATM); if (oatomtok && natomtok) *p++ = spacesub; (void) strcpy(p, *pvp); oatomtok = natomtok; p += i; sz -= i + 1; if (pvp++ == evp) break; } *p = '\0'; } /* ** SAMEADDR -- Determine if two addresses are the same ** ** This is not just a straight comparison -- if the mailer doesn't ** care about the host we just ignore it, etc. ** ** Parameters: ** a, b -- pointers to the internal forms to compare. ** ** Returns: ** TRUE -- they represent the same mailbox. ** FALSE -- they don't. ** ** Side Effects: ** none. */ bool sameaddr(a, b) register ADDRESS *a; register ADDRESS *b; { register ADDRESS *ca, *cb; /* if they don't have the same mailer, forget it */ if (a->q_mailer != b->q_mailer) return (FALSE); /* if the user isn't the same, we can drop out */ if (strcmp(a->q_user, b->q_user) != 0) return (FALSE); /* if we have good uids for both but they differ, these are different */ if (a->q_mailer == ProgMailer) { ca = getctladdr(a); cb = getctladdr(b); if (ca != NULL && cb != NULL && bitset(QGOODUID, ca->q_flags & cb->q_flags) && ca->q_uid != cb->q_uid) return (FALSE); } /* otherwise compare hosts (but be careful for NULL ptrs) */ if (a->q_host == b->q_host) { /* probably both null pointers */ return (TRUE); } if (a->q_host == NULL || b->q_host == NULL) { /* only one is a null pointer */ return (FALSE); } if (strcmp(a->q_host, b->q_host) != 0) return (FALSE); return (TRUE); } /* ** PRINTADDR -- print address (for debugging) ** ** Parameters: ** a -- the address to print ** follow -- follow the q_next chain. ** ** Returns: ** none. ** ** Side Effects: ** none. */ struct qflags { char *qf_name; u_long qf_bit; }; struct qflags AddressFlags[] = { { "QDONTSEND", QDONTSEND }, { "QBADADDR", QBADADDR }, { "QGOODUID", QGOODUID }, { "QPRIMARY", QPRIMARY }, { "QQUEUEUP", QQUEUEUP }, { "QSENT", QSENT }, { "QNOTREMOTE", QNOTREMOTE }, { "QSELFREF", QSELFREF }, { "QVERIFIED", QVERIFIED }, { "QBOGUSSHELL", QBOGUSSHELL }, { "QUNSAFEADDR", QUNSAFEADDR }, { "QPINGONSUCCESS", QPINGONSUCCESS }, { "QPINGONFAILURE", QPINGONFAILURE }, { "QPINGONDELAY", QPINGONDELAY }, { "QHASNOTIFY", QHASNOTIFY }, { "QRELAYED", QRELAYED }, { "QEXPANDED", QEXPANDED }, { "QDELIVERED", QDELIVERED }, { "QDELAYED", QDELAYED }, { "QTHISPASS", QTHISPASS }, { NULL } }; void printaddr(a, follow) register ADDRESS *a; bool follow; { register MAILER *m; MAILER pseudomailer; register struct qflags *qfp; bool firstone; if (a == NULL) { printf("[NULL]\n"); return; } while (a != NULL) { printf("%lx=", (u_long) a); (void) fflush(stdout); /* find the mailer -- carefully */ m = a->q_mailer; if (m == NULL) { m = &pseudomailer; m->m_mno = -1; m->m_name = "NULL"; } printf("%s:\n\tmailer %d (%s), host `%s'\n", a->q_paddr == NULL ? "" : a->q_paddr, m->m_mno, m->m_name, a->q_host == NULL ? "" : a->q_host); printf("\tuser `%s', ruser `%s'\n", a->q_user, a->q_ruser == NULL ? "" : a->q_ruser); printf("\tnext=%lx, alias %lx, uid %d, gid %d\n", (u_long) a->q_next, (u_long) a->q_alias, (int) a->q_uid, (int) a->q_gid); printf("\tflags=%lx<", a->q_flags); firstone = TRUE; for (qfp = AddressFlags; qfp->qf_name != NULL; qfp++) { if (!bitset(qfp->qf_bit, a->q_flags)) continue; if (!firstone) printf(","); firstone = FALSE; printf("%s", qfp->qf_name); } printf(">\n"); printf("\towner=%s, home=\"%s\", fullname=\"%s\"\n", a->q_owner == NULL ? "(none)" : a->q_owner, a->q_home == NULL ? "(none)" : a->q_home, a->q_fullname == NULL ? "(none)" : a->q_fullname); printf("\torcpt=\"%s\", statmta=%s, status=%s\n", a->q_orcpt == NULL ? "(none)" : a->q_orcpt, a->q_statmta == NULL ? "(none)" : a->q_statmta, a->q_status == NULL ? "(none)" : a->q_status); printf("\trstatus=\"%s\"\n", a->q_rstatus == NULL ? "(none)" : a->q_rstatus); printf("\tspecificity=%d, statdate=%s\n", a->q_specificity, ctime(&a->q_statdate)); if (!follow) return; a = a->q_next; } } /* ** EMPTYADDR -- return TRUE if this address is empty (``<>'') ** ** Parameters: ** a -- pointer to the address ** ** Returns: ** TRUE -- if this address is "empty" (i.e., no one should ** ever generate replies to it. ** FALSE -- if it is a "regular" (read: replyable) address. */ bool emptyaddr(a) register ADDRESS *a; { return a->q_paddr == NULL || strcmp(a->q_paddr, "<>") == 0 || a->q_user == NULL || strcmp(a->q_user, "<>") == 0; } /* ** REMOTENAME -- return the name relative to the current mailer ** ** Parameters: ** name -- the name to translate. ** m -- the mailer that we want to do rewriting relative ** to. ** flags -- fine tune operations. ** pstat -- pointer to status word. ** e -- the current envelope. ** ** Returns: ** the text string representing this address relative to ** the receiving mailer. ** ** Side Effects: ** none. ** ** Warnings: ** The text string returned is tucked away locally; ** copy it if you intend to save it. */ char * remotename(name, m, flags, pstat, e) char *name; struct mailer *m; int flags; int *pstat; register ENVELOPE *e; { register char **pvp; char *fancy; char *oldg = macvalue('g', e); int rwset; static char buf[MAXNAME + 1]; char lbuf[MAXNAME + 1]; char pvpbuf[PSBUFSIZE]; extern char *crackaddr(); if (tTd(12, 1)) printf("remotename(%s)\n", name); /* don't do anything if we are tagging it as special */ if (bitset(RF_SENDERADDR, flags)) rwset = bitset(RF_HEADERADDR, flags) ? m->m_sh_rwset : m->m_se_rwset; else rwset = bitset(RF_HEADERADDR, flags) ? m->m_rh_rwset : m->m_re_rwset; if (rwset < 0) return (name); /* ** Do a heuristic crack of this name to extract any comment info. ** This will leave the name as a comment and a $g macro. */ if (bitset(RF_CANONICAL, flags) || bitnset(M_NOCOMMENT, m->m_flags)) fancy = "\201g"; else fancy = crackaddr(name); /* ** Turn the name into canonical form. ** Normally this will be RFC 822 style, i.e., "user@domain". ** If this only resolves to "user", and the "C" flag is ** specified in the sending mailer, then the sender's ** domain will be appended. */ pvp = prescan(name, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL); if (pvp == NULL) return (name); if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; if (bitset(RF_ADDDOMAIN, flags) && e->e_fromdomain != NULL) { /* append from domain to this address */ register char **pxp = pvp; /* see if there is an "@domain" in the current name */ while (*pxp != NULL && strcmp(*pxp, "@") != 0) pxp++; if (*pxp == NULL) { /* no.... append the "@domain" from the sender */ register char **qxq = e->e_fromdomain; while ((*pxp++ = *qxq++) != NULL) continue; if (rewrite(pvp, 3, 0, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; } } /* ** Do more specific rewriting. ** Rewrite using ruleset 1 or 2 depending on whether this is ** a sender address or not. ** Then run it through any receiving-mailer-specific rulesets. */ if (bitset(RF_SENDERADDR, flags)) { if (rewrite(pvp, 1, 0, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; } else { if (rewrite(pvp, 2, 0, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; } if (rwset > 0) { if (rewrite(pvp, rwset, 0, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; } /* ** Do any final sanitation the address may require. ** This will normally be used to turn internal forms ** (e.g., user@host.LOCAL) into external form. This ** may be used as a default to the above rules. */ if (rewrite(pvp, 4, 0, e) == EX_TEMPFAIL) *pstat = EX_TEMPFAIL; /* ** Now restore the comment information we had at the beginning. */ cataddr(pvp, NULL, lbuf, sizeof lbuf, '\0'); define('g', lbuf, e); /* need to make sure route-addrs have */ if (bitset(RF_CANONICAL, flags) && lbuf[0] == '@') expand("<\201g>", buf, sizeof buf, e); else expand(fancy, buf, sizeof buf, e); define('g', oldg, e); if (tTd(12, 1)) printf("remotename => `%s'\n", buf); return (buf); } /* ** MAPLOCALUSER -- run local username through ruleset 5 for final redirection ** ** Parameters: ** a -- the address to map (but just the user name part). ** sendq -- the sendq in which to install any replacement ** addresses. ** aliaslevel -- the alias nesting depth. ** e -- the envelope. ** ** Returns: ** none. */ +#define Q_COPYFLAGS (QPRIMARY|QBOGUSSHELL|QUNSAFEADDR|\ + Q_PINGFLAGS|QHASNOTIFY|\ + QRELAYED|QEXPANDED|QDELIVERED|QDELAYED) + void maplocaluser(a, sendq, aliaslevel, e) register ADDRESS *a; ADDRESS **sendq; int aliaslevel; ENVELOPE *e; { register char **pvp; register ADDRESS *a1 = NULL; auto char *delimptr; char pvpbuf[PSBUFSIZE]; if (tTd(29, 1)) { printf("maplocaluser: "); printaddr(a, FALSE); } pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL); if (pvp == NULL) return; + define('h', a->q_host, e); + define('u', a->q_user, e); + define('z', a->q_home, e); + if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL) { a->q_flags |= QQUEUEUP; a->q_status = "4.4.3"; return; } if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET) return; /* if non-null, mailer destination specified -- has it changed? */ a1 = buildaddr(pvp, NULL, 0, e); if (a1 == NULL || sameaddr(a, a1)) + { + if (a1 != NULL) + free(a1); return; + } + /* make new address take on flags and print attributes of old */ + a1->q_flags &= ~Q_COPYFLAGS; + a1->q_flags |= a->q_flags & Q_COPYFLAGS; + a1->q_paddr = a->q_paddr; + /* mark old address as dead; insert new address */ a->q_flags |= QDONTSEND; if (tTd(29, 5)) { printf("maplocaluser: QDONTSEND "); printaddr(a, FALSE); } a1->q_alias = a; allocaddr(a1, RF_COPYALL, a->q_paddr); (void) recipient(a1, sendq, aliaslevel, e); } /* ** DEQUOTE_INIT -- initialize dequote map ** ** This is a no-op. ** ** Parameters: ** map -- the internal map structure. ** args -- arguments. ** ** Returns: ** TRUE. */ bool dequote_init(map, args) MAP *map; char *args; { register char *p = args; map->map_mflags |= MF_KEEPQUOTES; for (;;) { while (isascii(*p) && isspace(*p)) p++; if (*p != '-') break; switch (*++p) { case 'a': map->map_app = ++p; break; case 's': map->map_coldelim = *++p; break; } while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p != '\0') *p = '\0'; } if (map->map_app != NULL) map->map_app = newstr(map->map_app); return TRUE; } /* ** DEQUOTE_MAP -- unquote an address ** ** Parameters: ** map -- the internal map structure (ignored). ** name -- the name to dequote. ** av -- arguments (ignored). ** statp -- pointer to status out-parameter. ** ** Returns: ** NULL -- if there were no quotes, or if the resulting ** unquoted buffer would not be acceptable to prescan. ** else -- The dequoted buffer. */ char * dequote_map(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { register char *p; register char *q; register char c; int anglecnt = 0; int cmntcnt = 0; int quotecnt = 0; int spacecnt = 0; bool quotemode = FALSE; bool bslashmode = FALSE; char spacesub = map->map_coldelim; for (p = q = name; (c = *p++) != '\0'; ) { if (bslashmode) { bslashmode = FALSE; *q++ = c; continue; } if (c == ' ' && spacesub != '\0') c = spacesub; switch (c) { case '\\': bslashmode = TRUE; break; case '(': cmntcnt++; break; case ')': if (cmntcnt-- <= 0) return NULL; break; case ' ': spacecnt++; break; } if (cmntcnt > 0) { *q++ = c; continue; } switch (c) { case '"': quotemode = !quotemode; quotecnt++; continue; case '<': anglecnt++; break; case '>': if (anglecnt-- <= 0) return NULL; break; } *q++ = c; } if (anglecnt != 0 || cmntcnt != 0 || bslashmode || quotemode || quotecnt <= 0 || spacecnt != 0) return NULL; *q++ = '\0'; return map_rewrite(map, name, strlen(name), NULL); } /* ** RSCHECK -- check string(s) for validity using rewriting sets ** ** Parameters: ** rwset -- the rewriting set to use. ** p1 -- the first string to check. ** p2 -- the second string to check -- may be null. ** e -- the current envelope. ** ** Returns: ** EX_OK -- if the rwset doesn't resolve to $#error ** else -- the failure status (message printed) */ int rscheck(rwset, p1, p2, e) char *rwset; char *p1; char *p2; ENVELOPE *e; { char *buf; int bufsize; int saveexitstat; int rstat; char **pvp; int rsno; auto ADDRESS a1; bool saveQuickAbort = QuickAbort; + bool saveSuprErrs = SuprErrs; + bool saveOnlyOneError = OnlyOneError; char buf0[MAXLINE]; char pvpbuf[PSBUFSIZE]; extern char MsgBuf[]; if (tTd(48, 2)) printf("rscheck(%s, %s, %s)\n", rwset, p1, p2 == NULL ? "(NULL)" : p2); rsno = strtorwset(rwset, NULL, ST_FIND); if (rsno < 0) return EX_OK; if (p2 != NULL) { bufsize = strlen(p1) + strlen(p2) + 2; if (bufsize > sizeof buf0) buf = xalloc(bufsize); else { buf = buf0; bufsize = sizeof buf0; } (void) snprintf(buf, bufsize, "%s%c%s", p1, CONDELSE, p2); } else { bufsize = strlen(p1) + 1; if (bufsize > sizeof buf0) buf = xalloc(bufsize); else { buf = buf0; bufsize = sizeof buf0; } (void) snprintf(buf, bufsize, "%s", p1); } + SuprErrs = TRUE; + OnlyOneError = QuickAbort = FALSE; pvp = prescan(buf, '\0', pvpbuf, sizeof pvpbuf, NULL, NULL); + SuprErrs = saveSuprErrs; if (pvp == NULL) { + syserr("rscheck: cannot prescan input: \"%s\"", + shortenstring(buf, 203)); rstat = EX_DATAERR; goto finis; } (void) rewrite(pvp, rsno, 0, e); if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET || pvp[1] == NULL || strcmp(pvp[1], "error") != 0) - return EX_OK; + { + rstat = EX_OK; + goto finis; + } /* got an error -- process it */ saveexitstat = ExitStat; - QuickAbort = FALSE; (void) buildaddr(pvp, &a1, 0, e); - QuickAbort = saveQuickAbort; rstat = ExitStat; ExitStat = saveexitstat; -#ifdef LOG if (LogLevel >= 4) { if (p2 == NULL) - syslog(LOG_NOTICE, "Ruleset %s (%s) rejection: %s", + sm_syslog(LOG_NOTICE, e->e_id, + "Ruleset %s (%s) rejection: %s", rwset, p1, MsgBuf); else - syslog(LOG_NOTICE, "Ruleset %s (%s, %s) rejection: %s", + sm_syslog(LOG_NOTICE, e->e_id, + "Ruleset %s (%s, %s) rejection: %s", rwset, p1, p2, MsgBuf); } -#endif - if (QuickAbort) - longjmp(TopFrame, 2); - - /* clean up */ finis: + /* clean up */ + QuickAbort = saveQuickAbort; + OnlyOneError = saveOnlyOneError; setstat(rstat); if (buf != buf0) free(buf); + + if (rstat != EX_OK && (QuickAbort || (OnlyOneError && !HoldErrs))) + longjmp(TopFrame, 2); return rstat; } Index: head/usr.sbin/sendmail/src/readcf.c =================================================================== --- head/usr.sbin/sendmail/src/readcf.c (revision 26988) +++ head/usr.sbin/sendmail/src/readcf.c (revision 26989) @@ -1,2725 +1,2766 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)readcf.c 8.184 (Berkeley) 1/14/97"; +static char sccsid[] = "@(#)readcf.c 8.196 (Berkeley) 5/29/97"; #endif /* not lint */ # include "sendmail.h" # include #if NAMED_BIND # include #endif /* ** READCF -- read control file. ** ** This routine reads the control file and builds the internal ** form. ** ** The file is formatted as a sequence of lines, each taken ** atomically. The first character of each line describes how ** the line is to be interpreted. The lines are: ** Dxval Define macro x to have value val. ** Cxword Put word into class x. ** Fxfile [fmt] Read file for lines to put into ** class x. Use scanf string 'fmt' ** or "%s" if not present. Fmt should ** only produce one string-valued result. ** Hname: value Define header with field-name 'name' ** and value as specified; this will be ** macro expanded immediately before ** use. ** Sn Use rewriting set n. ** Rlhs rhs Rewrite addresses that match lhs to ** be rhs. ** Mn arg=val... Define mailer. n is the internal name. ** Args specify mailer parameters. ** Oxvalue Set option x to value. ** Pname=value Set precedence name to value. ** Vversioncode[/vendorcode] ** Version level/vendor name of ** configuration syntax. ** Kmapname mapclass arguments.... ** Define keyed lookup of a given class. ** Arguments are class dependent. ** Eenvar=value Set the environment value to the given value. ** ** Parameters: ** cfname -- control file name. ** safe -- TRUE if this is the system config file; ** FALSE otherwise. ** e -- the main envelope. ** ** Returns: ** none. ** ** Side Effects: ** Builds several internal tables. */ void readcf(cfname, safe, e) char *cfname; bool safe; register ENVELOPE *e; { FILE *cf; int ruleset = 0; char *q; struct rewrite *rwp = NULL; char *bp; auto char *ep; int nfuzzy; char *file; bool optional; int mid; char buf[MAXLINE]; register char *p; extern char **copyplist(); struct stat statb; char exbuf[MAXLINE]; char pvpbuf[MAXLINE + MAXATOM]; static char *null_list[1] = { NULL }; extern char *munchstring __P((char *, char **, int)); extern void fileclass __P((int, char *, char *, bool, bool)); extern void toomany __P((int, int)); extern void translate_dollars __P((char *)); extern void inithostmaps __P((void)); FileName = cfname; LineNumber = 0; - cf = fopen(cfname, "r"); + cf = safefopen(cfname, O_RDONLY, 0444, SFF_OPENASROOT|SFF_NOLOCK); if (cf == NULL) { syserr("cannot open"); exit(EX_OSFILE); } if (fstat(fileno(cf), &statb) < 0) { syserr("cannot fstat"); exit(EX_OSFILE); } if (!S_ISREG(statb.st_mode)) { syserr("not a plain file"); exit(EX_OSFILE); } if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) { if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS) fprintf(stderr, "%s: WARNING: dangerous write permissions\n", FileName); -#ifdef LOG if (LogLevel > 0) - syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", + sm_syslog(LOG_CRIT, NOQID, + "%s: WARNING: dangerous write permissions", FileName); -#endif } #ifdef XLA xla_zero(); #endif while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) { if (bp[0] == '#') { if (bp != buf) free(bp); continue; } /* do macro expansion mappings */ translate_dollars(bp); /* interpret this line */ errno = 0; switch (bp[0]) { case '\0': case '#': /* comment */ break; case 'R': /* rewriting rule */ for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) continue; if (*p == '\0') { syserr("invalid rewrite line \"%s\" (tab expected)", bp); break; } /* allocate space for the rule header */ if (rwp == NULL) { RewriteRules[ruleset] = rwp = (struct rewrite *) xalloc(sizeof *rwp); } else { rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); rwp = rwp->r_next; } rwp->r_next = NULL; /* expand and save the LHS */ *p = '\0'; expand(&bp[1], exbuf, sizeof exbuf, e); rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, sizeof pvpbuf, NULL, NULL); nfuzzy = 0; if (rwp->r_lhs != NULL) { register char **ap; rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); /* count the number of fuzzy matches in LHS */ for (ap = rwp->r_lhs; *ap != NULL; ap++) { char *botch; botch = NULL; switch (**ap & 0377) { case MATCHZANY: case MATCHANY: case MATCHONE: case MATCHCLASS: case MATCHNCLASS: nfuzzy++; break; case MATCHREPL: botch = "$0-$9"; break; case CANONNET: botch = "$#"; break; case CANONUSER: botch = "$:"; break; case CALLSUBR: botch = "$>"; break; case CONDIF: botch = "$?"; break; case CONDFI: botch = "$."; break; case HOSTBEGIN: botch = "$["; break; case HOSTEND: botch = "$]"; break; case LOOKUPBEGIN: botch = "$("; break; case LOOKUPEND: botch = "$)"; break; } if (botch != NULL) syserr("Inappropriate use of %s on LHS", botch); } } else { syserr("R line: null LHS"); rwp->r_lhs = null_list; } /* expand and save the RHS */ while (*++p == '\t') continue; q = p; while (*p != '\0' && *p != '\t') p++; *p = '\0'; expand(q, exbuf, sizeof exbuf, e); rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, sizeof pvpbuf, NULL, NULL); if (rwp->r_rhs != NULL) { register char **ap; rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); /* check no out-of-bounds replacements */ nfuzzy += '0'; for (ap = rwp->r_rhs; *ap != NULL; ap++) { char *botch; botch = NULL; switch (**ap & 0377) { case MATCHREPL: if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) { syserr("replacement $%c out of bounds", (*ap)[1]); } break; case MATCHZANY: botch = "$*"; break; case MATCHANY: botch = "$+"; break; case MATCHONE: botch = "$-"; break; case MATCHCLASS: botch = "$="; break; case MATCHNCLASS: botch = "$~"; break; } if (botch != NULL) syserr("Inappropriate use of %s on RHS", botch); } } else { syserr("R line: null RHS"); rwp->r_rhs = null_list; } break; case 'S': /* select rewriting set */ expand(&bp[1], exbuf, sizeof exbuf, e); ruleset = strtorwset(exbuf, NULL, ST_ENTER); if (ruleset < 0) break; rwp = RewriteRules[ruleset]; if (rwp != NULL) { if (OpMode == MD_TEST || tTd(37, 1)) printf("WARNING: Ruleset %s has multiple definitions\n", &bp[1]); while (rwp->r_next != NULL) rwp = rwp->r_next; } break; case 'D': /* macro definition */ mid = macid(&bp[1], &ep); p = munchstring(ep, NULL, '\0'); define(mid, newstr(p), e); break; case 'H': /* required header line */ (void) chompheader(&bp[1], TRUE, NULL, e); break; case 'C': /* word class */ case 'T': /* trusted user (set class `t') */ if (bp[0] == 'C') { mid = macid(&bp[1], &ep); expand(ep, exbuf, sizeof exbuf, e); p = exbuf; } else { mid = 't'; p = &bp[1]; } while (*p != '\0') { register char *wd; char delim; while (*p != '\0' && isascii(*p) && isspace(*p)) p++; wd = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; delim = *p; *p = '\0'; if (wd[0] != '\0') setclass(mid, wd); *p = delim; } break; case 'F': /* word class from file */ mid = macid(&bp[1], &ep); for (p = ep; isascii(*p) && isspace(*p); ) p++; if (p[0] == '-' && p[1] == 'o') { optional = TRUE; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; while (isascii(*p) && isspace(*p)) p++; } else optional = FALSE; file = p; if (*file == '|') p = "%s"; else { while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p == '\0') p = "%s"; else { *p = '\0'; while (isascii(*++p) && isspace(*p)) continue; } } fileclass(mid, file, p, safe, optional); break; #ifdef XLA case 'L': /* extended load average description */ xla_init(&bp[1]); break; #endif #ifdef SUN_EXTENSIONS case 'L': /* lookup macro */ case 'G': /* lookup class */ /* reserved for Sun -- NIS+ database lookup */ goto badline; #endif case 'M': /* define mailer */ makemailer(&bp[1]); break; case 'O': /* set option */ setoption(bp[1], &bp[2], safe, FALSE, e); break; case 'P': /* set precedence */ if (NumPriorities >= MAXPRIORITIES) { toomany('P', MAXPRIORITIES); break; } for (p = &bp[1]; *p != '\0' && *p != '='; p++) continue; if (*p == '\0') goto badline; *p = '\0'; Priorities[NumPriorities].pri_name = newstr(&bp[1]); Priorities[NumPriorities].pri_val = atoi(++p); NumPriorities++; break; case 'V': /* configuration syntax version */ for (p = &bp[1]; isascii(*p) && isspace(*p); p++) continue; if (!isascii(*p) || !isdigit(*p)) { syserr("invalid argument to V line: \"%.20s\"", &bp[1]); break; } ConfigLevel = strtol(p, &ep, 10); /* ** Do heuristic tweaking for back compatibility. */ if (ConfigLevel >= 5) { /* level 5 configs have short name in $w */ p = macvalue('w', e); if (p != NULL && (p = strchr(p, '.')) != NULL) *p = '\0'; define('w', macvalue('w', e), e); } if (ConfigLevel >= 6) { ColonOkInAddr = FALSE; } /* ** Look for vendor code. */ if (*ep++ == '/') { extern bool setvendor __P((char *)); /* extract vendor code */ for (p = ep; isascii(*p) && isalpha(*p); ) p++; *p = '\0'; if (!setvendor(ep)) syserr("invalid V line vendor code: \"%s\"", ep); } break; case 'K': expand(&bp[1], exbuf, sizeof exbuf, e); (void) makemapentry(exbuf); break; case 'E': p = strchr(bp, '='); if (p != NULL) *p++ = '\0'; setuserenv(&bp[1], p); break; default: badline: syserr("unknown control line \"%s\"", bp); } if (bp != buf) free(bp); } if (ferror(cf)) { syserr("I/O read error"); exit(EX_OSFILE); } fclose(cf); FileName = NULL; /* initialize host maps from local service tables */ inithostmaps(); /* determine if we need to do special name-server frotz */ { int nmaps; char *maptype[MAXMAPSTACK]; short mapreturn[MAXMAPACTIONS]; nmaps = switch_map_find("hosts", maptype, mapreturn); UseNameServer = FALSE; if (nmaps > 0 && nmaps <= MAXMAPSTACK) { register int mapno; for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++) { if (strcmp(maptype[mapno], "dns") == 0) UseNameServer = TRUE; } } #ifdef HESIOD nmaps = switch_map_find("passwd", maptype, mapreturn); UseHesiod = FALSE; if (nmaps > 0 && nmaps <= MAXMAPSTACK) { register int mapno; for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++) { if (strcmp(maptype[mapno], "hesiod") == 0) UseHesiod = TRUE; } } #endif } } /* ** TRANSLATE_DOLLARS -- convert $x into internal form ** ** Actually does all appropriate pre-processing of a config line ** to turn it into internal form. ** ** Parameters: ** bp -- the buffer to translate. ** ** Returns: ** None. The buffer is translated in place. Since the ** translations always make the buffer shorter, this is ** safe without a size parameter. */ void translate_dollars(bp) char *bp; { register char *p; auto char *ep; for (p = bp; *p != '\0'; p++) { if (*p == '#' && p > bp && ConfigLevel >= 3) { /* this is an on-line comment */ register char *e; switch (*--p & 0377) { case MACROEXPAND: /* it's from $# -- let it go through */ p++; break; case '\\': /* it's backslash escaped */ (void) strcpy(p, p + 1); break; default: /* delete preceeding white space */ while (isascii(*p) && isspace(*p) && *p != '\n' && p > bp) p--; if ((e = strchr(++p, '\n')) != NULL) (void) strcpy(p, e); else *p-- = '\0'; break; } continue; } if (*p != '$' || p[1] == '\0') continue; if (p[1] == '$') { /* actual dollar sign.... */ (void) strcpy(p, p + 1); continue; } /* convert to macro expansion character */ *p++ = MACROEXPAND; /* special handling for $=, $~, $&, and $? */ if (*p == '=' || *p == '~' || *p == '&' || *p == '?') p++; /* convert macro name to code */ *p = macid(p, &ep); if (ep != p) strcpy(p + 1, ep); } /* strip trailing white space from the line */ while (--p > bp && isascii(*p) && isspace(*p)) *p = '\0'; } /* ** TOOMANY -- signal too many of some option ** ** Parameters: ** id -- the id of the error line ** maxcnt -- the maximum possible values ** ** Returns: ** none. ** ** Side Effects: ** gives a syserr. */ void toomany(id, maxcnt) int id; int maxcnt; { syserr("too many %c lines, %d max", id, maxcnt); } /* ** FILECLASS -- read members of a class from a file ** ** Parameters: ** class -- class to define. ** filename -- name of file to read. ** fmt -- scanf string to use for match. ** safe -- if set, this is a safe read. ** optional -- if set, it is not an error for the file to ** not exist. ** ** Returns: ** none ** ** Side Effects: ** ** puts all lines in filename that match a scanf into ** the named class. */ void fileclass(class, filename, fmt, safe, optional) int class; char *filename; char *fmt; bool safe; bool optional; { FILE *f; int sff; pid_t pid; register char *p; char buf[MAXLINE]; if (tTd(37, 2)) printf("fileclass(%s, fmt=%s)\n", filename, fmt); if (filename[0] == '|') { auto int fd; int i; char *argv[MAXPV + 1]; i = 0; for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t")) { if (i >= MAXPV) break; argv[i++] = p; } argv[i] = NULL; pid = prog_open(argv, &fd, CurEnv); if (pid < 0) f = NULL; else f = fdopen(fd, "r"); } else { pid = -1; - sff = SFF_REGONLY; + sff = SFF_REGONLY|SFF_NOWLINK; if (safe) sff |= SFF_OPENASROOT; f = safefopen(filename, O_RDONLY, 0, sff); } if (f == NULL) { if (!optional) syserr("fileclass: cannot open %s", filename); return; } while (fgets(buf, sizeof buf, f) != NULL) { register char *p; # if SCANF - char wordbuf[MAXNAME+1]; + char wordbuf[MAXLINE + 1]; # endif if (buf[0] == '#') continue; # if SCANF if (sscanf(buf, fmt, wordbuf) != 1) continue; p = wordbuf; # else /* SCANF */ p = buf; # endif /* SCANF */ /* ** Break up the match into words. */ while (*p != '\0') { register char *q; /* strip leading spaces */ while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; /* find the end of the word */ q = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p != '\0') *p++ = '\0'; /* enter the word in the symbol table */ setclass(class, q); } } (void) fclose(f); if (pid > 0) (void) waitfor(pid); } /* ** MAKEMAILER -- define a new mailer. ** ** Parameters: ** line -- description of mailer. This is in labeled ** fields. The fields are: ** A -- the argv for this mailer ** C -- the character set for MIME conversions ** D -- the directory to run in ** E -- the eol string ** F -- the flags associated with the mailer ** L -- the maximum line length ** M -- the maximum message size ** N -- the niceness at which to run ** P -- the path to the mailer ** R -- the recipient rewriting set ** S -- the sender rewriting set ** T -- the mailer type (for DSNs) ** U -- the uid to run as ** The first word is the canonical name of the mailer. ** ** Returns: ** none. ** ** Side Effects: ** enters the mailer into the mailer table. */ void makemailer(line) char *line; { register char *p; register struct mailer *m; register STAB *s; int i; char fcode; auto char *endp; extern int NextMailer; extern char **makeargv(); extern char *munchstring __P((char *, char **, int)); /* allocate a mailer and set up defaults */ m = (struct mailer *) xalloc(sizeof *m); bzero((char *) m, sizeof *m); /* collect the mailer name */ for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) continue; if (*p != '\0') *p++ = '\0'; if (line[0] == '\0') syserr("name required for mailer"); m->m_name = newstr(line); /* now scan through and assign info from the fields */ while (*p != '\0') { auto char *delimptr; while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) p++; /* p now points to field code */ fcode = *p; while (*p != '\0' && *p != '=' && *p != ',') p++; if (*p++ != '=') { syserr("mailer %s: `=' expected", m->m_name); return; } while (isascii(*p) && isspace(*p)) p++; /* p now points to the field body */ p = munchstring(p, &delimptr, ','); /* install the field into the mailer struct */ switch (fcode) { case 'P': /* pathname */ if (*p == '\0') syserr("mailer %s: empty path name", m->m_name); m->m_mailer = newstr(p); break; case 'F': /* flags */ for (; *p != '\0'; p++) if (!(isascii(*p) && isspace(*p))) setbitn(*p, m->m_flags); break; case 'S': /* sender rewriting ruleset */ case 'R': /* recipient rewriting ruleset */ i = strtorwset(p, &endp, ST_ENTER); if (i < 0) return; if (fcode == 'S') m->m_sh_rwset = m->m_se_rwset = i; else m->m_rh_rwset = m->m_re_rwset = i; p = endp; if (*p++ == '/') { i = strtorwset(p, NULL, ST_ENTER); if (i < 0) return; if (fcode == 'S') m->m_sh_rwset = i; else m->m_rh_rwset = i; } break; case 'E': /* end of line string */ if (*p == '\0') syserr("mailer %s: null end-of-line string", m->m_name); m->m_eol = newstr(p); break; case 'A': /* argument vector */ if (*p == '\0') syserr("mailer %s: null argument vector", m->m_name); m->m_argv = makeargv(p); break; case 'M': /* maximum message size */ m->m_maxsize = atol(p); break; case 'L': /* maximum line length */ m->m_linelimit = atoi(p); if (m->m_linelimit < 0) m->m_linelimit = 0; break; case 'N': /* run niceness */ m->m_nice = atoi(p); break; case 'D': /* working directory */ if (*p == '\0') syserr("mailer %s: null working directory", m->m_name); m->m_execdir = newstr(p); break; case 'C': /* default charset */ if (*p == '\0') syserr("mailer %s: null charset", m->m_name); m->m_defcharset = newstr(p); break; case 'T': /* MTA-Name/Address/Diagnostic types */ /* extract MTA name type; default to "dns" */ m->m_mtatype = newstr(p); p = strchr(m->m_mtatype, '/'); if (p != NULL) { *p++ = '\0'; if (*p == '\0') p = NULL; } if (*m->m_mtatype == '\0') m->m_mtatype = "dns"; /* extract address type; default to "rfc822" */ m->m_addrtype = p; if (p != NULL) p = strchr(p, '/'); if (p != NULL) { *p++ = '\0'; if (*p == '\0') p = NULL; } if (m->m_addrtype == NULL || *m->m_addrtype == '\0') m->m_addrtype = "rfc822"; /* extract diagnostic type; default to "smtp" */ m->m_diagtype = p; if (m->m_diagtype == NULL || *m->m_diagtype == '\0') m->m_diagtype = "smtp"; break; case 'U': /* user id */ if (isascii(*p) && !isdigit(*p)) { char *q = p; struct passwd *pw; while (*p != '\0' && isascii(*p) && (isalnum(*p) || strchr("-_", *p) != NULL)) p++; while (isascii(*p) && isspace(*p)) *p++ = '\0'; if (*p != '\0') *p++ = '\0'; if (*q == '\0') syserr("mailer %s: null user name", m->m_name); pw = sm_getpwnam(q); if (pw == NULL) syserr("readcf: mailer U= flag: unknown user %s", q); else { m->m_uid = pw->pw_uid; m->m_gid = pw->pw_gid; } } else { auto char *q; m->m_uid = strtol(p, &q, 0); p = q; while (isascii(*p) && isspace(*p)) p++; if (*p != '\0') p++; } while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; if (isascii(*p) && !isdigit(*p)) { char *q = p; struct group *gr; while (isascii(*p) && isalnum(*p)) p++; *p++ = '\0'; if (*q == '\0') syserr("mailer %s: null group name", m->m_name); gr = getgrnam(q); if (gr == NULL) syserr("readcf: mailer U= flag: unknown group %s", q); else m->m_gid = gr->gr_gid; } else { m->m_gid = strtol(p, NULL, 0); } break; } p = delimptr; } /* do some rationality checking */ if (m->m_argv == NULL) { syserr("M%s: A= argument required", m->m_name); return; } if (m->m_mailer == NULL) { syserr("M%s: P= argument required", m->m_name); return; } if (NextMailer >= MAXMAILERS) { syserr("too many mailers defined (%d max)", MAXMAILERS); return; } /* do some heuristic cleanup for back compatibility */ if (bitnset(M_LIMITS, m->m_flags)) { if (m->m_linelimit == 0) m->m_linelimit = SMTPLINELIM; if (ConfigLevel < 2) setbitn(M_7BITS, m->m_flags); } if (strcmp(m->m_mailer, "[IPC]") == 0 || strcmp(m->m_mailer, "[TCP]") == 0) { if (m->m_mtatype == NULL) m->m_mtatype = "dns"; if (m->m_addrtype == NULL) m->m_addrtype = "rfc822"; if (m->m_diagtype == NULL) m->m_diagtype = "smtp"; } if (m->m_eol == NULL) { char **pp; /* default for SMTP is \r\n; use \n for local delivery */ for (pp = m->m_argv; *pp != NULL; pp++) { char *p; for (p = *pp; *p != '\0'; ) { if ((*p++ & 0377) == MACROEXPAND && *p == 'u') break; } if (*p != '\0') break; } if (*pp == NULL) m->m_eol = "\r\n"; else m->m_eol = "\n"; } /* enter the mailer into the symbol table */ s = stab(m->m_name, ST_MAILER, ST_ENTER); if (s->s_mailer != NULL) { i = s->s_mailer->m_mno; free(s->s_mailer); } else { i = NextMailer++; } Mailer[i] = s->s_mailer = m; m->m_mno = i; } /* ** MUNCHSTRING -- translate a string into internal form. ** ** Parameters: ** p -- the string to munch. ** delimptr -- if non-NULL, set to the pointer of the ** field delimiter character. ** delim -- the delimiter for the field. ** ** Returns: ** the munched string. */ char * munchstring(p, delimptr, delim) register char *p; char **delimptr; int delim; { register char *q; bool backslash = FALSE; bool quotemode = FALSE; static char buf[MAXLINE]; for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++) { if (backslash) { /* everything is roughly literal */ backslash = FALSE; switch (*p) { case 'r': /* carriage return */ *q++ = '\r'; continue; case 'n': /* newline */ *q++ = '\n'; continue; case 'f': /* form feed */ *q++ = '\f'; continue; case 'b': /* backspace */ *q++ = '\b'; continue; } *q++ = *p; } else { if (*p == '\\') backslash = TRUE; else if (*p == '"') quotemode = !quotemode; else if (quotemode || *p != delim) *q++ = *p; else break; } } if (delimptr != NULL) *delimptr = p; *q++ = '\0'; return (buf); } /* ** MAKEARGV -- break up a string into words ** ** Parameters: ** p -- the string to break up. ** ** Returns: ** a char **argv (dynamically allocated) ** ** Side Effects: ** munges p. */ char ** makeargv(p) register char *p; { char *q; int i; char **avp; char *argv[MAXPV + 1]; /* take apart the words */ i = 0; while (*p != '\0' && i < MAXPV) { q = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; while (isascii(*p) && isspace(*p)) *p++ = '\0'; argv[i++] = newstr(q); } argv[i++] = NULL; /* now make a copy of the argv */ avp = (char **) xalloc(sizeof *avp * i); bcopy((char *) argv, (char *) avp, sizeof *avp * i); return (avp); } /* ** PRINTRULES -- print rewrite rules (for debugging) ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** prints rewrite rules. */ void printrules() { register struct rewrite *rwp; register int ruleset; for (ruleset = 0; ruleset < 10; ruleset++) { if (RewriteRules[ruleset] == NULL) continue; printf("\n----Rule Set %d:", ruleset); for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) { printf("\nLHS:"); printav(rwp->r_lhs); printf("RHS:"); printav(rwp->r_rhs); } } } /* ** PRINTMAILER -- print mailer structure (for debugging) ** ** Parameters: ** m -- the mailer to print ** ** Returns: ** none. */ void printmailer(m) register MAILER *m; { int j; printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=", m->m_mno, m->m_name, m->m_mailer, m->m_se_rwset, m->m_sh_rwset, m->m_re_rwset, m->m_rh_rwset, m->m_maxsize, (int) m->m_uid, (int) m->m_gid); for (j = '\0'; j <= '\177'; j++) if (bitnset(j, m->m_flags)) (void) putchar(j); printf(" L=%d E=", m->m_linelimit); xputs(m->m_eol); if (m->m_defcharset != NULL) printf(" C=%s", m->m_defcharset); printf(" T=%s/%s/%s", m->m_mtatype == NULL ? "" : m->m_mtatype, m->m_addrtype == NULL ? "" : m->m_addrtype, m->m_diagtype == NULL ? "" : m->m_diagtype); if (m->m_argv != NULL) { char **a = m->m_argv; printf(" A="); while (*a != NULL) { if (a != m->m_argv) printf(" "); xputs(*a++); } } printf("\n"); } /* ** SETOPTION -- set global processing option ** ** Parameters: ** opt -- option name. ** val -- option value (as a text string). ** safe -- set if this came from a configuration file. ** Some options (if set from the command line) will ** reset the user id to avoid security problems. ** sticky -- if set, don't let other setoptions override ** this value. ** e -- the main envelope. ** ** Returns: ** none. ** ** Side Effects: ** Sets options as implied by the arguments. */ static BITMAP StickyOpt; /* set if option is stuck */ extern void settimeout __P((char *, char *)); #if NAMED_BIND struct resolverflags { char *rf_name; /* name of the flag */ long rf_bits; /* bits to set/clear */ } ResolverFlags[] = { { "debug", RES_DEBUG }, { "aaonly", RES_AAONLY }, { "usevc", RES_USEVC }, { "primary", RES_PRIMARY }, { "igntc", RES_IGNTC }, { "recurse", RES_RECURSE }, { "defnames", RES_DEFNAMES }, { "stayopen", RES_STAYOPEN }, { "dnsrch", RES_DNSRCH }, { "true", 0 }, /* avoid error on old syntax */ { NULL, 0 } }; #endif struct optioninfo { char *o_name; /* long name of option */ u_char o_code; /* short name of option */ bool o_safe; /* safe for random people to use */ } OptionTab[] = { { "SevenBitInput", '7', TRUE }, #if MIME8TO7 { "EightBitMode", '8', TRUE }, #endif { "AliasFile", 'A', FALSE }, { "AliasWait", 'a', FALSE }, { "BlankSub", 'B', FALSE }, { "MinFreeBlocks", 'b', TRUE }, { "CheckpointInterval", 'C', TRUE }, { "HoldExpensive", 'c', FALSE }, { "AutoRebuildAliases", 'D', FALSE }, { "DeliveryMode", 'd', TRUE }, { "ErrorHeader", 'E', FALSE }, { "ErrorMode", 'e', TRUE }, { "TempFileMode", 'F', FALSE }, { "SaveFromLine", 'f', FALSE }, { "MatchGECOS", 'G', FALSE }, { "HelpFile", 'H', FALSE }, { "MaxHopCount", 'h', FALSE }, { "ResolverOptions", 'I', FALSE }, { "IgnoreDots", 'i', TRUE }, { "ForwardPath", 'J', FALSE }, { "SendMimeErrors", 'j', TRUE }, { "ConnectionCacheSize", 'k', FALSE }, { "ConnectionCacheTimeout", 'K', FALSE }, { "UseErrorsTo", 'l', FALSE }, { "LogLevel", 'L', TRUE }, { "MeToo", 'm', TRUE }, { "CheckAliases", 'n', FALSE }, { "OldStyleHeaders", 'o', TRUE }, { "DaemonPortOptions", 'O', FALSE }, { "PrivacyOptions", 'p', TRUE }, { "PostmasterCopy", 'P', FALSE }, { "QueueFactor", 'q', FALSE }, { "QueueDirectory", 'Q', FALSE }, { "DontPruneRoutes", 'R', FALSE }, { "Timeout", 'r', FALSE }, { "StatusFile", 'S', FALSE }, { "SuperSafe", 's', TRUE }, { "QueueTimeout", 'T', FALSE }, { "TimeZoneSpec", 't', FALSE }, { "UserDatabaseSpec", 'U', FALSE }, { "DefaultUser", 'u', FALSE }, { "FallbackMXhost", 'V', FALSE }, { "Verbose", 'v', TRUE }, { "TryNullMXList", 'w', FALSE }, { "QueueLA", 'x', FALSE }, { "RefuseLA", 'X', FALSE }, { "RecipientFactor", 'y', FALSE }, { "ForkEachJob", 'Y', FALSE }, { "ClassFactor", 'z', FALSE }, { "RetryFactor", 'Z', FALSE }, #define O_QUEUESORTORD 0x81 { "QueueSortOrder", O_QUEUESORTORD, TRUE }, #define O_HOSTSFILE 0x82 { "HostsFile", O_HOSTSFILE, FALSE }, #define O_MQA 0x83 { "MinQueueAge", O_MQA, TRUE }, #define O_DEFCHARSET 0x85 { "DefaultCharSet", O_DEFCHARSET, TRUE }, #define O_SSFILE 0x86 { "ServiceSwitchFile", O_SSFILE, FALSE }, #define O_DIALDELAY 0x87 { "DialDelay", O_DIALDELAY, TRUE }, #define O_NORCPTACTION 0x88 { "NoRecipientAction", O_NORCPTACTION, TRUE }, #define O_SAFEFILEENV 0x89 { "SafeFileEnvironment", O_SAFEFILEENV, FALSE }, #define O_MAXMSGSIZE 0x8a { "MaxMessageSize", O_MAXMSGSIZE, FALSE }, #define O_COLONOKINADDR 0x8b { "ColonOkInAddr", O_COLONOKINADDR, TRUE }, #define O_MAXQUEUERUN 0x8c { "MaxQueueRunSize", O_MAXQUEUERUN, TRUE }, #define O_MAXCHILDREN 0x8d { "MaxDaemonChildren", O_MAXCHILDREN, FALSE }, #define O_KEEPCNAMES 0x8e { "DontExpandCnames", O_KEEPCNAMES, FALSE }, #define O_MUSTQUOTE 0x8f { "MustQuoteChars", O_MUSTQUOTE, FALSE }, #define O_SMTPGREETING 0x90 { "SmtpGreetingMessage", O_SMTPGREETING, FALSE }, #define O_UNIXFROM 0x91 { "UnixFromLine", O_UNIXFROM, FALSE }, #define O_OPCHARS 0x92 { "OperatorChars", O_OPCHARS, FALSE }, #define O_DONTINITGRPS 0x93 { "DontInitGroups", O_DONTINITGRPS, FALSE }, #define O_SLFH 0x94 { "SingleLineFromHeader", O_SLFH, TRUE }, #define O_ABH 0x95 { "AllowBogusHELO", O_ABH, TRUE }, #define O_CONNTHROT 0x97 { "ConnectionRateThrottle", O_CONNTHROT, FALSE }, #define O_UGW 0x99 { "UnsafeGroupWrites", O_UGW, FALSE }, #define O_DBLBOUNCE 0x9a { "DoubleBounceAddress", O_DBLBOUNCE, FALSE }, #define O_HSDIR 0x9b { "HostStatusDirectory", O_HSDIR, FALSE }, #define O_SINGTHREAD 0x9c { "SingleThreadDelivery", O_SINGTHREAD, FALSE }, #define O_RUNASUSER 0x9d { "RunAsUser", O_RUNASUSER, FALSE }, -#ifdef _FFR_DSN_RRT +#if _FFR_DSN_RRT_OPTION #define O_DSN_RRT 0x9e { "RrtImpliesDsn", O_DSN_RRT, FALSE }, #endif -#ifdef _FFR_PIDFILE_OPT +#if _FFR_PIDFILE_OPTION #define O_PIDFILE 0x9f { "PidFile", O_PIDFILE, FALSE }, #endif +#if _FFR_WRITABLE_DIRECTORIES_ARE_FATAL_OPTION +#define O_WDAF 0xa0 + { "WritableDirectoriesAreFatal", O_WDAF, FALSE }, +#endif +#if _FFR_CHOWN_IS_ALWAYS_SAFE_OPTION +#define O_CIAS 0xa1 + { "ChownIsAlwaysSafe", O_CIAS, FALSE }, +#endif +#if _FFR_DONT_PROBE_INTERFACES_OPTION +#define O_DPI 0xa2 + { "DontProbeInterfaces", O_DPI, FALSE }, +#endif +#if _FFR_MAXRCPT_OPTION +#define O_MAXRCPT 0xa3 + { "MaxRecipientPerMessage", O_MAXRCPT, FALSE }, +#endif { NULL, '\0', FALSE } }; void setoption(opt, val, safe, sticky, e) int opt; char *val; bool safe; bool sticky; register ENVELOPE *e; { register char *p; register struct optioninfo *o; char *subopt; int mid; auto char *ep; char buf[50]; extern bool atobool(); extern time_t convtime(); extern int QueueLA; extern int RefuseLA; extern bool Warn_Q_option; extern void setalias __P((char *)); extern int atooct __P((char *)); extern void setdefuser __P((void)); extern void setdaemonoptions __P((char *)); errno = 0; if (opt == ' ') { /* full word options */ struct optioninfo *sel; p = strchr(val, '='); if (p == NULL) p = &val[strlen(val)]; while (*--p == ' ') continue; while (*++p == ' ') *p = '\0'; if (p == val) { syserr("readcf: null option name"); return; } if (*p == '=') *p++ = '\0'; while (*p == ' ') p++; subopt = strchr(val, '.'); if (subopt != NULL) *subopt++ = '\0'; sel = NULL; for (o = OptionTab; o->o_name != NULL; o++) { if (strncasecmp(o->o_name, val, strlen(val)) != 0) continue; if (strlen(o->o_name) == strlen(val)) { /* completely specified -- this must be it */ sel = NULL; break; } if (sel != NULL) break; sel = o; } if (sel != NULL && o->o_name == NULL) o = sel; else if (o->o_name == NULL) { syserr("readcf: unknown option name %s", val); return; } else if (sel != NULL) { syserr("readcf: ambiguous option name %s (matches %s and %s)", val, sel->o_name, o->o_name); return; } if (strlen(val) != strlen(o->o_name)) { - bool oldVerbose = Verbose; + int oldVerbose = Verbose; - Verbose = TRUE; + Verbose = 1; message("Option %s used as abbreviation for %s", val, o->o_name); Verbose = oldVerbose; } opt = o->o_code; val = p; } else { for (o = OptionTab; o->o_name != NULL; o++) { if (o->o_code == opt) break; } subopt = NULL; } if (tTd(37, 1)) { printf(isascii(opt) && isprint(opt) ? "setoption %s (%c).%s=" : "setoption %s (0x%x).%s=", o->o_name == NULL ? "" : o->o_name, opt, subopt == NULL ? "" : subopt); xputs(val); } /* ** See if this option is preset for us. */ if (!sticky && bitnset(opt, StickyOpt)) { if (tTd(37, 1)) printf(" (ignored)\n"); return; } /* ** Check to see if this option can be specified by this user. */ if (!safe && RealUid == 0) safe = TRUE; if (!safe && !o->o_safe) { if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) { if (tTd(37, 1)) printf(" (unsafe)"); if (RealUid != geteuid()) { if (tTd(37, 1)) printf("(Resetting uid)"); endpwent(); (void) setgid(RealGid); (void) setuid(RealUid); } } } if (tTd(37, 1)) printf("\n"); switch (opt & 0xff) { case '7': /* force seven-bit input */ SevenBitInput = atobool(val); break; #if MIME8TO7 case '8': /* handling of 8-bit input */ switch (*val) { case 'm': /* convert 8-bit, convert MIME */ MimeMode = MM_CVTMIME|MM_MIME8BIT; break; case 'p': /* pass 8 bit, convert MIME */ MimeMode = MM_CVTMIME|MM_PASS8BIT; break; case 's': /* strict adherence */ MimeMode = MM_CVTMIME; break; #if 0 case 'r': /* reject 8-bit, don't convert MIME */ MimeMode = 0; break; case 'j': /* "just send 8" */ MimeMode = MM_PASS8BIT; break; case 'a': /* encode 8 bit if available */ MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; break; case 'c': /* convert 8 bit to MIME, never 7 bit */ MimeMode = MM_MIME8BIT; break; #endif default: syserr("Unknown 8-bit mode %c", *val); exit(EX_USAGE); } break; #endif case 'A': /* set default alias file */ if (val[0] == '\0') setalias("aliases"); else setalias(val); break; case 'a': /* look N minutes for "@:@" in alias file */ if (val[0] == '\0') SafeAlias = 5 * 60; /* five minutes */ else SafeAlias = convtime(val, 'm'); break; case 'B': /* substitution for blank character */ SpaceSub = val[0]; if (SpaceSub == '\0') SpaceSub = ' '; break; case 'b': /* min blocks free on queue fs/max msg size */ p = strchr(val, '/'); if (p != NULL) { *p++ = '\0'; MaxMessageSize = atol(p); } MinBlocksFree = atol(val); break; case 'c': /* don't connect to "expensive" mailers */ NoConnect = atobool(val); break; case 'C': /* checkpoint every N addresses */ CheckpointInterval = atoi(val); break; case 'd': /* delivery mode */ switch (*val) { case '\0': e->e_sendmode = SM_DELIVER; break; case SM_QUEUE: /* queue only */ case SM_DEFER: /* queue only and defer map lookups */ #if !QUEUE syserr("need QUEUE to set -odqueue or -oddefer"); #endif /* QUEUE */ /* fall through..... */ case SM_DELIVER: /* do everything */ case SM_FORK: /* fork after verification */ e->e_sendmode = *val; break; default: syserr("Unknown delivery mode %c", *val); exit(EX_USAGE); } break; case 'D': /* rebuild alias database as needed */ AutoRebuild = atobool(val); break; case 'E': /* error message header/header file */ if (*val != '\0') ErrMsgFile = newstr(val); break; case 'e': /* set error processing mode */ switch (*val) { case EM_QUIET: /* be silent about it */ case EM_MAIL: /* mail back */ case EM_BERKNET: /* do berknet error processing */ case EM_WRITE: /* write back (or mail) */ case EM_PRINT: /* print errors normally (default) */ e->e_errormode = *val; break; } break; case 'F': /* file mode */ FileMode = atooct(val) & 0777; break; case 'f': /* save Unix-style From lines on front */ SaveFrom = atobool(val); break; case 'G': /* match recipients against GECOS field */ MatchGecos = atobool(val); break; case 'g': /* default gid */ g_opt: if (isascii(*val) && isdigit(*val)) DefGid = atoi(val); else { register struct group *gr; DefGid = -1; gr = getgrnam(val); if (gr == NULL) syserr("readcf: option %c: unknown group %s", opt, val); else DefGid = gr->gr_gid; } break; case 'H': /* help file */ if (val[0] == '\0') HelpFile = "sendmail.hf"; else HelpFile = newstr(val); break; case 'h': /* maximum hop count */ MaxHopCount = atoi(val); break; case 'I': /* use internet domain name server */ #if NAMED_BIND for (p = val; *p != 0; ) { bool clearmode; char *q; struct resolverflags *rfp; while (*p == ' ') p++; if (*p == '\0') break; clearmode = FALSE; if (*p == '-') clearmode = TRUE; else if (*p != '+') p--; p++; q = p; while (*p != '\0' && !(isascii(*p) && isspace(*p))) p++; if (*p != '\0') *p++ = '\0'; if (strcasecmp(q, "HasWildcardMX") == 0) { HasWildcardMX = !clearmode; continue; } for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) { if (strcasecmp(q, rfp->rf_name) == 0) break; } if (rfp->rf_name == NULL) syserr("readcf: I option value %s unrecognized", q); else if (clearmode) _res.options &= ~rfp->rf_bits; else _res.options |= rfp->rf_bits; } if (tTd(8, 2)) printf("_res.options = %x, HasWildcardMX = %d\n", (u_int) _res.options, HasWildcardMX); #else usrerr("name server (I option) specified but BIND not compiled in"); #endif break; case 'i': /* ignore dot lines in message */ IgnrDot = atobool(val); break; case 'j': /* send errors in MIME (RFC 1341) format */ SendMIMEErrors = atobool(val); break; case 'J': /* .forward search path */ ForwardPath = newstr(val); break; case 'k': /* connection cache size */ MaxMciCache = atoi(val); if (MaxMciCache < 0) MaxMciCache = 0; break; case 'K': /* connection cache timeout */ MciCacheTimeout = convtime(val, 'm'); break; case 'l': /* use Errors-To: header */ UseErrorsTo = atobool(val); break; case 'L': /* log level */ if (safe || LogLevel < atoi(val)) LogLevel = atoi(val); break; case 'M': /* define macro */ mid = macid(val, &ep); p = newstr(ep); if (!safe) cleanstrcpy(p, p, MAXNAME); define(mid, p, CurEnv); sticky = FALSE; break; case 'm': /* send to me too */ MeToo = atobool(val); break; case 'n': /* validate RHS in newaliases */ CheckAliases = atobool(val); break; /* 'N' available -- was "net name" */ case 'O': /* daemon options */ #if DAEMON setdaemonoptions(val); #else syserr("DaemonPortOptions (O option) set but DAEMON not compiled in"); #endif break; case 'o': /* assume old style headers */ if (atobool(val)) CurEnv->e_flags |= EF_OLDSTYLE; else CurEnv->e_flags &= ~EF_OLDSTYLE; break; case 'p': /* select privacy level */ p = val; for (;;) { register struct prival *pv; extern struct prival PrivacyValues[]; while (isascii(*p) && (isspace(*p) || ispunct(*p))) p++; if (*p == '\0') break; val = p; while (isascii(*p) && isalnum(*p)) p++; if (*p != '\0') *p++ = '\0'; for (pv = PrivacyValues; pv->pv_name != NULL; pv++) { if (strcasecmp(val, pv->pv_name) == 0) break; } if (pv->pv_name == NULL) syserr("readcf: Op line: %s unrecognized", val); PrivacyFlags |= pv->pv_flag; } sticky = FALSE; break; case 'P': /* postmaster copy address for returned mail */ PostMasterCopy = newstr(val); break; case 'q': /* slope of queue only function */ QueueFactor = atoi(val); break; case 'Q': /* queue directory */ if (val[0] == '\0') QueueDir = "mqueue"; else QueueDir = newstr(val); if (RealUid != 0 && !safe) Warn_Q_option = TRUE; break; case 'R': /* don't prune routes */ DontPruneRoutes = atobool(val); break; case 'r': /* read timeout */ if (subopt == NULL) inittimeouts(val); else settimeout(subopt, val); break; case 'S': /* status file */ if (val[0] == '\0') StatFile = "sendmail.st"; else StatFile = newstr(val); break; case 's': /* be super safe, even if expensive */ SuperSafe = atobool(val); break; case 'T': /* queue timeout */ p = strchr(val, '/'); if (p != NULL) { *p++ = '\0'; settimeout("queuewarn", p); } settimeout("queuereturn", val); break; case 't': /* time zone name */ TimeZoneSpec = newstr(val); break; case 'U': /* location of user database */ UdbSpec = newstr(val); break; case 'u': /* set default uid */ for (p = val; *p != '\0'; p++) { if (*p == '.' || *p == '/' || *p == ':') { *p++ = '\0'; break; } } if (isascii(*val) && isdigit(*val)) DefUid = atoi(val); else { register struct passwd *pw; DefUid = -1; pw = sm_getpwnam(val); if (pw == NULL) syserr("readcf: option u: unknown user %s", val); else { DefUid = pw->pw_uid; DefGid = pw->pw_gid; } } #ifdef UID_MAX if (DefUid > UID_MAX) { syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored", DefUid, UID_MAX); } #endif setdefuser(); /* handle the group if it is there */ if (*p == '\0') break; val = p; goto g_opt; case 'V': /* fallback MX host */ if (val[0] != '\0') FallBackMX = newstr(val); break; case 'v': /* run in verbose mode */ - Verbose = atobool(val); + Verbose = atobool(val) ? 1 : 0; break; case 'w': /* if we are best MX, try host directly */ TryNullMXList = atobool(val); break; /* 'W' available -- was wizard password */ case 'x': /* load avg at which to auto-queue msgs */ QueueLA = atoi(val); break; case 'X': /* load avg at which to auto-reject connections */ RefuseLA = atoi(val); break; case 'y': /* work recipient factor */ WkRecipFact = atoi(val); break; case 'Y': /* fork jobs during queue runs */ ForkQueueRuns = atobool(val); break; case 'z': /* work message class factor */ WkClassFact = atoi(val); break; case 'Z': /* work time factor */ WkTimeFact = atoi(val); break; case O_QUEUESORTORD: /* queue sorting order */ switch (*val) { case 'h': /* Host first */ case 'H': QueueSortOrder = QS_BYHOST; break; case 'p': /* Priority order */ case 'P': QueueSortOrder = QS_BYPRIORITY; break; case 't': /* Submission time */ case 'T': QueueSortOrder = QS_BYTIME; break; default: syserr("Invalid queue sort order \"%s\"", val); } break; case O_HOSTSFILE: /* pathname of /etc/hosts file */ HostsFile = newstr(val); break; case O_MQA: /* minimum queue age between deliveries */ MinQueueAge = convtime(val, 'm'); break; case O_DEFCHARSET: /* default character set for mimefying */ DefaultCharSet = newstr(denlstring(val, TRUE, TRUE)); break; case O_SSFILE: /* service switch file */ ServiceSwitchFile = newstr(val); break; case O_DIALDELAY: /* delay for dial-on-demand operation */ DialDelay = convtime(val, 's'); break; case O_NORCPTACTION: /* what to do if no recipient */ if (strcasecmp(val, "none") == 0) NoRecipientAction = NRA_NO_ACTION; else if (strcasecmp(val, "add-to") == 0) NoRecipientAction = NRA_ADD_TO; else if (strcasecmp(val, "add-apparently-to") == 0) NoRecipientAction = NRA_ADD_APPARENTLY_TO; else if (strcasecmp(val, "add-bcc") == 0) NoRecipientAction = NRA_ADD_BCC; else if (strcasecmp(val, "add-to-undisclosed") == 0) NoRecipientAction = NRA_ADD_TO_UNDISCLOSED; else syserr("Invalid NoRecipientAction: %s", val); break; case O_SAFEFILEENV: /* chroot() environ for writing to files */ SafeFileEnv = newstr(val); break; case O_MAXMSGSIZE: /* maximum message size */ MaxMessageSize = atol(val); break; case O_COLONOKINADDR: /* old style handling of colon addresses */ ColonOkInAddr = atobool(val); break; case O_MAXQUEUERUN: /* max # of jobs in a single queue run */ MaxQueueRun = atol(val); break; case O_MAXCHILDREN: /* max # of children of daemon */ MaxChildren = atoi(val); break; case O_KEEPCNAMES: /* don't expand CNAME records */ DontExpandCnames = atobool(val); break; case O_MUSTQUOTE: /* must quote these characters in phrases */ strcpy(buf, "@,;:\\()[]"); if (strlen(val) < (SIZE_T) sizeof buf - 10) strcat(buf, val); MustQuoteChars = newstr(buf); break; case O_SMTPGREETING: /* SMTP greeting message (old $e macro) */ SmtpGreeting = newstr(munchstring(val, NULL, '\0')); break; case O_UNIXFROM: /* UNIX From_ line (old $l macro) */ UnixFromLine = newstr(munchstring(val, NULL, '\0')); break; case O_OPCHARS: /* operator characters (old $o macro) */ OperatorChars = newstr(munchstring(val, NULL, '\0')); break; case O_DONTINITGRPS: /* don't call initgroups(3) */ DontInitGroups = atobool(val); break; case O_SLFH: /* make sure from fits on one line */ SingleLineFromHeader = atobool(val); break; case O_ABH: /* allow HELO commands with syntax errors */ AllowBogusHELO = atobool(val); break; case O_CONNTHROT: /* connection rate throttle */ ConnRateThrottle = atoi(val); break; case O_UGW: /* group writable files are unsafe */ UnsafeGroupWrites = atobool(val); break; case O_DBLBOUNCE: /* address to which to send double bounces */ if (val[0] != '\0') DoubleBounceAddr = newstr(val); else syserr("readcf: option DoubleBounceAddress: value required"); break; case O_HSDIR: /* persistent host status directory */ if (val[0] != '\0') HostStatDir = newstr(val); break; case O_SINGTHREAD: /* single thread deliveries (requires hsdir) */ SingleThreadDelivery = atobool(val); break; case O_RUNASUSER: /* run bulk of code as this user */ for (p = val; *p != '\0'; p++) { if (*p == '.' || *p == '/' || *p == ':') { *p++ = '\0'; break; } } if (isascii(*val) && isdigit(*val)) RunAsUid = atoi(val); else { register struct passwd *pw; pw = sm_getpwnam(val); if (pw == NULL) syserr("readcf: option RunAsUser: unknown user %s", val); else { + if (*p == '\0') + RunAsUserName = newstr(val); RunAsUid = pw->pw_uid; RunAsGid = pw->pw_gid; } } if (*p == '\0') break; if (isascii(*p) && isdigit(*p)) - DefGid = atoi(p); + RunAsGid = atoi(p); else { register struct group *gr; gr = getgrnam(p); if (gr == NULL) syserr("readcf: option RunAsUser: unknown group %s", p); else RunAsGid = gr->gr_gid; } break; -#ifdef _FFR_DSN_RRT +#if _FFR_DSN_RRT_OPTION case O_DSN_RRT: - RrtImpliesDsn = atobool(p); + RrtImpliesDsn = atobool(val); break; #endif -#ifdef _FFR_PIDFILE_OPT +#if _FFR_PIDFILE_OPTION case O_PIDFILE: free(PidFile); - PidFile = newstr(p); + PidFile = newstr(val); + break; +#endif + +#if _FFR_WRITABLE_DIRECTORIES_ARE_FATAL_OPTION + case O_WDAF: + FatalWritableDirs = atobool(val); + break; +#endif + +#if _FFR_CHOWN_IS_ALWAYS_SAFE_OPTION + case O_CIAS: + ChownIsAlwaysSafe = atobool(val); + break; +#endif + +#if _FFR_DONT_PROBE_INTERFACES_OPTION + case O_DPI: + DontProbeInterfaces = atobool(val); + break; +#endif + +#if _FFR_MAXRCPT_OPTION + case O_MAXRCPT: + MaxRcptPerMsg = atoi(val); break; #endif default: if (tTd(37, 1)) { if (isascii(opt) && isprint(opt)) printf("Warning: option %c unknown\n", opt); else printf("Warning: option 0x%x unknown\n", opt); } break; } if (sticky) setbitn(opt, StickyOpt); } /* ** SETCLASS -- set a string into a class ** ** Parameters: ** class -- the class to put the string in. ** str -- the string to enter ** ** Returns: ** none. ** ** Side Effects: ** puts the word into the symbol table. */ void setclass(class, str) int class; char *str; { register STAB *s; if (tTd(37, 8)) printf("setclass(%s, %s)\n", macname(class), str); s = stab(str, ST_CLASS, ST_ENTER); setbitn(class, s->s_class); } /* ** MAKEMAPENTRY -- create a map entry ** ** Parameters: ** line -- the config file line ** ** Returns: ** A pointer to the map that has been created. ** NULL if there was a syntax error. ** ** Side Effects: ** Enters the map into the dictionary. */ MAP * makemapentry(line) char *line; { register char *p; char *mapname; char *classname; register STAB *s; STAB *class; for (p = line; isascii(*p) && isspace(*p); p++) continue; if (!(isascii(*p) && isalnum(*p))) { syserr("readcf: config K line: no map name"); return NULL; } mapname = p; while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.') continue; if (*p != '\0') *p++ = '\0'; while (isascii(*p) && isspace(*p)) p++; if (!(isascii(*p) && isalnum(*p))) { syserr("readcf: config K line, map %s: no map class", mapname); return NULL; } classname = p; while (isascii(*++p) && isalnum(*p)) continue; if (*p != '\0') *p++ = '\0'; while (isascii(*p) && isspace(*p)) p++; /* look up the class */ class = stab(classname, ST_MAPCLASS, ST_FIND); if (class == NULL) { syserr("readcf: map %s: class %s not available", mapname, classname); return NULL; } /* enter the map */ s = stab(mapname, ST_MAP, ST_ENTER); s->s_map.map_class = &class->s_mapclass; s->s_map.map_mname = newstr(mapname); if (class->s_mapclass.map_parse(&s->s_map, p)) s->s_map.map_mflags |= MF_VALID; if (tTd(37, 5)) { printf("map %s, class %s, flags %lx, file %s,\n", s->s_map.map_mname, s->s_map.map_class->map_cname, s->s_map.map_mflags, s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); printf("\tapp %s, domain %s, rebuild %s\n", s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); } return &s->s_map; } /* ** STRTORWSET -- convert string to rewriting set number ** ** Parameters: ** p -- the pointer to the string to decode. ** endp -- if set, store the trailing delimiter here. ** stabmode -- ST_ENTER to create this entry, ST_FIND if ** it must already exist. ** ** Returns: ** The appropriate ruleset number. ** -1 if it is not valid (error already printed) */ int strtorwset(p, endp, stabmode) char *p; char **endp; int stabmode; { int ruleset; static int nextruleset = MAXRWSETS; while (isascii(*p) && isspace(*p)) p++; if (!isascii(*p)) { syserr("invalid ruleset name: \"%.20s\"", p); return -1; } if (isdigit(*p)) { ruleset = strtol(p, endp, 10); if (ruleset >= MAXRWSETS / 2 || ruleset < 0) { syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS / 2); ruleset = -1; } } else { STAB *s; char delim; char *q; q = p; while (*p != '\0' && isascii(*p) && (isalnum(*p) || *p == '_')) p++; if (q == p || !isalpha(*q)) { /* no valid characters */ syserr("invalid ruleset name: \"%.20s\"", q); return -1; } while (isascii(*p) && isspace(*p)) *p++ = '\0'; delim = *p; if (delim != '\0') *p = '\0'; s = stab(q, ST_RULESET, stabmode); if (delim != '\0') *p = delim; if (s == NULL) return -1; if (stabmode == ST_ENTER && delim == '=') { while (isascii(*++p) && isspace(*p)) continue; if (!isdigit(*p)) { syserr("bad ruleset definition \"%s\" (number required after `=')", q); ruleset = -1; } else { ruleset = strtol(p, endp, 10); if (ruleset >= MAXRWSETS / 2 || ruleset < 0) { syserr("bad ruleset number %d in \"%s\" (%d max)", ruleset, q, MAXRWSETS / 2); ruleset = -1; } } } else { if (endp != NULL) *endp = p; if (s->s_ruleset > 0) ruleset = s->s_ruleset; else if ((ruleset = --nextruleset) < MAXRWSETS / 2) { syserr("%s: too many named rulesets (%d max)", q, MAXRWSETS / 2); ruleset = -1; } } if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset) { syserr("%s: ruleset changed value (old %d, new %d)", q, s->s_ruleset, ruleset); ruleset = s->s_ruleset; } else if (ruleset > 0) { s->s_ruleset = ruleset; } } return ruleset; } /* ** INITTIMEOUTS -- parse and set timeout values ** ** Parameters: ** val -- a pointer to the values. If NULL, do initial ** settings. ** ** Returns: ** none. ** ** Side Effects: ** Initializes the TimeOuts structure */ #define SECONDS #define MINUTES * 60 #define HOUR * 3600 void inittimeouts(val) register char *val; { register char *p; extern time_t convtime(); if (tTd(37, 2)) printf("inittimeouts(%s)\n", val == NULL ? "" : val); if (val == NULL) { TimeOuts.to_connect = (time_t) 0 SECONDS; TimeOuts.to_initial = (time_t) 5 MINUTES; TimeOuts.to_helo = (time_t) 5 MINUTES; TimeOuts.to_mail = (time_t) 10 MINUTES; TimeOuts.to_rcpt = (time_t) 1 HOUR; TimeOuts.to_datainit = (time_t) 5 MINUTES; TimeOuts.to_datablock = (time_t) 1 HOUR; TimeOuts.to_datafinal = (time_t) 1 HOUR; TimeOuts.to_rset = (time_t) 5 MINUTES; TimeOuts.to_quit = (time_t) 2 MINUTES; TimeOuts.to_nextcommand = (time_t) 1 HOUR; TimeOuts.to_miscshort = (time_t) 2 MINUTES; #if IDENTPROTO TimeOuts.to_ident = (time_t) 30 SECONDS; #else TimeOuts.to_ident = (time_t) 0 SECONDS; #endif TimeOuts.to_fileopen = (time_t) 60 SECONDS; if (tTd(37, 5)) { printf("Timeouts:\n"); printf(" connect = %ld\n", TimeOuts.to_connect); printf(" initial = %ld\n", TimeOuts.to_initial); printf(" helo = %ld\n", TimeOuts.to_helo); printf(" mail = %ld\n", TimeOuts.to_mail); printf(" rcpt = %ld\n", TimeOuts.to_rcpt); printf(" datainit = %ld\n", TimeOuts.to_datainit); printf(" datablock = %ld\n", TimeOuts.to_datablock); printf(" datafinal = %ld\n", TimeOuts.to_datafinal); printf(" rset = %ld\n", TimeOuts.to_rset); printf(" quit = %ld\n", TimeOuts.to_quit); printf(" nextcommand = %ld\n", TimeOuts.to_nextcommand); printf(" miscshort = %ld\n", TimeOuts.to_miscshort); printf(" ident = %ld\n", TimeOuts.to_ident); printf(" fileopen = %ld\n", TimeOuts.to_fileopen); } return; } for (;; val = p) { while (isascii(*val) && isspace(*val)) val++; if (*val == '\0') break; for (p = val; *p != '\0' && *p != ','; p++) continue; if (*p != '\0') *p++ = '\0'; if (isascii(*val) && isdigit(*val)) { /* old syntax -- set everything */ TimeOuts.to_mail = convtime(val, 'm'); TimeOuts.to_rcpt = TimeOuts.to_mail; TimeOuts.to_datainit = TimeOuts.to_mail; TimeOuts.to_datablock = TimeOuts.to_mail; TimeOuts.to_datafinal = TimeOuts.to_mail; TimeOuts.to_nextcommand = TimeOuts.to_mail; continue; } else { register char *q = strchr(val, ':'); if (q == NULL && (q = strchr(val, '=')) == NULL) { /* syntax error */ continue; } *q++ = '\0'; settimeout(val, q); } } } /* ** SETTIMEOUT -- set an individual timeout ** ** Parameters: ** name -- the name of the timeout. ** val -- the value of the timeout. ** ** Returns: ** none. */ void settimeout(name, val) char *name; char *val; { register char *p; time_t to; extern time_t convtime(); if (tTd(37, 2)) printf("settimeout(%s = %s)\n", name, val); to = convtime(val, 'm'); p = strchr(name, '.'); if (p != NULL) *p++ = '\0'; if (strcasecmp(name, "initial") == 0) TimeOuts.to_initial = to; else if (strcasecmp(name, "mail") == 0) TimeOuts.to_mail = to; else if (strcasecmp(name, "rcpt") == 0) TimeOuts.to_rcpt = to; else if (strcasecmp(name, "datainit") == 0) TimeOuts.to_datainit = to; else if (strcasecmp(name, "datablock") == 0) TimeOuts.to_datablock = to; else if (strcasecmp(name, "datafinal") == 0) TimeOuts.to_datafinal = to; else if (strcasecmp(name, "command") == 0) TimeOuts.to_nextcommand = to; else if (strcasecmp(name, "rset") == 0) TimeOuts.to_rset = to; else if (strcasecmp(name, "helo") == 0) TimeOuts.to_helo = to; else if (strcasecmp(name, "quit") == 0) TimeOuts.to_quit = to; else if (strcasecmp(name, "misc") == 0) TimeOuts.to_miscshort = to; else if (strcasecmp(name, "ident") == 0) TimeOuts.to_ident = to; else if (strcasecmp(name, "fileopen") == 0) TimeOuts.to_fileopen = to; else if (strcasecmp(name, "connect") == 0) TimeOuts.to_connect = to; else if (strcasecmp(name, "iconnect") == 0) TimeOuts.to_iconnect = to; else if (strcasecmp(name, "queuewarn") == 0) { to = convtime(val, 'h'); if (p == NULL || strcmp(p, "*") == 0) { TimeOuts.to_q_warning[TOC_NORMAL] = to; TimeOuts.to_q_warning[TOC_URGENT] = to; TimeOuts.to_q_warning[TOC_NONURGENT] = to; } else if (strcasecmp(p, "normal") == 0) TimeOuts.to_q_warning[TOC_NORMAL] = to; else if (strcasecmp(p, "urgent") == 0) TimeOuts.to_q_warning[TOC_URGENT] = to; else if (strcasecmp(p, "non-urgent") == 0) TimeOuts.to_q_warning[TOC_NONURGENT] = to; else syserr("settimeout: invalid queuewarn subtimeout %s", p); } else if (strcasecmp(name, "queuereturn") == 0) { to = convtime(val, 'd'); if (p == NULL || strcmp(p, "*") == 0) { TimeOuts.to_q_return[TOC_NORMAL] = to; TimeOuts.to_q_return[TOC_URGENT] = to; TimeOuts.to_q_return[TOC_NONURGENT] = to; } else if (strcasecmp(p, "normal") == 0) TimeOuts.to_q_return[TOC_NORMAL] = to; else if (strcasecmp(p, "urgent") == 0) TimeOuts.to_q_return[TOC_URGENT] = to; else if (strcasecmp(p, "non-urgent") == 0) TimeOuts.to_q_return[TOC_NONURGENT] = to; else syserr("settimeout: invalid queuereturn subtimeout %s", p); } else if (strcasecmp(name, "hoststatus") == 0) MciInfoTimeout = convtime(val, 'm'); else syserr("settimeout: invalid timeout %s", name); } Index: head/usr.sbin/sendmail/src/recipient.c =================================================================== --- head/usr.sbin/sendmail/src/recipient.c (revision 26988) +++ head/usr.sbin/sendmail/src/recipient.c (revision 26989) @@ -1,1360 +1,1378 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)recipient.c 8.118 (Berkeley) 12/1/96"; +static char sccsid[] = "@(#)recipient.c 8.130 (Berkeley) 5/29/97"; #endif /* not lint */ # include "sendmail.h" /* ** SENDTOLIST -- Designate a send list. ** ** The parameter is a comma-separated list of people to send to. ** This routine arranges to send to all of them. ** ** Parameters: ** list -- the send list. ** ctladdr -- the address template for the person to ** send to -- effective uid/gid are important. ** This is typically the alias that caused this ** expansion. ** sendq -- a pointer to the head of a queue to put ** these people into. ** aliaslevel -- the current alias nesting depth -- to ** diagnose loops. ** e -- the envelope in which to add these recipients. ** ** Returns: ** The number of addresses actually on the list. ** ** Side Effects: ** none. */ /* q_flags bits inherited from ctladdr */ #define QINHERITEDBITS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY) int sendtolist(list, ctladdr, sendq, aliaslevel, e) char *list; ADDRESS *ctladdr; ADDRESS **sendq; int aliaslevel; register ENVELOPE *e; { register char *p; register ADDRESS *al; /* list of addresses to send to */ - bool firstone; /* set on first address sent */ char delimiter; /* the address delimiter */ int naddrs; int i; char *oldto = e->e_to; char *bufp; char buf[MAXNAME + 1]; if (list == NULL) { syserr("sendtolist: null list"); return 0; } if (tTd(25, 1)) { printf("sendto: %s\n ctladdr=", list); printaddr(ctladdr, FALSE); } /* heuristic to determine old versus new style addresses */ if (ctladdr == NULL && (strchr(list, ',') != NULL || strchr(list, ';') != NULL || strchr(list, '<') != NULL || strchr(list, '(') != NULL)) e->e_flags &= ~EF_OLDSTYLE; delimiter = ' '; if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL) delimiter = ','; - firstone = TRUE; al = NULL; naddrs = 0; /* make sure we have enough space to copy the string */ i = strlen(list) + 1; if (i <= sizeof buf) bufp = buf; else bufp = xalloc(i); strcpy(bufp, denlstring(list, FALSE, TRUE)); for (p = bufp; *p != '\0'; ) { auto char *delimptr; register ADDRESS *a; /* parse the address */ while ((isascii(*p) && isspace(*p)) || *p == ',') p++; a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e); p = delimptr; if (a == NULL) continue; a->q_next = al; a->q_alias = ctladdr; /* arrange to inherit attributes from parent */ if (ctladdr != NULL) { ADDRESS *b; extern ADDRESS *self_reference(); /* self reference test */ if (sameaddr(ctladdr, a)) { if (tTd(27, 5)) { printf("sendtolist: QSELFREF "); printaddr(ctladdr, FALSE); } ctladdr->q_flags |= QSELFREF; } /* check for address loops */ b = self_reference(a, e); if (b != NULL) { b->q_flags |= QSELFREF; if (tTd(27, 5)) { printf("sendtolist: QSELFREF "); printaddr(b, FALSE); } if (a != b) { if (tTd(27, 5)) { printf("sendtolist: QDONTSEND "); printaddr(a, FALSE); } a->q_flags |= QDONTSEND; b->q_flags |= a->q_flags & QNOTREMOTE; continue; } } /* full name */ if (a->q_fullname == NULL) a->q_fullname = ctladdr->q_fullname; /* various flag bits */ a->q_flags &= ~QINHERITEDBITS; a->q_flags |= ctladdr->q_flags & QINHERITEDBITS; /* original recipient information */ a->q_orcpt = ctladdr->q_orcpt; } al = a; - firstone = FALSE; } /* arrange to send to everyone on the local send list */ while (al != NULL) { register ADDRESS *a = al; al = a->q_next; a = recipient(a, sendq, aliaslevel, e); naddrs++; } e->e_to = oldto; if (bufp != buf) free(bufp); return (naddrs); } /* ** RECIPIENT -- Designate a message recipient ** ** Saves the named person for future mailing. ** ** Parameters: ** a -- the (preparsed) address header for the recipient. ** sendq -- a pointer to the head of a queue to put the ** recipient in. Duplicate supression is done ** in this queue. ** aliaslevel -- the current alias nesting depth. ** e -- the current envelope. ** ** Returns: ** The actual address in the queue. This will be "a" if ** the address is not a duplicate, else the original address. ** ** Side Effects: ** none. */ ADDRESS * recipient(a, sendq, aliaslevel, e) register ADDRESS *a; register ADDRESS **sendq; int aliaslevel; register ENVELOPE *e; { register ADDRESS *q; ADDRESS **pq; register struct mailer *m; register char *p; bool quoted = FALSE; /* set if the addr has a quote bit */ int findusercount = 0; bool initialdontsend = bitset(QDONTSEND, a->q_flags); int i; char *buf; char buf0[MAXNAME + 1]; /* unquoted image of the user name */ extern void alias __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); e->e_to = a->q_paddr; m = a->q_mailer; errno = 0; if (aliaslevel == 0) a->q_flags |= QPRIMARY; if (tTd(26, 1)) { printf("\nrecipient (%d): ", aliaslevel); printaddr(a, FALSE); } /* if this is primary, add it to the original recipient list */ if (a->q_alias == NULL) { if (e->e_origrcpt == NULL) e->e_origrcpt = a->q_paddr; else if (e->e_origrcpt != a->q_paddr) e->e_origrcpt = ""; } /* break aliasing loops */ if (aliaslevel > MaxAliasRecursion) { a->q_status = "5.4.6"; usrerr("554 aliasing/forwarding loop broken (%d aliases deep; %d max)", aliaslevel, MaxAliasRecursion); return (a); } /* ** Finish setting up address structure. */ /* get unquoted user for file, program or user.name check */ i = strlen(a->q_user); if (i >= sizeof buf0) buf = xalloc(i + 1); else buf = buf0; (void) strcpy(buf, a->q_user); for (p = buf; *p != '\0' && !quoted; p++) { if (*p == '\\') quoted = TRUE; } stripquotes(buf); /* check for direct mailing to restricted mailers */ if (m == ProgMailer) { if (a->q_alias == NULL) { a->q_flags |= QBADADDR; a->q_status = "5.7.1"; usrerr("550 Cannot mail directly to programs"); } else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) { a->q_flags |= QBADADDR; a->q_status = "5.7.1"; - usrerr("550 User %s@%s doesn't have a valid shell for mailing to programs", - a->q_alias->q_ruser, MyHostName); + if (a->q_alias->q_ruser == NULL) + usrerr("550 UID %d is an unknown user: cannot mail to programs", + a->q_alias->q_uid); + else + usrerr("550 User %s@%s doesn't have a valid shell for mailing to programs", + a->q_alias->q_ruser, MyHostName); } else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) { a->q_flags |= QBADADDR; a->q_status = "5.7.1"; usrerr("550 Address %s is unsafe for mailing to programs", a->q_alias->q_paddr); } } /* ** Look up this person in the recipient list. ** If they are there already, return, otherwise continue. ** If the list is empty, just add it. Notice the cute ** hack to make from addresses suppress things correctly: ** the QDONTSEND bit will be set in the send list. ** [Please note: the emphasis is on "hack."] */ for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) { if (sameaddr(q, a)) { if (tTd(26, 1)) { printf("%s in sendq: ", a->q_paddr); printaddr(q, FALSE); } if (!bitset(QPRIMARY, q->q_flags)) { if (!bitset(QDONTSEND, a->q_flags)) message("duplicate suppressed"); q->q_flags |= a->q_flags; } else if (bitset(QSELFREF, q->q_flags)) q->q_flags |= a->q_flags & ~QDONTSEND; a = q; goto done; } } /* add address on list */ *pq = a; a->q_next = NULL; /* ** Alias the name and handle special mailer types. */ trylocaluser: if (tTd(29, 7)) printf("at trylocaluser %s\n", a->q_user); if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) goto testselfdestruct; if (m == InclMailer) { a->q_flags |= QDONTSEND; if (a->q_alias == NULL) { a->q_flags |= QBADADDR; a->q_status = "5.7.1"; usrerr("550 Cannot mail directly to :include:s"); } else { int ret; message("including file %s", a->q_user); ret = include(a->q_user, FALSE, a, sendq, aliaslevel, e); if (transienterror(ret)) { -#ifdef LOG if (LogLevel > 2) - syslog(LOG_ERR, "%s: include %s: transient error: %s", - e->e_id == NULL ? "NOQUEUE" : e->e_id, + sm_syslog(LOG_ERR, e->e_id, + "include %s: transient error: %s", shortenstring(a->q_user, 203), errstring(ret)); -#endif a->q_flags |= QQUEUEUP; a->q_flags &= ~QDONTSEND; usrerr("451 Cannot open %s: %s", shortenstring(a->q_user, 203), errstring(ret)); } else if (ret != 0) { a->q_flags |= QBADADDR; a->q_status = "5.2.4"; usrerr("550 Cannot open %s: %s", shortenstring(a->q_user, 203), errstring(ret)); } } } else if (m == FileMailer) { extern bool writable(); /* check if writable or creatable */ if (a->q_alias == NULL) { a->q_flags |= QBADADDR; a->q_status = "5.7.1"; usrerr("550 Cannot mail directly to files"); } else if (bitset(QBOGUSSHELL, a->q_alias->q_flags)) { a->q_flags |= QBADADDR; a->q_status = "5.7.1"; - usrerr("550 User %s@%s doesn't have a valid shell for mailing to files", - a->q_alias->q_ruser, MyHostName); + if (a->q_alias->q_ruser == NULL) + usrerr("550 UID %d is an unknown user: cannot mail to files", + a->q_alias->q_uid); + else + usrerr("550 User %s@%s doesn't have a valid shell for mailing to files", + a->q_alias->q_ruser, MyHostName); } else if (bitset(QUNSAFEADDR, a->q_alias->q_flags)) { a->q_flags |= QBADADDR; a->q_status = "5.7.1"; usrerr("550 Address %s is unsafe for mailing to files", a->q_alias->q_paddr); } + else if (strcmp(buf, "/dev/null") == 0) + { + /* /dev/null is always accepted */ + } else if (!writable(buf, a->q_alias, SFF_CREAT)) { a->q_flags |= QBADADDR; giveresponse(EX_CANTCREAT, m, NULL, a->q_alias, (time_t) 0, e); } } /* try aliasing */ if (!quoted && !bitset(QDONTSEND, a->q_flags) && bitnset(M_ALIASABLE, m->m_flags)) alias(a, sendq, aliaslevel, e); # if USERDB /* if not aliased, look it up in the user database */ if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags) && bitnset(M_CHECKUDB, m->m_flags)) { extern int udbexpand(); if (udbexpand(a, sendq, aliaslevel, e) == EX_TEMPFAIL) { a->q_flags |= QQUEUEUP; if (e->e_message == NULL) e->e_message = newstr("Deferred: user database error"); -# ifdef LOG if (LogLevel > 8) - syslog(LOG_INFO, "%s: deferred: udbexpand: %s", - e->e_id == NULL ? "NOQUEUE" : e->e_id, + sm_syslog(LOG_INFO, e->e_id, + "deferred: udbexpand: %s", errstring(errno)); -# endif message("queued (user database error): %s", errstring(errno)); e->e_nrcpts++; goto testselfdestruct; } } # endif /* ** If we have a level two config file, then pass the name through ** Ruleset 5 before sending it off. Ruleset 5 has the right ** to send rewrite it to another mailer. This gives us a hook ** after local aliasing has been done. */ if (tTd(29, 5)) { printf("recipient: testing local? cl=%d, rr5=%lx\n\t", ConfigLevel, (u_long) RewriteRules[5]); printaddr(a, FALSE); } if (!bitset(QNOTREMOTE|QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) && ConfigLevel >= 2 && RewriteRules[5] != NULL && bitnset(M_TRYRULESET5, m->m_flags)) { extern void maplocaluser __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); maplocaluser(a, sendq, aliaslevel + 1, e); } /* ** If it didn't get rewritten to another mailer, go ahead ** and deliver it. */ if (!bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags) && bitnset(M_HASPWENT, m->m_flags)) { auto bool fuzzy; register struct passwd *pw; extern void forward __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); /* warning -- finduser may trash buf */ pw = finduser(buf, &fuzzy); if (pw == NULL || strlen(pw->pw_name) > MAXNAME) { a->q_flags |= QBADADDR; a->q_status = "5.1.1"; giveresponse(EX_NOUSER, m, NULL, a->q_alias, (time_t) 0, e); } else { char nbuf[MAXNAME + 1]; if (fuzzy) { /* name was a fuzzy match */ a->q_user = newstr(pw->pw_name); if (findusercount++ > 3) { a->q_flags |= QBADADDR; a->q_status = "5.4.6"; usrerr("554 aliasing/forwarding loop for %s broken", pw->pw_name); goto done; } /* see if it aliases */ (void) strcpy(buf, pw->pw_name); goto trylocaluser; } if (strcmp(pw->pw_dir, "/") == 0) a->q_home = ""; else a->q_home = newstr(pw->pw_dir); a->q_uid = pw->pw_uid; a->q_gid = pw->pw_gid; a->q_ruser = newstr(pw->pw_name); a->q_flags |= QGOODUID; buildfname(pw->pw_gecos, pw->pw_name, nbuf, sizeof nbuf); if (nbuf[0] != '\0') a->q_fullname = newstr(nbuf); if (!usershellok(pw->pw_name, pw->pw_shell)) { a->q_flags |= QBOGUSSHELL; } if (bitset(EF_VRFYONLY, e->e_flags)) { /* don't do any more now */ a->q_flags |= QVERIFIED; } else if (!quoted) forward(a, sendq, aliaslevel, e); } } if (!bitset(QDONTSEND, a->q_flags)) e->e_nrcpts++; testselfdestruct: a->q_flags |= QTHISPASS; if (tTd(26, 8)) { printf("testselfdestruct: "); printaddr(a, FALSE); if (tTd(26, 10)) { printf("SENDQ:\n"); printaddr(*sendq, TRUE); printf("----\n"); } } if (a->q_alias == NULL && a != &e->e_from && bitset(QDONTSEND, a->q_flags)) { for (q = *sendq; q != NULL; q = q->q_next) { if (!bitset(QDONTSEND, q->q_flags)) break; } if (q == NULL) { a->q_flags |= QBADADDR; a->q_status = "5.4.6"; usrerr("554 aliasing/forwarding loop broken"); } } done: a->q_flags |= QTHISPASS; if (buf != buf0) free(buf); /* ** If we are at the top level, check to see if this has ** expanded to exactly one address. If so, it can inherit ** the primaryness of the address. ** ** While we're at it, clear the QTHISPASS bits. */ if (aliaslevel == 0) { int nrcpts = 0; ADDRESS *only = NULL; for (q = *sendq; q != NULL; q = q->q_next) { if (bitset(QTHISPASS, q->q_flags) && !bitset(QDONTSEND|QBADADDR, q->q_flags)) { nrcpts++; only = q; } q->q_flags &= ~QTHISPASS; } if (nrcpts == 1) { /* check to see if this actually got a new owner */ q = only; while ((q = q->q_alias) != NULL) { if (q->q_owner != NULL) break; } if (q == NULL) only->q_flags |= QPRIMARY; } else if (!initialdontsend && nrcpts > 0) { /* arrange for return receipt */ e->e_flags |= EF_SENDRECEIPT; a->q_flags |= QEXPANDED; if (e->e_xfp != NULL && bitset(QPINGONSUCCESS, a->q_flags)) fprintf(e->e_xfp, "%s... expanded to multiple addresses\n", a->q_paddr); } } return (a); } /* ** FINDUSER -- find the password entry for a user. ** ** This looks a lot like getpwnam, except that it may want to ** do some fancier pattern matching in /etc/passwd. ** ** This routine contains most of the time of many sendmail runs. ** It deserves to be optimized. ** ** Parameters: ** name -- the name to match against. ** fuzzyp -- an outarg that is set to TRUE if this entry ** was found using the fuzzy matching algorithm; ** set to FALSE otherwise. ** ** Returns: ** A pointer to a pw struct. ** NULL if name is unknown or ambiguous. ** ** Side Effects: ** may modify name. */ struct passwd * finduser(name, fuzzyp) char *name; bool *fuzzyp; { register struct passwd *pw; register char *p; bool tryagain; if (tTd(29, 4)) printf("finduser(%s): ", name); *fuzzyp = FALSE; #ifdef HESIOD /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ for (p = name; *p != '\0'; p++) if (!isascii(*p) || !isdigit(*p)) break; if (*p == '\0') { if (tTd(29, 4)) printf("failed (numeric input)\n"); return NULL; } #endif /* look up this login name using fast path */ if ((pw = sm_getpwnam(name)) != NULL) { if (tTd(29, 4)) printf("found (non-fuzzy)\n"); return (pw); } /* try mapping it to lower case */ tryagain = FALSE; for (p = name; *p != '\0'; p++) { if (isascii(*p) && isupper(*p)) { *p = tolower(*p); tryagain = TRUE; } } if (tryagain && (pw = sm_getpwnam(name)) != NULL) { if (tTd(29, 4)) printf("found (lower case)\n"); *fuzzyp = TRUE; return pw; } #if MATCHGECOS /* see if fuzzy matching allowed */ if (!MatchGecos) { if (tTd(29, 4)) printf("not found (fuzzy disabled)\n"); return NULL; } /* search for a matching full name instead */ for (p = name; *p != '\0'; p++) { if (*p == (SpaceSub & 0177) || *p == '_') *p = ' '; } (void) setpwent(); while ((pw = getpwent()) != NULL) { char buf[MAXNAME + 1]; # if 0 if (strcasecmp(pw->pw_name, name) == 0) { if (tTd(29, 4)) printf("found (case wrapped)\n"); break; } # endif buildfname(pw->pw_gecos, pw->pw_name, buf, sizeof buf); if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name)) { if (tTd(29, 4)) printf("fuzzy matches %s\n", pw->pw_name); message("sending to login name %s", pw->pw_name); break; } } if (pw != NULL) *fuzzyp = TRUE; else if (tTd(29, 4)) printf("no fuzzy match found\n"); # if DEC_OSF_BROKEN_GETPWENT /* DEC OSF/1 3.2 or earlier */ endpwent(); # endif return pw; #else if (tTd(29, 4)) printf("not found (fuzzy disabled)\n"); return NULL; #endif } /* ** WRITABLE -- predicate returning if the file is writable. ** ** This routine must duplicate the algorithm in sys/fio.c. ** Unfortunately, we cannot use the access call since we ** won't necessarily be the real uid when we try to ** actually open the file. ** ** Notice that ANY file with ANY execute bit is automatically ** not writable. This is also enforced by mailfile. ** ** Parameters: ** filename -- the file name to check. ** ctladdr -- the controlling address for this file. ** flags -- SFF_* flags to control the function. ** ** Returns: ** TRUE -- if we will be able to write this file. ** FALSE -- if we cannot write this file. ** ** Side Effects: ** none. */ bool writable(filename, ctladdr, flags) char *filename; ADDRESS *ctladdr; int flags; { uid_t euid; gid_t egid; char *uname; if (tTd(44, 5)) printf("writable(%s, 0x%x)\n", filename, flags); /* ** File does exist -- check that it is writable. */ - if (ctladdr != NULL && geteuid() == 0) + if (geteuid() != 0) { + euid = geteuid(); + egid = getegid(); + uname = NULL; + } + else if (ctladdr != NULL) + { euid = ctladdr->q_uid; egid = ctladdr->q_gid; uname = ctladdr->q_user; } else if (bitset(SFF_RUNASREALUID, flags)) { euid = RealUid; egid = RealGid; uname = RealUserName; } else if (FileMailer != NULL && !bitset(SFF_ROOTOK, flags)) { euid = FileMailer->m_uid; egid = FileMailer->m_gid; uname = NULL; } else { euid = egid = 0; uname = NULL; } if (!bitset(SFF_ROOTOK, flags)) { if (euid == 0) { euid = DefUid; uname = DefUser; } if (egid == 0) egid = DefGid; } if (geteuid() == 0 && (ctladdr == NULL || !bitset(QGOODUID, ctladdr->q_flags))) flags |= SFF_SETUIDOK; + flags |= SFF_NOLINK; errno = safefile(filename, euid, egid, uname, flags, S_IWRITE, NULL); return errno == 0; } /* ** INCLUDE -- handle :include: specification. ** ** Parameters: ** fname -- filename to include. ** forwarding -- if TRUE, we are reading a .forward file. ** if FALSE, it's a :include: file. ** ctladdr -- address template to use to fill in these ** addresses -- effective user/group id are ** the important things. ** sendq -- a pointer to the head of the send queue ** to put these addresses in. ** aliaslevel -- the alias nesting depth. ** e -- the current envelope. ** ** Returns: ** open error status ** ** Side Effects: ** reads the :include: file and sends to everyone ** listed in that file. ** ** Security Note: ** If you have restricted chown (that is, you can't ** give a file away), it is reasonable to allow programs ** and files called from this :include: file to be to be ** run as the owner of the :include: file. This is bogus ** if there is any chance of someone giving away a file. ** We assume that pre-POSIX systems can give away files. ** ** There is an additional restriction that if you ** forward to a :include: file, it will not take on ** the ownership of the :include: file. This may not ** be necessary, but shouldn't hurt. */ static jmp_buf CtxIncludeTimeout; static void includetimeout(); int include(fname, forwarding, ctladdr, sendq, aliaslevel, e) char *fname; bool forwarding; ADDRESS *ctladdr; ADDRESS **sendq; int aliaslevel; ENVELOPE *e; { FILE *volatile fp = NULL; char *oldto = e->e_to; char *oldfilename = FileName; int oldlinenumber = LineNumber; register EVENT *ev = NULL; int nincludes; register ADDRESS *ca; volatile uid_t saveduid, uid; volatile gid_t savedgid, gid; char *volatile uname; int rval = 0; volatile int sfflags = SFF_REGONLY; + register char *p; + bool safechown = FALSE; + bool safedir = FALSE; struct stat st; char buf[MAXLINE]; -#ifdef _POSIX_CHOWN_RESTRICTED -# if _POSIX_CHOWN_RESTRICTED == -1 -# define safechown FALSE -# else -# define safechown TRUE -# endif -#else -# ifdef _PC_CHOWN_RESTRICTED - bool safechown; -# else -# ifdef BSD -# define safechown TRUE -# else -# define safechown FALSE -# endif -# endif -#endif extern bool chownsafe(); if (tTd(27, 2)) printf("include(%s)\n", fname); if (tTd(27, 4)) printf(" ruid=%d euid=%d\n", (int) getuid(), (int) geteuid()); if (tTd(27, 14)) { printf("ctladdr "); printaddr(ctladdr, FALSE); } if (tTd(27, 9)) printf("include: old uid = %d/%d\n", (int) getuid(), (int) geteuid()); if (forwarding) sfflags |= SFF_MUSTOWN|SFF_ROOTOK|SFF_NOSLINK; ca = getctladdr(ctladdr); if (ca == NULL) { uid = DefUid; gid = DefGid; uname = DefUser; } else { uid = ca->q_uid; gid = ca->q_gid; uname = ca->q_user; } #if HASSETREUID || USESETEUID saveduid = geteuid(); savedgid = getegid(); if (saveduid == 0) { if (!DontInitGroups) initgroups(uname, gid); if (gid != 0) (void) setgid(gid); if (uid != 0) { # if USESETEUID if (seteuid(uid) < 0) syserr("seteuid(%d) failure (real=%d, eff=%d)", uid, getuid(), geteuid()); # else if (setreuid(0, uid) < 0) syserr("setreuid(0, %d) failure (real=%d, eff=%d)", uid, getuid(), geteuid()); # endif else sfflags |= SFF_NOPATHCHECK; } } #endif if (tTd(27, 9)) printf("include: new uid = %d/%d\n", (int) getuid(), (int) geteuid()); /* ** If home directory is remote mounted but server is down, ** this can hang or give errors; use a timeout to avoid this */ if (setjmp(CtxIncludeTimeout) != 0) { ctladdr->q_flags |= QQUEUEUP; errno = 0; /* return pseudo-error code */ - rval = EOPENTIMEOUT; + rval = E_SM_OPENTIMEOUT; goto resetuid; } if (TimeOuts.to_fileopen > 0) ev = setevent(TimeOuts.to_fileopen, includetimeout, 0); else ev = NULL; - /* the input file must be marked safe */ - rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD, NULL); + /* check for writable parent directory */ + p = strrchr(fname, '/'); + if (p != NULL) + { + *p = '\0'; + if (safedirpath(fname, uid, gid, uname, sfflags|SFF_SAFEDIRPATH) == 0) + { + /* in safe directory: relax chown & link rules */ + safedir = TRUE; + sfflags |= SFF_NOPATHCHECK; + } + *p = '/'; + } + + /* allow links only in unwritable directories */ + if (!safedir) + sfflags |= SFF_NOLINK; + + rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD, &st); if (rval != 0) { /* don't use this :include: file */ if (tTd(27, 4)) printf("include: not safe (uid=%d): %s\n", (int) uid, errstring(rval)); } - else + else if ((fp = fopen(fname, "r")) == NULL) { - fp = fopen(fname, "r"); - if (fp == NULL) - { - rval = errno; - if (tTd(27, 4)) - printf("include: open: %s\n", errstring(rval)); - } + rval = errno; + if (tTd(27, 4)) + printf("include: open: %s\n", errstring(rval)); } + else if (filechanged(fname, fileno(fp), &st, sfflags)) + { + rval = E_SM_FILECHANGE; + if (tTd(27, 4)) + printf("include: file changed after open\n"); + } if (ev != NULL) clrevent(ev); resetuid: #if HASSETREUID || USESETEUID if (saveduid == 0) { if (uid != 0) { # if USESETEUID if (seteuid(0) < 0) syserr("seteuid(0) failure (real=%d, eff=%d)", getuid(), geteuid()); # else if (setreuid(-1, 0) < 0) syserr("setreuid(-1, 0) failure (real=%d, eff=%d)", getuid(), geteuid()); if (setreuid(RealUid, 0) < 0) syserr("setreuid(%d, 0) failure (real=%d, eff=%d)", RealUid, getuid(), geteuid()); # endif } setgid(savedgid); } #endif if (tTd(27, 9)) printf("include: reset uid = %d/%d\n", (int) getuid(), (int) geteuid()); - if (rval == EOPENTIMEOUT) + if (rval == E_SM_OPENTIMEOUT) usrerr("451 open timeout on %s", fname); if (fp == NULL) return rval; if (fstat(fileno(fp), &st) < 0) { rval = errno; syserr("Cannot fstat %s!", fname); return rval; } -#ifndef safechown - safechown = chownsafe(fileno(fp)); -#endif + /* if path was writable, check to avoid file giveaway tricks */ + safechown = chownsafe(fileno(fp), safedir); + if (tTd(27, 6)) + printf("include: parent of %s is %s, chown is %ssafe\n", + fname, + safedir ? "safe" : "dangerous", + safechown ? "" : "un"); + if (ca == NULL && safechown) { ctladdr->q_uid = st.st_uid; ctladdr->q_gid = st.st_gid; ctladdr->q_flags |= QGOODUID; } if (ca != NULL && ca->q_uid == st.st_uid) { /* optimization -- avoid getpwuid if we already have info */ ctladdr->q_flags |= ca->q_flags & QBOGUSSHELL; ctladdr->q_ruser = ca->q_ruser; } else if (!forwarding) { register struct passwd *pw; pw = sm_getpwuid(st.st_uid); if (pw == NULL) ctladdr->q_flags |= QBOGUSSHELL; else { char *sh; ctladdr->q_ruser = newstr(pw->pw_name); if (safechown) sh = pw->pw_shell; else sh = "/SENDMAIL/ANY/SHELL/"; if (!usershellok(pw->pw_name, sh)) { -#ifdef LOG if (LogLevel >= 12) - syslog(LOG_INFO, "%s: user %s has bad shell %s, marked %s", + sm_syslog(LOG_INFO, e->e_id, + "%s: user %s has bad shell %s, marked %s", shortenstring(fname, 203), pw->pw_name, sh, safechown ? "bogus" : "unsafe"); -#endif if (safechown) ctladdr->q_flags |= QBOGUSSHELL; else ctladdr->q_flags |= QUNSAFEADDR; } } } if (bitset(EF_VRFYONLY, e->e_flags)) { /* don't do any more now */ ctladdr->q_flags |= QVERIFIED; e->e_nrcpts++; xfclose(fp, "include", fname); return rval; } /* ** Check to see if some bad guy can write this file ** ** Group write checking could be more clever, e.g., ** guessing as to which groups are actually safe ("sys" ** may be; "user" probably is not). ** Also, we don't check for writable ** directories in the path. We've got to leave ** something for the local sysad to do. */ if (bitset(S_IWOTH | (UnsafeGroupWrites ? S_IWGRP : 0), st.st_mode)) { -#ifdef LOG if (LogLevel >= 12) - syslog(LOG_INFO, "%s: %s writable %s file, marked unsafe", + sm_syslog(LOG_INFO, e->e_id, + "%s: %s writable %s file, marked unsafe", shortenstring(fname, 203), bitset(S_IWOTH, st.st_mode) ? "world" : "group", forwarding ? "forward" : ":include:"); -#endif ctladdr->q_flags |= QUNSAFEADDR; } /* read the file -- each line is a comma-separated list. */ FileName = fname; LineNumber = 0; ctladdr->q_flags &= ~QSELFREF; nincludes = 0; while (fgets(buf, sizeof buf, fp) != NULL) { register char *p = strchr(buf, '\n'); LineNumber++; if (p != NULL) *p = '\0'; if (buf[0] == '#' || buf[0] == '\0') continue; /* #@# introduces a comment anywhere */ /* for Japanese character sets */ for (p = buf; (p = strchr(++p, '#')) != NULL; ) { if (p[1] == '@' && p[2] == '#' && isascii(p[-1]) && isspace(p[-1]) && (p[3] == '\0' || (isascii(p[3]) && isspace(p[3])))) { p[-1] = '\0'; break; } } if (buf[0] == '\0') continue; e->e_to = NULL; message("%s to %s", forwarding ? "forwarding" : "sending", buf); -#ifdef LOG if (forwarding && LogLevel > 9) - syslog(LOG_INFO, "%s: forward %.200s => %s", - e->e_id == NULL ? "NOQUEUE" : e->e_id, + sm_syslog(LOG_INFO, e->e_id, + "forward %.200s => %s", oldto, shortenstring(buf, 203)); -#endif nincludes += sendtolist(buf, ctladdr, sendq, aliaslevel + 1, e); } if (ferror(fp) && tTd(27, 3)) printf("include: read error: %s\n", errstring(errno)); if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags)) { if (tTd(27, 5)) { printf("include: QDONTSEND "); printaddr(ctladdr, FALSE); } ctladdr->q_flags |= QDONTSEND; } (void) xfclose(fp, "include", fname); FileName = oldfilename; LineNumber = oldlinenumber; e->e_to = oldto; return rval; } static void includetimeout() { longjmp(CtxIncludeTimeout, 1); } /* ** SENDTOARGV -- send to an argument vector. ** ** Parameters: ** argv -- argument vector to send to. ** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** puts all addresses on the argument vector onto the ** send queue. */ void sendtoargv(argv, e) register char **argv; register ENVELOPE *e; { register char *p; while ((p = *argv++) != NULL) { (void) sendtolist(p, NULLADDR, &e->e_sendqueue, 0, e); } } /* ** GETCTLADDR -- get controlling address from an address header. ** ** If none, get one corresponding to the effective userid. ** ** Parameters: ** a -- the address to find the controller of. ** ** Returns: ** the controlling address. ** ** Side Effects: ** none. */ ADDRESS * getctladdr(a) register ADDRESS *a; { while (a != NULL && !bitset(QGOODUID, a->q_flags)) a = a->q_alias; return (a); } /* ** SELF_REFERENCE -- check to see if an address references itself ** ** The check is done through a chain of aliases. If it is part of ** a loop, break the loop at the "best" address, that is, the one ** that exists as a real user. ** ** This is to handle the case of: ** awc: Andrew.Chang ** Andrew.Chang: awc@mail.server ** which is a problem only on mail.server. ** ** Parameters: ** a -- the address to check. ** e -- the current envelope. ** ** Returns: ** The address that should be retained. */ ADDRESS * self_reference(a, e) ADDRESS *a; ENVELOPE *e; { ADDRESS *b; /* top entry in self ref loop */ ADDRESS *c; /* entry that point to a real mail box */ if (tTd(27, 1)) printf("self_reference(%s)\n", a->q_paddr); for (b = a->q_alias; b != NULL; b = b->q_alias) { if (sameaddr(a, b)) break; } if (b == NULL) { if (tTd(27, 1)) printf("\t... no self ref\n"); return NULL; } /* ** Pick the first address that resolved to a real mail box ** i.e has a pw entry. The returned value will be marked ** QSELFREF in recipient(), which in turn will disable alias() ** from marking it QDONTSEND, which mean it will be used ** as a deliverable address. ** ** The 2 key thing to note here are: ** 1) we are in a recursive call sequence: ** alias->sentolist->recipient->alias ** 2) normally, when we return back to alias(), the address ** will be marked QDONTSEND, since alias() assumes the ** expanded form will be used instead of the current address. ** This behaviour is turned off if the address is marked ** QSELFREF We set QSELFREF when we return to recipient(). */ c = a; while (c != NULL) { if (bitnset(M_HASPWENT, c->q_mailer->m_flags)) { if (tTd(27, 2)) printf("\t... getpwnam(%s)... ", c->q_user); if (sm_getpwnam(c->q_user) != NULL) { if (tTd(27, 2)) printf("found\n"); /* ought to cache results here */ if (sameaddr(b, c)) return b; else return c; } if (tTd(27, 2)) printf("failed\n"); } c = c->q_alias; } if (tTd(27, 1)) printf("\t... cannot break loop for \"%s\"\n", a->q_paddr); return NULL; } Index: head/usr.sbin/sendmail/src/savemail.c =================================================================== --- head/usr.sbin/sendmail/src/savemail.c (revision 26988) +++ head/usr.sbin/sendmail/src/savemail.c (revision 26989) @@ -1,1488 +1,1491 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)savemail.c 8.103 (Berkeley) 1/18/97"; +static char sccsid[] = "@(#)savemail.c 8.110 (Berkeley) 4/7/97"; #endif /* not lint */ # include "sendmail.h" /* ** SAVEMAIL -- Save mail on error ** ** If mailing back errors, mail it back to the originator ** together with an error message; otherwise, just put it in ** dead.letter in the user's home directory (if he exists on ** this machine). ** ** Parameters: ** e -- the envelope containing the message in error. ** sendbody -- if TRUE, also send back the body of the ** message; otherwise just send the header. ** ** Returns: ** none ** ** Side Effects: ** Saves the letter, by writing or mailing it back to the ** sender, or by putting it in dead.letter in her home ** directory. */ /* defines for state machine */ # define ESM_REPORT 0 /* report to sender's terminal */ # define ESM_MAIL 1 /* mail back to sender */ # define ESM_QUIET 2 /* messages have already been returned */ # define ESM_DEADLETTER 3 /* save in ~/dead.letter */ # define ESM_POSTMASTER 4 /* return to postmaster */ # define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ # define ESM_PANIC 6 /* leave the locked queue/transcript files */ # define ESM_DONE 7 /* the message is successfully delivered */ # ifndef _PATH_VARTMP # define _PATH_VARTMP "/usr/tmp/" # endif void savemail(e, sendbody) register ENVELOPE *e; bool sendbody; { register struct passwd *pw; register FILE *fp; int state; auto ADDRESS *q = NULL; register char *p; MCI mcibuf; int flags; char buf[MAXLINE+1]; extern char *ttypath(); typedef int (*fnptr)(); extern bool writable(); if (tTd(6, 1)) { printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, ExitStat); printaddr(&e->e_from, FALSE); } if (e->e_id == NULL) { /* can't return a message with no id */ return; } /* ** In the unhappy event we don't know who to return the mail ** to, make someone up. */ if (e->e_from.q_paddr == NULL) { e->e_sender = "Postmaster"; if (parseaddr(e->e_sender, &e->e_from, RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL) { syserr("553 Cannot parse Postmaster!"); ExitStat = EX_SOFTWARE; finis(); } } e->e_to = NULL; /* ** Basic state machine. ** ** This machine runs through the following states: ** ** ESM_QUIET Errors have already been printed iff the ** sender is local. ** ESM_REPORT Report directly to the sender's terminal. ** ESM_MAIL Mail response to the sender. ** ESM_DEADLETTER Save response in ~/dead.letter. ** ESM_POSTMASTER Mail response to the postmaster. ** ESM_PANIC Save response anywhere possible. */ /* determine starting state */ switch (e->e_errormode) { case EM_WRITE: state = ESM_REPORT; break; case EM_BERKNET: case EM_MAIL: state = ESM_MAIL; break; case EM_PRINT: case '\0': state = ESM_QUIET; break; case EM_QUIET: /* no need to return anything at all */ return; default: syserr("554 savemail: bogus errormode x%x\n", e->e_errormode); state = ESM_MAIL; break; } /* if this is already an error response, send to postmaster */ if (bitset(EF_RESPONSE, e->e_flags)) { if (e->e_parent != NULL && bitset(EF_RESPONSE, e->e_parent->e_flags)) { /* got an error sending a response -- can it */ return; } state = ESM_POSTMASTER; } while (state != ESM_DONE) { if (tTd(6, 5)) printf(" state %d\n", state); switch (state) { case ESM_QUIET: if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags)) state = ESM_DEADLETTER; else state = ESM_MAIL; break; case ESM_REPORT: /* ** If the user is still logged in on the same terminal, ** then write the error messages back to hir (sic). */ p = ttypath(); if (p == NULL || freopen(p, "w", stdout) == NULL) { state = ESM_MAIL; break; } expand("\201n", buf, sizeof buf, e); printf("\r\nMessage from %s...\r\n", buf); printf("Errors occurred while sending mail.\r\n"); if (e->e_xfp != NULL) { (void) fflush(e->e_xfp); fp = fopen(queuename(e, 'x'), "r"); } else fp = NULL; if (fp == NULL) { syserr("Cannot open %s", queuename(e, 'x')); printf("Transcript of session is unavailable.\r\n"); } else { printf("Transcript follows:\r\n"); while (fgets(buf, sizeof buf, fp) != NULL && !ferror(stdout)) fputs(buf, stdout); (void) xfclose(fp, "savemail transcript", e->e_id); } printf("Original message will be saved in dead.letter.\r\n"); state = ESM_DEADLETTER; break; case ESM_MAIL: /* ** If mailing back, do it. ** Throw away all further output. Don't alias, ** since this could cause loops, e.g., if joe ** mails to joe@x, and for some reason the network ** for @x is down, then the response gets sent to ** joe@x, which gives a response, etc. Also force ** the mail to be delivered even if a version of ** it has already been sent to the sender. ** ** If this is a configuration or local software ** error, send to the local postmaster as well, ** since the originator can't do anything ** about it anyway. Note that this is a full ** copy of the message (intentionally) so that ** the Postmaster can forward things along. */ if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) { (void) sendtolist("postmaster", NULLADDR, &e->e_errorqueue, 0, e); } if (!emptyaddr(&e->e_from)) { (void) sendtolist(e->e_from.q_paddr, NULLADDR, &e->e_errorqueue, 0, e); } /* ** Deliver a non-delivery report to the ** Postmaster-designate (not necessarily ** Postmaster). This does not include the ** body of the message, for privacy reasons. ** You really shouldn't need this. */ e->e_flags |= EF_PM_NOTIFY; /* check to see if there are any good addresses */ for (q = e->e_errorqueue; q != NULL; q = q->q_next) if (!bitset(QBADADDR|QDONTSEND, q->q_flags)) break; if (q == NULL) { /* this is an error-error */ state = ESM_POSTMASTER; break; } if (returntosender(e->e_message, e->e_errorqueue, sendbody ? RTSF_SEND_BODY : RTSF_NO_BODY, e) == 0) { state = ESM_DONE; break; } /* didn't work -- return to postmaster */ state = ESM_POSTMASTER; break; case ESM_POSTMASTER: /* ** Similar to previous case, but to system postmaster. */ q = NULL; if (sendtolist(DoubleBounceAddr, NULL, &q, 0, e) <= 0) { syserr("553 cannot parse %s!", DoubleBounceAddr); ExitStat = EX_SOFTWARE; state = ESM_USRTMP; break; } flags = RTSF_PM_BOUNCE; if (sendbody) flags |= RTSF_SEND_BODY; if (returntosender(e->e_message, q, flags, e) == 0) { state = ESM_DONE; break; } /* didn't work -- last resort */ state = ESM_USRTMP; break; case ESM_DEADLETTER: /* ** Save the message in dead.letter. ** If we weren't mailing back, and the user is ** local, we should save the message in ** ~/dead.letter so that the poor person doesn't ** have to type it over again -- and we all know ** what poor typists UNIX users are. */ p = NULL; if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) { if (e->e_from.q_home != NULL) p = e->e_from.q_home; else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL) p = pw->pw_dir; } if (p == NULL || e->e_dfp == NULL) { /* no local directory or no data file */ state = ESM_MAIL; break; } /* we have a home directory; write dead.letter */ define('z', p, e); expand("\201z/dead.letter", buf, sizeof buf, e); - flags = SFF_NOSLINK|SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; + flags = SFF_NOLINK|SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID; e->e_to = buf; if (mailfile(buf, NULL, flags, e) == EX_OK) { - bool oldverb = Verbose; + int oldverb = Verbose; - Verbose = TRUE; + Verbose = 1; message("Saved message in %s", buf); Verbose = oldverb; state = ESM_DONE; break; } state = ESM_MAIL; break; case ESM_USRTMP: /* ** Log the mail in /usr/tmp/dead.letter. */ if (e->e_class < 0) { state = ESM_DONE; break; } if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') { state = ESM_PANIC; break; } snprintf(buf, sizeof buf, "%sdead.letter", _PATH_VARTMP); - flags = SFF_NOSLINK|SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT; + flags = SFF_NOLINK|SFF_CREAT|SFF_REGONLY|SFF_OPENASROOT|SFF_MUSTOWN; if (!writable(buf, NULL, flags) || (fp = safefopen(buf, O_WRONLY|O_CREAT|O_APPEND, FileMode, flags)) == NULL) { state = ESM_PANIC; break; } bzero(&mcibuf, sizeof mcibuf); mcibuf.mci_out = fp; mcibuf.mci_mailer = FileMailer; if (bitnset(M_7BITS, FileMailer->m_flags)) mcibuf.mci_flags |= MCIF_7BIT; putfromline(&mcibuf, e); (*e->e_puthdr)(&mcibuf, e->e_header, e); (*e->e_putbody)(&mcibuf, e, NULL); putline("\n", &mcibuf); (void) fflush(fp); if (ferror(fp)) state = ESM_PANIC; else { - bool oldverb = Verbose; + int oldverb = Verbose; - Verbose = TRUE; + Verbose = 1; message("Saved message in %s", buf); Verbose = oldverb; -#ifdef LOG if (LogLevel > 3) - syslog(LOG_NOTICE, "Saved message in %s", buf); -#endif + sm_syslog(LOG_NOTICE, e->e_id, + "Saved message in %s", + buf); state = ESM_DONE; } (void) xfclose(fp, "savemail", buf); break; default: syserr("554 savemail: unknown state %d", state); /* fall through ... */ case ESM_PANIC: /* leave the locked queue & transcript files around */ loseqfile(e, "savemail panic"); syserr("!554 savemail: cannot save rejected email anywhere"); } } } /* ** RETURNTOSENDER -- return a message to the sender with an error. ** ** Parameters: ** msg -- the explanatory message. ** returnq -- the queue of people to send the message to. ** flags -- flags tweaking the operation: ** RTSF_SENDBODY -- include body of message (otherwise ** just send the header). ** RTSF_PMBOUNCE -- this is a postmaster bounce. ** e -- the current envelope. ** ** Returns: ** zero -- if everything went ok. ** else -- some error. ** ** Side Effects: ** Returns the current message to the sender via ** mail. */ #define MAXRETURNS 6 /* max depth of returning messages */ #define ERRORFUDGE 100 /* nominal size of error message text */ int returntosender(msg, returnq, flags, e) char *msg; ADDRESS *returnq; int flags; register ENVELOPE *e; { register ENVELOPE *ee; ENVELOPE *oldcur = CurEnv; ENVELOPE errenvelope; static int returndepth; register ADDRESS *q; char *p; char buf[MAXNAME + 1]; extern void errbody __P((MCI *, ENVELOPE *, char *)); if (returnq == NULL) return (-1); if (msg == NULL) msg = "Unable to deliver mail"; if (tTd(6, 1)) { printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%lx, returnq=", msg, returndepth, (u_long) e); printaddr(returnq, TRUE); if (tTd(6, 20)) { printf("Sendq="); printaddr(e->e_sendqueue, TRUE); } } if (++returndepth >= MAXRETURNS) { if (returndepth != MAXRETURNS) syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr); /* don't "unrecurse" and fake a clean exit */ /* returndepth--; */ return (0); } define('g', e->e_from.q_paddr, e); define('u', NULL, e); /* initialize error envelope */ ee = newenvelope(&errenvelope, e); define('a', "\201b", ee); define('r', "internal", ee); define('s', "localhost", ee); define('_', "localhost", ee); ee->e_puthdr = putheader; ee->e_putbody = errbody; ee->e_flags |= EF_RESPONSE|EF_METOO; if (!bitset(EF_OLDSTYLE, e->e_flags)) ee->e_flags &= ~EF_OLDSTYLE; ee->e_sendqueue = returnq; ee->e_msgsize = ERRORFUDGE; if (bitset(RTSF_SEND_BODY, flags)) ee->e_msgsize += e->e_msgsize; else ee->e_flags |= EF_NO_BODY_RETN; initsys(ee); for (q = returnq; q != NULL; q = q->q_next) { extern bool pruneroute __P((char *)); if (bitset(QBADADDR, q->q_flags)) continue; q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); q->q_flags |= QPINGONFAILURE; if (!DontPruneRoutes && pruneroute(q->q_paddr)) { register ADDRESS *p; parseaddr(q->q_paddr, q, RF_COPYPARSE, '\0', NULL, e); for (p = returnq; p != NULL; p = p->q_next) { if (p != q && sameaddr(p, q)) q->q_flags |= QDONTSEND; } } if (!bitset(QDONTSEND, q->q_flags)) ee->e_nrcpts++; if (q->q_alias == NULL) addheader("To", q->q_paddr, &ee->e_header); } -# ifdef LOG if (LogLevel > 5) { if (bitset(EF_RESPONSE|EF_WARNING, e->e_flags)) p = "return to sender"; else if (bitset(RTSF_PM_BOUNCE, flags)) p = "postmaster notify"; else p = "DSN"; - syslog(LOG_INFO, "%s: %s: %s: %s", - e->e_id, ee->e_id, p, shortenstring(msg, 203)); + sm_syslog(LOG_INFO, e->e_id, + "%s: %s: %s", + ee->e_id, p, shortenstring(msg, 203)); } -# endif if (SendMIMEErrors) { addheader("MIME-Version", "1.0", &ee->e_header); (void) snprintf(buf, sizeof buf, "%s.%ld/%.100s", ee->e_id, curtime(), MyHostName); ee->e_msgboundary = newstr(buf); (void) snprintf(buf, sizeof buf, #if DSN "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"", #else "multipart/mixed; boundary=\"%s\"", #endif ee->e_msgboundary); addheader("Content-Type", buf, &ee->e_header); p = hvalue("Content-Transfer-Encoding", e->e_header); if (p != NULL && strcasecmp(p, "binary") != 0) p = NULL; if (p == NULL && bitset(EF_HAS8BIT, e->e_flags)) p = "8bit"; if (p != NULL) addheader("Content-Transfer-Encoding", p, &ee->e_header); } if (strncmp(msg, "Warning:", 8) == 0) { addheader("Subject", msg, &ee->e_header); p = "warning-timeout"; } else if (strncmp(msg, "Postmaster warning:", 19) == 0) { addheader("Subject", msg, &ee->e_header); p = "postmaster-warning"; } else if (strcmp(msg, "Return receipt") == 0) { addheader("Subject", msg, &ee->e_header); p = "return-receipt"; } else if (bitset(RTSF_PM_BOUNCE, flags)) { snprintf(buf, sizeof buf, "Postmaster notify: %.*s", sizeof buf - 20, msg); addheader("Subject", buf, &ee->e_header); p = "postmaster-notification"; } else { snprintf(buf, sizeof buf, "Returned mail: %.*s", sizeof buf - 20, msg); addheader("Subject", buf, &ee->e_header); p = "failure"; } (void) snprintf(buf, sizeof buf, "auto-generated (%s)", p); addheader("Auto-Submitted", buf, &ee->e_header); /* fake up an address header for the from person */ expand("\201n", buf, sizeof buf, e); if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL) { syserr("553 Can't parse myself!"); ExitStat = EX_SOFTWARE; returndepth--; return (-1); } ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS); ee->e_from.q_flags |= QPINGONFAILURE; ee->e_sender = ee->e_from.q_paddr; /* push state into submessage */ CurEnv = ee; define('f', "\201n", ee); define('x', "Mail Delivery Subsystem", ee); eatheader(ee, TRUE); /* mark statistics */ markstats(ee, NULLADDR); /* actually deliver the error message */ sendall(ee, SM_DELIVER); /* restore state */ dropenvelope(ee, TRUE); CurEnv = oldcur; returndepth--; /* check for delivery errors */ if (ee->e_parent == NULL || !bitset(EF_RESPONSE, ee->e_parent->e_flags)) return 0; for (q = ee->e_sendqueue; q != NULL; q = q->q_next) { if (bitset(QSENT, q->q_flags)) return 0; } return -1; } /* ** ERRBODY -- output the body of an error message. ** ** Typically this is a copy of the transcript plus a copy of the ** original offending message. ** ** Parameters: ** mci -- the mailer connection information. ** e -- the envelope we are working in. ** separator -- any possible MIME separator. ** ** Returns: ** none ** ** Side Effects: ** Outputs the body of an error message. */ void errbody(mci, e, separator) register MCI *mci; register ENVELOPE *e; char *separator; { register FILE *xfile; char *p; register ADDRESS *q; bool printheader; bool sendbody; bool pm_notify; char buf[MAXLINE]; if (bitset(MCIF_INHEADER, mci->mci_flags)) { putline("", mci); mci->mci_flags &= ~MCIF_INHEADER; } if (e->e_parent == NULL) { syserr("errbody: null parent"); putline(" ----- Original message lost -----\n", mci); return; } /* ** Output MIME header. */ if (e->e_msgboundary != NULL) { putline("This is a MIME-encapsulated message", mci); putline("", mci); (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); putline(buf, mci); putline("", mci); } /* ** Output introductory information. */ pm_notify = FALSE; p = hvalue("subject", e->e_header); if (p != NULL && strncmp(p, "Postmaster ", 11) == 0) pm_notify = TRUE; else { for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) if (bitset(QBADADDR, q->q_flags)) break; } if (!pm_notify && q == NULL && !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) { putline(" **********************************************", mci); putline(" ** THIS IS A WARNING MESSAGE ONLY **", mci); putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", mci); putline(" **********************************************", mci); putline("", mci); } snprintf(buf, sizeof buf, "The original message was received at %s", arpadate(ctime(&e->e_parent->e_ctime))); putline(buf, mci); expand("from \201_", buf, sizeof buf, e->e_parent); putline(buf, mci); putline("", mci); /* ** Output error message header (if specified and available). */ if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) { if (*ErrMsgFile == '/') { - xfile = fopen(ErrMsgFile, "r"); + xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, + SFF_ROOTOK|SFF_REGONLY); if (xfile != NULL) { while (fgets(buf, sizeof buf, xfile) != NULL) { +#if _FFR_BUG_FIX + translate_dollars(buf); +#endif expand(buf, buf, sizeof buf, e); putline(buf, mci); } (void) fclose(xfile); putline("\n", mci); } } else { expand(ErrMsgFile, buf, sizeof buf, e); putline(buf, mci); putline("", mci); } } /* ** Output message introduction */ printheader = TRUE; for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) { if (!bitset(QBADADDR, q->q_flags) || !bitset(QPINGONFAILURE, q->q_flags)) continue; if (printheader) { putline(" ----- The following addresses had permanent fatal errors -----", mci); printheader = FALSE; } snprintf(buf, sizeof buf, "%s", shortenstring(q->q_paddr, 203)); putline(buf, mci); if (q->q_alias != NULL) { snprintf(buf, sizeof buf, " (expanded from: %s)", shortenstring(q->q_alias->q_paddr, 203)); putline(buf, mci); } } if (!printheader) putline("", mci); printheader = TRUE; for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) { if (bitset(QBADADDR, q->q_flags) || !bitset(QPRIMARY, q->q_flags) || !bitset(QDELAYED, q->q_flags)) continue; if (printheader) { putline(" ----- The following addresses had transient non-fatal errors -----", mci); printheader = FALSE; } snprintf(buf, sizeof buf, "%s", shortenstring(q->q_paddr, 203)); putline(buf, mci); if (q->q_alias != NULL) { snprintf(buf, sizeof buf, " (expanded from: %s)", shortenstring(q->q_alias->q_paddr, 203)); putline(buf, mci); } } if (!printheader) putline("", mci); printheader = TRUE; for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) { if (bitset(QBADADDR, q->q_flags) || !bitset(QPRIMARY, q->q_flags) || bitset(QDELAYED, q->q_flags)) continue; else if (!bitset(QPINGONSUCCESS, q->q_flags)) continue; else if (bitset(QRELAYED, q->q_flags)) p = "relayed to non-DSN-aware mailer"; else if (bitset(QDELIVERED, q->q_flags)) { if (bitset(QEXPANDED, q->q_flags)) p = "successfully delivered to mailing list"; else p = "successfully delivered to mailbox"; } else if (bitset(QEXPANDED, q->q_flags)) p = "expanded by alias"; else continue; if (printheader) { putline(" ----- The following addresses had successful delivery notifications -----", mci); printheader = FALSE; } snprintf(buf, sizeof buf, "%s (%s)", shortenstring(q->q_paddr, 203), p); putline(buf, mci); if (q->q_alias != NULL) { snprintf(buf, sizeof buf, " (expanded from: %s)", shortenstring(q->q_alias->q_paddr, 203)); putline(buf, mci); } } if (!printheader) putline("", mci); /* ** Output transcript of errors */ (void) fflush(stdout); p = queuename(e->e_parent, 'x'); if ((xfile = fopen(p, "r")) == NULL) { syserr("Cannot open %s", p); putline(" ----- Transcript of session is unavailable -----\n", mci); } else { printheader = TRUE; if (e->e_xfp != NULL) (void) fflush(e->e_xfp); while (fgets(buf, sizeof buf, xfile) != NULL) { if (printheader) putline(" ----- Transcript of session follows -----\n", mci); printheader = FALSE; putline(buf, mci); } (void) xfclose(xfile, "errbody xscript", p); } errno = 0; #if DSN /* ** Output machine-readable version. */ if (e->e_msgboundary != NULL) { putline("", mci); (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); putline(buf, mci); putline("Content-Type: message/delivery-status", mci); putline("", mci); /* ** Output per-message information. */ /* original envelope id from MAIL FROM: line */ if (e->e_parent->e_envid != NULL) { (void) snprintf(buf, sizeof buf, "Original-Envelope-Id: %.800s", xuntextify(e->e_parent->e_envid)); putline(buf, mci); } /* Reporting-MTA: is us (required) */ (void) snprintf(buf, sizeof buf, "Reporting-MTA: dns; %.800s", MyHostName); putline(buf, mci); /* DSN-Gateway: not relevant since we are not translating */ /* Received-From-MTA: shows where we got this message from */ if (RealHostName != NULL) { /* XXX use $s for type? */ if (e->e_parent->e_from.q_mailer == NULL || (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL) p = "dns"; (void) snprintf(buf, sizeof buf, "Received-From-MTA: %s; %.800s", p, RealHostName); putline(buf, mci); } /* Arrival-Date: -- when it arrived here */ (void) snprintf(buf, sizeof buf, "Arrival-Date: %s", arpadate(ctime(&e->e_parent->e_ctime))); putline(buf, mci); /* ** Output per-address information. */ for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) { register ADDRESS *r; char *action; if (bitset(QBADADDR, q->q_flags)) action = "failed"; else if (!bitset(QPRIMARY, q->q_flags)) continue; else if (bitset(QDELIVERED, q->q_flags)) { if (bitset(QEXPANDED, q->q_flags)) action = "delivered (to mailing list)"; else action = "delivered (to mailbox)"; } else if (bitset(QRELAYED, q->q_flags)) action = "relayed (to non-DSN-aware mailer)"; else if (bitset(QEXPANDED, q->q_flags)) action = "expanded (to multi-recipient alias)"; else if (bitset(QDELAYED, q->q_flags)) action = "delayed"; else continue; putline("", mci); /* Original-Recipient: -- passed from on high */ if (q->q_orcpt != NULL) { (void) snprintf(buf, sizeof buf, "Original-Recipient: %.800s", q->q_orcpt); putline(buf, mci); } /* Final-Recipient: -- the name from the RCPT command */ p = e->e_parent->e_from.q_mailer->m_addrtype; if (p == NULL) p = "rfc822"; for (r = q; r->q_alias != NULL; r = r->q_alias) continue; if (strchr(r->q_user, '@') == NULL) { (void) snprintf(buf, sizeof buf, "Final-Recipient: %s; %.700s@%.100s", p, r->q_user, MyHostName); } else { (void) snprintf(buf, sizeof buf, "Final-Recipient: %s; %.800s", p, r->q_user); } putline(buf, mci); /* X-Actual-Recipient: -- the real problem address */ if (r != q && q->q_user[0] != '\0') { if (strchr(q->q_user, '@') == NULL) { (void) snprintf(buf, sizeof buf, "X-Actual-Recipient: %s; %.700s@%.100s", p, q->q_user, MyHostName); } else { (void) snprintf(buf, sizeof buf, "X-Actual-Recipient: %s; %.800s", p, q->q_user); } putline(buf, mci); } /* Action: -- what happened? */ snprintf(buf, sizeof buf, "Action: %s", action); putline(buf, mci); /* Status: -- what _really_ happened? */ if (q->q_status != NULL) p = q->q_status; else if (bitset(QBADADDR, q->q_flags)) p = "5.0.0"; else if (bitset(QQUEUEUP, q->q_flags)) p = "4.0.0"; else p = "2.0.0"; snprintf(buf, sizeof buf, "Status: %s", p); putline(buf, mci); /* Remote-MTA: -- who was I talking to? */ if (q->q_statmta != NULL) { if (q->q_mailer == NULL || (p = q->q_mailer->m_mtatype) == NULL) p = "dns"; (void) snprintf(buf, sizeof buf, "Remote-MTA: %s; %.800s", p, q->q_statmta); p = &buf[strlen(buf) - 1]; if (*p == '.') *p = '\0'; putline(buf, mci); } /* Diagnostic-Code: -- actual result from other end */ if (q->q_rstatus != NULL) { p = q->q_mailer->m_diagtype; if (p == NULL) p = "smtp"; (void) snprintf(buf, sizeof buf, "Diagnostic-Code: %s; %.800s", p, q->q_rstatus); putline(buf, mci); } /* Last-Attempt-Date: -- fine granularity */ if (q->q_statdate == (time_t) 0L) q->q_statdate = curtime(); (void) snprintf(buf, sizeof buf, "Last-Attempt-Date: %s", arpadate(ctime(&q->q_statdate))); putline(buf, mci); /* Will-Retry-Until: -- for delayed messages only */ if (bitset(QQUEUEUP, q->q_flags) && !bitset(QBADADDR, q->q_flags)) { time_t xdate; xdate = e->e_parent->e_ctime + TimeOuts.to_q_return[e->e_parent->e_timeoutclass]; snprintf(buf, sizeof buf, "Will-Retry-Until: %s", arpadate(ctime(&xdate))); putline(buf, mci); } } } #endif /* ** Output text of original message */ putline("", mci); if (bitset(EF_HAS_DF, e->e_parent->e_flags)) { sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) && !bitset(EF_NO_BODY_RETN, e->e_flags); if (e->e_msgboundary == NULL) { if (sendbody) putline(" ----- Original message follows -----\n", mci); else putline(" ----- Message header follows -----\n", mci); (void) fflush(mci->mci_out); } else { (void) snprintf(buf, sizeof buf, "--%s", e->e_msgboundary); putline(buf, mci); (void) snprintf(buf, sizeof buf, "Content-Type: %s", sendbody ? "message/rfc822" : "text/rfc822-headers"); putline(buf, mci); p = hvalue("Content-Transfer-Encoding", e->e_parent->e_header); if (p != NULL && strcasecmp(p, "binary") != 0) p = NULL; if (p == NULL && bitset(EF_HAS8BIT, e->e_parent->e_flags)) p = "8bit"; if (p != NULL) { (void) snprintf(buf, sizeof buf, "Content-Transfer-Encoding: %s", p); putline(buf, mci); } } putline("", mci); putheader(mci, e->e_parent->e_header, e->e_parent); if (sendbody) putbody(mci, e->e_parent, e->e_msgboundary); else if (e->e_msgboundary == NULL) { putline("", mci); putline(" ----- Message body suppressed -----", mci); } } else if (e->e_msgboundary == NULL) { putline(" ----- No message was collected -----\n", mci); } if (e->e_msgboundary != NULL) { putline("", mci); (void) snprintf(buf, sizeof buf, "--%s--", e->e_msgboundary); putline(buf, mci); } putline("", mci); /* ** Cleanup and exit */ if (errno != 0) syserr("errbody: I/O error"); } /* ** SMTPTODSN -- convert SMTP to DSN status code ** ** Parameters: ** smtpstat -- the smtp status code (e.g., 550). ** ** Returns: ** The DSN version of the status code. */ char * smtptodsn(smtpstat) int smtpstat; { if (smtpstat < 0) return "4.4.2"; switch (smtpstat) { case 450: /* Req mail action not taken: mailbox unavailable */ return "4.2.0"; case 451: /* Req action aborted: local error in processing */ return "4.3.0"; case 452: /* Req action not taken: insufficient sys storage */ return "4.3.1"; case 500: /* Syntax error, command unrecognized */ return "5.5.2"; case 501: /* Syntax error in parameters or arguments */ return "5.5.4"; case 502: /* Command not implemented */ return "5.5.1"; case 503: /* Bad sequence of commands */ return "5.5.1"; case 504: /* Command parameter not implemented */ return "5.5.4"; case 550: /* Req mail action not taken: mailbox unavailable */ return "5.2.0"; case 551: /* User not local; please try <...> */ return "5.1.6"; case 552: /* Req mail action aborted: exceeded storage alloc */ return "5.2.2"; case 553: /* Req action not taken: mailbox name not allowed */ - return "5.1.3"; + return "5.1.0"; case 554: /* Transaction failed */ return "5.0.0"; } if ((smtpstat / 100) == 2) return "2.0.0"; if ((smtpstat / 100) == 4) return "4.0.0"; return "5.0.0"; } /* ** XTEXTIFY -- take regular text and turn it into DSN-style xtext ** ** Parameters: ** t -- the text to convert. ** taboo -- additional characters that must be encoded. ** ** Returns: ** The xtext-ified version of the same string. */ char * xtextify(t, taboo) register char *t; char *taboo; { register char *p; int l; int nbogus; static char *bp = NULL; static int bplen = 0; if (taboo == NULL) taboo = ""; /* figure out how long this xtext will have to be */ nbogus = l = 0; for (p = t; *p != '\0'; p++) { register int c = (*p & 0xff); /* ASCII dependence here -- this is the way the spec words it */ if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || strchr(taboo, c) != NULL) nbogus++; l++; } if (nbogus == 0) return t; l += nbogus * 2 + 1; /* now allocate space if necessary for the new string */ if (l > bplen) { if (bp != NULL) free(bp); bp = xalloc(l); bplen = l; } /* ok, copy the text with byte expansion */ for (p = bp; *t != '\0'; ) { register int c = (*t++ & 0xff); /* ASCII dependence here -- this is the way the spec words it */ if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' || strchr(taboo, c) != NULL) { *p++ = '+'; *p++ = "0123456789abcdef"[c >> 4]; *p++ = "0123456789abcdef"[c & 0xf]; } else *p++ = c; } *p = '\0'; return bp; } /* ** XUNTEXTIFY -- take xtext and turn it into plain text ** ** Parameters: ** t -- the xtextified text. ** ** Returns: ** The decoded text. No attempt is made to deal with ** null strings in the resulting text. */ char * xuntextify(t) register char *t; { register char *p; int l; static char *bp = NULL; static int bplen = 0; /* heuristic -- if no plus sign, just return the input */ if (strchr(t, '+') == NULL) return t; /* xtext is always longer than decoded text */ l = strlen(t); if (l > bplen) { if (bp != NULL) free(bp); bp = xalloc(l); bplen = l; } /* ok, copy the text with byte compression */ for (p = bp; *t != '\0'; t++) { register int c = *t & 0xff; if (c != '+') { *p++ = c; continue; } c = *++t & 0xff; if (!isascii(c) || !isxdigit(c)) { /* error -- first digit is not hex */ usrerr("bogus xtext: +%c", c); t--; continue; } if (isdigit(c)) c -= '0'; else if (isupper(c)) c -= 'A' - 10; else c -= 'a' - 10; *p = c << 4; c = *++t & 0xff; if (!isascii(c) || !isxdigit(c)) { /* error -- second digit is not hex */ usrerr("bogus xtext: +%x%c", *p >> 4, c); t--; continue; } if (isdigit(c)) c -= '0'; else if (isupper(c)) c -= 'A' - 10; else c -= 'a' - 10; *p++ |= c; } *p = '\0'; return bp; } /* ** XTEXTOK -- check if a string is legal xtext ** ** Xtext is used in Delivery Status Notifications. The spec was ** taken from RFC 1891, ``SMTP Service Extension for Delivery ** Status Notifications''. ** ** Parameters: ** s -- the string to check. ** ** Returns: ** TRUE -- if 's' is legal xtext. ** FALSE -- if it has any illegal characters in it. */ bool xtextok(s) char *s; { int c; while ((c = *s++) != '\0') { if (c == '+') { c = *s++; if (!isascii(c) || !isxdigit(c)) return FALSE; c = *s++; if (!isascii(c) || !isxdigit(c)) return FALSE; } else if (c < '!' || c > '~' || c == '=') return FALSE; } return TRUE; } /* ** PRUNEROUTE -- prune an RFC-822 source route ** ** Trims down a source route to the last internet-registered hop. ** This is encouraged by RFC 1123 section 5.3.3. ** ** Parameters: ** addr -- the address ** ** Returns: ** TRUE -- address was modified ** FALSE -- address could not be pruned ** ** Side Effects: ** modifies addr in-place */ bool pruneroute(addr) char *addr; { #if NAMED_BIND char *start, *at, *comma; char c; int rcode; int i; char hostbuf[BUFSIZ]; char *mxhosts[MAXMXHOSTS + 1]; /* check to see if this is really a route-addr */ if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') return FALSE; start = strchr(addr, ':'); at = strrchr(addr, '@'); if (start == NULL || at == NULL || at < start) return FALSE; /* slice off the angle brackets */ i = strlen(at + 1); if (i >= (SIZE_T) sizeof hostbuf) return FALSE; strcpy(hostbuf, at + 1); hostbuf[i - 1] = '\0'; while (start) { if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0) { strcpy(addr + 1, start + 1); return TRUE; } c = *start; *start = '\0'; comma = strrchr(addr, ','); if (comma != NULL && comma[1] == '@' && strlen(comma + 2) < (SIZE_T) sizeof hostbuf) strcpy(hostbuf, comma + 2); else comma = NULL; *start = c; start = comma; } #endif return FALSE; } Index: head/usr.sbin/sendmail/src/sendmail.8 =================================================================== --- head/usr.sbin/sendmail/src/sendmail.8 (revision 26988) +++ head/usr.sbin/sendmail/src/sendmail.8 (revision 26989) @@ -1,605 +1,606 @@ +.\" Copyright (c) 1983, 1997 Eric P. Allman .\" Copyright (c) 1988, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)sendmail.8 8.11 (Berkeley) 1/16/97 +.\" @(#)sendmail.8 8.12 (Berkeley) 2/1/97 .\" -.Dd January 16, 1997 +.Dd February 1, 1997 .Dt SENDMAIL 8 .Os BSD 4 .Sh NAME .Nm sendmail .Nd an electronic mail transport agent .Sh SYNOPSIS .Nm sendmail .Op Ar flags .Op Ar address ... .Nm newaliases .Nm mailq .Op Fl v .Sh DESCRIPTION .Nm Sendmail sends a message to one or more .Em recipients , routing the message over whatever networks are necessary. .Nm Sendmail does internetwork forwarding as necessary to deliver the message to the correct place. .Pp .Nm Sendmail is not intended as a user interface routine; other programs provide user-friendly front ends; .Nm sendmail is used only to deliver pre-formatted messages. .Pp With no flags, .Nm sendmail reads its standard input up to an end-of-file or a line consisting only of a single dot and sends a copy of the message found there to all of the addresses listed. It determines the network(s) to use based on the syntax and contents of the addresses. .Pp Local addresses are looked up in a file and aliased appropriately. Aliasing can be prevented by preceding the address with a backslash. Normally the sender is not included in any alias expansions, e.g., if `john' sends to `group', and `group' includes `john' in the expansion, then the letter will not be delivered to `john'. .Ss Parameters .Bl -tag -width Fl .It Fl B Ns Ar type Set the body type to .Ar type . Current legal values .Li 7BIT or .Li 8BITMIME . .It Fl ba Go into .Tn ARPANET mode. All input lines must end with a CR-LF, and all messages will be generated with a CR-LF at the end. Also, the ``From:'' and ``Sender:'' fields are examined for the name of the sender. .It Fl bd Run as a daemon. This requires Berkeley .Tn IPC . .Nm Sendmail will fork and run in background listening on socket 25 for incoming .Tn SMTP connections. This is normally run from .Pa /etc/rc . .It Fl bD Same as .Fl bd except runs in foreground. .It Fl bh Print the persistent host status database. .It Fl bH Purge the persistent host status database. .It Fl bi Initialize the alias database. .It Fl bm Deliver mail in the usual way (default). .It Fl bp Print a listing of the queue. .It Fl bs Use the .Tn SMTP protocol as described in .Tn RFC821 on standard input and output. This flag implies all the operations of the .Fl ba flag that are compatible with .Tn SMTP . .It Fl bt Run in address test mode. This mode reads addresses and shows the steps in parsing; it is used for debugging configuration tables. .It Fl bv Verify names only \- do not try to collect or deliver a message. Verify mode is normally used for validating users or mailing lists. .It Fl C Ns Ar file Use alternate configuration file. .Nm Sendmail refuses to run as root if an alternate configuration file is specified. .It Fl d Ns Ar X Set debugging value to .Ar X . .ne 1i .It Fl F Ns Ar fullname Set the full name of the sender. .It Fl f Ns Ar name Sets the name of the ``from'' person (i.e., the sender of the mail). .Fl f can only be used by ``trusted'' users (normally .Em root , .Em daemon , and .Em network ) or if the person you are trying to become is the same as the person you are. .It Fl h Ns Ar N Set the hop count to .Ar N . The hop count is incremented every time the mail is processed. When it reaches a limit, the mail is returned with an error message, the victim of an aliasing loop. If not specified, ``Received:'' lines in the message are counted. .It Fl i Ignore dots alone on lines by themselves in incoming messages. This should be set if you are reading data from a file. .It Fl N Ar dsn Set delivery status notification conditions to .Ar dsn, which can be .Ql never for no notifications or a comma separated list of the values .Ql failure to be notified if delivery failed, .Ql delay to be notified if delivery is delayed, and .Ql success to be notified when the message is successfully delivered. .It Fl n Don't do aliasing. .It Fl O Ar option Ns = Ns Em value Set option .Ar option to the specified .Em value . This form uses long names. See below for more details. .It Fl o Ns Ar x Em value Set option .Ar x to the specified .Em value . This form uses single character names only. The short names are not described in this manual page; see the .%T "Sendmail Installation and Operation Guide" for details. .It Fl p Ns Ar protocol Set the name of the protocol used to receive the message. This can be a simple protocol name such as ``UUCP'' or a protocol and hostname, such as ``UUCP:ucbvax''. .It Fl q Ns Bq Ar time Processed saved messages in the queue at given intervals. If .Ar time is omitted, process the queue once. .Xr Time is given as a tagged number, with .Ql s being seconds, .Ql m being minutes, .Ql h being hours, .Ql d being days, and .Ql w being weeks. For example, .Ql \-q1h30m or .Ql \-q90m would both set the timeout to one hour thirty minutes. If .Ar time is specified, .Nm sendmail will run in background. This option can be used safely with .Fl bd . .It Fl qI Ns Ar substr Limit processed jobs to those containing .Ar substr as a substring of the queue id. .It Fl qR Ns Ar substr Limit processed jobs to those containing .Ar substr as a substring of one of the recipients. .It Fl qS Ns Ar substr Limit processed jobs to those containing .Ar substr as a substring of the sender. .It Fl R Ar return Set the amount of the message to be returned if the message bounces. The .Ar return parameter can be .Ql full to return the entire message or .Ql hdrs to return only the headers. .It Fl r Ns Ar name An alternate and obsolete form of the .Fl f flag. .It Fl t Read message for recipients. To:, Cc:, and Bcc: lines will be scanned for recipient addresses. The Bcc: line will be deleted before transmission. Any addresses in the argument list will be suppressed, that is, they will .Em not receive copies even if listed in the message header. .It Fl U Initial (user) submission. This should .Em always be set when called from a user agent such as .Nm Mail or .Nm exmh and .Em never be set when called by a network delivery agent such as .Nm rmail . .It Fl V Ar envid Set the original envelope id. This is propagated across SMTP to servers that support DSNs and is returned in DSN-compliant error messages. .It Fl v Go into verbose mode. Alias expansions will be announced, etc. .It Fl X Ar logfile Log all traffic in and out of mailers in the indicated log file. This should only be used as a last resort for debugging mailer bugs. It will log a lot of data very quickly. .El .Ss Options There are also a number of processing options that may be set. Normally these will only be used by a system administrator. Options may be set either on the command line using the .Fl o flag (for short names), the .Fl O flag (for long names), or in the configuration file. This is a partial list limited to those options that are likely to be useful on the command line and only shows the long names; for a complete list (and details), consult the .%T "Sendmail Installation and Operation Guide" . The options are: .Bl -tag -width Fl .It Li AliasFile= Ns Ar file Use alternate alias file. .It Li HoldExpensive On mailers that are considered ``expensive'' to connect to, don't initiate immediate connection. This requires queueing. .It Li CheckpointInterval= Ns Ar N Checkpoint the queue file after every .Ar N successful deliveries (default 10). This avoids excessive duplicate deliveries when sending to long mailing lists interrupted by system crashes. .ne 1i .It Li DeliveryMode= Ns Ar x Set the delivery mode to .Ar x . Delivery modes are .Ql i for interactive (synchronous) delivery, .Ql b for background (asynchronous) delivery, .Ql q for queue only \- i.e., actual delivery is done the next time the queue is run, and .Ql d for deferred \- the same as .Ql q except that database lookups (notably DNS and NIS lookups) are avoided. .It Li ErrorMode= Ns Ar x Set error processing to mode .Ar x . Valid modes are .Ql m to mail back the error message, .Ql w to ``write'' back the error message (or mail it back if the sender is not logged in), .Ql p to print the errors on the terminal (default), .Ql q to throw away error messages (only exit status is returned), and .Ql e to do special processing for the BerkNet. If the text of the message is not mailed back by modes .Ql m or .Ql w and if the sender is local to this machine, a copy of the message is appended to the file .Pa dead.letter in the sender's home directory. .It Li SaveFromLine Save .Tn UNIX Ns \-style From lines at the front of messages. .It Li MaxHopCount= Ar N The maximum number of times a message is allowed to ``hop'' before we decide it is in a loop. .It Li IgnoreDots Do not take dots on a line by themselves as a message terminator. .It Li SendMimeErrors Send error messages in MIME format. If not set, the DSN (Delivery Status Notification) SMTP extension is disabled. .It Li ConnectionCacheTimeout= Ns Ar timeout Set connection cache timeout. .It Li ConnectionCacheSize= Ns Ar N Set connection cache size. .It Li LogLevel= Ns Ar n The log level. .It Li MeToo Send to ``me'' (the sender) also if I am in an alias expansion. .It Li CheckAliases Validate the right hand side of aliases during a .Xr newaliases 1 command. .It Li OldStyleHeaders If set, this message may have old style headers. If not set, this message is guaranteed to have new style headers (i.e., commas instead of spaces between addresses). If set, an adaptive algorithm is used that will correctly determine the header format in most cases. .It Li QueueDirectory= Ns Ar queuedir Select the directory in which to queue messages. .It Li StatusFile= Ns Ar file Save statistics in the named file. .It Li Timeout.queuereturn= Ns Ar time Set the timeout on undelivered messages in the queue to the specified time. After delivery has failed (e.g., because of a host being down) for this amount of time, failed messages will be returned to the sender. The default is five days. .It Li UserDatabaseSpec= Ns Ar userdatabase If set, a user database is consulted to get forwarding information. You can consider this an adjunct to the aliasing mechanism, except that the database is intended to be distributed; aliases are local to a particular host. This may not be available if your sendmail does not have the .Dv USERDB option compiled in. .It Li ForkEachJob Fork each job during queue runs. May be convenient on memory-poor machines. .It Li SevenBitInput Strip incoming messages to seven bits. .It Li EightBitMode= Ns Ar mode Set the handling of eight bit input to seven bit destinations to .Ar mode : .Li m (mimefy) will convert to seven-bit MIME format, .Li p (pass) will pass it as eight bits (but violates protocols), and .Li s (strict) will bounce the message. .It Li MinQueueAge= Ns Ar timeout Sets how long a job must ferment in the queue between attempts to send it. .It Li DefaultCharSet= Ns Ar charset Sets the default character set used to label 8-bit data that is not otherwise labelled. .It Li DialDelay= Ns Ar sleeptime If opening a connection fails, sleep for .Ar sleeptime seconds and try again. Useful on dial-on-demand sites. .It Li NoRecipientAction= Ns Ar action Set the behaviour when there are no recipient headers (To:, Cc: or Bcc:) in the message to .Ar action : .Li none leaves the message unchanged, .Li add-to adds a To: header with the envelope recipients, .Li add-apparently-to adds an Apparently-To: header with the envelope recipients, .Li add-bcc adds an empty Bcc: header, and .Li add-to-undisclosed adds a header reading .Ql "To: undisclosed-recipients:;" . .It Li MaxDaemonChildren= Ns Ar N Sets the maximum number of children that an incoming SMTP daemon will allow to spawn at any time to .Ar N . .It Li ConnectionRateThrottle= Ns Ar N Sets the maximum number of connections per second to the SMTP port to .Ar N . .El .Pp In aliases, the first character of a name may be a vertical bar to cause interpretation of the rest of the name as a command to pipe the mail to. It may be necessary to quote the name to keep .Nm sendmail from suppressing the blanks from between arguments. For example, a common alias is: .Pp .Bd -literal -offset indent -compact msgs: "|/usr/bin/msgs -s" .Ed .Pp Aliases may also have the syntax .Dq :include: Ns Ar filename to ask .Xr sendmail to read the named file for a list of recipients. For example, an alias such as: .Pp .Bd -literal -offset indent -compact poets: ":include:/usr/local/lib/poets.list" .Ed .Pp would read .Pa /usr/local/lib/poets.list for the list of addresses making up the group. .Pp .Nm Sendmail returns an exit status describing what it did. The codes are defined in .Aq Pa sysexits.h : .Bl -tag -width EX_UNAVAILABLE -compact -offset indent .It Dv EX_OK Successful completion on all addresses. .It Dv EX_NOUSER User name not recognized. .It Dv EX_UNAVAILABLE Catchall meaning necessary resources were not available. .It Dv EX_SYNTAX Syntax error in address. .It Dv EX_SOFTWARE Internal software error, including bad arguments. .It Dv EX_OSERR Temporary operating system error, such as .Dq cannot fork . .It Dv EX_NOHOST Host name not recognized. .It Dv EX_TEMPFAIL Message could not be sent immediately, but was queued. .El .Pp If invoked as .Nm newaliases , .Nm sendmail will rebuild the alias database. If invoked as .Nm mailq , .Nm sendmail will print the contents of the mail queue. .Sh FILES Except for the file .Pa /etc/sendmail.cf itself, the following pathnames are all specified in .Pa /etc/sendmail.cf. Thus, these values are only approximations. .Pp .Bl -tag -width /usr/lib/sendmail.fc -compact .It Pa /etc/aliases raw data for alias names .It Pa /etc/aliases.db data base of alias names .It Pa /etc/sendmail.cf configuration file .It Pa /usr/share/misc/sendmail.hf help file .It Pa /var/log/sendmail.st collected statistics .It Pa /var/spool/mqueue/* temp files .It Pa /var/run/sendmail.pid The process id of the daemon .El .Sh SEE ALSO .Xr mail 1 , .Xr syslog 3 , .Xr aliases 5 , .Xr mailaddr 7 , .Xr mail.local 8 , .Xr rc 8 , .Xr rmail 8 ; .Pp DARPA Internet Request For Comments .%T RFC819 , .%T RFC821 , .%T RFC822 . .Rs .%T "Sendmail \- An Internetwork Mail Router" .%V SMM .%N \&No. 9 .Re .Rs .%T "Sendmail Installation and Operation Guide" .%V SMM .%N \&No. 8 .Re .Sh HISTORY The .Nm command appeared in .Bx 4.2 . Index: head/usr.sbin/sendmail/src/sendmail.h =================================================================== --- head/usr.sbin/sendmail/src/sendmail.h (revision 26988) +++ head/usr.sbin/sendmail/src/sendmail.h (revision 26989) @@ -1,1405 +1,1442 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)sendmail.h 8.219 (Berkeley) 1/14/97 + * @(#)sendmail.h 8.236 (Berkeley) 6/5/97 */ /* ** SENDMAIL.H -- Global definitions for sendmail. */ # ifdef _DEFINE # define EXTERN # ifndef lint -static char SmailSccsId[] = "@(#)sendmail.h 8.219 1/14/97"; +static char SmailSccsId[] = "@(#)sendmail.h 8.236 6/5/97"; # endif # else /* _DEFINE */ # define EXTERN extern # endif /* _DEFINE */ # include # include # include # include # include # include # include # include # include # ifdef EX_OK # undef EX_OK /* for SVr4.2 SMP */ # endif # include # include "conf.h" # include "useful.h" # ifdef LOG # include # endif /* LOG */ # if NETINET || NETUNIX || NETISO || NETNS || NETX25 # include # endif # if NETUNIX # include # endif # if NETINET # include # endif # if NETISO # include # endif # if NETNS # include # endif # if NETX25 # include # endif +#if NAMED_BIND +# include +# ifdef NOERROR +# undef NOERROR /* avoid conflict */ +# endif +#endif + /* forward references for prototypes */ typedef struct envelope ENVELOPE; typedef struct mailer MAILER; /* ** Data structure for bit maps. ** ** Each bit in this map can be referenced by an ascii character. ** This is 256 possible bits, or 32 8-bit bytes. */ #define BITMAPBYTES 32 /* number of bytes in a bit map */ #define BYTEBITS 8 /* number of bits in a byte */ /* internal macros */ #define _BITWORD(bit) ((bit) / (BYTEBITS * sizeof (int))) #define _BITBIT(bit) (1 << ((bit) % (BYTEBITS * sizeof (int)))) typedef int BITMAP[BITMAPBYTES / sizeof (int)]; /* test bit number N */ #define bitnset(bit, map) ((map)[_BITWORD(bit)] & _BITBIT(bit)) /* set bit number N */ #define setbitn(bit, map) (map)[_BITWORD(bit)] |= _BITBIT(bit) /* clear bit number N */ #define clrbitn(bit, map) (map)[_BITWORD(bit)] &= ~_BITBIT(bit) /* clear an entire bit map */ #define clrbitmap(map) bzero((char *) map, BITMAPBYTES) /* ** Utility macros */ /* return number of bytes left in a buffer */ #define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf)) /* ** Address structure. ** Addresses are stored internally in this structure. */ struct address { char *q_paddr; /* the printname for the address */ char *q_user; /* user name */ char *q_ruser; /* real user name, or NULL if q_user */ char *q_host; /* host name */ struct mailer *q_mailer; /* mailer to use */ u_long q_flags; /* status flags, see below */ uid_t q_uid; /* user-id of receiver (if known) */ gid_t q_gid; /* group-id of receiver (if known) */ char *q_home; /* home dir (local mailer only) */ char *q_fullname; /* full name if known */ struct address *q_next; /* chain */ struct address *q_alias; /* address this results from */ char *q_owner; /* owner of q_alias */ struct address *q_tchain; /* temporary use chain */ char *q_orcpt; /* ORCPT parameter from RCPT TO: line */ char *q_status; /* status code for DSNs */ char *q_rstatus; /* remote status message for DSNs */ time_t q_statdate; /* date of status messages */ char *q_statmta; /* MTA generating q_rstatus */ short q_specificity; /* how "specific" this address is */ }; typedef struct address ADDRESS; # define QDONTSEND 0x00000001 /* don't send to this address */ # define QBADADDR 0x00000002 /* this address is verified bad */ # define QGOODUID 0x00000004 /* the q_uid q_gid fields are good */ # define QPRIMARY 0x00000008 /* set from RCPT or argv */ # define QQUEUEUP 0x00000010 /* queue for later transmission */ # define QSENT 0x00000020 /* has been successfully delivered */ # define QNOTREMOTE 0x00000040 /* address not for remote forwarding */ # define QSELFREF 0x00000080 /* this address references itself */ # define QVERIFIED 0x00000100 /* verified, but not expanded */ # define QBOGUSSHELL 0x00000400 /* user has no valid shell listed */ # define QUNSAFEADDR 0x00000800 /* address aquired via unsafe path */ # define QPINGONSUCCESS 0x00001000 /* give return on successful delivery */ # define QPINGONFAILURE 0x00002000 /* give return on failure */ # define QPINGONDELAY 0x00004000 /* give return on message delay */ # define QHASNOTIFY 0x00008000 /* propogate notify parameter */ # define QRELAYED 0x00010000 /* DSN: relayed to non-DSN aware sys */ # define QEXPANDED 0x00020000 /* DSN: undergone list expansion */ # define QDELIVERED 0x00040000 /* DSN: successful final delivery */ # define QDELAYED 0x00080000 /* DSN: message delayed */ # define QTHISPASS 0x40000000 /* temp: address set this pass */ # define Q_PINGFLAGS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY) # define NULLADDR ((ADDRESS *) NULL) /* functions */ extern ADDRESS *parseaddr __P((char *, ADDRESS *, int, int, char **, ENVELOPE *)); extern ADDRESS *buildaddr __P((char **, ADDRESS *, int, ENVELOPE *)); extern ADDRESS *recipient __P((ADDRESS *, ADDRESS **, int, ENVELOPE *)); extern char **prescan __P((char *, int, char[], int, char **, u_char *)); extern int rewrite __P((char **, int, int, ENVELOPE *)); extern char *remotename __P((char *, MAILER *, int, int *, ENVELOPE *)); extern ADDRESS *getctladdr __P((ADDRESS *)); extern bool sameaddr __P((ADDRESS *, ADDRESS *)); extern bool emptyaddr __P((ADDRESS *)); extern void printaddr __P((ADDRESS *, bool)); extern void cataddr __P((char **, char **, char *, int, int)); extern int sendtolist __P((char *, ADDRESS *, ADDRESS **, int, ENVELOPE *)); /* ** Mailer definition structure. ** Every mailer known to the system is declared in this ** structure. It defines the pathname of the mailer, some ** flags associated with it, and the argument vector to ** pass to it. The flags are defined in conf.c ** ** The argument vector is expanded before actual use. All ** words except the first are passed through the macro ** processor. */ struct mailer { char *m_name; /* symbolic name of this mailer */ char *m_mailer; /* pathname of the mailer to use */ char *m_mtatype; /* type of this MTA */ char *m_addrtype; /* type for addresses */ char *m_diagtype; /* type for diagnostics */ BITMAP m_flags; /* status flags, see below */ short m_mno; /* mailer number internally */ short m_nice; /* niceness to run at (mostly for prog) */ char **m_argv; /* template argument vector */ short m_sh_rwset; /* rewrite set: sender header addresses */ short m_se_rwset; /* rewrite set: sender envelope addresses */ short m_rh_rwset; /* rewrite set: recipient header addresses */ short m_re_rwset; /* rewrite set: recipient envelope addresses */ char *m_eol; /* end of line string */ long m_maxsize; /* size limit on message to this mailer */ int m_linelimit; /* max # characters per line */ char *m_execdir; /* directory to chdir to before execv */ uid_t m_uid; /* UID to run as */ gid_t m_gid; /* GID to run as */ char *m_defcharset; /* default character set */ }; /* bits for m_flags */ # define M_ESMTP 'a' /* run Extended SMTP protocol */ # define M_ALIASABLE 'A' /* user can be LHS of an alias */ # define M_BLANKEND 'b' /* ensure blank line at end of message */ # define M_NOCOMMENT 'c' /* don't include comment part of address */ # define M_CANONICAL 'C' /* make addresses canonical "u@dom" */ # define M_NOBRACKET 'd' /* never angle bracket envelope route-addrs */ /* 'D' CF: include Date: */ # define M_EXPENSIVE 'e' /* it costs to use this mailer.... */ # define M_ESCFROM 'E' /* escape From lines to >From */ # define M_FOPT 'f' /* mailer takes picky -f flag */ /* 'F' CF: include From: or Resent-From: */ # define M_NO_NULL_FROM 'g' /* sender of errors should be $g */ # define M_HST_UPPER 'h' /* preserve host case distinction */ # define M_PREHEAD 'H' /* MAIL11V3: preview headers */ # define M_UDBENVELOPE 'i' /* do udbsender rewriting on envelope */ # define M_INTERNAL 'I' /* SMTP to another sendmail site */ # define M_UDBRECIPIENT 'j' /* do udbsender rewriting on recipient lines */ # define M_NOLOOPCHECK 'k' /* don't check for loops in HELO command */ # define M_CHUNKING 'K' /* CHUNKING: reserved for future use */ # define M_LOCALMAILER 'l' /* delivery is to this host */ # define M_LIMITS 'L' /* must enforce SMTP line limits */ # define M_MUSER 'm' /* can handle multiple users at once */ /* 'M' CF: include Message-Id: */ # define M_NHDR 'n' /* don't insert From line */ # define M_MANYSTATUS 'N' /* MAIL11V3: DATA returns multi-status */ # define M_RUNASRCPT 'o' /* always run mailer as recipient */ # define M_FROMPATH 'p' /* use reverse-path in MAIL FROM: */ /* 'P' CF: include Return-Path: */ # define M_VRFY250 'q' /* VRFY command returns 250 instead of 252 */ # define M_ROPT 'r' /* mailer takes picky -r flag */ # define M_SECURE_PORT 'R' /* try to send on a reserved TCP port */ # define M_STRIPQ 's' /* strip quote chars from user/host */ # define M_SPECIFIC_UID 'S' /* run as specific uid/gid */ # define M_USR_UPPER 'u' /* preserve user case distinction */ # define M_UGLYUUCP 'U' /* this wants an ugly UUCP from line */ # define M_CONTENT_LEN 'v' /* add Content-Length: header (SVr4) */ /* 'V' UIUC: !-relativize all addresses */ # define M_HASPWENT 'w' /* check for /etc/passwd entry */ /* 'x' CF: include Full-Name: */ # define M_XDOT 'X' /* use hidden-dot algorithm */ # define M_LMTP 'z' /* run Local Mail Transport Protocol */ # define M_NOMX '0' /* turn off MX lookups */ # define M_NONULLS '1' /* don't send null bytes */ # define M_EBCDIC '3' /* extend Q-P encoding for EBCDIC */ # define M_TRYRULESET5 '5' /* use ruleset 5 after local aliasing */ # define M_7BITHDRS '6' /* strip headers to 7 bits even in 8 bit path */ # define M_7BITS '7' /* use 7-bit path */ # define M_8BITS '8' /* force "just send 8" behaviour */ # define M_MAKE8BIT '9' /* convert 7 -> 8 bit if appropriate */ # define M_CHECKINCLUDE ':' /* check for :include: files */ # define M_CHECKPROG '|' /* check for |program addresses */ # define M_CHECKFILE '/' /* check for /file addresses */ # define M_CHECKUDB '@' /* user can be user database key */ # define M_CHECKHDIR '~' /* SGI: check for valid home directory */ EXTERN MAILER *Mailer[MAXMAILERS+1]; EXTERN MAILER *LocalMailer; /* ptr to local mailer */ EXTERN MAILER *ProgMailer; /* ptr to program mailer */ EXTERN MAILER *FileMailer; /* ptr to *file* mailer */ EXTERN MAILER *InclMailer; /* ptr to *include* mailer */ /* ** Information about currently open connections to mailers, or to ** hosts that we have looked up recently. */ # define MCI struct mailer_con_info MCI { short mci_flags; /* flag bits, see below */ short mci_errno; /* error number on last connection */ short mci_herrno; /* h_errno from last DNS lookup */ short mci_exitstat; /* exit status from last connection */ short mci_state; /* SMTP state */ long mci_maxsize; /* max size this server will accept */ FILE *mci_in; /* input side of connection */ FILE *mci_out; /* output side of connection */ pid_t mci_pid; /* process id of subordinate proc */ char *mci_phase; /* SMTP phase string */ struct mailer *mci_mailer; /* ptr to the mailer for this conn */ char *mci_host; /* host name */ char *mci_status; /* DSN status to be copied to addrs */ char *mci_rstatus; /* SMTP status to be copied to addrs */ time_t mci_lastuse; /* last usage time */ FILE *mci_statfile; /* long term status file */ }; /* flag bits */ #define MCIF_VALID 0x0001 /* this entry is valid */ #define MCIF_TEMP 0x0002 /* don't cache this connection */ #define MCIF_CACHED 0x0004 /* currently in open cache */ #define MCIF_ESMTP 0x0008 /* this host speaks ESMTP */ #define MCIF_EXPN 0x0010 /* EXPN command supported */ #define MCIF_SIZE 0x0020 /* SIZE option supported */ #define MCIF_8BITMIME 0x0040 /* BODY=8BITMIME supported */ #define MCIF_7BIT 0x0080 /* strip this message to 7 bits */ #define MCIF_MULTSTAT 0x0100 /* MAIL11V3: handles MULT status */ #define MCIF_INHEADER 0x0200 /* currently outputing header */ #define MCIF_CVT8TO7 0x0400 /* convert from 8 to 7 bits */ #define MCIF_DSN 0x0800 /* DSN extension supported */ #define MCIF_8BITOK 0x1000 /* OK to send 8 bit characters */ #define MCIF_CVT7TO8 0x2000 /* convert from 7 to 8 bits */ #define MCIF_INMIME 0x4000 /* currently reading MIME header */ /* states */ #define MCIS_CLOSED 0 /* no traffic on this connection */ #define MCIS_OPENING 1 /* sending initial protocol */ #define MCIS_OPEN 2 /* open, initial protocol sent */ #define MCIS_ACTIVE 3 /* message being sent */ #define MCIS_QUITING 4 /* running quit protocol */ #define MCIS_SSD 5 /* service shutting down */ #define MCIS_ERROR 6 /* I/O error on connection */ /* functions */ extern MCI *mci_get __P((char *, MAILER *)); extern void mci_cache __P((MCI *)); extern void mci_flush __P((bool, MCI *)); extern void mci_dump __P((MCI *, bool)); extern void mci_dump_all __P((bool)); extern MCI **mci_scan __P((MCI *)); extern int mci_traverse_persistent __P((int (), char *)); extern int mci_print_persistent __P((char *, char *)); extern int mci_purge_persistent __P((char *, char *)); extern int mci_lock_host __P((MCI *)); extern void mci_unlock_host __P((MCI *)); extern int mci_lock_host_statfile __P((MCI *)); extern void mci_store_persistent __P((MCI *)); extern int mci_read_persistent __P((FILE *, MCI *)); /* ** Header structure. ** This structure is used internally to store header items. */ struct header { char *h_field; /* the name of the field */ char *h_value; /* the value of that field */ struct header *h_link; /* the next header */ u_short h_flags; /* status bits, see below */ BITMAP h_mflags; /* m_flags bits needed */ }; typedef struct header HDR; /* ** Header information structure. ** Defined in conf.c, this struct declares the header fields ** that have some magic meaning. */ struct hdrinfo { char *hi_field; /* the name of the field */ u_short hi_flags; /* status bits, see below */ + char *hi_ruleset; /* validity check ruleset */ }; extern struct hdrinfo HdrInfo[]; /* bits for h_flags and hi_flags */ # define H_EOH 0x0001 /* this field terminates header */ # define H_RCPT 0x0002 /* contains recipient addresses */ # define H_DEFAULT 0x0004 /* if another value is found, drop this */ # define H_RESENT 0x0008 /* this address is a "Resent-..." address */ # define H_CHECK 0x0010 /* check h_mflags against m_flags */ # define H_ACHECK 0x0020 /* ditto, but always (not just default) */ # define H_FORCE 0x0040 /* force this field, even if default */ # define H_TRACE 0x0080 /* this field contains trace information */ # define H_FROM 0x0100 /* this is a from-type field */ # define H_VALID 0x0200 /* this field has a validated value */ # define H_RECEIPTTO 0x0400 /* this field has return receipt info */ # define H_ERRORSTO 0x0800 /* this field has error address info */ # define H_CTE 0x1000 /* this field is a content-transfer-encoding */ # define H_CTYPE 0x2000 /* this is a content-type field */ # define H_BCC 0x4000 /* Bcc: header: strip value or delete */ # define H_ENCODABLE 0x8000 /* field can be RFC 1522 encoded */ /* functions */ extern void addheader __P((char *, char *, HDR **)); extern char *hvalue __P((char *, HDR *)); extern void commaize __P((HDR *, char *, bool, MCI *, ENVELOPE *)); extern void put_vanilla_header __P((HDR *, char *, MCI *)); extern void eatheader __P((ENVELOPE *e, bool)); extern int chompheader __P((char *, bool, HDR **, ENVELOPE *)); /* ** Envelope structure. ** This structure defines the message itself. There is usually ** only one of these -- for the message that we originally read ** and which is our primary interest -- but other envelopes can ** be generated during processing. For example, error messages ** will have their own envelope. */ struct envelope { HDR *e_header; /* head of header list */ long e_msgpriority; /* adjusted priority of this message */ time_t e_ctime; /* time message appeared in the queue */ char *e_to; /* the target person */ ADDRESS e_from; /* the person it is from */ char *e_sender; /* e_from.q_paddr w comments stripped */ char **e_fromdomain; /* the domain part of the sender */ ADDRESS *e_sendqueue; /* list of message recipients */ ADDRESS *e_errorqueue; /* the queue for error responses */ long e_msgsize; /* size of the message in bytes */ long e_flags; /* flags, see below */ int e_nrcpts; /* number of recipients */ short e_class; /* msg class (priority, junk, etc.) */ short e_hopcount; /* number of times processed */ short e_nsent; /* number of sends since checkpoint */ short e_sendmode; /* message send mode */ short e_errormode; /* error return mode */ short e_timeoutclass; /* message timeout class */ void (*e_puthdr)__P((MCI *, HDR *, ENVELOPE *)); /* function to put header of message */ void (*e_putbody)__P((MCI *, ENVELOPE *, char *)); /* function to put body of message */ struct envelope *e_parent; /* the message this one encloses */ struct envelope *e_sibling; /* the next envelope of interest */ char *e_bodytype; /* type of message body */ FILE *e_dfp; /* temporary file */ char *e_id; /* code for this entry in queue */ FILE *e_xfp; /* transcript file */ FILE *e_lockfp; /* the lock file for this message */ char *e_message; /* error message */ char *e_statmsg; /* stat msg (changes per delivery) */ char *e_msgboundary; /* MIME-style message part boundary */ char *e_origrcpt; /* original recipient (one only) */ char *e_envid; /* envelope id from MAIL FROM: line */ char *e_status; /* DSN status for this message */ time_t e_dtime; /* time of last delivery attempt */ int e_ntries; /* number of delivery attempts */ dev_t e_dfdev; /* df file's device, for crash recov */ ino_t e_dfino; /* df file's ino, for crash recovery */ char *e_macro[256]; /* macro definitions */ }; /* values for e_flags */ #define EF_OLDSTYLE 0x0000001 /* use spaces (not commas) in hdrs */ #define EF_INQUEUE 0x0000002 /* this message is fully queued */ #define EF_NO_BODY_RETN 0x0000004 /* omit message body on error */ #define EF_CLRQUEUE 0x0000008 /* disk copy is no longer needed */ #define EF_SENDRECEIPT 0x0000010 /* send a return receipt */ #define EF_FATALERRS 0x0000020 /* fatal errors occured */ #define EF_DELETE_BCC 0x0000040 /* delete Bcc: headers entirely */ #define EF_RESPONSE 0x0000080 /* this is an error or return receipt */ #define EF_RESENT 0x0000100 /* this message is being forwarded */ #define EF_VRFYONLY 0x0000200 /* verify only (don't expand aliases) */ #define EF_WARNING 0x0000400 /* warning message has been sent */ #define EF_QUEUERUN 0x0000800 /* this envelope is from queue */ #define EF_GLOBALERRS 0x0001000 /* treat errors as global */ #define EF_PM_NOTIFY 0x0002000 /* send return mail to postmaster */ #define EF_METOO 0x0004000 /* send to me too */ #define EF_LOGSENDER 0x0008000 /* need to log the sender */ #define EF_NORECEIPT 0x0010000 /* suppress all return-receipts */ #define EF_HAS8BIT 0x0020000 /* at least one 8-bit char in body */ #define EF_NL_NOT_EOL 0x0040000 /* don't accept raw NL as EOLine */ #define EF_CRLF_NOT_EOL 0x0080000 /* don't accept CR-LF as EOLine */ #define EF_RET_PARAM 0x0100000 /* RCPT command had RET argument */ #define EF_HAS_DF 0x0200000 /* set when df file is instantiated */ #define EF_IS_MIME 0x0400000 /* really is a MIME message */ #define EF_DONT_MIME 0x0800000 /* never MIME this message */ EXTERN ENVELOPE *CurEnv; /* envelope currently being processed */ /* functions */ extern ENVELOPE *newenvelope __P((ENVELOPE *, ENVELOPE *)); extern void dropenvelope __P((ENVELOPE *, bool)); extern void clearenvelope __P((ENVELOPE *, bool)); extern void putheader __P((MCI *, HDR *, ENVELOPE *)); extern void putbody __P((MCI *, ENVELOPE *, char *)); /* ** Message priority classes. ** ** The message class is read directly from the Priority: header ** field in the message. ** ** CurEnv->e_msgpriority is the number of bytes in the message plus ** the creation time (so that jobs ``tend'' to be ordered correctly), ** adjusted by the message class, the number of recipients, and the ** amount of time the message has been sitting around. This number ** is used to order the queue. Higher values mean LOWER priority. ** ** Each priority class point is worth WkClassFact priority points; ** each recipient is worth WkRecipFact priority points. Each time ** we reprocess a message the priority is adjusted by WkTimeFact. ** WkTimeFact should normally decrease the priority so that jobs ** that have historically failed will be run later; thanks go to ** Jay Lepreau at Utah for pointing out the error in my thinking. ** ** The "class" is this number, unadjusted by the age or size of ** this message. Classes with negative representations will have ** error messages thrown away if they are not local. */ struct priority { char *pri_name; /* external name of priority */ int pri_val; /* internal value for same */ }; EXTERN struct priority Priorities[MAXPRIORITIES]; EXTERN int NumPriorities; /* pointer into Priorities */ /* ** Rewrite rules. */ struct rewrite { char **r_lhs; /* pattern match */ char **r_rhs; /* substitution value */ struct rewrite *r_next;/* next in chain */ }; EXTERN struct rewrite *RewriteRules[MAXRWSETS]; /* ** Special characters in rewriting rules. ** These are used internally only. ** The COND* rules are actually used in macros rather than in ** rewriting rules, but are given here because they ** cannot conflict. */ /* left hand side items */ # define MATCHZANY ((u_char)0220) /* match zero or more tokens */ # define MATCHANY ((u_char)0221) /* match one or more tokens */ # define MATCHONE ((u_char)0222) /* match exactly one token */ # define MATCHCLASS ((u_char)0223) /* match one token in a class */ # define MATCHNCLASS ((u_char)0224) /* match anything not in class */ # define MATCHREPL ((u_char)0225) /* replacement on RHS for above */ /* right hand side items */ # define CANONNET ((u_char)0226) /* canonical net, next token */ # define CANONHOST ((u_char)0227) /* canonical host, next token */ # define CANONUSER ((u_char)0230) /* canonical user, next N tokens */ # define CALLSUBR ((u_char)0231) /* call another rewriting set */ /* conditionals in macros */ # define CONDIF ((u_char)0232) /* conditional if-then */ # define CONDELSE ((u_char)0233) /* conditional else */ # define CONDFI ((u_char)0234) /* conditional fi */ /* bracket characters for host name lookup */ # define HOSTBEGIN ((u_char)0235) /* hostname lookup begin */ # define HOSTEND ((u_char)0236) /* hostname lookup end */ /* bracket characters for generalized lookup */ # define LOOKUPBEGIN ((u_char)0205) /* generalized lookup begin */ # define LOOKUPEND ((u_char)0206) /* generalized lookup end */ /* macro substitution character */ # define MACROEXPAND ((u_char)0201) /* macro expansion */ # define MACRODEXPAND ((u_char)0202) /* deferred macro expansion */ /* to make the code clearer */ # define MATCHZERO CANONHOST /* external <==> internal mapping table */ struct metamac { char metaname; /* external code (after $) */ u_char metaval; /* internal code (as above) */ }; /* values for macros with external names only */ # define MID_OPMODE 0202 /* operation mode */ /* functions */ extern void expand __P((char *, char *, size_t, ENVELOPE *)); extern void define __P((int, char *, ENVELOPE *)); extern char *macvalue __P((int, ENVELOPE *)); extern char *macname __P((int)); extern int macid __P((char *, char **)); /* ** Name canonification short circuit. ** ** If the name server for a host is down, the process of trying to ** canonify the name can hang. This is similar to (but alas, not ** identical to) looking up the name for delivery. This stab type ** caches the result of the name server lookup so we don't hang ** multiple times. */ #define NAMECANON struct _namecanon NAMECANON { short nc_errno; /* cached errno */ short nc_herrno; /* cached h_errno */ short nc_stat; /* cached exit status code */ short nc_flags; /* flag bits */ char *nc_cname; /* the canonical name */ }; /* values for nc_flags */ #define NCF_VALID 0x0001 /* entry valid */ /* ** Mapping functions ** ** These allow arbitrary mappings in the config file. The idea ** (albeit not the implementation) comes from IDA sendmail. */ # define MAPCLASS struct _mapclass # define MAP struct _map # define MAXMAPACTIONS 3 /* size of map_actions array */ /* ** An actual map. */ MAP { MAPCLASS *map_class; /* the class of this map */ char *map_mname; /* name of this map */ long map_mflags; /* flags, see below */ char *map_file; /* the (nominal) filename */ ARBPTR_T map_db1; /* the open database ptr */ ARBPTR_T map_db2; /* an "extra" database pointer */ char *map_keycolnm; /* key column name */ char *map_valcolnm; /* value column name */ u_char map_keycolno; /* key column number */ u_char map_valcolno; /* value column number */ char map_coldelim; /* column delimiter */ char *map_app; /* to append to successful matches */ char *map_domain; /* the (nominal) NIS domain */ char *map_rebuild; /* program to run to do auto-rebuild */ time_t map_mtime; /* last database modification time */ int map_lockfd; /* auxiliary lock file descriptor */ short map_specificity; /* specificity of aliases */ MAP *map_stack[MAXMAPSTACK]; /* list for stacked maps */ short map_return[MAXMAPACTIONS]; /* return bitmaps for stacked maps */ }; /* bit values for map_mflags */ # define MF_VALID 0x00000001 /* this entry is valid */ # define MF_INCLNULL 0x00000002 /* include null byte in key */ # define MF_OPTIONAL 0x00000004 /* don't complain if map not found */ # define MF_NOFOLDCASE 0x00000008 /* don't fold case in keys */ # define MF_MATCHONLY 0x00000010 /* don't use the map value */ # define MF_OPEN 0x00000020 /* this entry is open */ # define MF_WRITABLE 0x00000040 /* open for writing */ # define MF_ALIAS 0x00000080 /* this is an alias file */ # define MF_TRY0NULL 0x00000100 /* try with no null byte */ # define MF_TRY1NULL 0x00000200 /* try with the null byte */ # define MF_LOCKED 0x00000400 /* this map is currently locked */ # define MF_ALIASWAIT 0x00000800 /* alias map in aliaswait state */ # define MF_IMPL_HASH 0x00001000 /* implicit: underlying hash database */ # define MF_IMPL_NDBM 0x00002000 /* implicit: underlying NDBM database */ # define MF_UNSAFEDB 0x00004000 /* this map is world writable */ # define MF_APPEND 0x00008000 /* append new entry on rebuiled */ # define MF_KEEPQUOTES 0x00010000 /* don't dequote key before lookup */ # define MF_NODEFER 0x00020000 /* don't defer if map lookup fails */ /* indices for map_actions */ # define MA_NOTFOUND 0 /* member map returned "not found" */ # define MA_UNAVAIL 1 /* member map is not available */ # define MA_TRYAGAIN 2 /* member map returns temp failure */ /* ** The class of a map -- essentially the functions to call */ MAPCLASS { char *map_cname; /* name of this map class */ char *map_ext; /* extension for database file */ short map_cflags; /* flag bits, see below */ bool (*map_parse)__P((MAP *, char *)); /* argument parsing function */ char *(*map_lookup)__P((MAP *, char *, char **, int *)); /* lookup function */ void (*map_store)__P((MAP *, char *, char *)); /* store function */ bool (*map_open)__P((MAP *, int)); /* open function */ void (*map_close)__P((MAP *)); /* close function */ }; /* bit values for map_cflags */ #define MCF_ALIASOK 0x0001 /* can be used for aliases */ #define MCF_ALIASONLY 0x0002 /* usable only for aliases */ #define MCF_REBUILDABLE 0x0004 /* can rebuild alias files */ #define MCF_OPTFILE 0x0008 /* file name is optional */ /* functions */ -extern char *map_rewrite __P((MAP *, char *, int, char **)); +extern char *map_rewrite __P((MAP *, const char *, int, char **)); extern MAP *makemapentry __P((char *)); extern void initmaps __P((bool, ENVELOPE *)); /* ** Symbol table definitions */ struct symtab { char *s_name; /* name to be entered */ short s_type; /* general type (see below) */ short s_len; /* length of this entry */ struct symtab *s_next; /* pointer to next in chain */ union { BITMAP sv_class; /* bit-map of word classes */ ADDRESS *sv_addr; /* pointer to address header */ MAILER *sv_mailer; /* pointer to mailer */ char *sv_alias; /* alias */ MAPCLASS sv_mapclass; /* mapping function class */ MAP sv_map; /* mapping function */ char *sv_hostsig; /* host signature */ MCI sv_mci; /* mailer connection info */ NAMECANON sv_namecanon; /* canonical name cache */ int sv_macro; /* macro name => id mapping */ int sv_ruleset; /* ruleset index */ + struct hdrinfo sv_header; /* header metainfo */ char *sv_service[MAXMAPSTACK]; /* service switch */ } s_value; }; typedef struct symtab STAB; /* symbol types */ # define ST_UNDEF 0 /* undefined type */ # define ST_CLASS 1 /* class map */ # define ST_ADDRESS 2 /* an address in parsed format */ # define ST_MAILER 3 /* a mailer header */ # define ST_ALIAS 4 /* an alias */ # define ST_MAPCLASS 5 /* mapping function class */ # define ST_MAP 6 /* mapping function */ # define ST_HOSTSIG 7 /* host signature */ # define ST_NAMECANON 8 /* cached canonical name */ # define ST_MACRO 9 /* macro name to id mapping */ # define ST_RULESET 10 /* ruleset index */ # define ST_SERVICE 11 /* service switch entry */ +# define ST_HEADER 12 /* special header flags */ # define ST_MCI 16 /* mailer connection info (offset) */ # define s_class s_value.sv_class # define s_address s_value.sv_addr # define s_mailer s_value.sv_mailer # define s_alias s_value.sv_alias # define s_mci s_value.sv_mci # define s_mapclass s_value.sv_mapclass # define s_hostsig s_value.sv_hostsig # define s_map s_value.sv_map # define s_namecanon s_value.sv_namecanon # define s_macro s_value.sv_macro # define s_ruleset s_value.sv_ruleset # define s_service s_value.sv_service +# define s_header s_value.sv_header extern STAB *stab __P((char *, int, int)); extern void stabapply __P((void (*)(STAB *, int), int)); /* opcodes to stab */ # define ST_FIND 0 /* find entry */ # define ST_ENTER 1 /* enter if not there */ /* ** STRUCT EVENT -- event queue. ** ** Maintained in sorted order. ** ** We store the pid of the process that set this event to insure ** that when we fork we will not take events intended for the parent. */ struct event { time_t ev_time; /* time of the function call */ void (*ev_func)__P((int)); /* function to call */ int ev_arg; /* argument to ev_func */ int ev_pid; /* pid that set this event */ struct event *ev_link; /* link to next item */ }; typedef struct event EVENT; EXTERN EVENT *EventQueue; /* head of event queue */ /* functions */ extern EVENT *setevent __P((time_t, void(*)(), int)); extern void clrevent __P((EVENT *)); /* ** Operation, send, error, and MIME modes ** ** The operation mode describes the basic operation of sendmail. ** This can be set from the command line, and is "send mail" by ** default. ** ** The send mode tells how to send mail. It can be set in the ** configuration file. It's setting determines how quickly the ** mail will be delivered versus the load on your system. If the ** -v (verbose) flag is given, it will be forced to SM_DELIVER ** mode. ** ** The error mode tells how to return errors. */ EXTERN char OpMode; /* operation mode, see below */ #define MD_DELIVER 'm' /* be a mail sender */ #define MD_SMTP 's' /* run SMTP on standard input */ #define MD_ARPAFTP 'a' /* obsolete ARPANET mode (Grey Book) */ #define MD_DAEMON 'd' /* run as a daemon */ #define MD_FGDAEMON 'D' /* run daemon in foreground */ #define MD_VERIFY 'v' /* verify: don't collect or deliver */ #define MD_TEST 't' /* test mode: resolve addrs only */ #define MD_INITALIAS 'i' /* initialize alias database */ #define MD_PRINT 'p' /* print the queue */ #define MD_FREEZE 'z' /* freeze the configuration file */ #define MD_HOSTSTAT 'h' /* print persistent host stat info */ #define MD_PURGESTAT 'H' /* purge persistent host stat info */ /* values for e_sendmode -- send modes */ #define SM_DELIVER 'i' /* interactive delivery */ #define SM_FORK 'b' /* deliver in background */ #define SM_QUEUE 'q' /* queue, don't deliver */ #define SM_DEFER 'd' /* defer map lookups as well as queue */ #define SM_VERIFY 'v' /* verify only (used internally) */ /* used only as a parameter to sendall */ #define SM_DEFAULT '\0' /* unspecified, use SendMode */ /* values for e_errormode -- error handling modes */ #define EM_PRINT 'p' /* print errors */ #define EM_MAIL 'm' /* mail back errors */ #define EM_WRITE 'w' /* write back errors */ #define EM_BERKNET 'e' /* special berknet processing */ #define EM_QUIET 'q' /* don't print messages (stat only) */ /* MIME processing mode */ EXTERN int MimeMode; /* bit values for MimeMode */ #define MM_CVTMIME 0x0001 /* convert 8 to 7 bit MIME */ #define MM_PASS8BIT 0x0002 /* just send 8 bit data blind */ #define MM_MIME8BIT 0x0004 /* convert 8-bit data to MIME */ /* queue sorting order algorithm */ EXTERN int QueueSortOrder; #define QS_BYPRIORITY 0 /* sort by message priority */ #define QS_BYHOST 1 /* sort by first host name */ #define QS_BYTIME 2 /* sort by submission time */ /* how to handle messages without any recipient addresses */ EXTERN int NoRecipientAction; #define NRA_NO_ACTION 0 /* just leave it as is */ #define NRA_ADD_TO 1 /* add To: header */ #define NRA_ADD_APPARENTLY_TO 2 /* add Apparently-To: header */ #define NRA_ADD_BCC 3 /* add empty Bcc: header */ #define NRA_ADD_TO_UNDISCLOSED 4 /* add To: undisclosed:; header */ /* flags to putxline */ #define PXLF_NOTHINGSPECIAL 0 /* no special mapping */ #define PXLF_MAPFROM 0x0001 /* map From_ to >From_ */ #define PXLF_STRIP8BIT 0x0002 /* strip 8th bit */ /* ** Additional definitions */ /* ** Privacy flags ** These are bit values for the PrivacyFlags word. */ #define PRIV_PUBLIC 0 /* what have I got to hide? */ #define PRIV_NEEDMAILHELO 0x0001 /* insist on HELO for MAIL, at least */ #define PRIV_NEEDEXPNHELO 0x0002 /* insist on HELO for EXPN */ #define PRIV_NEEDVRFYHELO 0x0004 /* insist on HELO for VRFY */ #define PRIV_NOEXPN 0x0008 /* disallow EXPN command entirely */ #define PRIV_NOVRFY 0x0010 /* disallow VRFY command entirely */ #define PRIV_AUTHWARNINGS 0x0020 /* flag possible authorization probs */ #define PRIV_NORECEIPTS 0x0040 /* disallow return receipts */ +#define PRIV_NOETRN 0x0080 /* disallow ETRN command entirely */ #define PRIV_RESTRICTMAILQ 0x1000 /* restrict mailq command */ #define PRIV_RESTRICTQRUN 0x2000 /* restrict queue run */ #define PRIV_GOAWAY 0x0fff /* don't give no info, anyway, anyhow */ /* struct defining such things */ struct prival { char *pv_name; /* name of privacy flag */ int pv_flag; /* numeric level */ }; /* ** Flags passed to remotename, parseaddr, allocaddr, and buildaddr. */ #define RF_SENDERADDR 0x001 /* this is a sender address */ #define RF_HEADERADDR 0x002 /* this is a header address */ #define RF_CANONICAL 0x004 /* strip comment information */ #define RF_ADDDOMAIN 0x008 /* OK to do domain extension */ #define RF_COPYPARSE 0x010 /* copy parsed user & host */ #define RF_COPYPADDR 0x020 /* copy print address */ #define RF_COPYALL (RF_COPYPARSE|RF_COPYPADDR) #define RF_COPYNONE 0 /* -** Flags passed to safefile. +** Flags passed to safefile/safedirpath. */ #define SFF_ANYFILE 0 /* no special restrictions */ #define SFF_MUSTOWN 0x0001 /* user must own this file */ #define SFF_NOSLINK 0x0002 /* file cannot be a symbolic link */ #define SFF_ROOTOK 0x0004 /* ok for root to own this file */ #define SFF_RUNASREALUID 0x0008 /* if no ctladdr, run as real uid */ #define SFF_NOPATHCHECK 0x0010 /* don't bother checking dir path */ #define SFF_SETUIDOK 0x0020 /* setuid files are ok */ #define SFF_CREAT 0x0040 /* ok to create file if necessary */ #define SFF_REGONLY 0x0080 /* regular files only */ +#define SFF_SAFEDIRPATH 0x0100 /* no writable directories allowed */ +#define SFF_NOHLINK 0x0200 /* file cannot have hard links */ +#define SFF_NOWLINK 0x0400 /* links only in non-writable dirs */ +#define SFF_NOWFILES 0x0800 /* disallow world writable files */ -/* flags that are actually specific to safefopen */ +/* flags that are actually specific to safeopen/safefopen/dfopen */ #define SFF_OPENASROOT 0x1000 /* open as root instead of real user */ +#define SFF_NOLOCK 0x2000 /* don't lock the file */ +/* pseudo-flags */ +#define SFF_NOLINK (SFF_NOHLINK|SFF_NOSLINK) + /* functions */ extern int safefile __P((char *, UID_T, GID_T, char *, int, int, struct stat *)); +extern int safedirpath __P((char *, UID_T, GID_T, char *, int)); +extern int safeopen __P((char *, int, int, int)); +extern FILE *safefopen __P((char *, int, int, int)); +extern int dfopen __P((char *, int, int, int)); +extern bool filechanged __P((char *, int, struct stat *, int)); /* ** Flags passed to mime8to7. */ #define M87F_OUTER 0 /* outer context */ #define M87F_NO8BIT 0x0001 /* can't have 8-bit in this section */ #define M87F_DIGEST 0x0002 /* processing multipart/digest */ /* ** Flags passed to returntosender. */ #define RTSF_NO_BODY 0 /* send headers only */ #define RTSF_SEND_BODY 0x0001 /* include body of message in return */ #define RTSF_PM_BOUNCE 0x0002 /* this is a postmaster bounce */ /* ** Regular UNIX sockaddrs are too small to handle ISO addresses, so ** we are forced to declare a supertype here. */ # if NETINET || NETUNIX || NETISO || NETNS || NETX25 union bigsockaddr { struct sockaddr sa; /* general version */ #if NETUNIX struct sockaddr_un sunix; /* UNIX family */ #endif #if NETINET struct sockaddr_in sin; /* INET family */ #endif #if NETISO struct sockaddr_iso siso; /* ISO family */ #endif #if NETNS struct sockaddr_ns sns; /* XNS family */ #endif #if NETX25 struct sockaddr_x25 sx25; /* X.25 family */ #endif }; #define SOCKADDR union bigsockaddr EXTERN SOCKADDR RealHostAddr; /* address of host we are talking to */ extern char *hostnamebyanyaddr __P((SOCKADDR *)); extern char *anynet_ntoa __P((SOCKADDR *)); # if DAEMON extern bool validate_connection __P((SOCKADDR *, char *, ENVELOPE *)); # endif #endif /* ** Vendor codes ** ** Vendors can customize sendmail to add special behaviour, ** generally for back compatibility. Ideally, this should ** be set up in the .cf file using the "V" command. However, ** it's quite reasonable for some vendors to want the default ** be their old version; this can be set using ** -DVENDOR_DEFAULT=VENDOR_xxx ** in the Makefile. ** ** Vendors should apply to sendmail@CS.Berkeley.EDU for ** unique vendor codes. */ #define VENDOR_BERKELEY 1 /* Berkeley-native configuration file */ #define VENDOR_SUN 2 /* Sun-native configuration file */ #define VENDOR_HP 3 /* Hewlett-Packard specific config syntax */ #define VENDOR_IBM 4 /* IBM specific config syntax */ EXTERN int VendorCode; /* vendor-specific operation enhancements */ /* prototypes for vendor-specific hook routines */ extern void vendor_set_uid __P((UID_T)); extern void vendor_daemon_setup __P((ENVELOPE *)); /* ** Terminal escape codes. ** ** To make debugging output clearer. */ struct termescape { char *te_rv_on; /* turn reverse-video on */ char *te_rv_off; /* turn reverse-video off */ }; EXTERN struct termescape TermEscape; /* ** Error return from inet_addr(3), in case not defined in /usr/include. */ #ifndef INADDR_NONE # define INADDR_NONE 0xffffffff #endif /* ** Global variables. */ EXTERN bool FromFlag; /* if set, "From" person is explicit */ EXTERN bool MeToo; /* send to the sender also */ EXTERN bool IgnrDot; /* don't let dot end messages */ EXTERN bool SaveFrom; /* save leading "From" lines */ -EXTERN bool Verbose; /* set if blow-by-blow desired */ EXTERN bool GrabTo; /* if set, get recipients from msg */ EXTERN bool SuprErrs; /* set if we are suppressing errors */ EXTERN bool HoldErrs; /* only output errors to transcript */ EXTERN bool NoConnect; /* don't connect to non-local mailers */ EXTERN bool SuperSafe; /* be extra careful, even if expensive */ EXTERN bool ForkQueueRuns; /* fork for each job when running the queue */ EXTERN bool AutoRebuild; /* auto-rebuild the alias database as needed */ EXTERN bool CheckAliases; /* parse addresses during newaliases */ EXTERN bool NoAlias; /* suppress aliasing */ EXTERN bool UseNameServer; /* using DNS -- interpret h_errno & MX RRs */ EXTERN bool UseHesiod; /* using Hesiod -- interpret Hesiod errors */ EXTERN bool SevenBitInput; /* force 7-bit data on input */ EXTERN bool HasEightBits; /* has at least one eight bit input byte */ EXTERN bool ConfigFileRead; /* configuration file has been read */ EXTERN time_t SafeAlias; /* interval to wait until @:@ in alias file */ EXTERN FILE *InChannel; /* input connection */ EXTERN FILE *OutChannel; /* output connection */ EXTERN char *RealUserName; /* real user name of caller */ EXTERN uid_t RealUid; /* real uid of caller */ EXTERN gid_t RealGid; /* real gid of caller */ EXTERN uid_t DefUid; /* default uid to run as */ EXTERN gid_t DefGid; /* default gid to run as */ EXTERN char *DefUser; /* default user to run as (from DefUid) */ EXTERN MODE_T OldUmask; /* umask when sendmail starts up */ +EXTERN int Verbose; /* set if blow-by-blow desired */ EXTERN int Errors; /* set if errors (local to single pass) */ EXTERN int ExitStat; /* exit status code */ EXTERN int LineNumber; /* line number in current input */ EXTERN int LogLevel; /* level of logging to perform */ EXTERN int FileMode; /* mode on files */ EXTERN int QueueLA; /* load average starting forced queueing */ EXTERN int RefuseLA; /* load average refusing connections are */ EXTERN int CurrentLA; /* current load average */ EXTERN long QueueFactor; /* slope of queue function */ EXTERN time_t QueueIntvl; /* intervals between running the queue */ EXTERN char *HelpFile; /* location of SMTP help file */ EXTERN char *ErrMsgFile; /* file to prepend to all error messages */ EXTERN char *StatFile; /* location of statistics summary */ EXTERN char *QueueDir; /* location of queue directory */ EXTERN char *FileName; /* name to print on error messages */ EXTERN char *SmtpPhase; /* current phase in SMTP processing */ EXTERN char *MyHostName; /* name of this host for SMTP messages */ EXTERN char *RealHostName; /* name of host we are talking to */ EXTERN char *CurHostName; /* current host we are dealing with */ EXTERN jmp_buf TopFrame; /* branch-to-top-of-loop-on-error frame */ EXTERN bool QuickAbort; /* .... but only if we want a quick abort */ +EXTERN bool OnlyOneError; /* .... or only want to give one SMTP reply */ EXTERN bool LogUsrErrs; /* syslog user errors (e.g., SMTP RCPT cmd) */ EXTERN bool SendMIMEErrors; /* send error messages in MIME format */ EXTERN bool MatchGecos; /* look for user names in gecos field */ EXTERN bool UseErrorsTo; /* use Errors-To: header (back compat) */ EXTERN bool TryNullMXList; /* if we are the best MX, try host directly */ EXTERN bool InChild; /* true if running in an SMTP subprocess */ EXTERN bool DisConnected; /* running with OutChannel redirected to xf */ EXTERN bool ColonOkInAddr; /* single colon legal in address */ EXTERN bool HasWildcardMX; /* don't use MX records when canonifying */ EXTERN char SpaceSub; /* substitution for */ EXTERN int PrivacyFlags; /* privacy flags */ EXTERN char *ConfFile; /* location of configuration file [conf.c] */ EXTERN char *PidFile; /* location of proc id file [conf.c] */ extern ADDRESS NullAddress; /* a null (template) address [main.c] */ EXTERN long WkClassFact; /* multiplier for message class -> priority */ EXTERN long WkRecipFact; /* multiplier for # of recipients -> priority */ EXTERN long WkTimeFact; /* priority offset each time this job is run */ EXTERN char *UdbSpec; /* user database source spec */ EXTERN int MaxHopCount; /* max # of hops until bounce */ EXTERN int ConfigLevel; /* config file level */ EXTERN char *TimeZoneSpec; /* override time zone specification */ EXTERN char *ForwardPath; /* path to search for .forward files */ EXTERN long MinBlocksFree; /* min # of blocks free on queue fs */ EXTERN char *FallBackMX; /* fall back MX host */ EXTERN long MaxMessageSize; /* advertised max size we will accept */ EXTERN time_t MinQueueAge; /* min delivery interval */ EXTERN time_t DialDelay; /* delay between dial-on-demand tries */ EXTERN char *SafeFileEnv; /* chroot location for file delivery */ EXTERN char *HostsFile; /* path to /etc/hosts file */ EXTERN char *HostStatDir; /* location of host status information */ EXTERN int MaxQueueRun; /* maximum number of jobs in one queue run */ EXTERN int MaxChildren; /* maximum number of daemonic children */ EXTERN int CurChildren; /* current number of daemonic children */ EXTERN char *SmtpGreeting; /* SMTP greeting message (old $e macro) */ EXTERN char *UnixFromLine; /* UNIX From_ line (old $l macro) */ EXTERN char *OperatorChars; /* operators (old $o macro) */ EXTERN bool DontInitGroups; /* avoid initgroups() because of NIS cost */ EXTERN int DefaultNotify; /* default DSN notification flags */ EXTERN bool AllowBogusHELO; /* allow syntax errors on HELO command */ EXTERN bool UserSubmission; /* initial (user) mail submission */ +EXTERN char *RunAsUserName; /* user to become for bulk of run */ EXTERN uid_t RunAsUid; /* UID to become for bulk of run */ EXTERN gid_t RunAsGid; /* GID to become for bulk of run */ -#ifdef _FFR_DSN_RRT +EXTERN int MaxRcptPerMsg; /* max recipients per SMTP message */ +EXTERN bool DoQueueRun; /* non-interrupt time queue run needed */ +#if _FFR_DSN_RRT_OPTION EXTERN bool RrtImpliesDsn; /* turn Return-Receipt-To: into DSN */ #endif +EXTERN bool DontProbeInterfaces; /* don't probe interfaces for names */ +EXTERN bool ChownAlwaysSafe; /* treat chown(2) as safe */ EXTERN bool IgnoreHostStatus; /* ignore long term host status files */ EXTERN bool SingleThreadDelivery; /* single thread hosts on delivery */ EXTERN bool UnsafeGroupWrites; /* group-writable files are unsafe */ EXTERN bool SingleLineFromHeader; /* force From: header to be one line */ EXTERN int ConnRateThrottle; /* throttle for SMTP connection rate */ EXTERN int MaxAliasRecursion; /* maximum depth of alias recursion */ EXTERN int MaxMacroRecursion; /* maximum depth of macro recursion */ EXTERN int MaxRuleRecursion; /* maximum depth of ruleset recursion */ EXTERN char *MustQuoteChars; /* quote these characters in phrases */ EXTERN char *ServiceSwitchFile; /* backup service switch */ EXTERN char *DefaultCharSet; /* default character set for MIME */ EXTERN int DeliveryNiceness; /* how nice to be during delivery */ EXTERN char *PostMasterCopy; /* address to get errs cc's */ EXTERN int CheckpointInterval; /* queue file checkpoint interval */ EXTERN bool DontPruneRoutes; /* don't prune source routes */ EXTERN bool DontExpandCnames; /* do not $[...$] expand CNAMEs */ EXTERN int MaxMciCache; /* maximum entries in MCI cache */ EXTERN time_t ServiceCacheTime; /* time service switch was cached */ EXTERN time_t ServiceCacheMaxAge; /* refresh interval for cache */ EXTERN time_t MciCacheTimeout; /* maximum idle time on connections */ EXTERN time_t MciInfoTimeout; /* how long 'til we retry down hosts */ EXTERN char *QueueLimitRecipient; /* limit queue runs to this recipient */ EXTERN char *QueueLimitSender; /* limit queue runs to this sender */ EXTERN char *QueueLimitId; /* limit queue runs to this id */ EXTERN FILE *TrafficLogFile; /* file in which to log all traffic */ EXTERN char *DoubleBounceAddr; /* where to send double bounces */ +EXTERN bool FatalWritableDirs; /* no writable dirs in map paths */ EXTERN char **ExternalEnviron; /* input environment */ EXTERN char *UserEnviron[MAXUSERENVIRON + 1]; /* saved user environment */ extern int errno; /* ** Timeouts ** ** Indicated values are the MINIMUM per RFC 1123 section 5.3.2. */ EXTERN struct { /* RFC 1123-specified timeouts [minimum value] */ time_t to_initial; /* initial greeting timeout [5m] */ time_t to_mail; /* MAIL command [5m] */ time_t to_rcpt; /* RCPT command [5m] */ time_t to_datainit; /* DATA initiation [2m] */ time_t to_datablock; /* DATA block [3m] */ time_t to_datafinal; /* DATA completion [10m] */ time_t to_nextcommand; /* next command [5m] */ /* following timeouts are not mentioned in RFC 1123 */ time_t to_iconnect; /* initial connection timeout (first try) */ time_t to_connect; /* initial connection timeout (later tries) */ time_t to_rset; /* RSET command */ time_t to_helo; /* HELO command */ time_t to_quit; /* QUIT command */ time_t to_miscshort; /* misc short commands (NOOP, VERB, etc) */ time_t to_ident; /* IDENT protocol requests */ time_t to_fileopen; /* opening :include: and .forward files */ /* following are per message */ time_t to_q_return[MAXTOCLASS]; /* queue return timeouts */ time_t to_q_warning[MAXTOCLASS]; /* queue warning timeouts */ } TimeOuts; /* timeout classes for return and warning timeouts */ # define TOC_NORMAL 0 /* normal delivery */ # define TOC_URGENT 1 /* urgent delivery */ # define TOC_NONURGENT 2 /* non-urgent delivery */ /* ** Trace information */ /* trace vector and macros for debugging flags */ EXTERN u_char tTdvect[100]; # define tTd(flag, level) (tTdvect[flag] >= level) # define tTdlevel(flag) (tTdvect[flag]) /* ** Miscellaneous information. */ +/* +** The "no queue id" queue id for sm_syslog +*/ +#define NOQID "*~*" + + /* ** Some in-line functions */ /* set exit status */ #define setstat(s) { \ if (ExitStat == EX_OK || ExitStat == EX_TEMPFAIL) \ ExitStat = s; \ } /* make a copy of a string */ #define newstr(s) strcpy(xalloc(strlen(s) + 1), s) #define STRUCTCOPY(s, d) d = s /* ** Declarations of useful functions */ extern char *xalloc __P((int)); -extern FILE *dfopen __P((char *, int, int)); extern char *sfgets __P((char *, int, FILE *, time_t, char *)); extern char *queuename __P((ENVELOPE *, int)); extern time_t curtime __P(()); extern bool transienterror __P((int)); extern char *fgetfolded __P((char *, int, FILE *)); extern char *username __P(()); extern char *pintvl __P((time_t, bool)); extern bool shouldqueue __P((long, time_t)); extern bool lockfile __P((int, char *, char *, int)); extern char *hostsignature __P((MAILER *, char *, ENVELOPE *)); extern void openxscript __P((ENVELOPE *)); extern void closexscript __P((ENVELOPE *)); extern char *shortenstring __P((const char *, int)); extern bool usershellok __P((char *, char *)); extern char *defcharset __P((ENVELOPE *)); extern bool wordinclass __P((char *, int)); extern char *denlstring __P((char *, bool, bool)); extern void makelower __P((char *)); extern void rebuildaliases __P((MAP *, bool)); extern void readaliases __P((MAP *, FILE *, bool, bool)); extern void finis __P(()); extern void setsender __P((char *, ENVELOPE *, char **, int, bool)); -extern FILE *safefopen __P((char *, int, int, int)); extern void xputs __P((const char *)); extern void logsender __P((ENVELOPE *, char *)); extern void smtprset __P((MAILER *, MCI *, ENVELOPE *)); extern void smtpquit __P((MAILER *, MCI *, ENVELOPE *)); extern void setuserenv __P((const char *, const char *)); extern char *getextenv __P((const char *)); extern void disconnect __P((int, ENVELOPE *)); -extern void putxline __P((char *, MCI *, int)); +extern void putxline __P((char *, size_t, MCI *, int)); extern void dumpfd __P((int, bool, bool)); extern void makemailer __P((char *)); extern void putfromline __P((MCI *, ENVELOPE *)); extern void setoption __P((int, char *, bool, bool, ENVELOPE *)); extern void setclass __P((int, char *)); extern void inittimeouts __P((char *)); extern void logdelivery __P((MAILER *, MCI *, const char *, ADDRESS *, time_t, ENVELOPE *)); extern void giveresponse __P((int, MAILER *, MCI *, ADDRESS *, time_t, ENVELOPE *)); extern void buildfname __P((char *, char *, char *, int)); extern void mci_setstat __P((MCI *, int, char *, char *)); extern char *smtptodsn __P((int)); extern int rscheck __P((char *, char *, char *, ENVELOPE *e)); extern void mime7to8 __P((MCI *, HDR *, ENVELOPE *)); extern int mime8to7 __P((MCI *, HDR *, ENVELOPE *, char **, int)); extern void xfclose __P((FILE *, char *, char *)); extern int switch_map_find __P((char *, char *[], short [])); extern void shorten_hostname __P((char [])); extern int waitfor __P((pid_t)); extern void proc_list_add __P((pid_t)); extern void proc_list_drop __P((pid_t)); extern void proc_list_clear __P((void)); extern void buffer_errors __P((void)); extern void flush_errors __P((bool)); extern void putline __P((char *, MCI *)); -extern void putxline __P((char *, MCI *, int)); extern bool xtextok __P((char *)); extern char *xtextify __P((char *, char *)); extern char *xuntextify __P((char *)); extern void cleanstrcpy __P((char *, char *, int)); extern int getmxrr __P((char *, char **, bool, int *)); extern int strtorwset __P((char *, char **, int)); extern void printav __P((char **)); extern void printopenfds __P((bool)); extern int endmailer __P((MCI *, ENVELOPE *, char **)); extern void fixcrlf __P((char *, bool)); extern int dofork __P((void)); extern void initsys __P((ENVELOPE *)); -extern void collect __P((FILE *, bool, bool, HDR **, ENVELOPE *)); +extern void collect __P((FILE *, bool, HDR **, ENVELOPE *)); extern void stripquotes __P((char *)); extern int include __P((char *, bool, ADDRESS *, ADDRESS **, int, ENVELOPE *)); extern void unlockqueue __P((ENVELOPE *)); extern void xunlink __P((char *)); extern bool runqueue __P((bool, bool)); extern int getla __P((void)); extern void sendall __P((ENVELOPE *, int)); extern void queueup __P((ENVELOPE *, bool)); extern void checkfds __P((char *)); extern int returntosender __P((char *, ADDRESS *, int, ENVELOPE *)); extern void markstats __P((ENVELOPE *, ADDRESS *)); extern void poststats __P((char *)); extern char *arpadate __P((char *)); extern int mailfile __P((char *, ADDRESS *, int, ENVELOPE *)); extern void loseqfile __P((ENVELOPE *, char *)); extern int prog_open __P((char **, int *, ENVELOPE *)); extern bool getcanonname __P((char *, int, bool)); extern bool path_is_dir __P((char *, bool)); extern pid_t dowork __P((char *, bool, bool, ENVELOPE *)); extern const char *errstring __P((int)); extern sigfunc_t setsignal __P((int, sigfunc_t)); extern int blocksignal __P((int)); extern int releasesignal __P((int)); extern struct hostent *sm_gethostbyname __P((char *)); extern struct hostent *sm_gethostbyaddr __P((char *, int, int)); extern struct passwd *sm_getpwnam __P((char *)); extern struct passwd *sm_getpwuid __P((UID_T)); extern struct passwd *finduser __P((char *, bool *)); #ifdef XDEBUG extern void checkfdopen __P((int, char *)); extern void checkfd012 __P((char *)); #endif /* ellipsis is a different case though */ #ifdef __STDC__ extern void auth_warning(ENVELOPE *, const char *, ...); extern void syserr(const char *, ...); extern void usrerr(const char *, ...); extern void message(const char *, ...); extern void nmessage(const char *, ...); extern void setproctitle(const char *fmt, ...); +extern void sm_syslog(int, const char *, const char *, ...); #else extern void auth_warning(); extern void syserr(); extern void usrerr(); extern void message(); extern void nmessage(); extern void setproctitle(); +extern void sm_syslog(); #endif #if !HASSNPRINTF # ifdef __STDC__ extern int snprintf(char *, size_t, const char *, ...); extern int vsnprintf(char *, size_t, const char *, va_list); # else extern int snprintf(); extern int vsnprintf(); # endif #endif Index: head/usr.sbin/sendmail/src/srvrsmtp.c =================================================================== --- head/usr.sbin/sendmail/src/srvrsmtp.c (revision 26988) +++ head/usr.sbin/sendmail/src/srvrsmtp.c (revision 26989) @@ -1,1458 +1,1479 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ # include "sendmail.h" #ifndef lint #if SMTP -static char sccsid[] = "@(#)srvrsmtp.c 8.136 (Berkeley) 1/17/97 (with SMTP)"; +static char sccsid[] = "@(#)srvrsmtp.c 8.146 (Berkeley) 6/11/97 (with SMTP)"; #else -static char sccsid[] = "@(#)srvrsmtp.c 8.136 (Berkeley) 1/17/97 (without SMTP)"; +static char sccsid[] = "@(#)srvrsmtp.c 8.146 (Berkeley) 6/11/97 (without SMTP)"; #endif #endif /* not lint */ # include # if SMTP /* ** SMTP -- run the SMTP protocol. ** ** Parameters: ** none. ** ** Returns: ** never. ** ** Side Effects: ** Reads commands from the input channel and processes ** them. */ struct cmd { char *cmdname; /* command name */ int cmdcode; /* internal code, see below */ }; /* values for cmdcode */ # define CMDERROR 0 /* bad command */ # define CMDMAIL 1 /* mail -- designate sender */ # define CMDRCPT 2 /* rcpt -- designate recipient */ # define CMDDATA 3 /* data -- send message text */ # define CMDRSET 4 /* rset -- reset state */ # define CMDVRFY 5 /* vrfy -- verify address */ # define CMDEXPN 6 /* expn -- expand address */ # define CMDNOOP 7 /* noop -- do nothing */ # define CMDQUIT 8 /* quit -- close connection and die */ # define CMDHELO 9 /* helo -- be polite */ # define CMDHELP 10 /* help -- give usage info */ # define CMDEHLO 11 /* ehlo -- extended helo (RFC 1425) */ # define CMDETRN 12 /* etrn -- flush queue */ /* non-standard commands */ # define CMDONEX 16 /* onex -- sending one transaction only */ # define CMDVERB 17 /* verb -- go into verbose mode */ # define CMDXUSR 18 /* xusr -- initial (user) submission */ /* use this to catch and log "door handle" attempts on your system */ # define CMDLOGBOGUS 23 /* bogus command that should be logged */ /* debugging-only commands, only enabled if SMTPDEBUG is defined */ # define CMDDBGQSHOW 24 /* showq -- show send queue */ # define CMDDBGDEBUG 25 /* debug -- set debug mode */ static struct cmd CmdTab[] = { { "mail", CMDMAIL }, { "rcpt", CMDRCPT }, { "data", CMDDATA }, { "rset", CMDRSET }, { "vrfy", CMDVRFY }, { "expn", CMDEXPN }, { "help", CMDHELP }, { "noop", CMDNOOP }, { "quit", CMDQUIT }, { "helo", CMDHELO }, { "ehlo", CMDEHLO }, { "etrn", CMDETRN }, { "verb", CMDVERB }, { "onex", CMDONEX }, { "xusr", CMDXUSR }, /* remaining commands are here only to trap and log attempts to use them */ { "showq", CMDDBGQSHOW }, { "debug", CMDDBGDEBUG }, { "wiz", CMDLOGBOGUS }, { NULL, CMDERROR } }; bool OneXact = FALSE; /* one xaction only this run */ char *CurSmtpClient; /* who's at the other end of channel */ static char *skipword(); #define MAXBADCOMMANDS 25 /* maximum number of bad commands */ #define MAXNOOPCOMMANDS 20 /* max "noise" commands before slowdown */ #define MAXHELOCOMMANDS 3 /* max HELO/EHLO commands before slowdown */ #define MAXVRFYCOMMANDS 6 /* max VRFY/EXPN commands before slowdown */ #define MAXETRNCOMMANDS 8 /* max ETRN commands before slowdown */ void smtp(nullserver, e) bool nullserver; register ENVELOPE *volatile e; { register char *volatile p; register struct cmd *c; char *cmd; auto ADDRESS *vrfyqueue; ADDRESS *a; volatile bool gotmail; /* mail command received */ volatile bool gothello; /* helo command received */ bool vrfy; /* set if this is a vrfy command */ char *volatile protocol; /* sending protocol */ char *volatile sendinghost; /* sending hostname */ char *volatile peerhostname; /* name of SMTP peer or "localhost" */ auto char *delimptr; char *id; volatile int nrcpts = 0; /* number of RCPT commands */ bool doublequeue; volatile int badcommands = 0; /* count of bad commands */ volatile int nverifies = 0; /* count of VRFY/EXPN commands */ volatile int n_etrn = 0; /* count of ETRN commands */ volatile int n_noop = 0; /* count of NOOP/VERB/ONEX etc cmds */ volatile int n_helo = 0; /* count of HELO/EHLO commands */ bool ok; + int lognullconnection = TRUE; char inp[MAXLINE]; char cmdbuf[MAXLINE]; extern ENVELOPE BlankEnvelope; extern void help __P((char *)); extern void settime __P((ENVELOPE *)); extern bool enoughdiskspace __P((long)); extern int runinchild __P((char *, ENVELOPE *)); - extern void checksmtpattack __P((volatile int *, int, char *)); + extern void checksmtpattack __P((volatile int *, int, char *, ENVELOPE *)); if (fileno(OutChannel) != fileno(stdout)) { /* arrange for debugging output to go to remote host */ (void) dup2(fileno(OutChannel), fileno(stdout)); } settime(e); peerhostname = RealHostName; if (peerhostname == NULL) peerhostname = "localhost"; CurHostName = peerhostname; CurSmtpClient = macvalue('_', e); if (CurSmtpClient == NULL) CurSmtpClient = CurHostName; setproctitle("server %s startup", CurSmtpClient); -#if defined(LOG) && DAEMON +#if DAEMON if (LogLevel > 11) { /* log connection information */ - syslog(LOG_INFO, "SMTP connect from %.100s (%.100s)", + sm_syslog(LOG_INFO, NOQID, + "SMTP connect from %.100s (%.100s)", CurSmtpClient, anynet_ntoa(&RealHostAddr)); } #endif /* output the first line, inserting "ESMTP" as second word */ expand(SmtpGreeting, inp, sizeof inp, e); p = strchr(inp, '\n'); if (p != NULL) *p++ = '\0'; id = strchr(inp, ' '); if (id == NULL) id = &inp[strlen(inp)]; cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s"; message(cmd, id - inp, inp, id); /* output remaining lines */ while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL) { *p++ = '\0'; if (isascii(*id) && isspace(*id)) id++; message("220-%s", id); } if (id != NULL) { if (isascii(*id) && isspace(*id)) id++; message("220 %s", id); } protocol = NULL; sendinghost = macvalue('s', e); gothello = FALSE; gotmail = FALSE; for (;;) { /* arrange for backout */ if (setjmp(TopFrame) > 0) { /* if() nesting is necessary for Cray UNICOS */ if (InChild) { QuickAbort = FALSE; SuprErrs = TRUE; finis(); } } QuickAbort = FALSE; HoldErrs = FALSE; + SuprErrs = FALSE; LogUsrErrs = FALSE; + OnlyOneError = TRUE; e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS); /* setup for the read */ e->e_to = NULL; Errors = 0; (void) fflush(stdout); /* read the input line */ SmtpPhase = "server cmd read"; setproctitle("server %s cmd read", CurSmtpClient); p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand, SmtpPhase); /* handle errors */ if (p == NULL) { /* end of file, just die */ disconnect(1, e); message("421 %s Lost input channel from %s", MyHostName, CurSmtpClient); -#ifdef LOG if (LogLevel > (gotmail ? 1 : 19)) - syslog(LOG_NOTICE, "lost input channel from %.100s", + sm_syslog(LOG_NOTICE, e->e_id, + "lost input channel from %.100s", CurSmtpClient); -#endif if (InChild) ExitStat = EX_QUIT; finis(); } /* clean up end of line */ fixcrlf(inp, TRUE); /* echo command to transcript */ if (e->e_xfp != NULL) fprintf(e->e_xfp, "<<< %s\n", inp); -#ifdef LOG if (LogLevel >= 15) - syslog(LOG_INFO, "<-- %s", inp); -#endif + sm_syslog(LOG_INFO, e->e_id, + "<-- %s", + inp); if (e->e_id == NULL) setproctitle("%s: %.80s", CurSmtpClient, inp); else setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); /* break off command */ for (p = inp; isascii(*p) && isspace(*p); p++) continue; cmd = cmdbuf; while (*p != '\0' && !(isascii(*p) && isspace(*p)) && cmd < &cmdbuf[sizeof cmdbuf - 2]) *cmd++ = *p++; *cmd = '\0'; /* throw away leading whitespace */ while (isascii(*p) && isspace(*p)) p++; /* decode command */ for (c = CmdTab; c->cmdname != NULL; c++) { if (!strcasecmp(c->cmdname, cmdbuf)) break; } /* reset errors */ errno = 0; /* ** Process command. ** ** If we are running as a null server, return 550 ** to everything. */ if (nullserver) { switch (c->cmdcode) { case CMDQUIT: case CMDHELO: case CMDEHLO: case CMDNOOP: /* process normally */ break; default: if (++badcommands > MAXBADCOMMANDS) sleep(1); - message("550 Access denied"); + usrerr("550 Access denied"); continue; } } /* non-null server */ switch (c->cmdcode) { + case CMDMAIL: + case CMDEXPN: + case CMDVRFY: + case CMDETRN: + lognullconnection = FALSE; + } + + switch (c->cmdcode) + { case CMDHELO: /* hello -- introduce yourself */ case CMDEHLO: /* extended hello */ if (c->cmdcode == CMDEHLO) { protocol = "ESMTP"; SmtpPhase = "server EHLO"; } else { protocol = "SMTP"; SmtpPhase = "server HELO"; } /* avoid denial-of-service */ - checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO"); + checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO", e); /* check for duplicate HELO/EHLO per RFC 1651 4.2 */ if (gothello) { - message("503 %s Duplicate HELO/EHLO", + usrerr("503 %s Duplicate HELO/EHLO", MyHostName); break; } /* check for valid domain name (re 1123 5.2.5) */ if (*p == '\0' && !AllowBogusHELO) { - message("501 %s requires domain address", + usrerr("501 %s requires domain address", cmdbuf); break; } else { register char *q; for (q = p; *q != '\0'; q++) { if (!isascii(*q)) break; if (isalnum(*q)) continue; if (isspace(*q)) { *q = '\0'; break; } if (strchr("[].-_#", *q) == NULL) break; } if (*q != '\0') { if (!AllowBogusHELO) - message("501 Invalid domain name"); + usrerr("501 Invalid domain name"); else { message("250 %s Invalid domain name, accepting anyway", MyHostName); gothello = TRUE; } break; } } sendinghost = newstr(p); gothello = TRUE; if (c->cmdcode != CMDEHLO) { /* print old message and be done with it */ message("250 %s Hello %s, pleased to meet you", MyHostName, CurSmtpClient); break; } /* print extended message and brag */ message("250-%s Hello %s, pleased to meet you", MyHostName, CurSmtpClient); if (!bitset(PRIV_NOEXPN, PrivacyFlags)) { message("250-EXPN"); message("250-VERB"); } #if MIME8TO7 message("250-8BITMIME"); #endif if (MaxMessageSize > 0) message("250-SIZE %ld", MaxMessageSize); else message("250-SIZE"); #if DSN if (SendMIMEErrors) message("250-DSN"); #endif message("250-ONEX"); message("250-ETRN"); message("250-XUSR"); message("250 HELP"); break; case CMDMAIL: /* mail -- designate sender */ SmtpPhase = "server MAIL"; /* check for validity of this command */ if (!gothello) { /* set sending host to our known value */ if (sendinghost == NULL) sendinghost = peerhostname; if (bitset(PRIV_NEEDMAILHELO, PrivacyFlags)) { - message("503 Polite people say HELO first"); + usrerr("503 Polite people say HELO first"); break; } } if (gotmail) { - message("503 Sender already specified"); + usrerr("503 Sender already specified"); if (InChild) finis(); break; } if (InChild) { errno = 0; syserr("503 Nested MAIL command: MAIL %s", p); finis(); } p = skipword(p, "from"); if (p == NULL) break; /* fork a subprocess to process this command */ if (runinchild("SMTP-MAIL", e) > 0) break; if (!gothello) { auth_warning(e, "%s didn't use HELO protocol", CurSmtpClient); } #ifdef PICKY_HELO_CHECK if (strcasecmp(sendinghost, peerhostname) != 0 && (strcasecmp(peerhostname, "localhost") != 0 || strcasecmp(sendinghost, MyHostName) != 0)) { auth_warning(e, "Host %s claimed to be %s", CurSmtpClient, sendinghost); } #endif if (protocol == NULL) protocol = "SMTP"; define('r', protocol, e); define('s', sendinghost, e); initsys(e); nrcpts = 0; e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE; setproctitle("%s %s: %.80s", e->e_id, CurSmtpClient, inp); /* child -- go do the processing */ if (setjmp(TopFrame) > 0) { /* this failed -- undo work */ undo_subproc: if (InChild) { QuickAbort = FALSE; SuprErrs = TRUE; e->e_flags &= ~EF_FATALERRS; finis(); } break; } QuickAbort = TRUE; /* must parse sender first */ delimptr = NULL; setsender(p, e, &delimptr, ' ', FALSE); if (delimptr != NULL && *delimptr != '\0') *delimptr++ = '\0'; /* do config file checking of the sender */ if (rscheck("check_mail", p, NULL, e) != EX_OK) goto undo_subproc; /* check for possible spoofing */ if (RealUid != 0 && OpMode == MD_SMTP && !wordinclass(RealUserName, 't') && !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && strcmp(e->e_from.q_user, RealUserName) != 0) { auth_warning(e, "%s owned process doing -bs", RealUserName); } /* now parse ESMTP arguments */ e->e_msgsize = 0; p = delimptr; while (p != NULL && *p != '\0') { char *kp; char *vp = NULL; extern void mail_esmtp_args __P((char *, char *, ENVELOPE *)); /* locate the beginning of the keyword */ while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; kp = p; /* skip to the value portion */ while ((isascii(*p) && isalnum(*p)) || *p == '-') p++; if (*p == '=') { *p++ = '\0'; vp = p; /* skip to the end of the value */ while (*p != '\0' && *p != ' ' && !(isascii(*p) && iscntrl(*p)) && *p != '=') p++; } if (*p != '\0') *p++ = '\0'; if (tTd(19, 1)) printf("MAIL: got arg %s=\"%s\"\n", kp, vp == NULL ? "" : vp); mail_esmtp_args(kp, vp, e); } if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) { usrerr("552 Message size exceeds fixed maximum message size (%ld)", MaxMessageSize); /* NOTREACHED */ } if (!enoughdiskspace(e->e_msgsize)) { - message("452 Insufficient disk space; try again later"); + usrerr("452 Insufficient disk space; try again later"); break; } message("250 Sender ok"); gotmail = TRUE; break; case CMDRCPT: /* rcpt -- designate recipient */ if (!gotmail) { usrerr("503 Need MAIL before RCPT"); break; } SmtpPhase = "server RCPT"; if (setjmp(TopFrame) > 0) { e->e_flags &= ~EF_FATALERRS; break; } QuickAbort = TRUE; LogUsrErrs = TRUE; + /* limit flooding of our machine */ + if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg) + { + usrerr("450 Too many recipients"); + break; + } + if (e->e_sendmode != SM_DELIVER) e->e_flags |= EF_VRFYONLY; p = skipword(p, "to"); if (p == NULL) break; a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e); if (a == NULL) break; if (delimptr != NULL && *delimptr != '\0') *delimptr++ = '\0'; /* do config file checking of the recipient */ if (rscheck("check_rcpt", p, NULL, e) != EX_OK) break; /* now parse ESMTP arguments */ p = delimptr; while (p != NULL && *p != '\0') { char *kp; char *vp = NULL; extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *)); /* locate the beginning of the keyword */ while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') break; kp = p; /* skip to the value portion */ while ((isascii(*p) && isalnum(*p)) || *p == '-') p++; if (*p == '=') { *p++ = '\0'; vp = p; /* skip to the end of the value */ while (*p != '\0' && *p != ' ' && !(isascii(*p) && iscntrl(*p)) && *p != '=') p++; } if (*p != '\0') *p++ = '\0'; if (tTd(19, 1)) printf("RCPT: got arg %s=\"%s\"\n", kp, vp == NULL ? "" : vp); rcpt_esmtp_args(a, kp, vp, e); } /* save in recipient list after ESMTP mods */ a = recipient(a, &e->e_sendqueue, 0, e); if (Errors != 0) break; /* no errors during parsing, but might be a duplicate */ e->e_to = a->q_paddr; if (!bitset(QBADADDR, a->q_flags)) { message("250 Recipient ok%s", bitset(QQUEUEUP, a->q_flags) ? " (will queue)" : ""); nrcpts++; } else { /* punt -- should keep message in ADDRESS.... */ - message("550 Addressee unknown"); + usrerr("550 Addressee unknown"); } - e->e_to = NULL; break; case CMDDATA: /* data -- text of mail */ SmtpPhase = "server DATA"; if (!gotmail) { - message("503 Need MAIL command"); + usrerr("503 Need MAIL command"); break; } else if (nrcpts <= 0) { - message("503 Need RCPT (recipient)"); + usrerr("503 Need RCPT (recipient)"); break; } /* check to see if we need to re-expand aliases */ /* also reset QBADADDR on already-diagnosted addrs */ doublequeue = FALSE; for (a = e->e_sendqueue; a != NULL; a = a->q_next) { if (bitset(QVERIFIED, a->q_flags)) { /* need to re-expand aliases */ doublequeue = TRUE; } if (bitset(QBADADDR, a->q_flags)) { /* make this "go away" */ a->q_flags |= QDONTSEND; a->q_flags &= ~QBADADDR; } } /* collect the text of the message */ SmtpPhase = "collect"; buffer_errors(); - collect(InChannel, TRUE, doublequeue, NULL, e); + collect(InChannel, TRUE, NULL, e); flush_errors(TRUE); if (Errors != 0) goto abortmessage; /* make sure we actually do delivery */ e->e_flags &= ~EF_CLRQUEUE; /* from now on, we have to operate silently */ buffer_errors(); e->e_errormode = EM_MAIL; /* ** Arrange to send to everyone. ** If sending to multiple people, mail back ** errors rather than reporting directly. ** In any case, don't mail back errors for ** anything that has happened up to ** now (the other end will do this). ** Truncate our transcript -- the mail has gotten ** to us successfully, and if we have ** to mail this back, it will be easier ** on the reader. ** Then send to everyone. ** Finally give a reply code. If an error has ** already been given, don't mail a ** message back. ** We goose error returns by clearing error bit. */ SmtpPhase = "delivery"; e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp); id = e->e_id; if (doublequeue) { /* make sure it is in the queue */ queueup(e, FALSE); } else { /* send to all recipients */ sendall(e, SM_DEFAULT); } e->e_to = NULL; /* issue success message */ message("250 %s Message accepted for delivery", id); /* if we just queued, poke it */ if (doublequeue && e->e_sendmode != SM_QUEUE && e->e_sendmode != SM_DEFER) { extern pid_t dowork(); unlockqueue(e); (void) dowork(id, TRUE, TRUE, e); } abortmessage: /* if in a child, pop back to our parent */ if (InChild) finis(); /* clean up a bit */ gotmail = FALSE; dropenvelope(e, TRUE); CurEnv = e = newenvelope(e, CurEnv); e->e_flags = BlankEnvelope.e_flags; break; case CMDRSET: /* rset -- reset state */ - message("250 Reset state"); + if (tTd(94, 100)) + message("451 Test failure"); + else + message("250 Reset state"); /* arrange to ignore any current send list */ e->e_sendqueue = NULL; e->e_flags |= EF_CLRQUEUE; if (InChild) finis(); /* clean up a bit */ gotmail = FALSE; + SuprErrs = TRUE; dropenvelope(e, TRUE); CurEnv = e = newenvelope(e, CurEnv); break; case CMDVRFY: /* vrfy -- verify address */ case CMDEXPN: /* expn -- expand address */ checksmtpattack(&nverifies, MAXVRFYCOMMANDS, - c->cmdcode == CMDVRFY ? "VRFY" : "EXPN"); + c->cmdcode == CMDVRFY ? "VRFY" : "EXPN", e); vrfy = c->cmdcode == CMDVRFY; if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN, PrivacyFlags)) { if (vrfy) message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)"); else message("502 Sorry, we do not allow this operation"); -#ifdef LOG if (LogLevel > 5) - syslog(LOG_INFO, "%.100s: %s [rejected]", + sm_syslog(LOG_INFO, e->e_id, + "%.100s: %s [rejected]", CurSmtpClient, shortenstring(inp, 203)); -#endif break; } else if (!gothello && bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO, PrivacyFlags)) { - message("503 I demand that you introduce yourself first"); + usrerr("503 I demand that you introduce yourself first"); break; } if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0) break; -#ifdef LOG if (LogLevel > 5) - syslog(LOG_INFO, "%.100s: %s", + sm_syslog(LOG_INFO, e->e_id, + "%.100s: %s", CurSmtpClient, shortenstring(inp, 203)); -#endif vrfyqueue = NULL; - QuickAbort = TRUE; if (vrfy) e->e_flags |= EF_VRFYONLY; while (*p != '\0' && isascii(*p) && isspace(*p)) p++; if (*p == '\0') { - message("501 Argument required"); - Errors++; + usrerr("501 Argument required"); } else { (void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e); } if (Errors != 0) { if (InChild) finis(); break; } if (vrfyqueue == NULL) { - message("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); + usrerr("554 Nothing to %s", vrfy ? "VRFY" : "EXPN"); } while (vrfyqueue != NULL) { extern void printvrfyaddr __P((ADDRESS *, bool, bool)); a = vrfyqueue; while ((a = a->q_next) != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags)) continue; if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags)) printvrfyaddr(vrfyqueue, a == NULL, vrfy); vrfyqueue = vrfyqueue->q_next; } if (InChild) finis(); break; case CMDETRN: /* etrn -- force queue flush */ if (strlen(p) <= 0) { - message("500 Parameter required"); + usrerr("500 Parameter required"); break; } /* crude way to avoid denial-of-service attacks */ - checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN"); + checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e); id = p; if (*id == '@') id++; else *--id = '@'; -#ifdef LOG if (LogLevel > 5) - syslog(LOG_INFO, "%.100s: ETRN %s", + sm_syslog(LOG_INFO, e->e_id, + "%.100s: ETRN %s", CurSmtpClient, shortenstring(id, 203)); -#endif QueueLimitRecipient = id; ok = runqueue(TRUE, TRUE); QueueLimitRecipient = NULL; if (ok) message("250 Queuing for node %s started", p); break; case CMDHELP: /* help -- give user info */ help(p); break; case CMDNOOP: /* noop -- do nothing */ - checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP"); + checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP", e); message("250 OK"); break; case CMDQUIT: /* quit -- leave mail */ message("221 %s closing connection", MyHostName); doquit: /* arrange to ignore any current send list */ e->e_sendqueue = NULL; /* avoid future 050 messages */ disconnect(1, e); if (InChild) ExitStat = EX_QUIT; + if (lognullconnection && LogLevel > 5) + sm_syslog(LOG_INFO, NULL, + "Null connection from %.100s", + CurSmtpClient); finis(); case CMDVERB: /* set verbose mode */ if (bitset(PRIV_NOEXPN, PrivacyFlags)) { /* this would give out the same info */ message("502 Verbose unavailable"); break; } - checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB"); - Verbose = TRUE; + checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB", e); + Verbose = 1; e->e_sendmode = SM_DELIVER; message("250 Verbose mode"); break; case CMDONEX: /* doing one transaction only */ - checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX"); + checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e); OneXact = TRUE; message("250 Only one transaction"); break; case CMDXUSR: /* initial (user) submission */ - checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR"); + checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR", e); UserSubmission = TRUE; message("250 Initial submission"); break; # if SMTPDEBUG case CMDDBGQSHOW: /* show queues */ printf("Send Queue="); printaddr(e->e_sendqueue, TRUE); break; case CMDDBGDEBUG: /* set debug mode */ tTsetup(tTdvect, sizeof tTdvect, "0-99.1"); tTflag(p); message("200 Debug set"); break; # else /* not SMTPDEBUG */ case CMDDBGQSHOW: /* show queues */ case CMDDBGDEBUG: /* set debug mode */ # endif /* SMTPDEBUG */ case CMDLOGBOGUS: /* bogus command */ -# ifdef LOG if (LogLevel > 0) - syslog(LOG_CRIT, + sm_syslog(LOG_CRIT, e->e_id, "\"%s\" command from %.100s (%.100s)", c->cmdname, CurSmtpClient, anynet_ntoa(&RealHostAddr)); -# endif /* FALL THROUGH */ case CMDERROR: /* unknown command */ if (++badcommands > MAXBADCOMMANDS) { message("421 %s Too many bad commands; closing connection", MyHostName); goto doquit; } - message("500 Command unrecognized"); + usrerr("500 Command unrecognized: \"%s\"", + shortenstring(inp, 203)); break; default: errno = 0; syserr("500 smtp: unknown code %d", c->cmdcode); break; } } } /* ** CHECKSMTPATTACK -- check for denial-of-service attack by repetition ** ** Parameters: ** pcounter -- pointer to a counter for this command. ** maxcount -- maximum value for this counter before we ** slow down. ** cname -- command name for logging. +** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** Slows down if we seem to be under attack. */ void -checksmtpattack(pcounter, maxcount, cname) +checksmtpattack(pcounter, maxcount, cname, e) volatile int *pcounter; int maxcount; char *cname; + ENVELOPE *e; { if (++(*pcounter) >= maxcount) { -#ifdef LOG if (*pcounter == maxcount && LogLevel > 5) { - syslog(LOG_INFO, "%.100s: %.40s attack?", + sm_syslog(LOG_INFO, e->e_id, + "%.100s: %.40s attack?", CurSmtpClient, cname); } -#endif sleep(*pcounter / maxcount); } } /* ** SKIPWORD -- skip a fixed word. ** ** Parameters: ** p -- place to start looking. ** w -- word to skip. ** ** Returns: ** p following w. ** NULL on error. ** ** Side Effects: ** clobbers the p data area. */ static char * skipword(p, w) register char *p; char *w; { register char *q; char *firstp = p; /* find beginning of word */ while (isascii(*p) && isspace(*p)) p++; q = p; /* find end of word */ while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p))) p++; while (isascii(*p) && isspace(*p)) *p++ = '\0'; if (*p != ':') { syntax: - message("501 Syntax error in parameters scanning \"%s\"", + usrerr("501 Syntax error in parameters scanning \"%s\"", shortenstring(firstp, 203)); - Errors++; return (NULL); } *p++ = '\0'; while (isascii(*p) && isspace(*p)) p++; if (*p == '\0') goto syntax; /* see if the input word matches desired word */ if (strcasecmp(q, w)) goto syntax; return (p); } /* ** MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line ** ** Parameters: ** kp -- the parameter key. ** vp -- the value of that parameter. ** e -- the envelope. ** ** Returns: ** none. */ void mail_esmtp_args(kp, vp, e) char *kp; char *vp; ENVELOPE *e; { if (strcasecmp(kp, "size") == 0) { if (vp == NULL) { usrerr("501 SIZE requires a value"); /* NOTREACHED */ } # if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY) e->e_msgsize = strtoul(vp, (char **) NULL, 10); # else e->e_msgsize = strtol(vp, (char **) NULL, 10); # endif } else if (strcasecmp(kp, "body") == 0) { if (vp == NULL) { usrerr("501 BODY requires a value"); /* NOTREACHED */ } else if (strcasecmp(vp, "8bitmime") == 0) { SevenBitInput = FALSE; } else if (strcasecmp(vp, "7bit") == 0) { SevenBitInput = TRUE; } else { usrerr("501 Unknown BODY type %s", vp); /* NOTREACHED */ } e->e_bodytype = newstr(vp); } else if (strcasecmp(kp, "envid") == 0) { if (vp == NULL) { usrerr("501 ENVID requires a value"); /* NOTREACHED */ } if (!xtextok(vp)) { usrerr("501 Syntax error in ENVID parameter value"); /* NOTREACHED */ } if (e->e_envid != NULL) { usrerr("501 Duplicate ENVID parameter"); /* NOTREACHED */ } e->e_envid = newstr(vp); } else if (strcasecmp(kp, "ret") == 0) { if (vp == NULL) { usrerr("501 RET requires a value"); /* NOTREACHED */ } if (bitset(EF_RET_PARAM, e->e_flags)) { usrerr("501 Duplicate RET parameter"); /* NOTREACHED */ } e->e_flags |= EF_RET_PARAM; if (strcasecmp(vp, "hdrs") == 0) e->e_flags |= EF_NO_BODY_RETN; else if (strcasecmp(vp, "full") != 0) { usrerr("501 Bad argument \"%s\" to RET", vp); /* NOTREACHED */ } } else { usrerr("501 %s parameter unrecognized", kp); /* NOTREACHED */ } } /* ** RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line ** ** Parameters: ** a -- the address corresponding to the To: parameter. ** kp -- the parameter key. ** vp -- the value of that parameter. ** e -- the envelope. ** ** Returns: ** none. */ void rcpt_esmtp_args(a, kp, vp, e) ADDRESS *a; char *kp; char *vp; ENVELOPE *e; { if (strcasecmp(kp, "notify") == 0) { char *p; if (vp == NULL) { usrerr("501 NOTIFY requires a value"); /* NOTREACHED */ } a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY); a->q_flags |= QHASNOTIFY; if (strcasecmp(vp, "never") == 0) return; for (p = vp; p != NULL; vp = p) { p = strchr(p, ','); if (p != NULL) *p++ = '\0'; if (strcasecmp(vp, "success") == 0) a->q_flags |= QPINGONSUCCESS; else if (strcasecmp(vp, "failure") == 0) a->q_flags |= QPINGONFAILURE; else if (strcasecmp(vp, "delay") == 0) a->q_flags |= QPINGONDELAY; else { usrerr("501 Bad argument \"%s\" to NOTIFY", vp); /* NOTREACHED */ } } } else if (strcasecmp(kp, "orcpt") == 0) { if (vp == NULL) { usrerr("501 ORCPT requires a value"); /* NOTREACHED */ } if (strchr(vp, ';') == NULL || !xtextok(vp)) { usrerr("501 Syntax error in ORCPT parameter value"); /* NOTREACHED */ } if (a->q_orcpt != NULL) { usrerr("501 Duplicate ORCPT parameter"); /* NOTREACHED */ } a->q_orcpt = newstr(vp); } else { usrerr("501 %s parameter unrecognized", kp); /* NOTREACHED */ } } /* ** PRINTVRFYADDR -- print an entry in the verify queue ** ** Parameters: ** a -- the address to print ** last -- set if this is the last one. ** vrfy -- set if this is a VRFY command. ** ** Returns: ** none. ** ** Side Effects: ** Prints the appropriate 250 codes. */ void printvrfyaddr(a, last, vrfy) register ADDRESS *a; bool last; bool vrfy; { char fmtbuf[20]; if (vrfy && a->q_mailer != NULL && !bitnset(M_VRFY250, a->q_mailer->m_flags)) strcpy(fmtbuf, "252"); else strcpy(fmtbuf, "250"); fmtbuf[3] = last ? ' ' : '-'; if (a->q_fullname == NULL) { if (strchr(a->q_user, '@') == NULL) strcpy(&fmtbuf[4], "<%s@%s>"); else strcpy(&fmtbuf[4], "<%s>"); message(fmtbuf, a->q_user, MyHostName); } else { if (strchr(a->q_user, '@') == NULL) strcpy(&fmtbuf[4], "%s <%s@%s>"); else strcpy(&fmtbuf[4], "%s <%s>"); message(fmtbuf, a->q_fullname, a->q_user, MyHostName); } } /* ** RUNINCHILD -- return twice -- once in the child, then in the parent again ** ** Parameters: ** label -- a string used in error messages ** ** Returns: ** zero in the child ** one in the parent ** ** Side Effects: ** none. */ int runinchild(label, e) char *label; register ENVELOPE *e; { pid_t childpid; if (!OneXact) { /* ** Disable child process reaping, in case ETRN has preceeded ** MAIL command, and then fork. */ (void) blocksignal(SIGCHLD); childpid = dofork(); if (childpid < 0) { syserr("451 %s: cannot fork", label); (void) releasesignal(SIGCHLD); return (1); } if (childpid > 0) { auto int st; /* parent -- wait for child to complete */ setproctitle("server %s child wait", CurSmtpClient); st = waitfor(childpid); if (st == -1) syserr("451 %s: lost child", label); else if (!WIFEXITED(st)) syserr("451 %s: died on signal %d", label, st & 0177); /* if we exited on a QUIT command, complete the process */ if (WEXITSTATUS(st) == EX_QUIT) { disconnect(1, e); finis(); } /* restore the child signal */ (void) releasesignal(SIGCHLD); return (1); } else { /* child */ InChild = TRUE; QuickAbort = FALSE; clearenvelope(e, FALSE); (void) setsignal(SIGCHLD, SIG_DFL); (void) releasesignal(SIGCHLD); } } /* open alias database */ initmaps(FALSE, e); return (0); } # endif /* SMTP */ /* ** HELP -- implement the HELP command. ** ** Parameters: ** topic -- the topic we want help for. ** ** Returns: ** none. ** ** Side Effects: ** outputs the help file to message output. */ void help(topic) char *topic; { register FILE *hf; int len; bool noinfo; char buf[MAXLINE]; extern char Version[]; - if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL) + if (HelpFile == NULL || + (hf = safefopen(HelpFile, O_RDONLY, 0444, SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK)) == NULL) { /* no help */ errno = 0; message("502 Sendmail %s -- HELP not implemented", Version); return; } if (topic == NULL || *topic == '\0') { topic = "smtp"; message("214-This is Sendmail version %s", Version); noinfo = FALSE; } else { makelower(topic); noinfo = TRUE; } len = strlen(topic); while (fgets(buf, sizeof buf, hf) != NULL) { if (strncmp(buf, topic, len) == 0) { register char *p; p = strchr(buf, '\t'); if (p == NULL) p = buf; else p++; fixcrlf(p, TRUE); message("214-%s", p); noinfo = FALSE; } } if (noinfo) message("504 HELP topic \"%.10s\" unknown", topic); else message("214 End of HELP info"); (void) fclose(hf); } Index: head/usr.sbin/sendmail/src/udb.c =================================================================== --- head/usr.sbin/sendmail/src/udb.c (revision 26988) +++ head/usr.sbin/sendmail/src/udb.c (revision 26989) @@ -1,1154 +1,1160 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "sendmail.h" #ifndef lint #if USERDB -static char sccsid [] = "@(#)udb.c 8.47 (Berkeley) 12/6/96 (with USERDB)"; +static char sccsid [] = "@(#)udb.c 8.51 (Berkeley) 5/29/97 (with USERDB)"; #else -static char sccsid [] = "@(#)udb.c 8.47 (Berkeley) 12/6/96 (without USERDB)"; +static char sccsid [] = "@(#)udb.c 8.51 (Berkeley) 5/29/97 (without USERDB)"; #endif #endif #if USERDB #include #ifdef NEWDB # include #else # define DBT struct _data_base_thang_ DBT { void *data; /* pointer to data */ size_t size; /* length of data */ }; #endif #ifdef HESIOD # include #endif /* HESIOD */ /* ** UDB.C -- interface between sendmail and Berkeley User Data Base. ** ** This depends on the 4.4BSD db package. */ struct udbent { char *udb_spec; /* string version of spec */ int udb_type; /* type of entry */ char *udb_default; /* default host for outgoing mail */ union { /* type UE_REMOTE -- do remote call for lookup */ struct { struct sockaddr_in _udb_addr; /* address */ int _udb_timeout; /* timeout */ } udb_remote; #define udb_addr udb_u.udb_remote._udb_addr #define udb_timeout udb_u.udb_remote._udb_timeout /* type UE_FORWARD -- forward message to remote */ struct { char *_udb_fwdhost; /* name of forward host */ } udb_forward; #define udb_fwdhost udb_u.udb_forward._udb_fwdhost #ifdef NEWDB /* type UE_FETCH -- lookup in local database */ struct { char *_udb_dbname; /* pathname of database */ DB *_udb_dbp; /* open database ptr */ } udb_lookup; #define udb_dbname udb_u.udb_lookup._udb_dbname #define udb_dbp udb_u.udb_lookup._udb_dbp #endif } udb_u; }; #define UDB_EOLIST 0 /* end of list */ #define UDB_SKIP 1 /* skip this entry */ #define UDB_REMOTE 2 /* look up in remote database */ #define UDB_DBFETCH 3 /* look up in local database */ #define UDB_FORWARD 4 /* forward to remote host */ #define UDB_HESIOD 5 /* look up via hesiod */ #define MAXUDBENT 10 /* maximum number of UDB entries */ struct option { char *name; char *val; }; -extern int _udbx_init __P((void)); +extern int _udbx_init __P((ENVELOPE *)); /* ** UDBEXPAND -- look up user in database and expand ** ** Parameters: ** a -- address to expand. ** sendq -- pointer to head of sendq to put the expansions in. ** aliaslevel -- the current alias nesting depth. ** e -- the current envelope. ** ** Returns: ** EX_TEMPFAIL -- if something "odd" happened -- probably due ** to accessing a file on an NFS server that is down. ** EX_OK -- otherwise. ** ** Side Effects: ** Modifies sendq. */ int UdbPort = 1616; int UdbTimeout = 10; struct udbent UdbEnts[MAXUDBENT + 1]; int UdbSock = -1; bool UdbInitialized = FALSE; int udbexpand(a, sendq, aliaslevel, e) register ADDRESS *a; ADDRESS **sendq; int aliaslevel; register ENVELOPE *e; { int i; DBT key; DBT info; bool breakout; register struct udbent *up; int keylen; int naddrs; char keybuf[MAXKEY]; if (tTd(28, 1)) printf("udbexpand(%s)\n", a->q_paddr); /* make certain we are supposed to send to this address */ if (bitset(QDONTSEND|QVERIFIED, a->q_flags)) return EX_OK; e->e_to = a->q_paddr; /* on first call, locate the database */ if (!UdbInitialized) { - if (_udbx_init() == EX_TEMPFAIL) + if (_udbx_init(e) == EX_TEMPFAIL) return EX_TEMPFAIL; } /* short circuit the process if no chance of a match */ if (UdbSpec == NULL || UdbSpec[0] == '\0') return EX_OK; /* short circuit name begins with '\\' since it can't possibly match */ if (a->q_user[0] == '\\') return EX_OK; /* if name is too long, assume it won't match */ if (strlen(a->q_user) > (SIZE_T) sizeof keybuf - 12) return EX_OK; /* if name begins with a colon, it indicates our metadata */ if (a->q_user[0] == ':') return EX_OK; /* build actual database key */ (void) strcpy(keybuf, a->q_user); (void) strcat(keybuf, ":maildrop"); keylen = strlen(keybuf); breakout = FALSE; for (up = UdbEnts; !breakout; up++) { char *user; int usersize; int userleft; char userbuf[MEMCHUNKSIZE]; #if defined(HESIOD) && defined(HES_GETMAILHOST) char pobuf[MAXNAME]; #endif user = userbuf; userbuf[0] = '\0'; usersize = sizeof userbuf; userleft = sizeof userbuf - 1; /* ** Select action based on entry type. ** ** On dropping out of this switch, "class" should ** explain the type of the data, and "user" should ** contain the user information. */ switch (up->udb_type) { #ifdef NEWDB case UDB_DBFETCH: key.data = keybuf; key.size = keylen; if (tTd(28, 80)) printf("udbexpand: trying %s (%d) via db\n", keybuf, keylen); i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); if (i > 0 || info.size <= 0) { if (tTd(28, 2)) printf("udbexpand: no match on %s (%d)\n", keybuf, keylen); break; } if (tTd(28, 80)) printf("udbexpand: match %.*s: %.*s\n", (int) key.size, (char *) key.data, (int) info.size, (char *) info.data); a->q_flags &= ~QSELFREF; while (i == 0 && key.size == keylen && bcmp(key.data, keybuf, keylen) == 0) { char *p; if (bitset(EF_VRFYONLY, e->e_flags)) { a->q_flags |= QVERIFIED; return EX_OK; } breakout = TRUE; if (info.size >= userleft - 1) { - char *nuser = xalloc(usersize + MEMCHUNKSIZE); + char *nuser; + int size = MEMCHUNKSIZE; + if (info.size > MEMCHUNKSIZE) + size = info.size; + nuser = xalloc(usersize + size); + bcopy(user, nuser, usersize); if (user != userbuf) free(user); user = nuser; - usersize += MEMCHUNKSIZE; - userleft += MEMCHUNKSIZE; + usersize += size; + userleft += size; } p = &user[strlen(user)]; if (p != user) { *p++ = ','; userleft--; } bcopy(info.data, p, info.size); p[info.size] = '\0'; userleft -= info.size; /* get the next record */ i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); } /* if nothing ever matched, try next database */ if (!breakout) break; message("expanded to %s", user); -#ifdef LOG if (LogLevel >= 10) - syslog(LOG_INFO, "%s: expand %.100s => %s", - e->e_id, e->e_to, + sm_syslog(LOG_INFO, e->e_id, + "expand %.100s => %s", + e->e_to, shortenstring(user, 203)); -#endif naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e); if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) { if (tTd(28, 5)) { printf("udbexpand: QDONTSEND "); printaddr(a, FALSE); } a->q_flags |= QDONTSEND; } if (i < 0) { syserr("udbexpand: db-get %.*s stat %d", key.size, key.data, i); return EX_TEMPFAIL; } /* ** If this address has a -request address, reflect ** it into the envelope. */ (void) strcpy(keybuf, a->q_user); (void) strcat(keybuf, ":mailsender"); keylen = strlen(keybuf); key.data = keybuf; key.size = keylen; i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); if (i != 0 || info.size <= 0) break; a->q_owner = xalloc(info.size + 1); bcopy(info.data, a->q_owner, info.size); a->q_owner[info.size] = '\0'; /* announce delivery; NORECEIPT bit set later */ if (e->e_xfp != NULL) { fprintf(e->e_xfp, "Message delivered to mailing list %s\n", a->q_paddr); } e->e_flags |= EF_SENDRECEIPT; a->q_flags |= QDELIVERED|QEXPANDED; break; #endif #ifdef HESIOD case UDB_HESIOD: key.data = keybuf; key.size = keylen; if (tTd(28, 80)) printf("udbexpand: trying %s (%d) via hesiod\n", keybuf, keylen); /* look up the key via hesiod */ i = hes_udb_get(&key, &info); if (i < 0) { syserr("udbexpand: hesiod-get %.*s stat %d", key.size, key.data, i); return EX_TEMPFAIL; } else if (i > 0 || info.size <= 0) { #if HES_GETMAILHOST struct hes_postoffice *hp; #endif if (tTd(28, 2)) printf("udbexpand: no match on %s (%d)\n", keybuf, keylen); #if HES_GETMAILHOST if (tTd(28, 8)) printf(" ... trying hes_getmailhost(%s)\n", a->q_user); hp = hes_getmailhost(a->q_user); if (hp == NULL) { if (hes_error() == HES_ER_NET) { syserr("udbexpand: hesiod-getmail %s stat %d", a->q_user, hes_error()); return EX_TEMPFAIL; } if (tTd(28, 2)) printf("hes_getmailhost(%s): %d\n", a->q_user, hes_error()); break; } if (strlen(hp->po_name) + strlen(hp->po_host) > sizeof pobuf - 2) { if (tTd(28, 2)) printf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n", a->q_user, hp->po_name, hp->po_host); break; } info.data = pobuf; snprintf(pobuf, sizeof pobuf, "%s@%s", hp->po_name, hp->po_host); info.size = strlen(info.data); #else break; #endif } if (tTd(28, 80)) printf("udbexpand: match %.*s: %.*s\n", key.size, key.data, info.size, info.data); a->q_flags &= ~QSELFREF; if (bitset(EF_VRFYONLY, e->e_flags)) { a->q_flags |= QVERIFIED; return EX_OK; } breakout = TRUE; if (info.size >= usersize) user = xalloc(info.size + 1); bcopy(info.data, user, info.size); user[info.size] = '\0'; message("hesioded to %s", user); -#ifdef LOG if (LogLevel >= 10) - syslog(LOG_INFO, "%s: hesiod %.100s => %s", - e->e_id, e->e_to, + sm_syslog(LOG_INFO, e->e_id, + "hesiod %.100s => %s", + e->e_to, shortenstring(user, 203)); -#endif naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e); if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) { if (tTd(28, 5)) { printf("udbexpand: QDONTSEND "); printaddr(a, FALSE); } a->q_flags |= QDONTSEND; } /* ** If this address has a -request address, reflect ** it into the envelope. */ (void) strcpy(keybuf, a->q_user); (void) strcat(keybuf, ":mailsender"); keylen = strlen(keybuf); key.data = keybuf; key.size = keylen; i = hes_udb_get(&key, &info); if (i != 0 || info.size <= 0) break; a->q_owner = xalloc(info.size + 1); bcopy(info.data, a->q_owner, info.size); a->q_owner[info.size] = '\0'; break; #endif /* HESIOD */ case UDB_REMOTE: /* not yet implemented */ break; case UDB_FORWARD: if (bitset(EF_VRFYONLY, e->e_flags)) return EX_OK; i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; if (i >= usersize) { usersize = i + 1; user = xalloc(usersize); } (void) snprintf(user, usersize, "%s@%s", a->q_user, up->udb_fwdhost); message("expanded to %s", user); a->q_flags &= ~QSELFREF; naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e); if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) { if (tTd(28, 5)) { printf("udbexpand: QDONTSEND "); printaddr(a, FALSE); } a->q_flags |= QDONTSEND; } breakout = TRUE; break; case UDB_EOLIST: breakout = TRUE; break; default: /* unknown entry type */ break; } if (user != userbuf) free(user); } return EX_OK; } /* ** UDBSENDER -- return canonical external name of sender, given local name ** ** Parameters: ** sender -- the name of the sender on the local machine. ** ** Returns: ** The external name for this sender, if derivable from the ** database. ** NULL -- if nothing is changed from the database. ** ** Side Effects: ** none. */ char * udbsender(sender) char *sender; { extern char *udbmatch(); return udbmatch(sender, "mailname"); } char * udbmatch(user, field) char *user; char *field; { register char *p; register struct udbent *up; int i; int keylen; DBT key, info; char keybuf[MAXKEY]; if (tTd(28, 1)) printf("udbmatch(%s, %s)\n", user, field); if (!UdbInitialized) { - if (_udbx_init() == EX_TEMPFAIL) + if (_udbx_init(CurEnv) == EX_TEMPFAIL) return NULL; } /* short circuit if no spec */ if (UdbSpec == NULL || UdbSpec[0] == '\0') return NULL; /* short circuit name begins with '\\' since it can't possibly match */ if (user[0] == '\\') return NULL; /* long names can never match and are a pain to deal with */ - if ((strlen(user) + strlen(field)) > sizeof keybuf - 4) + i = strlen(field); + if (i < sizeof "maildrop") + i = sizeof "maildrop"; + if ((strlen(user) + i) > sizeof keybuf - 4) return NULL; /* names beginning with colons indicate metadata */ if (user[0] == ':') return NULL; /* build database key */ (void) strcpy(keybuf, user); (void) strcat(keybuf, ":"); (void) strcat(keybuf, field); keylen = strlen(keybuf); for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { /* ** Select action based on entry type. */ switch (up->udb_type) { #ifdef NEWDB case UDB_DBFETCH: key.data = keybuf; key.size = keylen; i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); if (i != 0 || info.size <= 0) { if (tTd(28, 2)) printf("udbmatch: no match on %s (%d) via db\n", keybuf, keylen); continue; } p = xalloc(info.size + 1); bcopy(info.data, p, info.size); p[info.size] = '\0'; if (tTd(28, 1)) printf("udbmatch ==> %s\n", p); return p; break; #endif #ifdef HESIOD case UDB_HESIOD: key.data = keybuf; key.size = keylen; i = hes_udb_get(&key, &info); if (i != 0 || info.size <= 0) { if (tTd(28, 2)) printf("udbmatch: no match on %s (%d) via hesiod\n", keybuf, keylen); continue; } p = xalloc(info.size + 1); bcopy(info.data, p, info.size); p[info.size] = '\0'; if (tTd(28, 1)) printf("udbmatch ==> %s\n", p); return p; #endif /* HESIOD */ } } if (strcmp(field, "mailname") != 0) return NULL; /* ** Nothing yet. Search again for a default case. But only ** use it if we also have a forward (:maildrop) pointer already ** in the database. */ /* build database key */ (void) strcpy(keybuf, user); (void) strcat(keybuf, ":maildrop"); keylen = strlen(keybuf); for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { switch (up->udb_type) { #ifdef NEWDB case UDB_DBFETCH: /* get the default case for this database */ if (up->udb_default == NULL) { key.data = ":default:mailname"; key.size = strlen(key.data); i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); if (i != 0 || info.size <= 0) { /* no default case */ up->udb_default = ""; continue; } /* save the default case */ up->udb_default = xalloc(info.size + 1); bcopy(info.data, up->udb_default, info.size); up->udb_default[info.size] = '\0'; } else if (up->udb_default[0] == '\0') continue; /* we have a default case -- verify user:maildrop */ key.data = keybuf; key.size = keylen; i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); if (i != 0 || info.size <= 0) { /* nope -- no aliasing for this user */ continue; } /* they exist -- build the actual address */ p = xalloc(strlen(user) + strlen(up->udb_default) + 2); (void) strcpy(p, user); (void) strcat(p, "@"); (void) strcat(p, up->udb_default); if (tTd(28, 1)) printf("udbmatch ==> %s\n", p); return p; break; #endif #ifdef HESIOD case UDB_HESIOD: /* get the default case for this database */ if (up->udb_default == NULL) { key.data = ":default:mailname"; key.size = strlen(key.data); i = hes_udb_get(&key, &info); if (i != 0 || info.size <= 0) { /* no default case */ up->udb_default = ""; continue; } /* save the default case */ up->udb_default = xalloc(info.size + 1); bcopy(info.data, up->udb_default, info.size); up->udb_default[info.size] = '\0'; } else if (up->udb_default[0] == '\0') continue; /* we have a default case -- verify user:maildrop */ key.data = keybuf; key.size = keylen; i = hes_udb_get(&key, &info); if (i != 0 || info.size <= 0) { /* nope -- no aliasing for this user */ continue; } /* they exist -- build the actual address */ p = xalloc(strlen(user) + strlen(up->udb_default) + 2); (void) strcpy(p, user); (void) strcat(p, "@"); (void) strcat(p, up->udb_default); if (tTd(28, 1)) printf("udbmatch ==> %s\n", p); return p; break; #endif /* HESIOD */ } } /* still nothing.... too bad */ return NULL; } /* ** UDB_MAP_LOOKUP -- look up arbitrary entry in user database map ** ** Parameters: ** map -- the map being queried. ** name -- the name to look up. ** av -- arguments to the map lookup. ** statp -- to get any error status. ** ** Returns: ** NULL if name not found in map. ** The rewritten name otherwise. */ char * udb_map_lookup(map, name, av, statp) MAP *map; char *name; char **av; int *statp; { char *val; char *key; char keybuf[MAXNAME + 1]; if (tTd(28, 20) || tTd(38, 20)) printf("udb_map_lookup(%s, %s)\n", map->map_mname, name); if (bitset(MF_NOFOLDCASE, map->map_mflags)) { key = name; } else { int keysize = strlen(name); if (keysize > sizeof keybuf - 1) keysize = sizeof keybuf - 1; bcopy(name, keybuf, keysize); keybuf[keysize] = '\0'; makelower(keybuf); key = keybuf; } val = udbmatch(key, map->map_file); if (val == NULL) return NULL; if (bitset(MF_MATCHONLY, map->map_mflags)) return map_rewrite(map, name, strlen(name), NULL); else return map_rewrite(map, val, strlen(val), av); } /* ** _UDBX_INIT -- parse the UDB specification, opening any valid entries. ** ** Parameters: -** none. +** e -- the current envelope. ** ** Returns: ** EX_TEMPFAIL -- if it appeared it couldn't get hold of a ** database due to a host being down or some similar ** (recoverable) situation. ** EX_OK -- otherwise. ** ** Side Effects: ** Fills in the UdbEnts structure from UdbSpec. */ #define MAXUDBOPTS 27 int -_udbx_init() +_udbx_init(e) + ENVELOPE *e; { register char *p; register struct udbent *up; if (UdbInitialized) return EX_OK; # ifdef UDB_DEFAULT_SPEC if (UdbSpec == NULL) UdbSpec = UDB_DEFAULT_SPEC; # endif p = UdbSpec; up = UdbEnts; while (p != NULL) { char *spec; int nopts; int l; # if 0 auto int rcode; int nmx; int i; register struct hostent *h; char *mxhosts[MAXMXHOSTS + 1]; # endif struct option opts[MAXUDBOPTS + 1]; extern int _udb_parsespec __P((char *, struct option [], int)); while (*p == ' ' || *p == '\t' || *p == ',') p++; if (*p == '\0') break; spec = p; p = strchr(p, ','); if (p != NULL) *p++ = '\0'; /* extract options */ nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); /* ** Decode database specification. ** ** In the sendmail tradition, the leading character ** defines the semantics of the rest of the entry. ** ** +hostname -- send a datagram to the udb server ** on host "hostname" asking for the ** home mail server for this user. ** *hostname -- similar to +hostname, except that the ** hostname is searched as an MX record; ** resulting hosts are searched as for ** +mxhostname. If no MX host is found, ** this is the same as +hostname. ** @hostname -- forward email to the indicated host. ** This should be the last in the list, ** since it always matches the input. ** /dbname -- search the named database on the local ** host using the Berkeley db package. ** Hesiod -- search the named database with BIND ** using the MIT Hesiod package. */ switch (*spec) { #if 0 case '+': /* search remote database */ case '*': /* search remote database (expand MX) */ if (*spec == '*') { #if NAMED_BIND nmx = getmxrr(spec + 1, mxhosts, FALSE, &rcode); #else mxhosts[0] = spec + 1; nmx = 1; rcode = 0; #endif if (tTd(28, 16)) { int i; printf("getmxrr(%s): %d", spec + 1, nmx); for (i = 0; i <= nmx; i++) printf(" %s", mxhosts[i]); printf("\n"); } } else { nmx = 1; mxhosts[0] = spec + 1; } for (i = 0; i < nmx; i++) { h = sm_gethostbyname(mxhosts[i]); if (h == NULL) continue; up->udb_type = UDB_REMOTE; up->udb_addr.sin_family = h->h_addrtype; bcopy(h->h_addr_list[0], (char *) &up->udb_addr.sin_addr, INADDRSZ); up->udb_addr.sin_port = UdbPort; up->udb_timeout = UdbTimeout; up++; } /* set up a datagram socket */ if (UdbSock < 0) { UdbSock = socket(AF_INET, SOCK_DGRAM, 0); (void) fcntl(UdbSock, F_SETFD, 1); } break; #endif case '@': /* forward to remote host */ up->udb_type = UDB_FORWARD; up->udb_fwdhost = spec + 1; up++; break; #ifdef HESIOD case 'h': /* use hesiod */ case 'H': if (strcasecmp(spec, "hesiod") != 0) goto badspec; up->udb_type = UDB_HESIOD; up++; break; #endif /* HESIOD */ #ifdef NEWDB case '/': /* look up remote name */ l = strlen(spec); if (l > 3 && strcmp(&spec[l - 3], ".db") == 0) { up->udb_dbname = spec; } else { up->udb_dbname = xalloc(l + 4); strcpy(up->udb_dbname, spec); strcat(up->udb_dbname, ".db"); } errno = 0; up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY, 0644, DB_BTREE, NULL); if (up->udb_dbp == NULL) { if (tTd(28, 1)) { int saveerrno = errno; - printf("dbopen(%s): %s", + printf("dbopen(%s): %s\n", up->udb_dbname, errstring(errno)); errno = saveerrno; } if (errno != ENOENT && errno != EACCES) { -#ifdef LOG if (LogLevel > 2) - syslog(LOG_ERR, "dbopen(%s): %s", + sm_syslog(LOG_ERR, e->e_id, + "dbopen(%s): %s", up->udb_dbname, errstring(errno)); -#endif up->udb_type = UDB_EOLIST; if (up->udb_dbname != spec) free(up->udb_dbname); goto tempfail; } if (up->udb_dbname != spec) free(up->udb_dbname); break; } up->udb_type = UDB_DBFETCH; up++; break; #endif default: badspec: syserr("Unknown UDB spec %s", spec); break; } } up->udb_type = UDB_EOLIST; if (tTd(28, 4)) { for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { switch (up->udb_type) { #if DAEMON case UDB_REMOTE: printf("REMOTE: addr %s, timeo %d\n", anynet_ntoa((SOCKADDR *) &up->udb_addr), up->udb_timeout); break; #endif case UDB_DBFETCH: #ifdef NEWDB printf("FETCH: file %s\n", up->udb_dbname); #else printf("FETCH\n"); #endif break; case UDB_FORWARD: printf("FORWARD: host %s\n", up->udb_fwdhost); break; case UDB_HESIOD: printf("HESIOD\n"); break; default: printf("UNKNOWN\n"); break; } } } UdbInitialized = TRUE; errno = 0; return EX_OK; /* ** On temporary failure, back out anything we've already done */ tempfail: #ifdef NEWDB for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) { if (up->udb_type == UDB_DBFETCH) { (*up->udb_dbp->close)(up->udb_dbp); } } #endif return EX_TEMPFAIL; } int _udb_parsespec(udbspec, opt, maxopts) char *udbspec; struct option opt[]; int maxopts; { register char *spec; register char *spec_end; register int optnum; spec_end = strchr(udbspec, ':'); for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) { register char *p; while (isascii(*spec) && isspace(*spec)) spec++; spec_end = strchr(spec, ':'); if (spec_end != NULL) *spec_end++ = '\0'; opt[optnum].name = spec; opt[optnum].val = NULL; p = strchr(spec, '='); if (p != NULL) opt[optnum].val = ++p; } return optnum; } #ifdef HESIOD int hes_udb_get(key, info) DBT *key; DBT *info; { char *name, *type; char *p, **hp; char kbuf[MAXKEY + 1]; if (strlen(key->data) >= (SIZE_T) sizeof kbuf) return 0; strcpy(kbuf, key->data); name = kbuf; type = strrchr(name, ':'); if (type == NULL) return 1; *type++ = '\0'; if (strchr(name, '@') != NULL) return 1; if (tTd(28, 1)) printf("hes_udb_get(%s, %s)\n", name, type); /* make the hesiod query */ hp = hes_resolve(name, type); *--type = ':'; if (hp == NULL || hp[0] == NULL) { /* network problem or timeout */ if (hes_error() == HES_ER_NET) return -1; return 1; } else { /* ** If there are multiple matches, just return the ** first one. ** ** XXX These should really be returned; for example, ** XXX it is legal for :maildrop to be multi-valued. */ info->data = hp[0]; info->size = (size_t) strlen(info->data); } if (tTd(28, 80)) printf("hes_udb_get => %s\n", *hp); return 0; } #endif /* HESIOD */ #else /* not USERDB */ int udbexpand(a, sendq, aliaslevel, e) ADDRESS *a; ADDRESS **sendq; int aliaslevel; ENVELOPE *e; { return EX_OK; } #endif /* USERDB */ Index: head/usr.sbin/sendmail/src/usersmtp.c =================================================================== --- head/usr.sbin/sendmail/src/usersmtp.c (revision 26988) +++ head/usr.sbin/sendmail/src/usersmtp.c (revision 26989) @@ -1,1213 +1,1212 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ # include "sendmail.h" #ifndef lint #if SMTP -static char sccsid[] = "@(#)usersmtp.c 8.80 (Berkeley) 1/18/97 (with SMTP)"; +static char sccsid[] = "@(#)usersmtp.c 8.87 (Berkeley) 6/3/97 (with SMTP)"; #else -static char sccsid[] = "@(#)usersmtp.c 8.80 (Berkeley) 1/18/97 (without SMTP)"; +static char sccsid[] = "@(#)usersmtp.c 8.87 (Berkeley) 6/3/97 (without SMTP)"; #endif #endif /* not lint */ # include # include # if SMTP /* ** USERSMTP -- run SMTP protocol from the user end. ** ** This protocol is described in RFC821. */ #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ #define SMTPCLOSING 421 /* "Service Shutting Down" */ char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ char SmtpError[MAXLINE] = ""; /* save failure error messages */ int SmtpPid; /* pid of mailer */ bool SmtpNeedIntro; /* need "while talking" in transcript */ extern void smtpmessage __P((char *f, MAILER *m, MCI *mci, ...)); extern int reply __P((MAILER *, MCI *, ENVELOPE *, time_t, void (*)())); /* ** SMTPINIT -- initialize SMTP. ** ** Opens the connection and sends the initial protocol. ** ** Parameters: ** m -- mailer to create connection to. ** pvp -- pointer to parameter vector to pass to ** the mailer. ** ** Returns: ** none. ** ** Side Effects: ** creates connection and sends initial protocol. */ void smtpinit(m, mci, e) MAILER *m; register MCI *mci; ENVELOPE *e; { register int r; register char *p; extern void esmtp_check(); extern void helo_options(); if (tTd(18, 1)) { printf("smtpinit "); mci_dump(mci, FALSE); } /* ** Open the connection to the mailer. */ SmtpError[0] = '\0'; CurHostName = mci->mci_host; /* XXX UGLY XXX */ if (CurHostName == NULL) CurHostName = MyHostName; SmtpNeedIntro = TRUE; switch (mci->mci_state) { case MCIS_ACTIVE: /* need to clear old information */ smtprset(m, mci, e); /* fall through */ case MCIS_OPEN: return; case MCIS_ERROR: case MCIS_SSD: /* shouldn't happen */ smtpquit(m, mci, e); /* fall through */ case MCIS_CLOSED: syserr("451 smtpinit: state CLOSED"); return; case MCIS_OPENING: break; } mci->mci_state = MCIS_OPENING; /* ** Get the greeting message. ** This should appear spontaneously. Give it five minutes to ** happen. */ SmtpPhase = mci->mci_phase = "client greeting"; setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); if (r < 0) goto tempfail1; if (REPLYTYPE(r) == 4) goto tempfail2; if (REPLYTYPE(r) != 2) goto unavailable; /* ** Send the HELO command. ** My mother taught me to always introduce myself. */ #if _FFR_LMTP if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags)) #else if (bitnset(M_ESMTP, m->m_flags)) #endif mci->mci_flags |= MCIF_ESMTP; tryhelo: #if _FFR_LMTP if (bitnset(M_LMTP, m->m_flags)) { smtpmessage("LHLO %s", m, mci, MyHostName); SmtpPhase = mci->mci_phase = "client LHLO"; } else if (bitset(MCIF_ESMTP, mci->mci_flags)) #else if (bitset(MCIF_ESMTP, mci->mci_flags)) #endif { smtpmessage("EHLO %s", m, mci, MyHostName); SmtpPhase = mci->mci_phase = "client EHLO"; } else { smtpmessage("HELO %s", m, mci, MyHostName); SmtpPhase = mci->mci_phase = "client HELO"; } setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_helo, helo_options); if (r < 0) goto tempfail1; else if (REPLYTYPE(r) == 5) { #if _FFR_LMTP if (bitset(MCIF_ESMTP, mci->mci_flags) && !bitnset(M_LMTP, m->m_flags)) #else if (bitset(MCIF_ESMTP, mci->mci_flags)) #endif { /* try old SMTP instead */ mci->mci_flags &= ~MCIF_ESMTP; goto tryhelo; } goto unavailable; } else if (REPLYTYPE(r) != 2) goto tempfail2; /* ** Check to see if we actually ended up talking to ourself. ** This means we didn't know about an alias or MX, or we managed ** to connect to an echo server. */ p = strchr(&SmtpReplyBuffer[4], ' '); if (p != NULL) *p = '\0'; if (!bitnset(M_NOLOOPCHECK, m->m_flags) && #if _FFR_LMTP !bitnset(M_LMTP, m->m_flags) && #endif strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) { syserr("553 %s config error: mail loops back to me (MX problem?)", mci->mci_host); mci_setstat(mci, EX_CONFIG, NULL, NULL); mci->mci_errno = 0; smtpquit(m, mci, e); return; } /* ** If this is expected to be another sendmail, send some internal ** commands. */ if (bitnset(M_INTERNAL, m->m_flags)) { /* tell it to be verbose */ smtpmessage("VERB", m, mci); r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); if (r < 0) goto tempfail1; } if (mci->mci_state != MCIS_CLOSED) { mci->mci_state = MCIS_OPEN; return; } /* got a 421 error code during startup */ tempfail1: if (mci->mci_errno == 0) mci->mci_errno = errno; mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); if (mci->mci_state != MCIS_CLOSED) smtpquit(m, mci, e); return; tempfail2: if (mci->mci_errno == 0) mci->mci_errno = errno; /* XXX should use code from other end iff ENHANCEDSTATUSCODES */ mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer); if (mci->mci_state != MCIS_CLOSED) smtpquit(m, mci, e); return; unavailable: mci->mci_errno = errno; mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer); smtpquit(m, mci, e); return; } /* ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol ** ** Parameters: ** line -- the response line. ** firstline -- set if this is the first line of the reply. ** m -- the mailer. ** mci -- the mailer connection info. ** e -- the envelope. ** ** Returns: ** none. */ void esmtp_check(line, firstline, m, mci, e) char *line; bool firstline; MAILER *m; register MCI *mci; ENVELOPE *e; { if (strstr(line, "ESMTP") != NULL) mci->mci_flags |= MCIF_ESMTP; if (strstr(line, "8BIT-OK") != NULL) mci->mci_flags |= MCIF_8BITOK; } /* ** HELO_OPTIONS -- process the options on a HELO line. ** ** Parameters: ** line -- the response line. ** firstline -- set if this is the first line of the reply. ** m -- the mailer. ** mci -- the mailer connection info. ** e -- the envelope. ** ** Returns: ** none. */ void helo_options(line, firstline, m, mci, e) char *line; bool firstline; MAILER *m; register MCI *mci; ENVELOPE *e; { register char *p; if (firstline) return; if (strlen(line) < (SIZE_T) 5) return; line += 4; p = strchr(line, ' '); if (p != NULL) *p++ = '\0'; if (strcasecmp(line, "size") == 0) { mci->mci_flags |= MCIF_SIZE; if (p != NULL) mci->mci_maxsize = atol(p); } else if (strcasecmp(line, "8bitmime") == 0) { mci->mci_flags |= MCIF_8BITMIME; mci->mci_flags &= ~MCIF_7BIT; } else if (strcasecmp(line, "expn") == 0) mci->mci_flags |= MCIF_EXPN; else if (strcasecmp(line, "dsn") == 0) mci->mci_flags |= MCIF_DSN; } /* ** SMTPMAILFROM -- send MAIL command ** ** Parameters: ** m -- the mailer. ** mci -- the mailer connection structure. ** e -- the envelope (including the sender to specify). */ int smtpmailfrom(m, mci, e) MAILER *m; MCI *mci; ENVELOPE *e; { int r; int l; char *bufp; char *bodytype; char buf[MAXNAME + 1]; char optbuf[MAXLINE]; if (tTd(18, 2)) printf("smtpmailfrom: CurHost=%s\n", CurHostName); /* set up appropriate options to include */ if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize); else strcpy(optbuf, ""); l = sizeof optbuf - strlen(optbuf) - 1; bodytype = e->e_bodytype; if (bitset(MCIF_8BITMIME, mci->mci_flags)) { if (bodytype == NULL && bitset(MM_MIME8BIT, MimeMode) && bitset(EF_HAS8BIT, e->e_flags) && !bitset(EF_DONT_MIME, e->e_flags) && !bitnset(M_8BITS, m->m_flags)) bodytype = "8BITMIME"; - if (bodytype != NULL) + if (bodytype != NULL && strlen(bodytype) + 7 < l) { strcat(optbuf, " BODY="); strcat(optbuf, bodytype); l -= strlen(optbuf); } } else if (bitnset(M_8BITS, m->m_flags) || !bitset(EF_HAS8BIT, e->e_flags) || bitset(MCIF_8BITOK, mci->mci_flags)) { /* just pass it through */ } #if MIME8TO7 else if (bitset(MM_CVTMIME, MimeMode) && !bitset(EF_DONT_MIME, e->e_flags) && (!bitset(MM_PASS8BIT, MimeMode) || bitset(EF_IS_MIME, e->e_flags))) { /* must convert from 8bit MIME format to 7bit encoded */ mci->mci_flags |= MCIF_CVT8TO7; } #endif else if (!bitset(MM_PASS8BIT, MimeMode)) { /* cannot just send a 8-bit version */ extern char MsgBuf[]; usrerr("%s does not support 8BITMIME", mci->mci_host); mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf); return EX_DATAERR; } if (bitset(MCIF_DSN, mci->mci_flags)) { if (e->e_envid != NULL && strlen(e->e_envid) < (SIZE_T) (l - 7)) { strcat(optbuf, " ENVID="); strcat(optbuf, e->e_envid); l -= strlen(optbuf); } /* RET= parameter */ if (bitset(EF_RET_PARAM, e->e_flags) && l >= 9) { strcat(optbuf, " RET="); if (bitset(EF_NO_BODY_RETN, e->e_flags)) strcat(optbuf, "HDRS"); else strcat(optbuf, "FULL"); l -= 9; } } /* ** Send the MAIL command. ** Designates the sender. */ mci->mci_state = MCIS_ACTIVE; if (bitset(EF_RESPONSE, e->e_flags) && !bitnset(M_NO_NULL_FROM, m->m_flags)) (void) strcpy(buf, ""); else expand("\201g", buf, sizeof buf, e); if (buf[0] == '<') { /* strip off (put back on below) */ bufp = &buf[strlen(buf) - 1]; if (*bufp == '>') *bufp = '\0'; bufp = &buf[1]; } else bufp = buf; if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) || !bitnset(M_FROMPATH, m->m_flags)) { smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf); } else { smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, *bufp == '@' ? ',' : ':', bufp, optbuf); } SmtpPhase = mci->mci_phase = "client MAIL"; setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_mail, NULL); if (r < 0) { /* communications failure */ mci->mci_errno = errno; mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); smtpquit(m, mci, e); return EX_TEMPFAIL; } else if (r == 421) { /* service shutting down */ mci_setstat(mci, EX_TEMPFAIL, "4.5.0", SmtpReplyBuffer); smtpquit(m, mci, e); return EX_TEMPFAIL; } else if (r == 452 && bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) { mci_setstat(mci, EX_NOTSTICKY, smtptodsn(r), SmtpReplyBuffer); return EX_TEMPFAIL; } else if (REPLYTYPE(r) == 4) { mci_setstat(mci, EX_TEMPFAIL, smtptodsn(r), SmtpReplyBuffer); return EX_TEMPFAIL; } else if (REPLYTYPE(r) == 2) { return EX_OK; } else if (r == 501) { /* syntax error in arguments */ mci_setstat(mci, EX_NOTSTICKY, "5.5.2", SmtpReplyBuffer); return EX_DATAERR; } else if (r == 553) { /* mailbox name not allowed */ mci_setstat(mci, EX_NOTSTICKY, "5.1.3", SmtpReplyBuffer); return EX_DATAERR; } else if (r == 552) { /* exceeded storage allocation */ mci_setstat(mci, EX_NOTSTICKY, "5.2.2", SmtpReplyBuffer); + if (bitset(MCIF_SIZE, mci->mci_flags)) + e->e_flags |= EF_NO_BODY_RETN; return EX_UNAVAILABLE; } else if (REPLYTYPE(r) == 5) { /* unknown error */ mci_setstat(mci, EX_NOTSTICKY, "5.0.0", SmtpReplyBuffer); return EX_UNAVAILABLE; } -#ifdef LOG if (LogLevel > 1) { - syslog(LOG_CRIT, "%s: %.100s: SMTP MAIL protocol error: %s", - e->e_id, mci->mci_host, + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP MAIL protocol error: %s", + mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } -#endif /* protocol error -- close up */ mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer); smtpquit(m, mci, e); return EX_PROTOCOL; } /* ** SMTPRCPT -- designate recipient. ** ** Parameters: ** to -- address of recipient. ** m -- the mailer we are sending to. ** mci -- the connection info for this transaction. ** e -- the envelope for this transaction. ** ** Returns: ** exit status corresponding to recipient status. ** ** Side Effects: ** Sends the mail via SMTP. */ int smtprcpt(to, m, mci, e) ADDRESS *to; register MAILER *m; MCI *mci; ENVELOPE *e; { register int r; int l; char optbuf[MAXLINE]; strcpy(optbuf, ""); l = sizeof optbuf - 1; if (bitset(MCIF_DSN, mci->mci_flags)) { /* NOTIFY= parameter */ if (bitset(QHASNOTIFY, to->q_flags) && bitset(QPRIMARY, to->q_flags) && !bitnset(M_LOCALMAILER, m->m_flags)) { bool firstone = TRUE; strcat(optbuf, " NOTIFY="); if (bitset(QPINGONSUCCESS, to->q_flags)) { strcat(optbuf, "SUCCESS"); firstone = FALSE; } if (bitset(QPINGONFAILURE, to->q_flags)) { if (!firstone) strcat(optbuf, ","); strcat(optbuf, "FAILURE"); firstone = FALSE; } if (bitset(QPINGONDELAY, to->q_flags)) { if (!firstone) strcat(optbuf, ","); strcat(optbuf, "DELAY"); firstone = FALSE; } if (firstone) strcat(optbuf, "NEVER"); l -= strlen(optbuf); } /* ORCPT= parameter */ if (to->q_orcpt != NULL && strlen(to->q_orcpt) + 7 < l) { strcat(optbuf, " ORCPT="); strcat(optbuf, to->q_orcpt); l -= strlen(optbuf); } } smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf); SmtpPhase = mci->mci_phase = "client RCPT"; setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); to->q_rstatus = newstr(SmtpReplyBuffer); to->q_status = smtptodsn(r); to->q_statmta = mci->mci_host; if (r < 0 || REPLYTYPE(r) == 4) return EX_TEMPFAIL; else if (REPLYTYPE(r) == 2) return EX_OK; else if (r == 550) { to->q_status = "5.1.1"; return EX_NOUSER; } else if (r == 551) { to->q_status = "5.1.6"; return EX_NOUSER; } else if (r == 553) { to->q_status = "5.1.3"; return EX_NOUSER; } else if (REPLYTYPE(r) == 5) { return EX_UNAVAILABLE; } -#ifdef LOG if (LogLevel > 1) { - syslog(LOG_CRIT, "%s: %.100s: SMTP RCPT protocol error: %s", - e->e_id, mci->mci_host, + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP RCPT protocol error: %s", + mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } -#endif mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer); return EX_PROTOCOL; } /* ** SMTPDATA -- send the data and clean up the transaction. ** ** Parameters: ** m -- mailer being sent to. ** mci -- the mailer connection information. ** e -- the envelope for this message. ** ** Returns: ** exit status corresponding to DATA command. ** ** Side Effects: ** none. */ static jmp_buf CtxDataTimeout; static void datatimeout(); int smtpdata(m, mci, e) MAILER *m; register MCI *mci; register ENVELOPE *e; { register int r; register EVENT *ev; int rstat; int xstat; time_t timeout; /* ** Send the data. ** First send the command and check that it is ok. ** Then send the data. ** Follow it up with a dot to terminate. ** Finally get the results of the transaction. */ /* send the command and check ok to proceed */ smtpmessage("DATA", m, mci); SmtpPhase = mci->mci_phase = "client DATA 354"; setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); r = reply(m, mci, e, TimeOuts.to_datainit, NULL); if (r < 0 || REPLYTYPE(r) == 4) { smtpquit(m, mci, e); return EX_TEMPFAIL; } else if (REPLYTYPE(r) == 5) { smtprset(m, mci, e); return EX_UNAVAILABLE; } else if (r != 354) { -#ifdef LOG if (LogLevel > 1) { - syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-1 protocol error: %s", - e->e_id, mci->mci_host, + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP DATA-1 protocol error: %s", + mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } -#endif smtprset(m, mci, e); mci_setstat(mci, EX_PROTOCOL, "5.5.1", SmtpReplyBuffer); return (EX_PROTOCOL); } /* ** Set timeout around data writes. Make it at least large ** enough for DNS timeouts on all recipients plus some fudge ** factor. The main thing is that it should not be infinite. */ if (setjmp(CtxDataTimeout) != 0) { mci->mci_errno = errno; mci->mci_state = MCIS_ERROR; mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL); syserr("451 timeout writing message to %s", mci->mci_host); smtpquit(m, mci, e); return EX_TEMPFAIL; } timeout = e->e_msgsize / 16; if (timeout < (time_t) 600) timeout = (time_t) 600; timeout += e->e_nrcpts * 300; ev = setevent(timeout, datatimeout, 0); /* ** Output the actual message. */ (*e->e_puthdr)(mci, e->e_header, e); (*e->e_putbody)(mci, e, NULL); /* ** Cleanup after sending message. */ clrevent(ev); if (ferror(mci->mci_out)) { /* error during processing -- don't send the dot */ mci->mci_errno = EIO; mci->mci_state = MCIS_ERROR; mci_setstat(mci, EX_IOERR, "4.4.2", NULL); smtpquit(m, mci, e); return EX_IOERR; } /* terminate the message */ fprintf(mci->mci_out, ".%s", m->m_eol); if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid()); if (Verbose) nmessage(">>> ."); /* check for the results of the transaction */ SmtpPhase = mci->mci_phase = "client DATA status"; setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); #if _FFR_LMTP if (bitnset(M_LMTP, m->m_flags)) return EX_OK; #endif r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); if (r < 0) { smtpquit(m, mci, e); return EX_TEMPFAIL; } mci->mci_state = MCIS_OPEN; xstat = EX_NOTSTICKY; if (r == 452) rstat = EX_TEMPFAIL; - else if (r == 552) - rstat = EX_UNAVAILABLE; else if (REPLYTYPE(r) == 4) rstat = xstat = EX_TEMPFAIL; else if (REPLYCLASS(r) != 5) rstat = xstat = EX_PROTOCOL; else if (REPLYTYPE(r) == 2) rstat = xstat = EX_OK; else if (REPLYTYPE(r) == 5) - rstat = xstat = EX_UNAVAILABLE; + rstat = EX_UNAVAILABLE; else rstat = EX_PROTOCOL; mci_setstat(mci, xstat, smtptodsn(r), SmtpReplyBuffer); if (e->e_statmsg != NULL) free(e->e_statmsg); e->e_statmsg = newstr(&SmtpReplyBuffer[4]); if (rstat != EX_PROTOCOL) return rstat; -#ifdef LOG if (LogLevel > 1) { - syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-2 protocol error: %s", - e->e_id, mci->mci_host, + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP DATA-2 protocol error: %s", + mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } -#endif return rstat; } static void datatimeout() { longjmp(CtxDataTimeout, 1); } /* ** SMTPGETSTAT -- get status code from DATA in LMTP ** ** Parameters: ** m -- the mailer to which we are sending the message. ** mci -- the mailer connection structure. ** e -- the current envelope. ** ** Returns: ** The exit status corresponding to the reply code. */ #if _FFR_LMTP int smtpgetstat(m, mci, e) MAILER *m; MCI *mci; ENVELOPE *e; { int r; int stat; /* check for the results of the transaction */ r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); if (r < 0) { smtpquit(m, mci, e); return EX_TEMPFAIL; } if (e->e_statmsg != NULL) free(e->e_statmsg); e->e_statmsg = newstr(&SmtpReplyBuffer[4]); if (REPLYTYPE(r) == 4) stat = EX_TEMPFAIL; else if (REPLYCLASS(r) != 5) stat = EX_PROTOCOL; else if (REPLYTYPE(r) == 2) stat = EX_OK; else if (REPLYTYPE(r) == 5) stat = EX_UNAVAILABLE; mci_setstat(mci, stat, smtptodsn(r), SmtpReplyBuffer); -#ifdef LOG if (LogLevel > 1 && stat == EX_PROTOCOL) { - syslog(LOG_CRIT, "%s: %.100s: SMTP DATA-3 protocol error: %s", - e->e_id, mci->mci_host, + sm_syslog(LOG_CRIT, e->e_id, + "%.100s: SMTP DATA-3 protocol error: %s", + mci->mci_host, shortenstring(SmtpReplyBuffer, 403)); } -#endif return stat; } #endif /* ** SMTPQUIT -- close the SMTP connection. ** ** Parameters: ** m -- a pointer to the mailer. ** mci -- the mailer connection information. ** e -- the current envelope. ** ** Returns: ** none. ** ** Side Effects: ** sends the final protocol and closes the connection. */ void smtpquit(m, mci, e) register MAILER *m; register MCI *mci; ENVELOPE *e; { bool oldSuprErrs = SuprErrs; /* ** Suppress errors here -- we may be processing a different ** job when we do the quit connection, and we don't want the ** new job to be penalized for something that isn't it's ** problem. */ SuprErrs = TRUE; /* send the quit message if we haven't gotten I/O error */ if (mci->mci_state != MCIS_ERROR) { SmtpPhase = "client QUIT"; smtpmessage("QUIT", m, mci); (void) reply(m, mci, e, TimeOuts.to_quit, NULL); SuprErrs = oldSuprErrs; if (mci->mci_state == MCIS_CLOSED) - { - SuprErrs = oldSuprErrs; return; - } } /* now actually close the connection and pick up the zombie */ (void) endmailer(mci, e, NULL); SuprErrs = oldSuprErrs; } /* ** SMTPRSET -- send a RSET (reset) command */ void smtprset(m, mci, e) register MAILER *m; register MCI *mci; ENVELOPE *e; { int r; SmtpPhase = "client RSET"; smtpmessage("RSET", m, mci); r = reply(m, mci, e, TimeOuts.to_rset, NULL); if (r < 0) mci->mci_state = MCIS_ERROR; else if (REPLYTYPE(r) == 2) { mci->mci_state = MCIS_OPEN; return; } smtpquit(m, mci, e); } /* ** SMTPPROBE -- check the connection state */ int smtpprobe(mci) register MCI *mci; { int r; MAILER *m = mci->mci_mailer; extern ENVELOPE BlankEnvelope; ENVELOPE *e = &BlankEnvelope; SmtpPhase = "client probe"; smtpmessage("RSET", m, mci); r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); if (r < 0 || REPLYTYPE(r) != 2) smtpquit(m, mci, e); return r; } /* ** REPLY -- read arpanet reply ** ** Parameters: ** m -- the mailer we are reading the reply from. ** mci -- the mailer connection info structure. ** e -- the current envelope. ** timeout -- the timeout for reads. ** pfunc -- processing function called on each line of response. ** If null, no special processing is done. ** ** Returns: ** reply code it reads. ** ** Side Effects: ** flushes the mail file. */ int reply(m, mci, e, timeout, pfunc) MAILER *m; MCI *mci; ENVELOPE *e; time_t timeout; void (*pfunc)(); { register char *bufp; register int r; bool firstline = TRUE; char junkbuf[MAXLINE]; if (mci->mci_out != NULL) (void) fflush(mci->mci_out); if (tTd(18, 1)) printf("reply\n"); /* ** Read the input line, being careful not to hang. */ - for (bufp = SmtpReplyBuffer;; bufp = junkbuf) + bufp = SmtpReplyBuffer; + for (;;) { register char *p; extern time_t curtime(); /* actually do the read */ if (e->e_xfp != NULL) (void) fflush(e->e_xfp); /* for debugging */ /* if we are in the process of closing just give the code */ if (mci->mci_state == MCIS_CLOSED) return (SMTPCLOSING); if (mci->mci_out != NULL) fflush(mci->mci_out); /* get the line from the other side */ p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); mci->mci_lastuse = curtime(); if (p == NULL) { bool oldholderrs; extern char MsgBuf[]; /* if the remote end closed early, fake an error */ if (errno == 0) # ifdef ECONNRESET errno = ECONNRESET; # else /* ECONNRESET */ errno = EPIPE; # endif /* ECONNRESET */ mci->mci_errno = errno; oldholderrs = HoldErrs; HoldErrs = TRUE; usrerr("451 reply: read error from %s", mci->mci_host); mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf); /* if debugging, pause so we can see state */ if (tTd(18, 100)) pause(); mci->mci_state = MCIS_ERROR; smtpquit(m, mci, e); #if XDEBUG { char wbuf[MAXLINE]; char *p = wbuf; int wbufleft = sizeof wbuf; if (e->e_to != NULL) { int plen; snprintf(p, wbufleft, "%s... ", shortenstring(e->e_to, 203)); plen = strlen(p); p += plen; wbufleft -= plen; } snprintf(p, wbufleft, "reply(%.100s) during %s", mci->mci_host, SmtpPhase); checkfd012(wbuf); } #endif HoldErrs = oldholderrs; return (-1); } fixcrlf(bufp, TRUE); /* EHLO failure is not a real error */ if (e->e_xfp != NULL && (bufp[0] == '4' || (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) { /* serious error -- log the previous command */ if (SmtpNeedIntro) { /* inform user who we are chatting with */ fprintf(CurEnv->e_xfp, "... while talking to %s:\n", CurHostName); SmtpNeedIntro = FALSE; } if (SmtpMsgBuffer[0] != '\0') fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); SmtpMsgBuffer[0] = '\0'; /* now log the message as from the other side */ fprintf(e->e_xfp, "<<< %s\n", bufp); } /* display the input for verbose mode */ if (Verbose) nmessage("050 %s", bufp); + /* ignore improperly formated input */ + if (!(isascii(bufp[0]) && isdigit(bufp[0])) || + !(isascii(bufp[1]) && isdigit(bufp[1])) || + !(isascii(bufp[2]) && isdigit(bufp[2])) || + !(bufp[3] == ' ' || bufp[3] == '-')) + continue; + /* process the line */ if (pfunc != NULL) (*pfunc)(bufp, firstline, m, mci, e); firstline = FALSE; - /* if continuation is required, we can go on */ - if (bufp[3] == '-') - continue; - - /* ignore improperly formated input */ - if (!(isascii(bufp[0]) && isdigit(bufp[0]))) - continue; - /* decode the reply code */ r = atoi(bufp); /* extra semantics: 0xx codes are "informational" */ - if (r >= 100) + if (r < 100) + continue; + + /* if no continuation lines, return this line */ + if (bufp[3] != '-') break; + + /* first line of real reply -- ignore rest */ + bufp = junkbuf; } /* ** Now look at SmtpReplyBuffer -- only care about the first ** line of the response from here on out. */ /* save temporary failure messages for posterity */ if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer); /* reply code 421 is "Service Shutting Down" */ if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) { /* send the quit protocol */ mci->mci_state = MCIS_SSD; smtpquit(m, mci, e); } return (r); } /* ** SMTPMESSAGE -- send message to server ** ** Parameters: ** f -- format ** m -- the mailer to control formatting. ** a, b, c -- parameters ** ** Returns: ** none. ** ** Side Effects: ** writes message to mci->mci_out. */ /*VARARGS1*/ void #ifdef __STDC__ smtpmessage(char *f, MAILER *m, MCI *mci, ...) #else smtpmessage(f, m, mci, va_alist) char *f; MAILER *m; MCI *mci; va_dcl #endif { VA_LOCAL_DECL VA_START(mci); (void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap); VA_END; if (tTd(18, 1) || Verbose) nmessage(">>> %s", SmtpMsgBuffer); if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(), SmtpMsgBuffer); if (mci->mci_out != NULL) { fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, m == NULL ? "\r\n" : m->m_eol); } else if (tTd(18, 1)) { printf("smtpmessage: NULL mci_out\n"); } } # endif /* SMTP */ Index: head/usr.sbin/sendmail/src/util.c =================================================================== --- head/usr.sbin/sendmail/src/util.c (revision 26988) +++ head/usr.sbin/sendmail/src/util.c (revision 26989) @@ -1,2334 +1,2013 @@ /* - * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint -static char sccsid[] = "@(#)util.c 8.115 (Berkeley) 1/5/97"; +static char sccsid[] = "@(#)util.c 8.129 (Berkeley) 6/11/97"; #endif /* not lint */ # include "sendmail.h" # include /* ** STRIPQUOTES -- Strip quotes & quote bits from a string. ** ** Runs through a string and strips off unquoted quote ** characters and quote bits. This is done in place. ** ** Parameters: ** s -- the string to strip. ** ** Returns: ** none. ** ** Side Effects: ** none. ** ** Called By: ** deliver */ void stripquotes(s) char *s; { register char *p; register char *q; register char c; if (s == NULL) return; p = q = s; do { c = *p++; if (c == '\\') c = *p++; else if (c == '"') continue; *q++ = c; } while (c != '\0'); } /* ** XALLOC -- Allocate memory and bitch wildly on failure. ** ** THIS IS A CLUDGE. This should be made to give a proper ** error -- but after all, what can we do? ** ** Parameters: ** sz -- size of area to allocate. ** ** Returns: ** pointer to data region. ** ** Side Effects: ** Memory is allocated. */ char * xalloc(sz) register int sz; { register char *p; /* some systems can't handle size zero mallocs */ if (sz <= 0) sz = 1; p = malloc((unsigned) sz); if (p == NULL) { syserr("!Out of memory!!"); /* exit(EX_UNAVAILABLE); */ } return (p); } /* ** COPYPLIST -- copy list of pointers. ** ** This routine is the equivalent of newstr for lists of ** pointers. ** ** Parameters: ** list -- list of pointers to copy. ** Must be NULL terminated. ** copycont -- if TRUE, copy the contents of the vector ** (which must be a string) also. ** ** Returns: ** a copy of 'list'. ** ** Side Effects: ** none. */ char ** copyplist(list, copycont) char **list; bool copycont; { register char **vp; register char **newvp; for (vp = list; *vp != NULL; vp++) continue; vp++; newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); if (copycont) { for (vp = newvp; *vp != NULL; vp++) *vp = newstr(*vp); } return (newvp); } /* ** COPYQUEUE -- copy address queue. ** ** This routine is the equivalent of newstr for address queues ** addresses marked with QDONTSEND aren't copied ** ** Parameters: ** addr -- list of address structures to copy. ** ** Returns: ** a copy of 'addr'. ** ** Side Effects: ** none. */ ADDRESS * copyqueue(addr) ADDRESS *addr; { register ADDRESS *newaddr; ADDRESS *ret; register ADDRESS **tail = &ret; while (addr != NULL) { if (!bitset(QDONTSEND, addr->q_flags)) { newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS)); STRUCTCOPY(*addr, *newaddr); *tail = newaddr; tail = &newaddr->q_next; } addr = addr->q_next; } *tail = NULL; return ret; } /* ** PRINTAV -- print argument vector. ** ** Parameters: ** av -- argument vector. ** ** Returns: ** none. ** ** Side Effects: ** prints av. */ void printav(av) register char **av; { while (*av != NULL) { if (tTd(0, 44)) printf("\n\t%08lx=", (u_long) *av); else (void) putchar(' '); xputs(*av++); } (void) putchar('\n'); } /* ** LOWER -- turn letter into lower case. ** ** Parameters: ** c -- character to turn into lower case. ** ** Returns: ** c, in lower case. ** ** Side Effects: ** none. */ char lower(c) register char c; { return((isascii(c) && isupper(c)) ? tolower(c) : c); } /* ** XPUTS -- put string doing control escapes. ** ** Parameters: ** s -- string to put. ** ** Returns: ** none. ** ** Side Effects: ** output to stdout */ void xputs(s) register const char *s; { register int c; register struct metamac *mp; bool shiftout = FALSE; extern struct metamac MetaMacros[]; if (s == NULL) { printf("%s%s", TermEscape.te_rv_on, TermEscape.te_rv_off); return; } while ((c = (*s++ & 0377)) != '\0') { if (shiftout) { printf("%s", TermEscape.te_rv_off); shiftout = FALSE; } if (!isascii(c)) { if (c == MATCHREPL) { printf("%s$", TermEscape.te_rv_on); shiftout = TRUE; if (*s == '\0') continue; c = *s++ & 0377; goto printchar; } if (c == MACROEXPAND) { printf("%s$", TermEscape.te_rv_on); shiftout = TRUE; if (strchr("=~&?", *s) != NULL) putchar(*s++); if (bitset(0200, *s)) printf("{%s}", macname(*s++ & 0377)); else printf("%c", *s++); continue; } for (mp = MetaMacros; mp->metaname != '\0'; mp++) { if ((mp->metaval & 0377) == c) { printf("%s$%c", TermEscape.te_rv_on, mp->metaname); shiftout = TRUE; break; } } if (c == MATCHCLASS || c == MATCHNCLASS) { if (bitset(0200, *s)) printf("{%s}", macname(*s++ & 0377)); else if (*s != '\0') printf("%c", *s++); } if (mp->metaname != '\0') continue; /* unrecognized meta character */ printf("%sM-", TermEscape.te_rv_on); shiftout = TRUE; c &= 0177; } printchar: if (isprint(c)) { putchar(c); continue; } /* wasn't a meta-macro -- find another way to print it */ switch (c) { case '\n': c = 'n'; break; case '\r': c = 'r'; break; case '\t': c = 't'; break; } if (!shiftout) { printf("%s", TermEscape.te_rv_on); shiftout = TRUE; } if (isprint(c)) { (void) putchar('\\'); (void) putchar(c); } else { (void) putchar('^'); (void) putchar(c ^ 0100); } } if (shiftout) printf("%s", TermEscape.te_rv_off); (void) fflush(stdout); } /* ** MAKELOWER -- Translate a line into lower case ** ** Parameters: ** p -- the string to translate. If NULL, return is ** immediate. ** ** Returns: ** none. ** ** Side Effects: ** String pointed to by p is translated to lower case. ** ** Called By: ** parse */ void makelower(p) register char *p; { register char c; if (p == NULL) return; for (; (c = *p) != '\0'; p++) if (isascii(c) && isupper(c)) *p = tolower(c); } /* ** BUILDFNAME -- build full name from gecos style entry. ** ** This routine interprets the strange entry that would appear ** in the GECOS field of the password file. ** ** Parameters: ** p -- name to build. ** login -- the login name of this user (for &). ** buf -- place to put the result. ** buflen -- length of buf. ** ** Returns: ** none. ** ** Side Effects: ** none. */ void buildfname(gecos, login, buf, buflen) register char *gecos; char *login; char *buf; int buflen; { register char *p; register char *bp = buf; if (*gecos == '*') gecos++; /* copy gecos, interpolating & to be full name */ for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) { if (bp >= &buf[buflen - 1]) { /* buffer overflow -- just use login name */ snprintf(buf, buflen, "%s", login); return; } if (*p == '&') { /* interpolate full name */ snprintf(bp, buflen - (bp - buf), "%s", login); *bp = toupper(*bp); bp += strlen(bp); } else *bp++ = *p; } *bp = '\0'; } /* -** SAFEFILE -- return true if a file exists and is safe for a user. -** -** Parameters: -** fn -- filename to check. -** uid -- user id to compare against. -** gid -- group id to compare against. -** uname -- user name to compare against (used for group -** sets). -** flags -- modifiers: -** SFF_MUSTOWN -- "uid" must own this file. -** SFF_NOSLINK -- file cannot be a symbolic link. -** mode -- mode bits that must match. -** st -- if set, points to a stat structure that will -** get the stat info for the file. -** -** Returns: -** 0 if fn exists, is owned by uid, and matches mode. -** An errno otherwise. The actual errno is cleared. -** -** Side Effects: -** none. -*/ - -#include - -#ifndef S_IXOTH -# define S_IXOTH (S_IEXEC >> 6) -#endif - -#ifndef S_IXGRP -# define S_IXGRP (S_IEXEC >> 3) -#endif - -#ifndef S_IXUSR -# define S_IXUSR (S_IEXEC) -#endif - -#define ST_MODE_NOFILE 0171147 /* unlikely to occur */ - -int -safefile(fn, uid, gid, uname, flags, mode, st) - char *fn; - UID_T uid; - GID_T gid; - char *uname; - int flags; - int mode; - struct stat *st; -{ - register char *p; - register struct group *gr = NULL; - int file_errno = 0; - struct stat stbuf; - struct stat fstbuf; - - if (tTd(44, 4)) - printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n", - fn, (int) uid, (int) gid, flags, mode); - errno = 0; - if (st == NULL) - st = &fstbuf; - - /* first check to see if the file exists at all */ -#ifdef HASLSTAT - if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) - : stat(fn, st)) < 0) -#else - if (stat(fn, st) < 0) -#endif - { - file_errno = errno; - } - else if (bitset(SFF_SETUIDOK, flags) && - !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) && - S_ISREG(st->st_mode)) - { - /* - ** If final file is setuid, run as the owner of that - ** file. Gotta be careful not to reveal anything too - ** soon here! - */ - -#ifdef SUID_ROOT_FILES_OK - if (bitset(S_ISUID, st->st_mode)) -#else - if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0) -#endif - { - uid = st->st_uid; - uname = NULL; - } -#ifdef SUID_ROOT_FILES_OK - if (bitset(S_ISGID, st->st_mode)) -#else - if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) -#endif - gid = st->st_gid; - } - - if (!bitset(SFF_NOPATHCHECK, flags) || - (uid == 0 && !bitset(SFF_ROOTOK, flags))) - { - /* check the path to the file for acceptability */ - for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/') - { - *p = '\0'; - if (stat(fn, &stbuf) < 0) - break; - if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode)) - message("051 WARNING: writable directory %s", - fn); - if (uid == 0 && !bitset(SFF_ROOTOK, flags)) - { - if (bitset(S_IXOTH, stbuf.st_mode)) - continue; - break; - } - if (stbuf.st_uid == uid && - bitset(S_IXUSR, stbuf.st_mode)) - continue; - if (stbuf.st_gid == gid && - bitset(S_IXGRP, stbuf.st_mode)) - continue; -#ifndef NO_GROUP_SET - if (uname != NULL && !DontInitGroups && - ((gr != NULL && gr->gr_gid == stbuf.st_gid) || - (gr = getgrgid(stbuf.st_gid)) != NULL)) - { - register char **gp; - - for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) - if (strcmp(*gp, uname) == 0) - break; - if (gp != NULL && *gp != NULL && - bitset(S_IXGRP, stbuf.st_mode)) - continue; - } -#endif - if (!bitset(S_IXOTH, stbuf.st_mode)) - break; - } - if (p != NULL) - { - int ret = errno; - - if (ret == 0) - ret = EACCES; - if (tTd(44, 4)) - printf("\t[dir %s] %s\n", fn, errstring(ret)); - *p = '/'; - return ret; - } - } - - /* - ** If the target file doesn't exist, check the directory to - ** ensure that it is writable by this user. - */ - - if (file_errno != 0) - { - int ret = file_errno; - - if (tTd(44, 4)) - printf("\t%s\n", errstring(ret)); - - errno = 0; - if (!bitset(SFF_CREAT, flags)) - return ret; - - /* check to see if legal to create the file */ - p = strrchr(fn, '/'); - if (p == NULL) - return ENOTDIR; - *p = '\0'; - if (stat(fn, &stbuf) >= 0) - { - int md = S_IWRITE|S_IEXEC; - if (stbuf.st_uid != uid) - md >>= 6; - if ((stbuf.st_mode & md) != md) - errno = EACCES; - } - ret = errno; - if (tTd(44, 4)) - printf("\t[final dir %s uid %d mode %lo] %s\n", - fn, (int) stbuf.st_uid, (u_long) stbuf.st_mode, - errstring(ret)); - *p = '/'; - st->st_mode = ST_MODE_NOFILE; - return ret; - } - -#ifdef S_ISLNK - if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) - { - if (tTd(44, 4)) - printf("\t[slink mode %o]\tEPERM\n", st->st_mode); - return EPERM; - } -#endif - if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) - { - if (tTd(44, 4)) - printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode); - return EPERM; - } - if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && - bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode)) - { - if (tTd(44, 4)) - printf("\t[exec bits %o]\tEPERM]\n", st->st_mode); - return EPERM; - } - if (st->st_nlink > 1) - { - if (tTd(44, 4)) - printf("\t[link count %d]\tEPERM\n", st->st_nlink); - return EPERM; - } - - if (uid == 0 && bitset(SFF_OPENASROOT, flags)) - ; - else if (uid == 0 && !bitset(SFF_ROOTOK, flags)) - mode >>= 6; - else if (st->st_uid != uid) - { - mode >>= 3; - if (st->st_gid == gid) - ; -#ifndef NO_GROUP_SET - else if (uname != NULL && !DontInitGroups && - ((gr != NULL && gr->gr_gid == st->st_gid) || - (gr = getgrgid(st->st_gid)) != NULL)) - { - register char **gp; - - for (gp = gr->gr_mem; *gp != NULL; gp++) - if (strcmp(*gp, uname) == 0) - break; - if (*gp == NULL) - mode >>= 3; - } -#endif - else - mode >>= 3; - } - if (tTd(44, 4)) - printf("\t[uid %d, nlink %d, stat %lo, mode %lo] ", - (int) st->st_uid, (int) st->st_nlink, - (u_long) st->st_mode, (u_long) mode); - if ((st->st_uid == uid || st->st_uid == 0 || - !bitset(SFF_MUSTOWN, flags)) && - (st->st_mode & mode) == mode) - { - if (tTd(44, 4)) - printf("\tOK\n"); - return 0; - } - if (tTd(44, 4)) - printf("\tEACCES\n"); - return EACCES; -} - /* -** SAFEFOPEN -- do a file open with extra checking -** -** Parameters: -** fn -- the file name to open. -** omode -- the open-style mode flags. -** cmode -- the create-style mode flags. -** sff -- safefile flags. -** -** Returns: -** Same as fopen. -*/ - -#ifndef O_ACCMODE -# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) -#endif - -FILE * -safefopen(fn, omode, cmode, sff) - char *fn; - int omode; - int cmode; - int sff; -{ - int rval; - FILE *fp; - int smode; - struct stat stb, sta; - - if (bitset(O_CREAT, omode)) - sff |= SFF_CREAT; - smode = 0; - switch (omode & O_ACCMODE) - { - case O_RDONLY: - smode = S_IREAD; - break; - - case O_WRONLY: - smode = S_IWRITE; - break; - - case O_RDWR: - smode = S_IREAD|S_IWRITE; - break; - - default: - smode = 0; - break; - } - if (bitset(SFF_OPENASROOT, sff)) - rval = safefile(fn, 0, 0, NULL, sff, smode, &stb); - else - rval = safefile(fn, RealUid, RealGid, RealUserName, - sff, smode, &stb); - if (rval != 0) - { - errno = rval; - return NULL; - } - if (stb.st_mode == ST_MODE_NOFILE) - omode |= O_EXCL; - - fp = dfopen(fn, omode, cmode); - if (fp == NULL) - return NULL; - if (bitset(O_EXCL, omode)) - return fp; - if (fstat(fileno(fp), &sta) < 0 || - sta.st_nlink != stb.st_nlink || - sta.st_dev != stb.st_dev || - sta.st_ino != stb.st_ino || - sta.st_uid != stb.st_uid || - sta.st_gid != stb.st_gid) - { - syserr("554 cannot open: file %s changed after open", fn); - fclose(fp); - errno = EPERM; - return NULL; - } - return fp; -} - /* ** FIXCRLF -- fix in line. ** ** Looks for the combination and turns it into the ** UNIX canonical character. It only takes one line, ** i.e., it is assumed that the first found is the end ** of the line. ** ** Parameters: ** line -- the line to fix. ** stripnl -- if true, strip the newline also. ** ** Returns: ** none. ** ** Side Effects: ** line is changed in place. */ void fixcrlf(line, stripnl) char *line; bool stripnl; { register char *p; p = strchr(line, '\n'); if (p == NULL) return; if (p > line && p[-1] == '\r') p--; if (!stripnl) *p++ = '\n'; *p = '\0'; } /* -** DFOPEN -- determined file open -** -** This routine has the semantics of fopen, except that it will -** keep trying a few times to make this happen. The idea is that -** on very loaded systems, we may run out of resources (inodes, -** whatever), so this tries to get around it. -*/ - -struct omodes -{ - int mask; - int mode; - char *farg; -} OpenModes[] = -{ - { O_ACCMODE, O_RDONLY, "r" }, - { O_ACCMODE|O_APPEND, O_WRONLY, "w" }, - { O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a" }, - { O_TRUNC, 0, "w+" }, - { O_APPEND, O_APPEND, "a+" }, - { 0, 0, "r+" }, -}; - -FILE * -dfopen(filename, omode, cmode) - char *filename; - int omode; - int cmode; -{ - register int tries; - int fd; - register struct omodes *om; - struct stat st; - - for (om = OpenModes; om->mask != 0; om++) - if ((omode & om->mask) == om->mode) - break; - - for (tries = 0; tries < 10; tries++) - { - sleep((unsigned) (10 * tries)); - errno = 0; - fd = open(filename, omode, cmode); - if (fd >= 0) - break; - switch (errno) - { - case ENFILE: /* system file table full */ - case EINTR: /* interrupted syscall */ -#ifdef ETXTBSY - case ETXTBSY: /* Apollo: net file locked */ -#endif - continue; - } - break; - } - if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) - { - int locktype; - - /* lock the file to avoid accidental conflicts */ - if ((omode & O_ACCMODE) != O_RDONLY) - locktype = LOCK_EX; - else - locktype = LOCK_SH; - (void) lockfile(fd, filename, NULL, locktype); - errno = 0; - } - if (fd < 0) - return NULL; - else - return fdopen(fd, om->farg); -} - /* ** PUTLINE -- put a line like fputs obeying SMTP conventions ** ** This routine always guarantees outputing a newline (or CRLF, ** as appropriate) at the end of the string. ** ** Parameters: ** l -- line to put. ** mci -- the mailer connection information. ** ** Returns: ** none ** ** Side Effects: ** output of l to fp. */ void putline(l, mci) register char *l; register MCI *mci; { - putxline(l, mci, PXLF_MAPFROM); + putxline(l, strlen(l), mci, PXLF_MAPFROM); } /* ** PUTXLINE -- putline with flags bits. ** ** This routine always guarantees outputing a newline (or CRLF, ** as appropriate) at the end of the string. ** ** Parameters: ** l -- line to put. +** len -- the length of the line. ** mci -- the mailer connection information. ** pxflags -- flag bits: ** PXLF_MAPFROM -- map From_ to >From_. ** PXLF_STRIP8BIT -- strip 8th bit. ** ** Returns: ** none ** ** Side Effects: ** output of l to fp. */ void -putxline(l, mci, pxflags) +putxline(l, len, mci, pxflags) register char *l; + size_t len; register MCI *mci; int pxflags; { - register char *p; + register char *p, *end; register char svchar; int slop = 0; /* strip out 0200 bits -- these can look like TELNET protocol */ if (bitset(MCIF_7BIT, mci->mci_flags) || bitset(PXLF_STRIP8BIT, pxflags)) { for (p = l; (svchar = *p) != '\0'; ++p) if (bitset(0200, svchar)) *p = svchar &~ 0200; } + end = l + len; do { /* find the end of the line */ - p = strchr(l, '\n'); + p = memchr(l, '\n', end - l); if (p == NULL) - p = &l[strlen(l)]; + p = end; if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d >>> ", (int) getpid()); /* check for line overflow */ while (mci->mci_mailer->m_linelimit > 0 && (p - l + slop) > mci->mci_mailer->m_linelimit) { register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; svchar = *q; *q = '\0'; if (l[0] == '.' && slop == 0 && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { (void) putc('.', mci->mci_out); if (TrafficLogFile != NULL) (void) putc('.', TrafficLogFile); } else if (l[0] == 'F' && slop == 0 && bitset(PXLF_MAPFROM, pxflags) && strncmp(l, "From ", 5) == 0 && bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) { (void) putc('>', mci->mci_out); if (TrafficLogFile != NULL) (void) putc('>', TrafficLogFile); } fputs(l, mci->mci_out); (void) putc('!', mci->mci_out); fputs(mci->mci_mailer->m_eol, mci->mci_out); (void) putc(' ', mci->mci_out); if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%s!\n%05d >>> ", - l, (int) getpid()); + { + for ( ; l < q; ++l) + (void) putc(*l, TrafficLogFile); + fprintf(TrafficLogFile, "!\n%05d >>> ", + (int) getpid()); + } *q = svchar; l = q; slop = 1; } /* output last part */ if (l[0] == '.' && slop == 0 && bitnset(M_XDOT, mci->mci_mailer->m_flags)) { (void) putc('.', mci->mci_out); if (TrafficLogFile != NULL) (void) putc('.', TrafficLogFile); } else if (l[0] == 'F' && slop == 0 && bitset(PXLF_MAPFROM, pxflags) && strncmp(l, "From ", 5) == 0 && bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) { (void) putc('>', mci->mci_out); if (TrafficLogFile != NULL) (void) putc('>', TrafficLogFile); } - if (TrafficLogFile != NULL) - fprintf(TrafficLogFile, "%.*s\n", p - l, l); for ( ; l < p; ++l) + { + if (TrafficLogFile != NULL) + (void) putc(*l, TrafficLogFile); (void) putc(*l, mci->mci_out); + } + if (TrafficLogFile != NULL) + (void) putc('\n', TrafficLogFile); fputs(mci->mci_mailer->m_eol, mci->mci_out); if (*l == '\n') { if (*++l != ' ' && *l != '\t' && *l != '\0') { (void) putc(' ', mci->mci_out); if (TrafficLogFile != NULL) (void) putc(' ', TrafficLogFile); } } - } while (l[0] != '\0'); + } while (l < end); } /* ** XUNLINK -- unlink a file, doing logging as appropriate. ** ** Parameters: ** f -- name of file to unlink. ** ** Returns: ** none. ** ** Side Effects: ** f is unlinked. */ void xunlink(f) char *f; { register int i; -# ifdef LOG if (LogLevel > 98) - syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f); -# endif /* LOG */ + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "unlink %s", + f); i = unlink(f); -# ifdef LOG if (i < 0 && LogLevel > 97) - syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); -# endif /* LOG */ + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "%s: unlink-fail %d", + f, errno); } /* ** XFCLOSE -- close a file, doing logging as appropriate. ** ** Parameters: ** fp -- file pointer for the file to close ** a, b -- miscellaneous crud to print for debugging ** ** Returns: ** none. ** ** Side Effects: ** fp is closed. */ void xfclose(fp, a, b) FILE *fp; char *a, *b; { if (tTd(53, 99)) printf("xfclose(%lx) %s %s\n", (u_long) fp, a, b); #if XDEBUG if (fileno(fp) == 1) syserr("xfclose(%s %s): fd = 1", a, b); #endif if (fclose(fp) < 0 && tTd(53, 99)) printf("xfclose FAILURE: %s\n", errstring(errno)); } /* ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. ** ** Parameters: ** buf -- place to put the input line. ** siz -- size of buf. ** fp -- file to read from. ** timeout -- the timeout before error occurs. ** during -- what we are trying to read (for error messages). ** ** Returns: ** NULL on error (including timeout). This will also leave ** buf containing a null string. ** buf otherwise. ** ** Side Effects: ** none. */ static jmp_buf CtxReadTimeout; static void readtimeout(); char * sfgets(buf, siz, fp, timeout, during) char *buf; int siz; FILE *fp; time_t timeout; char *during; { register EVENT *ev = NULL; register char *p; if (fp == NULL) { buf[0] = '\0'; return NULL; } /* set the timeout */ if (timeout != 0) { if (setjmp(CtxReadTimeout) != 0) { -# ifdef LOG if (LogLevel > 1) - syslog(LOG_NOTICE, + sm_syslog(LOG_NOTICE, CurEnv->e_id, "timeout waiting for input from %.100s during %s", CurHostName ? CurHostName : "local", during); -# endif errno = 0; - usrerr("451 timeout waiting for input during %s", - during); buf[0] = '\0'; #if XDEBUG checkfd012(during); #endif + if (TrafficLogFile != NULL) + fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n", + (int) getpid()); return (NULL); } ev = setevent(timeout, readtimeout, 0); } /* try to read */ p = NULL; while (!feof(fp) && !ferror(fp)) { errno = 0; p = fgets(buf, siz, fp); if (p != NULL || errno != EINTR) break; clearerr(fp); } /* clear the event if it has not sprung */ clrevent(ev); /* clean up the books and exit */ LineNumber++; if (p == NULL) { buf[0] = '\0'; if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid()); return (NULL); } if (TrafficLogFile != NULL) fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf); if (SevenBitInput) { for (p = buf; *p != '\0'; p++) *p &= ~0200; } else if (!HasEightBits) { for (p = buf; *p != '\0'; p++) { if (bitset(0200, *p)) { HasEightBits = TRUE; break; } } } return (buf); } static void readtimeout(timeout) time_t timeout; { longjmp(CtxReadTimeout, 1); } /* ** FGETFOLDED -- like fgets, but know about folded lines. ** ** Parameters: ** buf -- place to put result. ** n -- bytes available. ** f -- file to read from. ** ** Returns: ** input line(s) on success, NULL on error or EOF. ** This will normally be buf -- unless the line is too ** long, when it will be xalloc()ed. ** ** Side Effects: ** buf gets lines from f, with continuation lines (lines ** with leading white space) appended. CRLF's are mapped ** into single newlines. Any trailing NL is stripped. */ char * fgetfolded(buf, n, f) char *buf; register int n; FILE *f; { register char *p = buf; char *bp = buf; register int i; n--; while ((i = getc(f)) != EOF) { if (i == '\r') { i = getc(f); if (i != '\n') { if (i != EOF) (void) ungetc(i, f); i = '\r'; } } if (--n <= 0) { /* allocate new space */ char *nbp; int nn; nn = (p - bp); if (nn < MEMCHUNKSIZE) nn *= 2; else nn += MEMCHUNKSIZE; nbp = xalloc(nn); bcopy(bp, nbp, p - bp); p = &nbp[p - bp]; if (bp != buf) free(bp); bp = nbp; n = nn - (p - bp); } *p++ = i; if (i == '\n') { LineNumber++; i = getc(f); if (i != EOF) (void) ungetc(i, f); if (i != ' ' && i != '\t') break; } } if (p == bp) return (NULL); if (p[-1] == '\n') p--; *p = '\0'; return (bp); } /* ** CURTIME -- return current time. ** ** Parameters: ** none. ** ** Returns: ** the current time. ** ** Side Effects: ** none. */ time_t curtime() { auto time_t t; (void) time(&t); return (t); } /* ** ATOBOOL -- convert a string representation to boolean. ** ** Defaults to "TRUE" ** ** Parameters: ** s -- string to convert. Takes "tTyY" as true, ** others as false. ** ** Returns: ** A boolean representation of the string. ** ** Side Effects: ** none. */ bool atobool(s) register char *s; { if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) return (TRUE); return (FALSE); } /* ** ATOOCT -- convert a string representation to octal. ** ** Parameters: ** s -- string to convert. ** ** Returns: ** An integer representing the string interpreted as an ** octal number. ** ** Side Effects: ** none. */ int atooct(s) register char *s; { register int i = 0; while (*s >= '0' && *s <= '7') i = (i << 3) | (*s++ - '0'); return (i); } /* -** WAITFOR -- wait for a particular process id. -** -** Parameters: -** pid -- process id to wait for. -** -** Returns: -** status of pid. -** -1 if pid never shows up. -** -** Side Effects: -** none. -*/ - -int -waitfor(pid) - pid_t pid; -{ -#ifdef WAITUNION - union wait st; -#else - auto int st; -#endif - pid_t i; - - do - { - errno = 0; - i = wait(&st); - if (i > 0) - proc_list_drop(i); - } while ((i >= 0 || errno == EINTR) && i != pid); - if (i < 0) - return -1; -#ifdef WAITUNION - return st.w_status; -#else - return st; -#endif -} - /* ** BITINTERSECT -- tell if two bitmaps intersect ** ** Parameters: ** a, b -- the bitmaps in question ** ** Returns: ** TRUE if they have a non-null intersection ** FALSE otherwise ** ** Side Effects: ** none. */ bool bitintersect(a, b) BITMAP a; BITMAP b; { int i; for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) if ((a[i] & b[i]) != 0) return (TRUE); return (FALSE); } /* ** BITZEROP -- tell if a bitmap is all zero ** ** Parameters: ** map -- the bit map to check ** ** Returns: ** TRUE if map is all zero. ** FALSE if there are any bits set in map. ** ** Side Effects: ** none. */ bool bitzerop(map) BITMAP map; { int i; for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) if (map[i] != 0) return (FALSE); return (TRUE); } /* ** STRCONTAINEDIN -- tell if one string is contained in another ** ** Parameters: ** a -- possible substring. ** b -- possible superstring. ** ** Returns: ** TRUE if a is contained in b. ** FALSE otherwise. */ bool strcontainedin(a, b) register char *a; register char *b; { int la; int lb; int c; la = strlen(a); lb = strlen(b); c = *a; if (isascii(c) && isupper(c)) c = tolower(c); for (; lb-- >= la; b++) { if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) continue; if (strncasecmp(a, b, la) == 0) return TRUE; } return FALSE; } /* ** CHECKFD012 -- check low numbered file descriptors ** ** File descriptors 0, 1, and 2 should be open at all times. ** This routine verifies that, and fixes it if not true. ** ** Parameters: ** where -- a tag printed if the assertion failed ** ** Returns: ** none */ void checkfd012(where) char *where; { #if XDEBUG register int i; struct stat stbuf; for (i = 0; i < 3; i++) { if (fstat(i, &stbuf) < 0 && errno == EBADF) { /* oops.... */ int fd; syserr("%s: fd %d not open", where, i); fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666); if (fd != i) { (void) dup2(fd, i); (void) close(fd); } } } #endif /* XDEBUG */ } /* ** CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging ** ** Parameters: ** fd -- file descriptor to check. ** where -- tag to print on failure. ** ** Returns: ** none. */ void checkfdopen(fd, where) int fd; char *where; { #if XDEBUG struct stat st; if (fstat(fd, &st) < 0 && errno == EBADF) { syserr("checkfdopen(%d): %s not open as expected!", fd, where); printopenfds(TRUE); } #endif } /* ** CHECKFDS -- check for new or missing file descriptors ** ** Parameters: ** where -- tag for printing. If null, take a base line. ** ** Returns: ** none ** ** Side Effects: ** If where is set, shows changes since the last call. */ void checkfds(where) char *where; { int maxfd; register int fd; bool printhdr = TRUE; int save_errno = errno; static BITMAP baseline; extern int DtableSize; if (DtableSize > 256) maxfd = 256; else maxfd = DtableSize; if (where == NULL) clrbitmap(baseline); for (fd = 0; fd < maxfd; fd++) { struct stat stbuf; if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP) { if (!bitnset(fd, baseline)) continue; clrbitn(fd, baseline); } else if (!bitnset(fd, baseline)) setbitn(fd, baseline); else continue; /* file state has changed */ if (where == NULL) continue; if (printhdr) { - syslog(LOG_DEBUG, "%s: changed fds:", where); + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "%s: changed fds:", + where); printhdr = FALSE; } dumpfd(fd, TRUE, TRUE); } errno = save_errno; } /* ** PRINTOPENFDS -- print the open file descriptors (for debugging) ** ** Parameters: ** logit -- if set, send output to syslog; otherwise ** print for debugging. ** ** Returns: ** none. */ #include void printopenfds(logit) bool logit; { register int fd; extern int DtableSize; for (fd = 0; fd < DtableSize; fd++) dumpfd(fd, FALSE, logit); } /* ** DUMPFD -- dump a file descriptor ** ** Parameters: ** fd -- the file descriptor to dump. ** printclosed -- if set, print a notification even if ** it is closed; otherwise print nothing. ** logit -- if set, send output to syslog instead of stdout. */ void dumpfd(fd, printclosed, logit) int fd; bool printclosed; bool logit; { register char *p; char *hp; char *fmtstr; #ifdef S_IFSOCK SOCKADDR sa; #endif auto int slen; struct stat st; char buf[200]; extern char *hostnamebyanyaddr(); p = buf; snprintf(p, SPACELEFT(buf, p), "%3d: ", fd); p += strlen(p); if (fstat(fd, &st) < 0) { if (errno != EBADF) { snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)", errstring(errno)); goto printit; } else if (printclosed) { snprintf(p, SPACELEFT(buf, p), "CLOSED"); goto printit; } return; } slen = fcntl(fd, F_GETFL, NULL); if (slen != -1) { snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", slen); p += strlen(p); } snprintf(p, SPACELEFT(buf, p), "mode=%o: ", st.st_mode); p += strlen(p); switch (st.st_mode & S_IFMT) { #ifdef S_IFSOCK case S_IFSOCK: snprintf(p, SPACELEFT(buf, p), "SOCK "); p += strlen(p); slen = sizeof sa; if (getsockname(fd, &sa.sa, &slen) < 0) snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); else { hp = hostnamebyanyaddr(&sa); if (sa.sa.sa_family == AF_INET) snprintf(p, SPACELEFT(buf, p), "%s/%d", hp, ntohs(sa.sin.sin_port)); else snprintf(p, SPACELEFT(buf, p), "%s", hp); } p += strlen(p); snprintf(p, SPACELEFT(buf, p), "->"); p += strlen(p); slen = sizeof sa; if (getpeername(fd, &sa.sa, &slen) < 0) snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno)); else { hp = hostnamebyanyaddr(&sa); if (sa.sa.sa_family == AF_INET) snprintf(p, SPACELEFT(buf, p), "%s/%d", hp, ntohs(sa.sin.sin_port)); else snprintf(p, SPACELEFT(buf, p), "%s", hp); } break; #endif case S_IFCHR: snprintf(p, SPACELEFT(buf, p), "CHR: "); p += strlen(p); goto defprint; case S_IFBLK: snprintf(p, SPACELEFT(buf, p), "BLK: "); p += strlen(p); goto defprint; #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) case S_IFIFO: snprintf(p, SPACELEFT(buf, p), "FIFO: "); p += strlen(p); goto defprint; #endif #ifdef S_IFDIR case S_IFDIR: snprintf(p, SPACELEFT(buf, p), "DIR: "); p += strlen(p); goto defprint; #endif #ifdef S_IFLNK case S_IFLNK: snprintf(p, SPACELEFT(buf, p), "LNK: "); p += strlen(p); goto defprint; #endif default: defprint: if (sizeof st.st_size > sizeof (long)) fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd"; else fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld"; snprintf(p, SPACELEFT(buf, p), fmtstr, major(st.st_dev), minor(st.st_dev), st.st_ino, st.st_nlink, st.st_uid, st.st_gid, st.st_size); break; } printit: -#ifdef LOG if (logit) - syslog(LOG_DEBUG, "%.800s", buf); + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "%.800s", buf); else -#endif printf("%s\n", buf); } /* ** SHORTENSTRING -- return short version of a string ** ** If the string is already short, just return it. If it is too ** long, return the head and tail of the string. ** ** Parameters: ** s -- the string to shorten. ** m -- the max length of the string. ** ** Returns: ** Either s or a short version of s. */ #ifndef MAXSHORTSTR # define MAXSHORTSTR 203 #endif char * shortenstring(s, m) register const char *s; int m; { int l; static char buf[MAXSHORTSTR + 1]; l = strlen(s); if (l < m) return (char *) s; if (m > MAXSHORTSTR) m = MAXSHORTSTR; else if (m < 10) { if (m < 5) { strncpy(buf, s, m); buf[m] = '\0'; return buf; } strncpy(buf, s, m - 3); strcpy(buf + m - 3, "..."); return buf; } m = (m - 3) / 2; strncpy(buf, s, m); strcpy(buf + m, "..."); strcpy(buf + m + 3, s + l - m); return buf; } /* ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. ** ** Parameters: ** host -- the host to shorten (stripped in place). ** ** Returns: ** none. */ void shorten_hostname(host) char host[]; { register char *p; char *mydom; int i; bool canon = FALSE; /* strip off final dot */ p = &host[strlen(host) - 1]; if (*p == '.') { *p = '\0'; canon = TRUE; } /* see if there is any domain at all -- if not, we are done */ p = strchr(host, '.'); if (p == NULL) return; /* yes, we have a domain -- see if it looks like us */ mydom = macvalue('m', CurEnv); if (mydom == NULL) mydom = ""; i = strlen(++p); if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 && (mydom[i] == '.' || mydom[i] == '\0')) *--p = '\0'; } /* ** PROG_OPEN -- open a program for reading ** ** Parameters: ** argv -- the argument list. ** pfd -- pointer to a place to store the file descriptor. ** e -- the current envelope. ** ** Returns: ** pid of the process -- -1 if it failed. */ int prog_open(argv, pfd, e) char **argv; int *pfd; ENVELOPE *e; { int pid; int i; int saveerrno; int fdv[2]; char *p, *q; char buf[MAXLINE + 1]; extern int DtableSize; if (pipe(fdv) < 0) { syserr("%s: cannot create pipe for stdout", argv[0]); return -1; } pid = fork(); if (pid < 0) { syserr("%s: cannot fork", argv[0]); close(fdv[0]); close(fdv[1]); return -1; } if (pid > 0) { /* parent */ close(fdv[1]); *pfd = fdv[0]; return pid; } /* child -- close stdin */ close(0); /* stdout goes back to parent */ close(fdv[0]); if (dup2(fdv[1], 1) < 0) { syserr("%s: cannot dup2 for stdout", argv[0]); _exit(EX_OSERR); } close(fdv[1]); /* stderr goes to transcript if available */ if (e->e_xfp != NULL) { if (dup2(fileno(e->e_xfp), 2) < 0) { syserr("%s: cannot dup2 for stderr", argv[0]); _exit(EX_OSERR); } } /* this process has no right to the queue file */ if (e->e_lockfp != NULL) close(fileno(e->e_lockfp)); /* run as default user */ endpwent(); if (setgid(DefGid) < 0) syserr("prog_open: setgid(%ld) failed", (long) DefGid); if (setuid(DefUid) < 0) syserr("prog_open: setuid(%ld) failed", (long) DefUid); /* run in some directory */ if (ProgMailer != NULL) p = ProgMailer->m_execdir; else p = NULL; for (; p != NULL; p = q) { q = strchr(p, ':'); if (q != NULL) *q = '\0'; expand(p, buf, sizeof buf, e); if (q != NULL) *q++ = ':'; if (buf[0] != '\0' && chdir(buf) >= 0) break; } if (p == NULL) { /* backup directories */ if (chdir("/tmp") < 0) (void) chdir("/"); } /* arrange for all the files to be closed */ for (i = 3; i < DtableSize; i++) { register int j; if ((j = fcntl(i, F_GETFD, 0)) != -1) (void) fcntl(i, F_SETFD, j | 1); } /* now exec the process */ execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); /* woops! failed */ saveerrno = errno; syserr("%s: cannot exec", argv[0]); if (transienterror(saveerrno)) _exit(EX_OSERR); _exit(EX_CONFIG); return -1; /* avoid compiler warning on IRIX */ } /* ** GET_COLUMN -- look up a Column in a line buffer ** ** Parameters: ** line -- the raw text line to search. ** col -- the column number to fetch. ** delim -- the delimiter between columns. If null, ** use white space. ** buf -- the output buffer. ** buflen -- the length of buf. ** ** Returns: ** buf if successful. ** NULL otherwise. */ char * get_column(line, col, delim, buf, buflen) char line[]; int col; char delim; char buf[]; int buflen; { char *p; char *begin, *end; int i; char delimbuf[4]; if (delim == '\0') strcpy(delimbuf, "\n\t "); else { delimbuf[0] = delim; delimbuf[1] = '\0'; } p = line; if (*p == '\0') return NULL; /* line empty */ if (*p == delim && col == 0) return NULL; /* first column empty */ begin = line; if (col == 0 && delim == '\0') { while (*begin != '\0' && isascii(*begin) && isspace(*begin)) begin++; } for (i = 0; i < col; i++) { if ((begin = strpbrk(begin, delimbuf)) == NULL) return NULL; /* no such column */ begin++; if (delim == '\0') { while (*begin != '\0' && isascii(*begin) && isspace(*begin)) begin++; } } end = strpbrk(begin, delimbuf); if (end == NULL) i = strlen(begin); else i = end - begin; if (i >= buflen) i = buflen - 1; strncpy(buf, begin, i); buf[i] = '\0'; return buf; } /* ** CLEANSTRCPY -- copy string keeping out bogus characters ** ** Parameters: ** t -- "to" string. ** f -- "from" string. ** l -- length of space available in "to" string. ** ** Returns: ** none. */ void cleanstrcpy(t, f, l) register char *t; register char *f; int l; { -#ifdef LOG /* check for newlines and log if necessary */ (void) denlstring(f, TRUE, TRUE); -#endif l--; while (l > 0 && *f != '\0') { if (isascii(*f) && (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) { l--; *t++ = *f; } f++; } *t = '\0'; } /* ** DENLSTRING -- convert newlines in a string to spaces ** ** Parameters: ** s -- the input string ** strict -- if set, don't permit continuation lines. ** logattacks -- if set, log attempted attacks. ** ** Returns: ** A pointer to a version of the string with newlines ** mapped to spaces. This should be copied. */ char * denlstring(s, strict, logattacks) char *s; bool strict; bool logattacks; { register char *p; int l; static char *bp = NULL; static int bl = 0; p = s; while ((p = strchr(p, '\n')) != NULL) if (strict || (*++p != ' ' && *p != '\t')) break; if (p == NULL) return s; l = strlen(s) + 1; if (bl < l) { /* allocate more space */ if (bp != NULL) free(bp); bp = xalloc(l); bl = l; } strcpy(bp, s); for (p = bp; (p = strchr(p, '\n')) != NULL; ) *p++ = ' '; -#ifdef LOG if (logattacks) { - syslog(LOG_NOTICE, "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", + sm_syslog(LOG_NOTICE, CurEnv->e_id, + "POSSIBLE ATTACK from %.100s: newline in string \"%s\"", RealHostName == NULL ? "[UNKNOWN]" : RealHostName, shortenstring(bp, 203)); } -#endif return bp; } /* ** PATH_IS_DIR -- check to see if file exists and is a directory. ** +** There are some additional checks for security violations in +** here. This routine is intended to be used for the host status +** support. +** ** Parameters: ** pathname -- pathname to check for directory-ness. ** createflag -- if set, create directory if needed. ** ** Returns: ** TRUE -- if the indicated pathname is a directory ** FALSE -- otherwise */ int path_is_dir(pathname, createflag) char *pathname; bool createflag; { struct stat statbuf; +#if HASLSTAT + if (lstat(pathname, &statbuf) < 0) +#else if (stat(pathname, &statbuf) < 0) +#endif { if (errno != ENOENT || !createflag) return FALSE; if (mkdir(pathname, 0755) < 0) return FALSE; return TRUE; } if (!S_ISDIR(statbuf.st_mode)) { errno = ENOTDIR; return FALSE; } + + /* security: don't allow writable directories */ + if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode)) + { + errno = EACCES; + return FALSE; + } + return TRUE; } /* ** PROC_LIST_ADD -- add process id to list of our children ** ** Parameters: ** pid -- pid to add to list. ** ** Returns: ** none */ static pid_t *ProcListVec = NULL; static int ProcListSize = 0; #define NO_PID ((pid_t) 0) #ifndef PROC_LIST_SEG # define PROC_LIST_SEG 32 /* number of pids to alloc at a time */ #endif void proc_list_add(pid) pid_t pid; { int i; extern void proc_list_probe __P((void)); for (i = 0; i < ProcListSize; i++) { if (ProcListVec[i] == NO_PID) break; } if (i >= ProcListSize) { /* probe the existing vector to avoid growing infinitely */ proc_list_probe(); /* now scan again */ for (i = 0; i < ProcListSize; i++) { if (ProcListVec[i] == NO_PID) break; } } if (i >= ProcListSize) { /* grow process list */ pid_t *npv; npv = (pid_t *) xalloc(sizeof (pid_t) * (ProcListSize + PROC_LIST_SEG)); if (ProcListSize > 0) { bcopy(ProcListVec, npv, ProcListSize * sizeof (pid_t)); free(ProcListVec); } for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++) npv[i] = NO_PID; i = ProcListSize; ProcListSize += PROC_LIST_SEG; ProcListVec = npv; } ProcListVec[i] = pid; CurChildren++; } /* ** PROC_LIST_DROP -- drop pid from process list ** ** Parameters: ** pid -- pid to drop ** ** Returns: ** none. */ void proc_list_drop(pid) pid_t pid; { int i; for (i = 0; i < ProcListSize; i++) { if (ProcListVec[i] == pid) { ProcListVec[i] = NO_PID; break; } } if (CurChildren > 0) CurChildren--; } /* ** PROC_LIST_CLEAR -- clear the process list ** ** Parameters: ** none. ** ** Returns: ** none. */ void proc_list_clear() { int i; for (i = 0; i < ProcListSize; i++) ProcListVec[i] = NO_PID; CurChildren = 0; } /* ** PROC_LIST_PROBE -- probe processes in the list to see if they still exist ** ** Parameters: ** none ** ** Returns: ** none */ void proc_list_probe() { int i; for (i = 0; i < ProcListSize; i++) { if (ProcListVec[i] == NO_PID) continue; if (kill(ProcListVec[i], 0) < 0) { -#ifdef LOG if (LogLevel > 3) - syslog(LOG_DEBUG, "proc_list_probe: lost pid %d", + sm_syslog(LOG_DEBUG, CurEnv->e_id, + "proc_list_probe: lost pid %d", ProcListVec[i]); -#endif ProcListVec[i] = NO_PID; CurChildren--; } } if (CurChildren < 0) CurChildren = 0; +} + /* +** SM_STRCASECMP -- 8-bit clean version of strcasecmp +** +** Thank you, vendors, for making this all necessary. +*/ + +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strcasecmp.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +/* + * This array is designed for mapping upper and lower case letter + * together for a case independent comparison. The mappings are + * based upon ascii character sequences. + */ +static const u_char charmap[] = { + 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, + 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, + 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, + 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, + 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, + 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, + 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, + 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, + 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137, + 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, + 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, + 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, + 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, + 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, + 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, + 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, + 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, + 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, + 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, + 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, + 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, + 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, + 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, + 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377, +}; + +int +sm_strcasecmp(s1, s2) + const char *s1, *s2; +{ + register const u_char *cm = charmap, + *us1 = (const u_char *)s1, + *us2 = (const u_char *)s2; + + while (cm[*us1] == cm[*us2++]) + if (*us1++ == '\0') + return (0); + return (cm[*us1] - cm[*--us2]); +} + +int +sm_strncasecmp(s1, s2, n) + const char *s1, *s2; + register size_t n; +{ + if (n != 0) { + register const u_char *cm = charmap, + *us1 = (const u_char *)s1, + *us2 = (const u_char *)s2; + + do { + if (cm[*us1] != cm[*us2++]) + return (cm[*us1] - cm[*--us2]); + if (*us1++ == '\0') + break; + } while (--n != 0); + } + return (0); }