mail.local.c revision 98125
142580Speter/* 294337Sgshapiro * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. 364565Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1990, 1993, 1994 538032Speter * The Regents of the University of California. All rights reserved. 638032Speter * 738032Speter * By using this file, you agree to the terms and conditions set 838032Speter * forth in the LICENSE file which can be found at the top level of 938032Speter * the sendmail distribution. 1038032Speter * 1138032Speter */ 1238032Speter 1390795Sgshapiro#include <sm/gen.h> 1490795Sgshapiro 1590795SgshapiroSM_IDSTR(copyright, 1673191Sgshapiro"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ 1764565Sgshapiro All rights reserved.\n\ 1864565Sgshapiro Copyright (c) 1990, 1993, 1994\n\ 1990795Sgshapiro The Regents of the University of California. All rights reserved.\n") 2038032Speter 2198125SgshapiroSM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.239 2002/05/24 20:56:32 gshapiro Exp $") 2238032Speter 2390795Sgshapiro#include <stdlib.h> 2490795Sgshapiro#include <sm/errstring.h> 2590795Sgshapiro#include <sm/io.h> 2690795Sgshapiro#include <sm/limits.h> 2790795Sgshapiro# include <unistd.h> 2890795Sgshapiro# ifdef EX_OK 2990795Sgshapiro# undef EX_OK /* unistd.h may have another use for this */ 3090795Sgshapiro# endif /* EX_OK */ 3190795Sgshapiro#include <sm/mbdb.h> 3290795Sgshapiro#include <sm/sysexits.h> 3390795Sgshapiro 3464565Sgshapiro/* $FreeBSD: head/contrib/sendmail/mail.local/mail.local.c 98125 2002-06-11 21:16:51Z gshapiro $ */ 3564565Sgshapiro 3638032Speter/* 3764565Sgshapiro** This is not intended to work on System V derived systems 3864565Sgshapiro** such as Solaris or HP-UX, since they use a totally different 3990795Sgshapiro** approach to mailboxes (essentially, they have a set-group-ID program 4090795Sgshapiro** rather than set-user-ID, and they rely on the ability to "give away" 4164565Sgshapiro** files to do their work). IT IS NOT A BUG that this doesn't 4264565Sgshapiro** work on such architectures. 4364565Sgshapiro*/ 4438032Speter 4564565Sgshapiro 4690795Sgshapiro#include <stdio.h> 4790795Sgshapiro#include <errno.h> 4890795Sgshapiro#include <fcntl.h> 4990795Sgshapiro#include <sys/types.h> 5090795Sgshapiro#include <sys/stat.h> 5190795Sgshapiro#include <time.h> 5290795Sgshapiro#include <stdlib.h> 5366497Sgshapiro# include <sys/socket.h> 5466497Sgshapiro# include <sys/file.h> 5566497Sgshapiro# include <netinet/in.h> 5666497Sgshapiro# include <arpa/nameser.h> 5766497Sgshapiro# include <netdb.h> 5890795Sgshapiro# include <pwd.h> 5964565Sgshapiro 6090795Sgshapiro#include <sm/string.h> 6190795Sgshapiro#include <syslog.h> 6290795Sgshapiro#include <ctype.h> 6366497Sgshapiro 6490795Sgshapiro#include <sm/conf.h> 6590795Sgshapiro#include <sendmail/pathnames.h> 6664565Sgshapiro 6764565Sgshapiro 6890795Sgshapiro/* additional mode for open() */ 6990795Sgshapiro# define EXTRA_MODE 0 7064565Sgshapiro 7164565Sgshapiro 7290795Sgshapiro#ifndef LOCKTO_RM 7390795Sgshapiro# define LOCKTO_RM 300 /* timeout for stale lockfile removal */ 7490795Sgshapiro#endif /* ! LOCKTO_RM */ 7590795Sgshapiro#ifndef LOCKTO_GLOB 7690795Sgshapiro# define LOCKTO_GLOB 400 /* global timeout for lockfile creation */ 7790795Sgshapiro#endif /* ! LOCKTO_GLOB */ 7890795Sgshapiro 7964565Sgshapiro/* define a realloc() which works for NULL pointers */ 8090795Sgshapiro#define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size)) 8138032Speter 8238032Speter/* 8390795Sgshapiro** If you don't have flock, you could try using lockf instead. 8490795Sgshapiro*/ 8538032Speter 8690795Sgshapiro#ifdef LDA_USE_LOCKF 8790795Sgshapiro# define flock(a, b) lockf(a, b, 0) 8890795Sgshapiro# ifdef LOCK_EX 8990795Sgshapiro# undef LOCK_EX 9090795Sgshapiro# endif /* LOCK_EX */ 9190795Sgshapiro# define LOCK_EX F_LOCK 9290795Sgshapiro#endif /* LDA_USE_LOCKF */ 9338032Speter 9490795Sgshapiro#ifndef LOCK_EX 9590795Sgshapiro# include <sys/file.h> 9690795Sgshapiro#endif /* ! LOCK_EX */ 9738032Speter 9838032Speter/* 9964565Sgshapiro** If you don't have setreuid, and you have saved uids, and you have 10064565Sgshapiro** a seteuid() call that doesn't try to emulate using setuid(), then 10190795Sgshapiro** you can try defining LDA_USE_SETEUID. 10264565Sgshapiro*/ 10373191Sgshapiro 10490795Sgshapiro#ifdef LDA_USE_SETEUID 10590795Sgshapiro# define setreuid(r, e) seteuid(e) 10690795Sgshapiro#endif /* LDA_USE_SETEUID */ 10738032Speter 10890795Sgshapiro#ifdef LDA_CONTENTLENGTH 10990795Sgshapiro# define CONTENTLENGTH 1 11090795Sgshapiro#endif /* LDA_CONTENTLENGTH */ 11173191Sgshapiro 11264565Sgshapiro#ifndef INADDRSZ 11364565Sgshapiro# define INADDRSZ 4 /* size of an IPv4 address in bytes */ 11464565Sgshapiro#endif /* ! INADDRSZ */ 11564565Sgshapiro 11690795Sgshapiro#ifdef MAILLOCK 11790795Sgshapiro# include <maillock.h> 11890795Sgshapiro#endif /* MAILLOCK */ 11990795Sgshapiro 12042580Speter#ifndef MAILER_DAEMON 12142580Speter# define MAILER_DAEMON "MAILER-DAEMON" 12264565Sgshapiro#endif /* ! MAILER_DAEMON */ 12342580Speter 12464565Sgshapiro#ifdef CONTENTLENGTH 12564565Sgshapirochar ContentHdr[40] = "Content-Length: "; 12664565Sgshapirooff_t HeaderLength; 12764565Sgshapirooff_t BodyLength; 12864565Sgshapiro#endif /* CONTENTLENGTH */ 12938032Speter 13090795Sgshapirobool EightBitMime = true; /* advertise 8BITMIME in LMTP */ 13173191Sgshapirochar ErrBuf[10240]; /* error buffer */ 13264565Sgshapiroint ExitVal = EX_OK; /* sysexits.h error value. */ 13390795Sgshapirobool nobiff = false; 13490795Sgshapirobool nofsync = false; 13590795Sgshapirobool HoldErrs = false; /* Hold errors in ErrBuf */ 13690795Sgshapirobool LMTPMode = false; 13790795Sgshapirobool BounceQuota = false; /* permanent error when over quota */ 13890795Sgshapirochar *HomeMailFile = NULL; /* store mail in homedir */ 13938032Speter 14073191Sgshapirovoid deliver __P((int, char *)); 14164565Sgshapiroint e_to_sys __P((int)); 14264565Sgshapirovoid notifybiff __P((char *)); 14390795Sgshapiroint store __P((char *, bool *)); 14464565Sgshapirovoid usage __P((void)); 14564565Sgshapiroint lockmbox __P((char *)); 14664565Sgshapirovoid unlockmbox __P((void)); 14764565Sgshapirovoid mailerr __P((const char *, const char *, ...)); 14873191Sgshapirovoid flush_error __P((void)); 14964565Sgshapiro 15064565Sgshapiro 15138032Speterint 15238032Spetermain(argc, argv) 15338032Speter int argc; 15438032Speter char *argv[]; 15538032Speter{ 15638032Speter struct passwd *pw; 15764565Sgshapiro int ch, fd; 15838032Speter uid_t uid; 15938032Speter char *from; 16090795Sgshapiro char *mbdbname = "pw"; 16190795Sgshapiro int err; 16238032Speter extern char *optarg; 16338032Speter extern int optind; 16438032Speter 16564565Sgshapiro 16638032Speter /* make sure we have some open file descriptors */ 16738032Speter for (fd = 10; fd < 30; fd++) 16838032Speter (void) close(fd); 16938032Speter 17038032Speter /* use a reasonable umask */ 17138032Speter (void) umask(0077); 17238032Speter 17364565Sgshapiro# ifdef LOG_MAIL 17438032Speter openlog("mail.local", 0, LOG_MAIL); 17564565Sgshapiro# else /* LOG_MAIL */ 17638032Speter openlog("mail.local", 0); 17764565Sgshapiro# endif /* LOG_MAIL */ 17838032Speter 17938032Speter from = NULL; 18090795Sgshapiro while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1) 18164565Sgshapiro { 18264565Sgshapiro switch(ch) 18364565Sgshapiro { 18464565Sgshapiro case '7': /* Do not advertise 8BITMIME */ 18590795Sgshapiro EightBitMime = false; 18638084Speter break; 18764565Sgshapiro 18864565Sgshapiro case 'B': 18990825Sgshapiro nobiff = true; 19038032Speter break; 19164565Sgshapiro 19264565Sgshapiro case 'b': /* bounce mail when over quota. */ 19390795Sgshapiro BounceQuota = true; 19464565Sgshapiro break; 19564565Sgshapiro 19664565Sgshapiro case 'd': /* Backward compatible. */ 19764565Sgshapiro break; 19864565Sgshapiro 19990795Sgshapiro case 'D': /* mailbox database type */ 20090795Sgshapiro mbdbname = optarg; 20190795Sgshapiro break; 20290795Sgshapiro 20364565Sgshapiro case 'f': 20464565Sgshapiro case 'r': /* Backward compatible. */ 20564565Sgshapiro if (from != NULL) 20664565Sgshapiro { 20773191Sgshapiro mailerr(NULL, "Multiple -f options"); 20838032Speter usage(); 20938032Speter } 21038032Speter from = optarg; 21138032Speter break; 21264565Sgshapiro 21390795Sgshapiro case 'h': 21490795Sgshapiro if (optarg != NULL || *optarg != '\0') 21590795Sgshapiro HomeMailFile = optarg; 21690795Sgshapiro else 21790795Sgshapiro { 21890795Sgshapiro mailerr(NULL, "-h: missing filename"); 21990795Sgshapiro usage(); 22090795Sgshapiro } 22190795Sgshapiro break; 22290795Sgshapiro 22364565Sgshapiro case 'l': 22490795Sgshapiro LMTPMode = true; 22538032Speter break; 22664565Sgshapiro 22764565Sgshapiro case 's': 22838089Speter nofsync++; 22938084Speter break; 23064565Sgshapiro 23164565Sgshapiro case '?': 23264565Sgshapiro default: 23338032Speter usage(); 23438032Speter } 23564565Sgshapiro } 23638032Speter argc -= optind; 23738032Speter argv += optind; 23838032Speter 23964565Sgshapiro /* initialize biff structures */ 24064565Sgshapiro if (!nobiff) 24164565Sgshapiro notifybiff(NULL); 24238032Speter 24390795Sgshapiro err = sm_mbdb_initialize(mbdbname); 24490795Sgshapiro if (err != EX_OK) 24590795Sgshapiro { 24690795Sgshapiro char *errcode = "521"; 24790795Sgshapiro 24890795Sgshapiro if (err == EX_TEMPFAIL) 24990795Sgshapiro errcode = "421"; 25090795Sgshapiro 25190795Sgshapiro mailerr(errcode, "Can not open mailbox database %s: %s", 25290795Sgshapiro mbdbname, sm_strexit(err)); 25390795Sgshapiro exit(err); 25490795Sgshapiro } 25590795Sgshapiro 25664565Sgshapiro if (LMTPMode) 25773191Sgshapiro { 25873191Sgshapiro extern void dolmtp __P((void)); 25964565Sgshapiro 26073191Sgshapiro if (argc > 0) 26173191Sgshapiro { 26273191Sgshapiro mailerr("421", "Users should not be specified in command line if LMTP required"); 26373191Sgshapiro exit(EX_TEMPFAIL); 26473191Sgshapiro } 26573191Sgshapiro 26673191Sgshapiro dolmtp(); 26773191Sgshapiro /* NOTREACHED */ 26873191Sgshapiro exit(EX_OK); 26973191Sgshapiro } 27073191Sgshapiro 27173191Sgshapiro /* Non-LMTP from here on out */ 27264565Sgshapiro if (*argv == '\0') 27338032Speter usage(); 27438032Speter 27538032Speter /* 27664565Sgshapiro ** If from not specified, use the name from getlogin() if the 27764565Sgshapiro ** uid matches, otherwise, use the name from the password file 27864565Sgshapiro ** corresponding to the uid. 27964565Sgshapiro */ 28073191Sgshapiro 28138032Speter uid = getuid(); 28264565Sgshapiro if (from == NULL && ((from = getlogin()) == NULL || 28364565Sgshapiro (pw = getpwnam(from)) == NULL || 28464565Sgshapiro pw->pw_uid != uid)) 28564565Sgshapiro from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???"; 28664565Sgshapiro 28738032Speter /* 28864565Sgshapiro ** There is no way to distinguish the error status of one delivery 28964565Sgshapiro ** from the rest of the deliveries. So, if we failed hard on one 29064565Sgshapiro ** or more deliveries, but had no failures on any of the others, we 29164565Sgshapiro ** return a hard failure. If we failed temporarily on one or more 29264565Sgshapiro ** deliveries, we return a temporary failure regardless of the other 29364565Sgshapiro ** failures. This results in the delivery being reattempted later 29464565Sgshapiro ** at the expense of repeated failures and multiple deliveries. 29564565Sgshapiro */ 29673191Sgshapiro 29790795Sgshapiro HoldErrs = true; 29890795Sgshapiro fd = store(from, NULL); 29990795Sgshapiro HoldErrs = false; 30073191Sgshapiro if (fd < 0) 30173191Sgshapiro { 30273191Sgshapiro flush_error(); 30373191Sgshapiro exit(ExitVal); 30473191Sgshapiro } 30573191Sgshapiro for (; *argv != NULL; ++argv) 30673191Sgshapiro deliver(fd, *argv); 30764565Sgshapiro exit(ExitVal); 30864565Sgshapiro /* NOTREACHED */ 30964565Sgshapiro return ExitVal; 31038032Speter} 31138032Speter 31238032Speterchar * 31364565Sgshapiroparseaddr(s, rcpt) 31438032Speter char *s; 31564565Sgshapiro bool rcpt; 31638032Speter{ 31738032Speter char *p; 31864565Sgshapiro int l; 31938032Speter 32038032Speter if (*s++ != '<') 32138032Speter return NULL; 32238032Speter 32338032Speter p = s; 32438032Speter 32538032Speter /* at-domain-list */ 32664565Sgshapiro while (*p == '@') 32764565Sgshapiro { 32838032Speter p++; 32964565Sgshapiro while (*p != ',' && *p != ':' && *p != '\0') 33038032Speter p++; 33164565Sgshapiro if (*p == '\0') 33238032Speter return NULL; 33364565Sgshapiro 33464565Sgshapiro /* Skip over , or : */ 33564565Sgshapiro p++; 33638032Speter } 33738032Speter 33842580Speter s = p; 33942580Speter 34038032Speter /* local-part */ 34164565Sgshapiro while (*p != '\0' && *p != '@' && *p != '>') 34264565Sgshapiro { 34364565Sgshapiro if (*p == '\\') 34464565Sgshapiro { 34564565Sgshapiro if (*++p == '\0') 34664565Sgshapiro return NULL; 34764565Sgshapiro } 34864565Sgshapiro else if (*p == '\"') 34964565Sgshapiro { 35064565Sgshapiro p++; 35164565Sgshapiro while (*p != '\0' && *p != '\"') 35264565Sgshapiro { 35364565Sgshapiro if (*p == '\\') 35464565Sgshapiro { 35564565Sgshapiro if (*++p == '\0') 35664565Sgshapiro return NULL; 35764565Sgshapiro } 35864565Sgshapiro p++; 35938032Speter } 36064565Sgshapiro if (*p == '\0' || *(p + 1) == '\0') 36138032Speter return NULL; 36238032Speter } 36364565Sgshapiro /* +detail ? */ 36464565Sgshapiro if (*p == '+' && rcpt) 36564565Sgshapiro *p = '\0'; 36664565Sgshapiro p++; 36738032Speter } 36838032Speter 36938032Speter /* @domain */ 37064565Sgshapiro if (*p == '@') 37164565Sgshapiro { 37264565Sgshapiro if (rcpt) 37364565Sgshapiro *p++ = '\0'; 37464565Sgshapiro while (*p != '\0' && *p != '>') 37538032Speter p++; 37638032Speter } 37738032Speter 37864565Sgshapiro if (*p != '>') 37938032Speter return NULL; 38064565Sgshapiro else 38164565Sgshapiro *p = '\0'; 38264565Sgshapiro p++; 38364565Sgshapiro 38464565Sgshapiro if (*p != '\0' && *p != ' ') 38538032Speter return NULL; 38664565Sgshapiro 38764565Sgshapiro if (*s == '\0') 38842580Speter s = MAILER_DAEMON; 38938032Speter 39064565Sgshapiro l = strlen(s) + 1; 39190795Sgshapiro if (l < 0) 39290795Sgshapiro return NULL; 39364565Sgshapiro p = malloc(l); 39464565Sgshapiro if (p == NULL) 39564565Sgshapiro { 39673191Sgshapiro mailerr("421 4.3.0", "Memory exhausted"); 39738032Speter exit(EX_TEMPFAIL); 39838032Speter } 39938032Speter 40090795Sgshapiro (void) sm_strlcpy(p, s, l); 40138032Speter return p; 40238032Speter} 40338032Speter 40438032Speterchar * 40538032Speterprocess_recipient(addr) 40638032Speter char *addr; 40738032Speter{ 40890795Sgshapiro SM_MBDB_T user; 40990795Sgshapiro 41090795Sgshapiro switch (sm_mbdb_lookup(addr, &user)) 41190795Sgshapiro { 41290795Sgshapiro case EX_OK: 41390795Sgshapiro return NULL; 41490795Sgshapiro 41590795Sgshapiro case EX_NOUSER: 41673191Sgshapiro return "550 5.1.1 User unknown"; 41790795Sgshapiro 41890795Sgshapiro case EX_TEMPFAIL: 41990795Sgshapiro return "451 4.3.0 User database failure; retry later"; 42090795Sgshapiro 42190795Sgshapiro default: 42290795Sgshapiro return "550 5.3.0 User database failure"; 42390795Sgshapiro } 42438032Speter} 42538032Speter 42638032Speter#define RCPT_GROW 30 42738032Speter 42838032Spetervoid 42973191Sgshapirodolmtp() 43038032Speter{ 43138032Speter char *return_path = NULL; 43238032Speter char **rcpt_addr = NULL; 43338032Speter int rcpt_num = 0; 43438032Speter int rcpt_alloc = 0; 43590795Sgshapiro bool gotlhlo = false; 43638032Speter char *err; 43738032Speter int msgfd; 43838032Speter char *p; 43938032Speter int i; 44064565Sgshapiro char myhostname[1024]; 44164565Sgshapiro char buf[4096]; 44238032Speter 44373191Sgshapiro memset(myhostname, '\0', sizeof myhostname); 44464565Sgshapiro (void) gethostname(myhostname, sizeof myhostname - 1); 44573191Sgshapiro if (myhostname[0] == '\0') 44690795Sgshapiro sm_strlcpy(myhostname, "localhost", sizeof myhostname); 44738032Speter 44838032Speter printf("220 %s LMTP ready\r\n", myhostname); 44964565Sgshapiro for (;;) 45064565Sgshapiro { 45164565Sgshapiro (void) fflush(stdout); 45264565Sgshapiro if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) 45338032Speter exit(EX_OK); 45438032Speter p = buf + strlen(buf) - 1; 45538032Speter if (p >= buf && *p == '\n') 45638032Speter *p-- = '\0'; 45738032Speter if (p >= buf && *p == '\r') 45838032Speter *p-- = '\0'; 45938032Speter 46064565Sgshapiro switch (buf[0]) 46164565Sgshapiro { 46264565Sgshapiro case 'd': 46364565Sgshapiro case 'D': 46490795Sgshapiro if (sm_strcasecmp(buf, "data") == 0) 46564565Sgshapiro { 46690795Sgshapiro bool inbody = false; 46773191Sgshapiro 46864565Sgshapiro if (rcpt_num == 0) 46964565Sgshapiro { 47073191Sgshapiro mailerr("503 5.5.1", "No recipients"); 47138032Speter continue; 47238032Speter } 47390795Sgshapiro HoldErrs = true; 47490795Sgshapiro msgfd = store(return_path, &inbody); 47590795Sgshapiro HoldErrs = false; 47673191Sgshapiro if (msgfd < 0 && !inbody) 47773191Sgshapiro { 47873191Sgshapiro flush_error(); 47938032Speter continue; 48073191Sgshapiro } 48138032Speter 48264565Sgshapiro for (i = 0; i < rcpt_num; i++) 48364565Sgshapiro { 48473191Sgshapiro if (msgfd < 0) 48573191Sgshapiro { 48673191Sgshapiro /* print error for rcpt */ 48773191Sgshapiro flush_error(); 48873191Sgshapiro continue; 48973191Sgshapiro } 49038032Speter p = strchr(rcpt_addr[i], '+'); 49138032Speter if (p != NULL) 49273191Sgshapiro *p = '\0'; 49373191Sgshapiro deliver(msgfd, rcpt_addr[i]); 49438032Speter } 49573191Sgshapiro if (msgfd >= 0) 49673191Sgshapiro (void) close(msgfd); 49738032Speter goto rset; 49838032Speter } 49938032Speter goto syntaxerr; 50064565Sgshapiro /* NOTREACHED */ 50164565Sgshapiro break; 50238032Speter 50364565Sgshapiro case 'l': 50464565Sgshapiro case 'L': 50590795Sgshapiro if (sm_strncasecmp(buf, "lhlo ", 5) == 0) 50664565Sgshapiro { 50764565Sgshapiro /* check for duplicate per RFC 1651 4.2 */ 50864565Sgshapiro if (gotlhlo) 50964565Sgshapiro { 51073191Sgshapiro mailerr("503", "%s Duplicate LHLO", 51164565Sgshapiro myhostname); 51264565Sgshapiro continue; 51364565Sgshapiro } 51490795Sgshapiro gotlhlo = true; 51564565Sgshapiro printf("250-%s\r\n", myhostname); 51664565Sgshapiro if (EightBitMime) 51764565Sgshapiro printf("250-8BITMIME\r\n"); 51864565Sgshapiro printf("250-ENHANCEDSTATUSCODES\r\n"); 51964565Sgshapiro printf("250 PIPELINING\r\n"); 52038032Speter continue; 52138032Speter } 52238032Speter goto syntaxerr; 52364565Sgshapiro /* NOTREACHED */ 52464565Sgshapiro break; 52538032Speter 52664565Sgshapiro case 'm': 52764565Sgshapiro case 'M': 52890795Sgshapiro if (sm_strncasecmp(buf, "mail ", 5) == 0) 52964565Sgshapiro { 53064565Sgshapiro if (return_path != NULL) 53164565Sgshapiro { 53273191Sgshapiro mailerr("503 5.5.1", 53373191Sgshapiro "Nested MAIL command"); 53438032Speter continue; 53538032Speter } 53698125Sgshapiro if (sm_strncasecmp(buf + 5, "from:", 5) != 0 || 53764565Sgshapiro ((return_path = parseaddr(buf + 10, 53890795Sgshapiro false)) == NULL)) 53964565Sgshapiro { 54073191Sgshapiro mailerr("501 5.5.4", 54173191Sgshapiro "Syntax error in parameters"); 54238032Speter continue; 54338032Speter } 54473191Sgshapiro printf("250 2.5.0 Ok\r\n"); 54538032Speter continue; 54638032Speter } 54738032Speter goto syntaxerr; 54864565Sgshapiro /* NOTREACHED */ 54964565Sgshapiro break; 55038032Speter 55164565Sgshapiro case 'n': 55264565Sgshapiro case 'N': 55390795Sgshapiro if (sm_strcasecmp(buf, "noop") == 0) 55464565Sgshapiro { 55573191Sgshapiro printf("250 2.0.0 Ok\r\n"); 55638032Speter continue; 55738032Speter } 55838032Speter goto syntaxerr; 55964565Sgshapiro /* NOTREACHED */ 56064565Sgshapiro break; 56138032Speter 56264565Sgshapiro case 'q': 56364565Sgshapiro case 'Q': 56490795Sgshapiro if (sm_strcasecmp(buf, "quit") == 0) 56564565Sgshapiro { 56673191Sgshapiro printf("221 2.0.0 Bye\r\n"); 56738032Speter exit(EX_OK); 56838032Speter } 56938032Speter goto syntaxerr; 57064565Sgshapiro /* NOTREACHED */ 57164565Sgshapiro break; 57238032Speter 57364565Sgshapiro case 'r': 57464565Sgshapiro case 'R': 57590795Sgshapiro if (sm_strncasecmp(buf, "rcpt ", 5) == 0) 57664565Sgshapiro { 57764565Sgshapiro if (return_path == NULL) 57864565Sgshapiro { 57973191Sgshapiro mailerr("503 5.5.1", 58073191Sgshapiro "Need MAIL command"); 58138032Speter continue; 58238032Speter } 58364565Sgshapiro if (rcpt_num >= rcpt_alloc) 58464565Sgshapiro { 58538032Speter rcpt_alloc += RCPT_GROW; 58638032Speter rcpt_addr = (char **) 58766497Sgshapiro REALLOC((char *) rcpt_addr, 58864565Sgshapiro rcpt_alloc * 58964565Sgshapiro sizeof(char **)); 59064565Sgshapiro if (rcpt_addr == NULL) 59164565Sgshapiro { 59273191Sgshapiro mailerr("421 4.3.0", 59373191Sgshapiro "Memory exhausted"); 59438032Speter exit(EX_TEMPFAIL); 59538032Speter } 59638032Speter } 59790795Sgshapiro if (sm_strncasecmp(buf + 5, "to:", 3) != 0 || 59864565Sgshapiro ((rcpt_addr[rcpt_num] = parseaddr(buf + 8, 59990795Sgshapiro true)) == NULL)) 60064565Sgshapiro { 60173191Sgshapiro mailerr("501 5.5.4", 60273191Sgshapiro "Syntax error in parameters"); 60338032Speter continue; 60438032Speter } 60573191Sgshapiro err = process_recipient(rcpt_addr[rcpt_num]); 60673191Sgshapiro if (err != NULL) 60764565Sgshapiro { 60873191Sgshapiro mailerr(NULL, "%s", err); 60938032Speter continue; 61038032Speter } 61138032Speter rcpt_num++; 61273191Sgshapiro printf("250 2.1.5 Ok\r\n"); 61338032Speter continue; 61438032Speter } 61590795Sgshapiro else if (sm_strcasecmp(buf, "rset") == 0) 61664565Sgshapiro { 61773191Sgshapiro printf("250 2.0.0 Ok\r\n"); 61838032Speter 61964565Sgshapirorset: 62071348Sgshapiro while (rcpt_num > 0) 62138032Speter free(rcpt_addr[--rcpt_num]); 62238032Speter if (return_path != NULL) 62338032Speter free(return_path); 62438032Speter return_path = NULL; 62538032Speter continue; 62638032Speter } 62738032Speter goto syntaxerr; 62864565Sgshapiro /* NOTREACHED */ 62964565Sgshapiro break; 63038032Speter 63164565Sgshapiro case 'v': 63264565Sgshapiro case 'V': 63390795Sgshapiro if (sm_strncasecmp(buf, "vrfy ", 5) == 0) 63464565Sgshapiro { 63573191Sgshapiro printf("252 2.3.3 Try RCPT to attempt delivery\r\n"); 63638032Speter continue; 63738032Speter } 63838032Speter goto syntaxerr; 63964565Sgshapiro /* NOTREACHED */ 64064565Sgshapiro break; 64138032Speter 64264565Sgshapiro default: 64338032Speter syntaxerr: 64473191Sgshapiro mailerr("500 5.5.2", "Syntax error"); 64538032Speter continue; 64664565Sgshapiro /* NOTREACHED */ 64764565Sgshapiro break; 64838032Speter } 64938032Speter } 65038032Speter} 65138032Speter 65238032Speterint 65390795Sgshapirostore(from, inbody) 65438032Speter char *from; 65573191Sgshapiro bool *inbody; 65638032Speter{ 65742580Speter FILE *fp = NULL; 65838032Speter time_t tval; 65990795Sgshapiro bool eline; /* previous line was empty */ 66090795Sgshapiro bool fullline = true; /* current line is terminated */ 66164565Sgshapiro bool prevfl; /* previous line was terminated */ 66238032Speter char line[2048]; 66364565Sgshapiro int fd; 66438032Speter char tmpbuf[sizeof _PATH_LOCTMP + 1]; 66538032Speter 66673191Sgshapiro if (inbody != NULL) 66790795Sgshapiro *inbody = false; 66873191Sgshapiro 66964565Sgshapiro (void) umask(0077); 67090795Sgshapiro (void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf); 67173191Sgshapiro if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL) 67264565Sgshapiro { 67373191Sgshapiro mailerr("451 4.3.0", "Unable to open temporary file"); 67473191Sgshapiro return -1; 67538032Speter } 67664565Sgshapiro (void) unlink(tmpbuf); 67738032Speter 67864565Sgshapiro if (LMTPMode) 67964565Sgshapiro { 68073191Sgshapiro printf("354 Go ahead\r\n"); 68164565Sgshapiro (void) fflush(stdout); 68238032Speter } 68373191Sgshapiro if (inbody != NULL) 68490795Sgshapiro *inbody = true; 68538032Speter 68664565Sgshapiro (void) time(&tval); 68764565Sgshapiro (void) fprintf(fp, "From %s %s", from, ctime(&tval)); 68838032Speter 68964565Sgshapiro#ifdef CONTENTLENGTH 69064565Sgshapiro HeaderLength = 0; 69164565Sgshapiro BodyLength = -1; 69264565Sgshapiro#endif /* CONTENTLENGTH */ 69364565Sgshapiro 69438032Speter line[0] = '\0'; 69590795Sgshapiro eline = true; 69673191Sgshapiro while (fgets(line, sizeof(line), stdin) != (char *) NULL) 69764565Sgshapiro { 69864565Sgshapiro size_t line_len = 0; 69964565Sgshapiro int peek; 70042580Speter 70164565Sgshapiro prevfl = fullline; /* preserve state of previous line */ 70264565Sgshapiro while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 70364565Sgshapiro line_len++; 70464565Sgshapiro line_len++; 70542580Speter 70664565Sgshapiro /* Check for dot-stuffing */ 70773191Sgshapiro if (prevfl && LMTPMode && line[0] == '.') 70864565Sgshapiro { 70964565Sgshapiro if (line[1] == '\n' || 71064565Sgshapiro (line[1] == '\r' && line[2] == '\n')) 71138032Speter goto lmtpdot; 71264565Sgshapiro memcpy(line, line + 1, line_len); 71364565Sgshapiro line_len--; 71438032Speter } 71564565Sgshapiro 71664565Sgshapiro /* Check to see if we have the full line from fgets() */ 71790795Sgshapiro fullline = false; 71864565Sgshapiro if (line_len > 0) 71964565Sgshapiro { 72064565Sgshapiro if (line[line_len - 1] == '\n') 72164565Sgshapiro { 72264565Sgshapiro if (line_len >= 2 && 72364565Sgshapiro line[line_len - 2] == '\r') 72464565Sgshapiro { 72564565Sgshapiro line[line_len - 2] = '\n'; 72664565Sgshapiro line[line_len - 1] = '\0'; 72764565Sgshapiro line_len--; 72864565Sgshapiro } 72990795Sgshapiro fullline = true; 73064565Sgshapiro } 73164565Sgshapiro else if (line[line_len - 1] == '\r') 73264565Sgshapiro { 73364565Sgshapiro /* Did we just miss the CRLF? */ 73464565Sgshapiro peek = fgetc(stdin); 73564565Sgshapiro if (peek == '\n') 73664565Sgshapiro { 73764565Sgshapiro line[line_len - 1] = '\n'; 73890795Sgshapiro fullline = true; 73964565Sgshapiro } 74064565Sgshapiro else 74164565Sgshapiro (void) ungetc(peek, stdin); 74264565Sgshapiro } 74364565Sgshapiro } 74464565Sgshapiro else 74590795Sgshapiro fullline = true; 74664565Sgshapiro 74764565Sgshapiro#ifdef CONTENTLENGTH 74864565Sgshapiro if (prevfl && line[0] == '\n' && HeaderLength == 0) 74964565Sgshapiro { 75090795Sgshapiro eline = false; 75173191Sgshapiro if (fp != NULL) 75273191Sgshapiro HeaderLength = ftell(fp); 75364565Sgshapiro if (HeaderLength <= 0) 75464565Sgshapiro { 75564565Sgshapiro /* 75664565Sgshapiro ** shouldn't happen, unless ftell() is 75764565Sgshapiro ** badly broken 75864565Sgshapiro */ 75964565Sgshapiro 76064565Sgshapiro HeaderLength = -1; 76164565Sgshapiro } 76264565Sgshapiro } 76364565Sgshapiro#else /* CONTENTLENGTH */ 76464565Sgshapiro if (prevfl && line[0] == '\n') 76590795Sgshapiro eline = true; 76664565Sgshapiro#endif /* CONTENTLENGTH */ 76764565Sgshapiro else 76864565Sgshapiro { 76938032Speter if (eline && line[0] == 'F' && 77073191Sgshapiro fp != NULL && 77138032Speter !memcmp(line, "From ", 5)) 77266497Sgshapiro (void) putc('>', fp); 77390795Sgshapiro eline = false; 77464565Sgshapiro#ifdef CONTENTLENGTH 77564565Sgshapiro /* discard existing "Content-Length:" headers */ 77664565Sgshapiro if (prevfl && HeaderLength == 0 && 77764565Sgshapiro (line[0] == 'C' || line[0] == 'c') && 77890795Sgshapiro sm_strncasecmp(line, ContentHdr, 15) == 0) 77964565Sgshapiro { 78064565Sgshapiro /* 78164565Sgshapiro ** be paranoid: clear the line 78264565Sgshapiro ** so no "wrong matches" may occur later 78364565Sgshapiro */ 78464565Sgshapiro line[0] = '\0'; 78564565Sgshapiro continue; 78664565Sgshapiro } 78764565Sgshapiro#endif /* CONTENTLENGTH */ 78864565Sgshapiro 78938032Speter } 79073191Sgshapiro if (fp != NULL) 79164565Sgshapiro { 79273191Sgshapiro (void) fwrite(line, sizeof(char), line_len, fp); 79373191Sgshapiro if (ferror(fp)) 79464565Sgshapiro { 79542580Speter mailerr("451 4.3.0", 79673191Sgshapiro "Temporary file write error"); 79764565Sgshapiro (void) fclose(fp); 79873191Sgshapiro fp = NULL; 79973191Sgshapiro continue; 80038032Speter } 80138032Speter } 80238032Speter } 80338032Speter 80473191Sgshapiro /* check if an error occurred */ 80573191Sgshapiro if (fp == NULL) 80673191Sgshapiro return -1; 80773191Sgshapiro 80873191Sgshapiro if (LMTPMode) 80964565Sgshapiro { 81038032Speter /* Got a premature EOF -- toss message and exit */ 81138032Speter exit(EX_OK); 81238032Speter } 81338032Speter 81438032Speter /* If message not newline terminated, need an extra. */ 81573191Sgshapiro if (fp != NULL && strchr(line, '\n') == NULL) 81664565Sgshapiro (void) putc('\n', fp); 81738032Speter 81838032Speter lmtpdot: 81938032Speter 82064565Sgshapiro#ifdef CONTENTLENGTH 82173191Sgshapiro if (fp != NULL) 82273191Sgshapiro BodyLength = ftell(fp); 82364565Sgshapiro if (HeaderLength == 0 && BodyLength > 0) /* empty body */ 82464565Sgshapiro { 82564565Sgshapiro HeaderLength = BodyLength; 82664565Sgshapiro BodyLength = 0; 82764565Sgshapiro } 82864565Sgshapiro else 82964565Sgshapiro BodyLength = BodyLength - HeaderLength - 1 ; 83064565Sgshapiro 83164565Sgshapiro if (HeaderLength > 0 && BodyLength >= 0) 83264565Sgshapiro { 83390795Sgshapiro (void) sm_snprintf(line, sizeof line, "%lld\n", 83490795Sgshapiro (LONGLONG_T) BodyLength); 83590795Sgshapiro (void) sm_strlcpy(&ContentHdr[16], line, 83690795Sgshapiro sizeof(ContentHdr) - 16); 83764565Sgshapiro } 83864565Sgshapiro else 83964565Sgshapiro BodyLength = -1; /* Something is wrong here */ 84064565Sgshapiro#endif /* CONTENTLENGTH */ 84164565Sgshapiro 84238032Speter /* Output a newline; note, empty messages are allowed. */ 84373191Sgshapiro if (fp != NULL) 84473191Sgshapiro (void) putc('\n', fp); 84538032Speter 84673191Sgshapiro if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0) 84764565Sgshapiro { 84873191Sgshapiro mailerr("451 4.3.0", "Temporary file write error"); 84973191Sgshapiro if (fp != NULL) 85064565Sgshapiro (void) fclose(fp); 85173191Sgshapiro return -1; 85238032Speter } 85364565Sgshapiro return fd; 85438032Speter} 85538032Speter 85638032Spetervoid 85773191Sgshapirodeliver(fd, name) 85838032Speter int fd; 85938032Speter char *name; 86038032Speter{ 86164565Sgshapiro struct stat fsb; 86264565Sgshapiro struct stat sb; 86364565Sgshapiro char path[MAXPATHLEN]; 86471348Sgshapiro int mbfd = -1, nr = 0, nw, off; 86590795Sgshapiro int exitval; 86638032Speter char *p; 86773191Sgshapiro char *errcode; 86838032Speter off_t curoff; 86964565Sgshapiro#ifdef CONTENTLENGTH 87064565Sgshapiro off_t headerbytes; 87164565Sgshapiro int readamount; 87264565Sgshapiro#endif /* CONTENTLENGTH */ 87398125Sgshapiro char biffmsg[100], buf[8 * 1024]; 87490795Sgshapiro SM_MBDB_T user; 87538032Speter 87638032Speter /* 87764565Sgshapiro ** Disallow delivery to unknown names -- special mailboxes can be 87864565Sgshapiro ** handled in the sendmail aliases file. 87964565Sgshapiro */ 88073191Sgshapiro 88190795Sgshapiro exitval = sm_mbdb_lookup(name, &user); 88290795Sgshapiro switch (exitval) 88364565Sgshapiro { 88490795Sgshapiro case EX_OK: 88590795Sgshapiro break; 88690795Sgshapiro 88790795Sgshapiro case EX_NOUSER: 88890795Sgshapiro exitval = EX_UNAVAILABLE; 88990795Sgshapiro mailerr("550 5.1.1", "%s: User unknown", name); 89090795Sgshapiro break; 89190795Sgshapiro 89290795Sgshapiro case EX_TEMPFAIL: 89390795Sgshapiro mailerr("451 4.3.0", "%s: User database failure; retry later", 89490795Sgshapiro name); 89590795Sgshapiro break; 89690795Sgshapiro 89790795Sgshapiro default: 89890795Sgshapiro exitval = EX_UNAVAILABLE; 89990795Sgshapiro mailerr("550 5.3.0", "%s: User database failure", name); 90090795Sgshapiro break; 90190795Sgshapiro } 90290795Sgshapiro 90390795Sgshapiro if (exitval != EX_OK) 90490795Sgshapiro { 90590795Sgshapiro if (ExitVal != EX_TEMPFAIL) 90690795Sgshapiro ExitVal = exitval; 90738032Speter return; 90838032Speter } 90990795Sgshapiro 91038032Speter endpwent(); 91138032Speter 91238032Speter /* 91364565Sgshapiro ** Keep name reasonably short to avoid buffer overruns. 91464565Sgshapiro ** This isn't necessary on BSD because of the proper 91564565Sgshapiro ** definition of snprintf(), but it can cause problems 91664565Sgshapiro ** on other systems. 91764565Sgshapiro ** Also, clear out any bogus characters. 91864565Sgshapiro */ 91938032Speter 92038032Speter if (strlen(name) > 40) 92138032Speter name[40] = '\0'; 92238032Speter for (p = name; *p != '\0'; p++) 92338032Speter { 92438032Speter if (!isascii(*p)) 92538032Speter *p &= 0x7f; 92638032Speter else if (!isprint(*p)) 92738032Speter *p = '.'; 92838032Speter } 92938032Speter 93038032Speter 93190795Sgshapiro if (HomeMailFile == NULL) 93290795Sgshapiro { 93390795Sgshapiro if (sm_snprintf(path, sizeof(path), "%s/%s", 93490795Sgshapiro _PATH_MAILDIR, name) >= sizeof(path)) 93590795Sgshapiro { 93690795Sgshapiro exitval = EX_UNAVAILABLE; 93790795Sgshapiro mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 93890795Sgshapiro return; 93990795Sgshapiro } 94090795Sgshapiro } 94190795Sgshapiro else if (*user.mbdb_homedir == '\0') 94290795Sgshapiro { 94390795Sgshapiro exitval = EX_UNAVAILABLE; 94490795Sgshapiro mailerr("550 5.1.1", "%s: User missing home directory", name); 94590795Sgshapiro return; 94690795Sgshapiro } 94790795Sgshapiro else if (sm_snprintf(path, sizeof(path), "%s/%s", 94890795Sgshapiro user.mbdb_homedir, HomeMailFile) >= sizeof(path)) 94990795Sgshapiro { 95090795Sgshapiro exitval = EX_UNAVAILABLE; 95190795Sgshapiro mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 95290795Sgshapiro return; 95390795Sgshapiro } 95464565Sgshapiro 95564565Sgshapiro 95638032Speter /* 95764565Sgshapiro ** If the mailbox is linked or a symlink, fail. There's an obvious 95864565Sgshapiro ** race here, that the file was replaced with a symbolic link after 95964565Sgshapiro ** the lstat returned, but before the open. We attempt to detect 96064565Sgshapiro ** this by comparing the original stat information and information 96164565Sgshapiro ** returned by an fstat of the file descriptor returned by the open. 96264565Sgshapiro ** 96364565Sgshapiro ** NB: this is a symptom of a larger problem, that the mail spooling 96464565Sgshapiro ** directory is writeable by the wrong users. If that directory is 96564565Sgshapiro ** writeable, system security is compromised for other reasons, and 96664565Sgshapiro ** it cannot be fixed here. 96764565Sgshapiro ** 96864565Sgshapiro ** If we created the mailbox, set the owner/group. If that fails, 96964565Sgshapiro ** just return. Another process may have already opened it, so we 97064565Sgshapiro ** can't unlink it. Historically, binmail set the owner/group at 97164565Sgshapiro ** each mail delivery. We no longer do this, assuming that if the 97264565Sgshapiro ** ownership or permissions were changed there was a reason. 97364565Sgshapiro ** 97464565Sgshapiro ** XXX 97564565Sgshapiro ** open(2) should support flock'ing the file. 97664565Sgshapiro */ 97764565Sgshapiro 97838032Spetertryagain: 97964565Sgshapiro#ifdef MAILLOCK 98064565Sgshapiro p = name; 98164565Sgshapiro#else /* MAILLOCK */ 98264565Sgshapiro p = path; 98364565Sgshapiro#endif /* MAILLOCK */ 98464565Sgshapiro if ((off = lockmbox(p)) != 0) 98564565Sgshapiro { 98664565Sgshapiro if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL) 98764565Sgshapiro { 98864565Sgshapiro ExitVal = EX_TEMPFAIL; 98973191Sgshapiro errcode = "451 4.3.0"; 99064565Sgshapiro } 99164565Sgshapiro else 99273191Sgshapiro errcode = "551 5.3.0"; 99373191Sgshapiro 99473191Sgshapiro mailerr(errcode, "lockmailbox %s failed; error code %d %s", 99590795Sgshapiro p, off, errno > 0 ? sm_errstring(errno) : ""); 99664565Sgshapiro return; 99764565Sgshapiro } 99864565Sgshapiro 99964565Sgshapiro if (lstat(path, &sb) < 0) 100064565Sgshapiro { 100164565Sgshapiro int save_errno; 100264565Sgshapiro int mode = S_IRUSR|S_IWUSR; 100390795Sgshapiro gid_t gid = user.mbdb_gid; 100464565Sgshapiro 100564565Sgshapiro#ifdef MAILGID 100664565Sgshapiro (void) umask(0007); 100764565Sgshapiro gid = MAILGID; 100864565Sgshapiro mode |= S_IRGRP|S_IWGRP; 100964565Sgshapiro#endif /* MAILGID */ 101064565Sgshapiro 101166497Sgshapiro mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|EXTRA_MODE, 101266497Sgshapiro mode); 101364565Sgshapiro save_errno = errno; 101464565Sgshapiro 101538032Speter if (lstat(path, &sb) < 0) 101638032Speter { 101764565Sgshapiro ExitVal = EX_CANTCREAT; 101842580Speter mailerr("550 5.2.0", 101942580Speter "%s: lstat: file changed after open", path); 102038032Speter goto err1; 102138032Speter } 102273191Sgshapiro if (mbfd < 0) 102364565Sgshapiro { 102464565Sgshapiro if (save_errno == EEXIST) 102538032Speter goto tryagain; 102666497Sgshapiro 102766497Sgshapiro /* open failed, don't try again */ 102866497Sgshapiro mailerr("450 4.2.0", "%s: %s", path, 102990795Sgshapiro sm_errstring(save_errno)); 103066497Sgshapiro goto err0; 103164565Sgshapiro } 103290795Sgshapiro else if (fchown(mbfd, user.mbdb_uid, gid) < 0) 103364565Sgshapiro { 103438032Speter mailerr("451 4.3.0", "chown %u.%u: %s", 103590795Sgshapiro user.mbdb_uid, gid, name); 103638032Speter goto err1; 103738032Speter } 103866497Sgshapiro else 103966497Sgshapiro { 104066497Sgshapiro /* 104166497Sgshapiro ** open() was successful, now close it so can 104266497Sgshapiro ** be opened as the right owner again. 104366497Sgshapiro ** Paranoia: reset mbdf since the file descriptor 104466497Sgshapiro ** is no longer valid; better safe than sorry. 104566497Sgshapiro */ 104666497Sgshapiro 104790795Sgshapiro sb.st_uid = user.mbdb_uid; 104866497Sgshapiro (void) close(mbfd); 104966497Sgshapiro mbfd = -1; 105066497Sgshapiro } 105164565Sgshapiro } 105264565Sgshapiro else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) 105364565Sgshapiro { 105438032Speter mailerr("550 5.2.0", "%s: irregular file", path); 105538032Speter goto err0; 105664565Sgshapiro } 105790795Sgshapiro else if (sb.st_uid != user.mbdb_uid) 105864565Sgshapiro { 105964565Sgshapiro ExitVal = EX_CANTCREAT; 106038032Speter mailerr("550 5.2.0", "%s: wrong ownership (%d)", 106190795Sgshapiro path, (int) sb.st_uid); 106238032Speter goto err0; 106364565Sgshapiro } 106438032Speter 106566497Sgshapiro /* change UID for quota checks */ 106690795Sgshapiro if (setreuid(0, user.mbdb_uid) < 0) 106764565Sgshapiro { 106866497Sgshapiro mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", 106990795Sgshapiro (int) user.mbdb_uid, sm_errstring(errno), 107090795Sgshapiro (int) getuid(), (int) geteuid()); 107166497Sgshapiro goto err1; 107266497Sgshapiro } 107366497Sgshapiro#ifdef DEBUG 107490795Sgshapiro fprintf(stderr, "new euid = %d\n", (int) geteuid()); 107566497Sgshapiro#endif /* DEBUG */ 107666497Sgshapiro mbfd = open(path, O_APPEND|O_WRONLY|EXTRA_MODE, 0); 107766497Sgshapiro if (mbfd < 0) 107866497Sgshapiro { 107990795Sgshapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 108038032Speter goto err0; 108164565Sgshapiro } 108264565Sgshapiro else if (fstat(mbfd, &fsb) < 0 || 108364565Sgshapiro fsb.st_nlink != 1 || 108464565Sgshapiro sb.st_nlink != 1 || 108564565Sgshapiro !S_ISREG(fsb.st_mode) || 108664565Sgshapiro sb.st_dev != fsb.st_dev || 108764565Sgshapiro sb.st_ino != fsb.st_ino || 108866497Sgshapiro# if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 108964565Sgshapiro sb.st_gen != fsb.st_gen || 109066497Sgshapiro# endif /* HAS_ST_GEN && 0 */ 109164565Sgshapiro sb.st_uid != fsb.st_uid) 109264565Sgshapiro { 109364565Sgshapiro ExitVal = EX_TEMPFAIL; 109442580Speter mailerr("550 5.2.0", "%s: fstat: file changed after open", 109542580Speter path); 109638032Speter goto err1; 109738032Speter } 109838032Speter 109990795Sgshapiro#if 0 110090795Sgshapiro /* 110190795Sgshapiro ** This code could be reused if we decide to add a 110290795Sgshapiro ** per-user quota field to the sm_mbdb interface. 110390795Sgshapiro */ 110464565Sgshapiro 110590795Sgshapiro /* 110690795Sgshapiro ** Fail if the user has a quota specified, and delivery of this 110790795Sgshapiro ** message would exceed that quota. We bounce such failures using 110890795Sgshapiro ** EX_UNAVAILABLE, unless there were internal problems, since 110990795Sgshapiro ** storing immense messages for later retries can cause queueing 111090795Sgshapiro ** issues. 111190795Sgshapiro */ 111290795Sgshapiro 111390795Sgshapiro if (ui.quota > 0) 111490795Sgshapiro { 111590795Sgshapiro struct stat dsb; 111690795Sgshapiro 111790795Sgshapiro if (fstat(fd, &dsb) < 0) 111890795Sgshapiro { 111990795Sgshapiro ExitVal = EX_TEMPFAIL; 112090795Sgshapiro mailerr("451 4.3.0", 112190795Sgshapiro "%s: fstat: can't stat temporary storage: %s", 112290795Sgshapiro ui.mailspool, sm_errstring(errno)); 112390795Sgshapiro goto err1; 112490795Sgshapiro } 112590795Sgshapiro 112690795Sgshapiro if (dsb.st_size + sb.st_size + 1 > ui.quota) 112790795Sgshapiro { 112890795Sgshapiro ExitVal = EX_UNAVAILABLE; 112990795Sgshapiro mailerr("551 5.2.2", 113090795Sgshapiro "%s: Mailbox full or quota exceeded", 113190795Sgshapiro ui.mailspool); 113290795Sgshapiro goto err1; 113390795Sgshapiro } 113490795Sgshapiro } 113590795Sgshapiro#endif /* 0 */ 113690795Sgshapiro 113738032Speter /* Wait until we can get a lock on the file. */ 113864565Sgshapiro if (flock(mbfd, LOCK_EX) < 0) 113964565Sgshapiro { 114090795Sgshapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 114138032Speter goto err1; 114238032Speter } 114338032Speter 114464565Sgshapiro if (!nobiff) 114564565Sgshapiro { 114638084Speter /* Get the starting offset of the new message for biff. */ 114773191Sgshapiro curoff = lseek(mbfd, (off_t) 0, SEEK_END); 114890795Sgshapiro (void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n", 114990795Sgshapiro name, (LONGLONG_T) curoff); 115038084Speter } 115138032Speter 115238032Speter /* Copy the message into the file. */ 115373191Sgshapiro if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1) 115464565Sgshapiro { 115573191Sgshapiro mailerr("450 4.2.0", "Temporary file: %s", 115690795Sgshapiro sm_errstring(errno)); 115738032Speter goto err1; 115838032Speter } 115938032Speter#ifdef DEBUG 116090795Sgshapiro fprintf(stderr, "before writing: euid = %d\n", (int) geteuid()); 116164565Sgshapiro#endif /* DEBUG */ 116264565Sgshapiro#ifdef CONTENTLENGTH 116364565Sgshapiro headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; 116464565Sgshapiro for (;;) 116564565Sgshapiro { 116664565Sgshapiro if (headerbytes == 0) 116764565Sgshapiro { 116890795Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr); 116964565Sgshapiro nr = strlen(buf); 117064565Sgshapiro headerbytes = -1; 117164565Sgshapiro readamount = 0; 117264565Sgshapiro } 117364565Sgshapiro else if (headerbytes > sizeof(buf) || headerbytes < 0) 117464565Sgshapiro readamount = sizeof(buf); 117564565Sgshapiro else 117664565Sgshapiro readamount = headerbytes; 117764565Sgshapiro if (readamount != 0) 117864565Sgshapiro nr = read(fd, buf, readamount); 117964565Sgshapiro if (nr <= 0) 118064565Sgshapiro break; 118164565Sgshapiro if (headerbytes > 0) 118264565Sgshapiro headerbytes -= nr ; 118364565Sgshapiro 118464565Sgshapiro#else /* CONTENTLENGTH */ 118538032Speter while ((nr = read(fd, buf, sizeof(buf))) > 0) 118664565Sgshapiro { 118764565Sgshapiro#endif /* CONTENTLENGTH */ 118838032Speter for (off = 0; off < nr; off += nw) 118964565Sgshapiro { 119064565Sgshapiro if ((nw = write(mbfd, buf + off, nr - off)) < 0) 119164565Sgshapiro { 119273191Sgshapiro errcode = "450 4.2.0"; 119364565Sgshapiro#ifdef EDQUOT 119473191Sgshapiro if (errno == EDQUOT && BounceQuota) 119573191Sgshapiro errcode = "552 5.2.2"; 119664565Sgshapiro#endif /* EDQUOT */ 119773191Sgshapiro mailerr(errcode, "%s: %s", 119890795Sgshapiro path, sm_errstring(errno)); 119938032Speter goto err3; 120038032Speter } 120164565Sgshapiro } 120264565Sgshapiro } 120364565Sgshapiro if (nr < 0) 120464565Sgshapiro { 120573191Sgshapiro mailerr("450 4.2.0", "Temporary file: %s", 120690795Sgshapiro sm_errstring(errno)); 120738032Speter goto err3; 120838032Speter } 120938032Speter 121038032Speter /* Flush to disk, don't wait for update. */ 121164565Sgshapiro if (!nofsync && fsync(mbfd) < 0) 121264565Sgshapiro { 121390795Sgshapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 121438032Spetererr3: 121573191Sgshapiro (void) setreuid(0, 0); 121638032Speter#ifdef DEBUG 121790795Sgshapiro fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 121864565Sgshapiro#endif /* DEBUG */ 121964565Sgshapiro (void) ftruncate(mbfd, curoff); 122066497Sgshapiroerr1: if (mbfd >= 0) 122166497Sgshapiro (void) close(mbfd); 122238032Spetererr0: unlockmbox(); 122338032Speter return; 122438032Speter } 122538032Speter 122638032Speter /* Close and check -- NFS doesn't write until the close. */ 122764565Sgshapiro if (close(mbfd)) 122864565Sgshapiro { 122973191Sgshapiro errcode = "450 4.2.0"; 123064565Sgshapiro#ifdef EDQUOT 123173191Sgshapiro if (errno == EDQUOT && BounceQuota) 123273191Sgshapiro errcode = "552 5.2.2"; 123364565Sgshapiro#endif /* EDQUOT */ 123490795Sgshapiro mailerr(errcode, "%s: %s", path, sm_errstring(errno)); 123564565Sgshapiro (void) truncate(path, curoff); 123664565Sgshapiro } 123764565Sgshapiro else if (!nobiff) 123838032Speter notifybiff(biffmsg); 123938032Speter 124064565Sgshapiro if (setreuid(0, 0) < 0) 124164565Sgshapiro { 124238032Speter mailerr("450 4.2.0", "setreuid(0, 0): %s", 124390795Sgshapiro sm_errstring(errno)); 124438032Speter goto err0; 124538032Speter } 124638032Speter#ifdef DEBUG 124790795Sgshapiro fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 124864565Sgshapiro#endif /* DEBUG */ 124938032Speter unlockmbox(); 125064565Sgshapiro if (LMTPMode) 125173191Sgshapiro printf("250 2.1.5 %s Ok\r\n", name); 125238032Speter} 125338032Speter 125438032Speter/* 125564565Sgshapiro** user.lock files are necessary for compatibility with other 125664565Sgshapiro** systems, e.g., when the mail spool file is NFS exported. 125764565Sgshapiro** Alas, mailbox locking is more than just a local matter. 125864565Sgshapiro** EPA 11/94. 125964565Sgshapiro*/ 126038032Speter 126190795Sgshapirobool Locked = false; 126238032Speter 126364565Sgshapiro#ifdef MAILLOCK 126464565Sgshapiroint 126564565Sgshapirolockmbox(name) 126664565Sgshapiro char *name; 126764565Sgshapiro{ 126866497Sgshapiro int r = 0; 126964565Sgshapiro 127064565Sgshapiro if (Locked) 127164565Sgshapiro return 0; 127264565Sgshapiro if ((r = maillock(name, 15)) == L_SUCCESS) 127364565Sgshapiro { 127490795Sgshapiro Locked = true; 127564565Sgshapiro return 0; 127664565Sgshapiro } 127764565Sgshapiro switch (r) 127864565Sgshapiro { 127964565Sgshapiro case L_TMPLOCK: /* Can't create tmp file */ 128064565Sgshapiro case L_TMPWRITE: /* Can't write pid into lockfile */ 128164565Sgshapiro case L_MAXTRYS: /* Failed after retrycnt attempts */ 128264565Sgshapiro errno = 0; 128364565Sgshapiro r = EX_TEMPFAIL; 128464565Sgshapiro break; 128564565Sgshapiro case L_ERROR: /* Check errno for reason */ 128664565Sgshapiro r = errno; 128764565Sgshapiro break; 128864565Sgshapiro default: /* other permanent errors */ 128964565Sgshapiro errno = 0; 129064565Sgshapiro r = EX_UNAVAILABLE; 129164565Sgshapiro break; 129264565Sgshapiro } 129364565Sgshapiro return r; 129464565Sgshapiro} 129564565Sgshapiro 129638032Spetervoid 129764565Sgshapirounlockmbox() 129864565Sgshapiro{ 129964565Sgshapiro if (Locked) 130064565Sgshapiro mailunlock(); 130190795Sgshapiro Locked = false; 130264565Sgshapiro} 130364565Sgshapiro#else /* MAILLOCK */ 130464565Sgshapiro 130564565Sgshapirochar LockName[MAXPATHLEN]; 130664565Sgshapiro 130764565Sgshapiroint 130838032Speterlockmbox(path) 130938032Speter char *path; 131038032Speter{ 131138032Speter int statfailed = 0; 131264565Sgshapiro time_t start; 131338032Speter 131464565Sgshapiro if (Locked) 131564565Sgshapiro return 0; 131664565Sgshapiro if (strlen(path) + 6 > sizeof LockName) 131764565Sgshapiro return EX_SOFTWARE; 131890795Sgshapiro (void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path); 131964565Sgshapiro (void) time(&start); 132064565Sgshapiro for (; ; sleep(5)) 132164565Sgshapiro { 132238032Speter int fd; 132338032Speter struct stat st; 132438032Speter time_t now; 132538032Speter 132664565Sgshapiro /* global timeout */ 132764565Sgshapiro (void) time(&now); 132864565Sgshapiro if (now > start + LOCKTO_GLOB) 132964565Sgshapiro { 133064565Sgshapiro errno = 0; 133164565Sgshapiro return EX_TEMPFAIL; 133264565Sgshapiro } 133364565Sgshapiro fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, 0); 133464565Sgshapiro if (fd >= 0) 133564565Sgshapiro { 133638032Speter /* defeat lock checking programs which test pid */ 133764565Sgshapiro (void) write(fd, "0", 2); 133890795Sgshapiro Locked = true; 133964565Sgshapiro (void) close(fd); 134064565Sgshapiro return 0; 134138032Speter } 134264565Sgshapiro if (stat(LockName, &st) < 0) 134364565Sgshapiro { 134438032Speter if (statfailed++ > 5) 134564565Sgshapiro { 134664565Sgshapiro errno = 0; 134764565Sgshapiro return EX_TEMPFAIL; 134864565Sgshapiro } 134938032Speter continue; 135038032Speter } 135138032Speter statfailed = 0; 135264565Sgshapiro (void) time(&now); 135364565Sgshapiro if (now < st.st_ctime + LOCKTO_RM) 135438032Speter continue; 135564565Sgshapiro 135664565Sgshapiro /* try to remove stale lockfile */ 135764565Sgshapiro if (unlink(LockName) < 0) 135864565Sgshapiro return errno; 135938032Speter } 136038032Speter} 136138032Speter 136238032Spetervoid 136338032Speterunlockmbox() 136438032Speter{ 136564565Sgshapiro if (!Locked) 136638032Speter return; 136764565Sgshapiro (void) unlink(LockName); 136890795Sgshapiro Locked = false; 136938032Speter} 137064565Sgshapiro#endif /* MAILLOCK */ 137138032Speter 137238032Spetervoid 137338032Speternotifybiff(msg) 137438032Speter char *msg; 137538032Speter{ 137690795Sgshapiro static bool initialized = false; 137738032Speter static int f = -1; 137838032Speter struct hostent *hp; 137938032Speter struct servent *sp; 138038032Speter int len; 138164565Sgshapiro static struct sockaddr_in addr; 138238032Speter 138364565Sgshapiro if (!initialized) 138464565Sgshapiro { 138590795Sgshapiro initialized = true; 138664565Sgshapiro 138738032Speter /* Be silent if biff service not available. */ 138864565Sgshapiro if ((sp = getservbyname("biff", "udp")) == NULL || 138964565Sgshapiro (hp = gethostbyname("localhost")) == NULL || 139064565Sgshapiro hp->h_length != INADDRSZ) 139138032Speter return; 139264565Sgshapiro 139338032Speter addr.sin_family = hp->h_addrtype; 139464565Sgshapiro memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ); 139538032Speter addr.sin_port = sp->s_port; 139638032Speter } 139764565Sgshapiro 139864565Sgshapiro /* No message, just return */ 139964565Sgshapiro if (msg == NULL) 140038032Speter return; 140164565Sgshapiro 140264565Sgshapiro /* Couldn't initialize addr struct */ 140364565Sgshapiro if (addr.sin_family == AF_UNSPEC) 140464565Sgshapiro return; 140564565Sgshapiro 140673191Sgshapiro if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 140764565Sgshapiro return; 140838032Speter len = strlen(msg) + 1; 140964565Sgshapiro (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr)); 141038032Speter} 141138032Speter 141238032Spetervoid 141338032Speterusage() 141438032Speter{ 141564565Sgshapiro ExitVal = EX_USAGE; 141690795Sgshapiro mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ..."); 141764565Sgshapiro exit(ExitVal); 141838032Speter} 141938032Speter 142038032Spetervoid 142190795Sgshapiro/*VARARGS2*/ 142238032Speter#ifdef __STDC__ 142338032Spetermailerr(const char *hdr, const char *fmt, ...) 142464565Sgshapiro#else /* __STDC__ */ 142538032Spetermailerr(hdr, fmt, va_alist) 142638032Speter const char *hdr; 142738032Speter const char *fmt; 142838032Speter va_dcl 142964565Sgshapiro#endif /* __STDC__ */ 143038032Speter{ 143173191Sgshapiro size_t len = 0; 143290795Sgshapiro SM_VA_LOCAL_DECL 143338032Speter 143473191Sgshapiro (void) e_to_sys(errno); 143573191Sgshapiro 143690795Sgshapiro SM_VA_START(ap, fmt); 143773191Sgshapiro 143890795Sgshapiro if (LMTPMode && hdr != NULL) 143938032Speter { 144090795Sgshapiro sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); 144190795Sgshapiro len = strlen(ErrBuf); 144238032Speter } 144390795Sgshapiro (void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap); 144490795Sgshapiro SM_VA_END(ap); 144573191Sgshapiro 144673191Sgshapiro if (!HoldErrs) 144773191Sgshapiro flush_error(); 144873191Sgshapiro 144973191Sgshapiro /* Log the message to syslog. */ 145073191Sgshapiro if (!LMTPMode) 145173191Sgshapiro syslog(LOG_ERR, "%s", ErrBuf); 145238032Speter} 145338032Speter 145438032Spetervoid 145573191Sgshapiroflush_error() 145638032Speter{ 145773191Sgshapiro if (LMTPMode) 145873191Sgshapiro printf("%s\r\n", ErrBuf); 145973191Sgshapiro else 146038032Speter { 146173191Sgshapiro if (ExitVal != EX_USAGE) 146273191Sgshapiro (void) fprintf(stderr, "mail.local: "); 146373191Sgshapiro fprintf(stderr, "%s\n", ErrBuf); 146438032Speter } 146538032Speter} 146638032Speter 146738032Speter/* 146838032Speter * e_to_sys -- 146938032Speter * Guess which errno's are temporary. Gag me. 147038032Speter */ 147173191Sgshapiro 147264565Sgshapiroint 147338032Spetere_to_sys(num) 147438032Speter int num; 147538032Speter{ 147638032Speter /* Temporary failures override hard errors. */ 147764565Sgshapiro if (ExitVal == EX_TEMPFAIL) 147864565Sgshapiro return ExitVal; 147938032Speter 148064565Sgshapiro switch (num) /* Hopefully temporary errors. */ 148164565Sgshapiro { 148264565Sgshapiro#ifdef EDQUOT 148364565Sgshapiro case EDQUOT: /* Disc quota exceeded */ 148473191Sgshapiro if (BounceQuota) 148564565Sgshapiro { 148664565Sgshapiro ExitVal = EX_UNAVAILABLE; 148764565Sgshapiro break; 148864565Sgshapiro } 148964565Sgshapiro /* FALLTHROUGH */ 149064565Sgshapiro#endif /* EDQUOT */ 149138032Speter#ifdef EAGAIN 149264565Sgshapiro case EAGAIN: /* Resource temporarily unavailable */ 149364565Sgshapiro#endif /* EAGAIN */ 149438032Speter#ifdef EBUSY 149564565Sgshapiro case EBUSY: /* Device busy */ 149664565Sgshapiro#endif /* EBUSY */ 149738032Speter#ifdef EPROCLIM 149864565Sgshapiro case EPROCLIM: /* Too many processes */ 149964565Sgshapiro#endif /* EPROCLIM */ 150038032Speter#ifdef EUSERS 150164565Sgshapiro case EUSERS: /* Too many users */ 150264565Sgshapiro#endif /* EUSERS */ 150338032Speter#ifdef ECONNABORTED 150464565Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 150564565Sgshapiro#endif /* ECONNABORTED */ 150638032Speter#ifdef ECONNREFUSED 150764565Sgshapiro case ECONNREFUSED: /* Connection refused */ 150864565Sgshapiro#endif /* ECONNREFUSED */ 150938032Speter#ifdef ECONNRESET 151064565Sgshapiro case ECONNRESET: /* Connection reset by peer */ 151164565Sgshapiro#endif /* ECONNRESET */ 151238032Speter#ifdef EDEADLK 151364565Sgshapiro case EDEADLK: /* Resource deadlock avoided */ 151464565Sgshapiro#endif /* EDEADLK */ 151538032Speter#ifdef EFBIG 151664565Sgshapiro case EFBIG: /* File too large */ 151764565Sgshapiro#endif /* EFBIG */ 151838032Speter#ifdef EHOSTDOWN 151964565Sgshapiro case EHOSTDOWN: /* Host is down */ 152064565Sgshapiro#endif /* EHOSTDOWN */ 152138032Speter#ifdef EHOSTUNREACH 152264565Sgshapiro case EHOSTUNREACH: /* No route to host */ 152364565Sgshapiro#endif /* EHOSTUNREACH */ 152438032Speter#ifdef EMFILE 152564565Sgshapiro case EMFILE: /* Too many open files */ 152664565Sgshapiro#endif /* EMFILE */ 152738032Speter#ifdef ENETDOWN 152864565Sgshapiro case ENETDOWN: /* Network is down */ 152964565Sgshapiro#endif /* ENETDOWN */ 153038032Speter#ifdef ENETRESET 153164565Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 153264565Sgshapiro#endif /* ENETRESET */ 153338032Speter#ifdef ENETUNREACH 153464565Sgshapiro case ENETUNREACH: /* Network is unreachable */ 153564565Sgshapiro#endif /* ENETUNREACH */ 153638032Speter#ifdef ENFILE 153764565Sgshapiro case ENFILE: /* Too many open files in system */ 153864565Sgshapiro#endif /* ENFILE */ 153938032Speter#ifdef ENOBUFS 154064565Sgshapiro case ENOBUFS: /* No buffer space available */ 154164565Sgshapiro#endif /* ENOBUFS */ 154238032Speter#ifdef ENOMEM 154364565Sgshapiro case ENOMEM: /* Cannot allocate memory */ 154464565Sgshapiro#endif /* ENOMEM */ 154538032Speter#ifdef ENOSPC 154664565Sgshapiro case ENOSPC: /* No space left on device */ 154764565Sgshapiro#endif /* ENOSPC */ 154838032Speter#ifdef EROFS 154964565Sgshapiro case EROFS: /* Read-only file system */ 155064565Sgshapiro#endif /* EROFS */ 155138032Speter#ifdef ESTALE 155264565Sgshapiro case ESTALE: /* Stale NFS file handle */ 155364565Sgshapiro#endif /* ESTALE */ 155438032Speter#ifdef ETIMEDOUT 155564565Sgshapiro case ETIMEDOUT: /* Connection timed out */ 155664565Sgshapiro#endif /* ETIMEDOUT */ 155738032Speter#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK 155864565Sgshapiro case EWOULDBLOCK: /* Operation would block. */ 155964565Sgshapiro#endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */ 156064565Sgshapiro ExitVal = EX_TEMPFAIL; 156138032Speter break; 156264565Sgshapiro 156364565Sgshapiro default: 156464565Sgshapiro ExitVal = EX_UNAVAILABLE; 156538032Speter break; 156638032Speter } 156764565Sgshapiro return ExitVal; 156838032Speter} 156938032Speter 157038032Speter#if defined(ultrix) || defined(_CRAY) 157138032Speter/* 157238032Speter * Copyright (c) 1987, 1993 157338032Speter * The Regents of the University of California. All rights reserved. 157438032Speter * 157538032Speter * Redistribution and use in source and binary forms, with or without 157638032Speter * modification, are permitted provided that the following conditions 157738032Speter * are met: 157838032Speter * 1. Redistributions of source code must retain the above copyright 157938032Speter * notice, this list of conditions and the following disclaimer. 158038032Speter * 2. Redistributions in binary form must reproduce the above copyright 158138032Speter * notice, this list of conditions and the following disclaimer in the 158238032Speter * documentation and/or other materials provided with the distribution. 158338032Speter * 3. All advertising materials mentioning features or use of this software 158438032Speter * must display the following acknowledgement: 158538032Speter * This product includes software developed by the University of 158638032Speter * California, Berkeley and its contributors. 158738032Speter * 4. Neither the name of the University nor the names of its contributors 158838032Speter * may be used to endorse or promote products derived from this software 158938032Speter * without specific prior written permission. 159038032Speter * 159138032Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 159238032Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 159338032Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 159438032Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 159538032Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 159638032Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 159738032Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 159838032Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 159938032Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 160038032Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 160138032Speter * SUCH DAMAGE. 160238032Speter */ 160338032Speter 160464565Sgshapiro# if defined(LIBC_SCCS) && !defined(lint) 160538032Speterstatic char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; 160664565Sgshapiro# endif /* defined(LIBC_SCCS) && !defined(lint) */ 160738032Speter 160864565Sgshapiro# include <sys/types.h> 160964565Sgshapiro# include <sys/stat.h> 161064565Sgshapiro# include <fcntl.h> 161164565Sgshapiro# include <errno.h> 161264565Sgshapiro# include <stdio.h> 161364565Sgshapiro# include <ctype.h> 161438032Speter 161538032Speterstatic int _gettemp(); 161638032Speter 161738032Spetermkstemp(path) 161838032Speter char *path; 161938032Speter{ 162038032Speter int fd; 162138032Speter 162238032Speter return (_gettemp(path, &fd) ? fd : -1); 162338032Speter} 162438032Speter 162538032Speterstatic 162638032Speter_gettemp(path, doopen) 162738032Speter char *path; 162838032Speter register int *doopen; 162938032Speter{ 163038032Speter extern int errno; 163138032Speter register char *start, *trv; 163238032Speter struct stat sbuf; 163390795Sgshapiro unsigned int pid; 163438032Speter 163538032Speter pid = getpid(); 163638032Speter for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ 163764565Sgshapiro while (*--trv == 'X') 163864565Sgshapiro { 163938032Speter *trv = (pid % 10) + '0'; 164038032Speter pid /= 10; 164138032Speter } 164238032Speter 164338032Speter /* 164438032Speter * check the target directory; if you have six X's and it 164538032Speter * doesn't exist this runs for a *very* long time. 164638032Speter */ 164764565Sgshapiro for (start = trv + 1;; --trv) 164864565Sgshapiro { 164938032Speter if (trv <= path) 165038032Speter break; 165164565Sgshapiro if (*trv == '/') 165264565Sgshapiro { 165338032Speter *trv = '\0'; 165438032Speter if (stat(path, &sbuf) < 0) 165538032Speter return(0); 165664565Sgshapiro if (!S_ISDIR(sbuf.st_mode)) 165764565Sgshapiro { 165838032Speter errno = ENOTDIR; 165938032Speter return(0); 166038032Speter } 166138032Speter *trv = '/'; 166238032Speter break; 166338032Speter } 166438032Speter } 166538032Speter 166664565Sgshapiro for (;;) 166764565Sgshapiro { 166864565Sgshapiro if (doopen) 166964565Sgshapiro { 167066497Sgshapiro if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 167166497Sgshapiro 0600)) >= 0) 167238032Speter return(1); 167338032Speter if (errno != EEXIST) 167438032Speter return(0); 167538032Speter } 167638032Speter else if (stat(path, &sbuf) < 0) 167738032Speter return(errno == ENOENT ? 1 : 0); 167838032Speter 167938032Speter /* tricky little algorithm for backward compatibility */ 168064565Sgshapiro for (trv = start;;) 168164565Sgshapiro { 168238032Speter if (!*trv) 168338032Speter return(0); 168438032Speter if (*trv == 'z') 168538032Speter *trv++ = 'a'; 168664565Sgshapiro else 168764565Sgshapiro { 168838032Speter if (isascii(*trv) && isdigit(*trv)) 168938032Speter *trv = 'a'; 169038032Speter else 169138032Speter ++*trv; 169238032Speter break; 169338032Speter } 169438032Speter } 169538032Speter } 169664565Sgshapiro /* NOTREACHED */ 169738032Speter} 169864565Sgshapiro#endif /* defined(ultrix) || defined(_CRAY) */ 1699