142580Speter/* 2261363Sgshapiro * Copyright (c) 1998-2004 Proofpoint, 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 * 11102533Sgshapiro * $FreeBSD$ 12102533Sgshapiro * 1338032Speter */ 1438032Speter 1590795Sgshapiro#include <sm/gen.h> 1690795Sgshapiro 1790795SgshapiroSM_IDSTR(copyright, 18261363Sgshapiro"@(#) Copyright (c) 1998-2004 Proofpoint, Inc. and its suppliers.\n\ 1964565Sgshapiro All rights reserved.\n\ 2064565Sgshapiro Copyright (c) 1990, 1993, 1994\n\ 2190795Sgshapiro The Regents of the University of California. All rights reserved.\n") 2238032Speter 23266692SgshapiroSM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.257 2013-11-22 20:51:51 ca Exp $") 2438032Speter 2590795Sgshapiro#include <stdlib.h> 2690795Sgshapiro#include <sm/errstring.h> 2790795Sgshapiro#include <sm/io.h> 2890795Sgshapiro#include <sm/limits.h> 2990795Sgshapiro# include <unistd.h> 3090795Sgshapiro# ifdef EX_OK 3190795Sgshapiro# undef EX_OK /* unistd.h may have another use for this */ 3290795Sgshapiro# endif /* EX_OK */ 33120259Sgshapiro# define LOCKFILE_PMODE 0 3490795Sgshapiro#include <sm/mbdb.h> 3590795Sgshapiro#include <sm/sysexits.h> 3690795Sgshapiro 37132946Sgshapiro#ifndef HASHSPOOL 38132946Sgshapiro# define HASHSPOOL 0 39132946Sgshapiro#endif /* ! HASHSPOOL */ 40132946Sgshapiro#ifndef HASHSPOOLMD5 41132946Sgshapiro# define HASHSPOOLMD5 0 42132946Sgshapiro#endif /* ! HASHSPOOLMD5 */ 43132946Sgshapiro 4438032Speter/* 4564565Sgshapiro** This is not intended to work on System V derived systems 4664565Sgshapiro** such as Solaris or HP-UX, since they use a totally different 4790795Sgshapiro** approach to mailboxes (essentially, they have a set-group-ID program 4890795Sgshapiro** rather than set-user-ID, and they rely on the ability to "give away" 4964565Sgshapiro** files to do their work). IT IS NOT A BUG that this doesn't 5064565Sgshapiro** work on such architectures. 5164565Sgshapiro*/ 5238032Speter 5364565Sgshapiro 5490795Sgshapiro#include <stdio.h> 5590795Sgshapiro#include <errno.h> 5690795Sgshapiro#include <fcntl.h> 5790795Sgshapiro#include <sys/types.h> 5890795Sgshapiro#include <sys/stat.h> 5990795Sgshapiro#include <time.h> 6090795Sgshapiro#include <stdlib.h> 6166497Sgshapiro# include <sys/socket.h> 6266497Sgshapiro# include <sys/file.h> 6366497Sgshapiro# include <netinet/in.h> 6466497Sgshapiro# include <arpa/nameser.h> 6566497Sgshapiro# include <netdb.h> 6690795Sgshapiro# include <pwd.h> 6764565Sgshapiro 6890795Sgshapiro#include <sm/string.h> 6990795Sgshapiro#include <syslog.h> 7090795Sgshapiro#include <ctype.h> 7166497Sgshapiro 7290795Sgshapiro#include <sm/conf.h> 7390795Sgshapiro#include <sendmail/pathnames.h> 7464565Sgshapiro 75132946Sgshapiro#if HASHSPOOL 76132946Sgshapiro# define HASH_NONE 0 77132946Sgshapiro# define HASH_USER 1 78132946Sgshapiro# if HASHSPOOLMD5 79132946Sgshapiro# define HASH_MD5 2 80132946Sgshapiro# include <openssl/md5.h> 81132946Sgshapiro# endif /* HASHSPOOLMD5 */ 82132946Sgshapiro#endif /* HASHSPOOL */ 8364565Sgshapiro 84182352Sgshapiro#if _FFR_SPOOL_PATH 85182352Sgshapiro /* 86182352Sgshapiro ** Override path to mail store at run time (using -p). 87182352Sgshapiro ** From: Eugene Grosbein of Svyaz Service JSC 88182352Sgshapiro ** See: http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/114195 89182352Sgshapiro ** NOTE: Update man page before adding this to a release. 90182352Sgshapiro */ 91182352Sgshapiro#endif /* _FFR_SPOOL_PATH */ 92132946Sgshapiro 93182352Sgshapiro 9490795Sgshapiro#ifndef LOCKTO_RM 9590795Sgshapiro# define LOCKTO_RM 300 /* timeout for stale lockfile removal */ 9690795Sgshapiro#endif /* ! LOCKTO_RM */ 9790795Sgshapiro#ifndef LOCKTO_GLOB 9890795Sgshapiro# define LOCKTO_GLOB 400 /* global timeout for lockfile creation */ 9990795Sgshapiro#endif /* ! LOCKTO_GLOB */ 10090795Sgshapiro 10164565Sgshapiro/* define a realloc() which works for NULL pointers */ 10290795Sgshapiro#define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size)) 10338032Speter 10438032Speter/* 10590795Sgshapiro** If you don't have flock, you could try using lockf instead. 10690795Sgshapiro*/ 10738032Speter 10890795Sgshapiro#ifdef LDA_USE_LOCKF 10990795Sgshapiro# define flock(a, b) lockf(a, b, 0) 11090795Sgshapiro# ifdef LOCK_EX 11190795Sgshapiro# undef LOCK_EX 11290795Sgshapiro# endif /* LOCK_EX */ 11390795Sgshapiro# define LOCK_EX F_LOCK 11490795Sgshapiro#endif /* LDA_USE_LOCKF */ 11538032Speter 11690795Sgshapiro#ifndef LOCK_EX 11790795Sgshapiro# include <sys/file.h> 11890795Sgshapiro#endif /* ! LOCK_EX */ 11938032Speter 12038032Speter/* 12164565Sgshapiro** If you don't have setreuid, and you have saved uids, and you have 12264565Sgshapiro** a seteuid() call that doesn't try to emulate using setuid(), then 12390795Sgshapiro** you can try defining LDA_USE_SETEUID. 12464565Sgshapiro*/ 12573191Sgshapiro 12690795Sgshapiro#ifdef LDA_USE_SETEUID 12790795Sgshapiro# define setreuid(r, e) seteuid(e) 12890795Sgshapiro#endif /* LDA_USE_SETEUID */ 12938032Speter 13090795Sgshapiro#ifdef LDA_CONTENTLENGTH 13190795Sgshapiro# define CONTENTLENGTH 1 13290795Sgshapiro#endif /* LDA_CONTENTLENGTH */ 13373191Sgshapiro 13464565Sgshapiro#ifndef INADDRSZ 13564565Sgshapiro# define INADDRSZ 4 /* size of an IPv4 address in bytes */ 13664565Sgshapiro#endif /* ! INADDRSZ */ 13764565Sgshapiro 13890795Sgshapiro#ifdef MAILLOCK 13990795Sgshapiro# include <maillock.h> 14090795Sgshapiro#endif /* MAILLOCK */ 14190795Sgshapiro 14242580Speter#ifndef MAILER_DAEMON 14342580Speter# define MAILER_DAEMON "MAILER-DAEMON" 14464565Sgshapiro#endif /* ! MAILER_DAEMON */ 14542580Speter 14664565Sgshapiro#ifdef CONTENTLENGTH 14764565Sgshapirochar ContentHdr[40] = "Content-Length: "; 14864565Sgshapirooff_t HeaderLength; 14964565Sgshapirooff_t BodyLength; 15064565Sgshapiro#endif /* CONTENTLENGTH */ 15138032Speter 15290795Sgshapirobool EightBitMime = true; /* advertise 8BITMIME in LMTP */ 15373191Sgshapirochar ErrBuf[10240]; /* error buffer */ 15464565Sgshapiroint ExitVal = EX_OK; /* sysexits.h error value. */ 15590795Sgshapirobool nobiff = false; 15690795Sgshapirobool nofsync = false; 15790795Sgshapirobool HoldErrs = false; /* Hold errors in ErrBuf */ 15890795Sgshapirobool LMTPMode = false; 15990795Sgshapirobool BounceQuota = false; /* permanent error when over quota */ 160141862Sgshapirobool CloseMBDB = false; 16190795Sgshapirochar *HomeMailFile = NULL; /* store mail in homedir */ 16238032Speter 163132946Sgshapiro#if HASHSPOOL 164132946Sgshapiroint HashType = HASH_NONE; 165132946Sgshapiroint HashDepth = 0; 166132946Sgshapirobool StripRcptDomain = true; 167132946Sgshapiro#else /* HASHSPOOL */ 168132946Sgshapiro# define StripRcptDomain true 169132946Sgshapiro#endif /* HASHSPOOL */ 170132946Sgshapirochar SpoolPath[MAXPATHLEN]; 171132946Sgshapiro 172141862Sgshapirochar *parseaddr __P((char *, bool)); 173141862Sgshapirochar *process_recipient __P((char *)); 174141862Sgshapirovoid dolmtp __P((void)); 17573191Sgshapirovoid deliver __P((int, char *)); 17664565Sgshapiroint e_to_sys __P((int)); 17764565Sgshapirovoid notifybiff __P((char *)); 17890795Sgshapiroint store __P((char *, bool *)); 17964565Sgshapirovoid usage __P((void)); 18064565Sgshapiroint lockmbox __P((char *)); 18164565Sgshapirovoid unlockmbox __P((void)); 18264565Sgshapirovoid mailerr __P((const char *, const char *, ...)); 18373191Sgshapirovoid flush_error __P((void)); 184132946Sgshapiro#if HASHSPOOL 185132946Sgshapiroconst char *hashname __P((char *)); 186132946Sgshapiro#endif /* HASHSPOOL */ 18764565Sgshapiro 18864565Sgshapiro 189168520Sgshapirostatic void sm_exit __P((int)); 190168520Sgshapiro 191141862Sgshapirostatic void 192141862Sgshapirosm_exit(status) 193141862Sgshapiro int status; 194141862Sgshapiro{ 195141862Sgshapiro if (CloseMBDB) 196141862Sgshapiro { 197141862Sgshapiro sm_mbdb_terminate(); 198141862Sgshapiro CloseMBDB = false; /* not really necessary, but ... */ 199141862Sgshapiro } 200141862Sgshapiro exit(status); 201141862Sgshapiro} 202141862Sgshapiro 20338032Speterint 20438032Spetermain(argc, argv) 20538032Speter int argc; 20638032Speter char *argv[]; 20738032Speter{ 20838032Speter struct passwd *pw; 20964565Sgshapiro int ch, fd; 21038032Speter uid_t uid; 21138032Speter char *from; 21290795Sgshapiro char *mbdbname = "pw"; 21390795Sgshapiro int err; 21438032Speter extern char *optarg; 21538032Speter extern int optind; 21638032Speter 21764565Sgshapiro 21838032Speter /* make sure we have some open file descriptors */ 21938032Speter for (fd = 10; fd < 30; fd++) 22038032Speter (void) close(fd); 22138032Speter 22238032Speter /* use a reasonable umask */ 22338032Speter (void) umask(0077); 22438032Speter 22564565Sgshapiro# ifdef LOG_MAIL 22638032Speter openlog("mail.local", 0, LOG_MAIL); 22764565Sgshapiro# else /* LOG_MAIL */ 22838032Speter openlog("mail.local", 0); 22964565Sgshapiro# endif /* LOG_MAIL */ 23038032Speter 23138032Speter from = NULL; 232141862Sgshapiro 233141862Sgshapiro /* XXX can this be converted to a compile time check? */ 234132946Sgshapiro if (sm_strlcpy(SpoolPath, _PATH_MAILDIR, sizeof(SpoolPath)) >= 235132946Sgshapiro sizeof(SpoolPath)) 236132946Sgshapiro { 237132946Sgshapiro mailerr("421", "Configuration error: _PATH_MAILDIR too large"); 238141862Sgshapiro sm_exit(EX_CONFIG); 239132946Sgshapiro } 240132946Sgshapiro#if HASHSPOOL 241132946Sgshapiro while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lH:p:ns")) != -1) 242132946Sgshapiro#else /* HASHSPOOL */ 243182352Sgshapiro# if _FFR_SPOOL_PATH 244182352Sgshapiro while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lp:s")) != -1) 245182352Sgshapiro# else /* _FFR_SPOOL_PATH */ 24690795Sgshapiro while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1) 247182352Sgshapiro# endif /* _FFR_SPOOL_PATH */ 248132946Sgshapiro#endif /* HASHSPOOL */ 24964565Sgshapiro { 25064565Sgshapiro switch(ch) 25164565Sgshapiro { 25264565Sgshapiro case '7': /* Do not advertise 8BITMIME */ 25390795Sgshapiro EightBitMime = false; 25438084Speter break; 25564565Sgshapiro 25664565Sgshapiro case 'B': 25790825Sgshapiro nobiff = true; 25838032Speter break; 25964565Sgshapiro 26064565Sgshapiro case 'b': /* bounce mail when over quota. */ 26190795Sgshapiro BounceQuota = true; 26264565Sgshapiro break; 26364565Sgshapiro 26464565Sgshapiro case 'd': /* Backward compatible. */ 26564565Sgshapiro break; 26664565Sgshapiro 26790795Sgshapiro case 'D': /* mailbox database type */ 26890795Sgshapiro mbdbname = optarg; 26990795Sgshapiro break; 27090795Sgshapiro 27164565Sgshapiro case 'f': 27264565Sgshapiro case 'r': /* Backward compatible. */ 27364565Sgshapiro if (from != NULL) 27464565Sgshapiro { 27573191Sgshapiro mailerr(NULL, "Multiple -f options"); 27638032Speter usage(); 27738032Speter } 27838032Speter from = optarg; 27938032Speter break; 28064565Sgshapiro 28190795Sgshapiro case 'h': 28290795Sgshapiro if (optarg != NULL || *optarg != '\0') 28390795Sgshapiro HomeMailFile = optarg; 28490795Sgshapiro else 28590795Sgshapiro { 28690795Sgshapiro mailerr(NULL, "-h: missing filename"); 28790795Sgshapiro usage(); 28890795Sgshapiro } 28990795Sgshapiro break; 29090795Sgshapiro 29164565Sgshapiro case 'l': 29290795Sgshapiro LMTPMode = true; 29338032Speter break; 29464565Sgshapiro 29564565Sgshapiro case 's': 29638089Speter nofsync++; 29738084Speter break; 29864565Sgshapiro 299132946Sgshapiro#if HASHSPOOL 300132946Sgshapiro case 'H': 301132946Sgshapiro if (optarg == NULL || *optarg == '\0') 302132946Sgshapiro { 303132946Sgshapiro mailerr(NULL, "-H: missing hashinfo"); 304132946Sgshapiro usage(); 305132946Sgshapiro } 306132946Sgshapiro switch(optarg[0]) 307132946Sgshapiro { 308132946Sgshapiro case 'u': 309132946Sgshapiro HashType = HASH_USER; 310132946Sgshapiro break; 311132946Sgshapiro 312132946Sgshapiro# if HASHSPOOLMD5 313132946Sgshapiro case 'm': 314132946Sgshapiro HashType = HASH_MD5; 315132946Sgshapiro break; 316132946Sgshapiro# endif /* HASHSPOOLMD5 */ 317132946Sgshapiro 318132946Sgshapiro default: 319132946Sgshapiro mailerr(NULL, "-H: unknown hash type"); 320132946Sgshapiro usage(); 321132946Sgshapiro } 322132946Sgshapiro if (optarg[1] == '\0') 323132946Sgshapiro { 324132946Sgshapiro mailerr(NULL, "-H: invalid hash depth"); 325132946Sgshapiro usage(); 326132946Sgshapiro } 327132946Sgshapiro HashDepth = atoi(&optarg[1]); 328132946Sgshapiro if ((HashDepth <= 0) || ((HashDepth * 2) >= MAXPATHLEN)) 329132946Sgshapiro { 330132946Sgshapiro mailerr(NULL, "-H: invalid hash depth"); 331132946Sgshapiro usage(); 332132946Sgshapiro } 333132946Sgshapiro break; 334132946Sgshapiro 335182352Sgshapiro case 'n': 336182352Sgshapiro StripRcptDomain = false; 337182352Sgshapiro break; 338182352Sgshapiro#endif /* HASHSPOOL */ 339182352Sgshapiro 340182352Sgshapiro#if HASHSPOOL || _FFR_SPOOL_PATH 341132946Sgshapiro case 'p': 342132946Sgshapiro if (optarg == NULL || *optarg == '\0') 343132946Sgshapiro { 344132946Sgshapiro mailerr(NULL, "-p: missing spool path"); 345132946Sgshapiro usage(); 346132946Sgshapiro } 347132946Sgshapiro if (sm_strlcpy(SpoolPath, optarg, sizeof(SpoolPath)) >= 348132946Sgshapiro sizeof(SpoolPath)) 349132946Sgshapiro { 350132946Sgshapiro mailerr(NULL, "-p: invalid spool path"); 351132946Sgshapiro usage(); 352132946Sgshapiro } 353132946Sgshapiro break; 354182352Sgshapiro#endif /* HASHSPOOL || _FFR_SPOOL_PATH */ 355132946Sgshapiro 35664565Sgshapiro case '?': 35764565Sgshapiro default: 35838032Speter usage(); 35938032Speter } 36064565Sgshapiro } 36138032Speter argc -= optind; 36238032Speter argv += optind; 36338032Speter 36464565Sgshapiro /* initialize biff structures */ 36564565Sgshapiro if (!nobiff) 36664565Sgshapiro notifybiff(NULL); 36738032Speter 36890795Sgshapiro err = sm_mbdb_initialize(mbdbname); 36990795Sgshapiro if (err != EX_OK) 37090795Sgshapiro { 37190795Sgshapiro char *errcode = "521"; 37290795Sgshapiro 37390795Sgshapiro if (err == EX_TEMPFAIL) 37490795Sgshapiro errcode = "421"; 37590795Sgshapiro 37690795Sgshapiro mailerr(errcode, "Can not open mailbox database %s: %s", 37790795Sgshapiro mbdbname, sm_strexit(err)); 378141862Sgshapiro sm_exit(err); 37990795Sgshapiro } 380141862Sgshapiro CloseMBDB = true; 38190795Sgshapiro 38264565Sgshapiro if (LMTPMode) 38373191Sgshapiro { 38473191Sgshapiro if (argc > 0) 38573191Sgshapiro { 38673191Sgshapiro mailerr("421", "Users should not be specified in command line if LMTP required"); 387141862Sgshapiro sm_exit(EX_TEMPFAIL); 38873191Sgshapiro } 38973191Sgshapiro 39073191Sgshapiro dolmtp(); 39173191Sgshapiro /* NOTREACHED */ 392141862Sgshapiro sm_exit(EX_OK); 39373191Sgshapiro } 39473191Sgshapiro 39573191Sgshapiro /* Non-LMTP from here on out */ 39664565Sgshapiro if (*argv == '\0') 39738032Speter usage(); 39838032Speter 39938032Speter /* 40064565Sgshapiro ** If from not specified, use the name from getlogin() if the 40164565Sgshapiro ** uid matches, otherwise, use the name from the password file 40264565Sgshapiro ** corresponding to the uid. 40364565Sgshapiro */ 40473191Sgshapiro 40538032Speter uid = getuid(); 40664565Sgshapiro if (from == NULL && ((from = getlogin()) == NULL || 40764565Sgshapiro (pw = getpwnam(from)) == NULL || 40864565Sgshapiro pw->pw_uid != uid)) 40964565Sgshapiro from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???"; 41064565Sgshapiro 41138032Speter /* 41264565Sgshapiro ** There is no way to distinguish the error status of one delivery 41364565Sgshapiro ** from the rest of the deliveries. So, if we failed hard on one 41464565Sgshapiro ** or more deliveries, but had no failures on any of the others, we 41564565Sgshapiro ** return a hard failure. If we failed temporarily on one or more 41664565Sgshapiro ** deliveries, we return a temporary failure regardless of the other 41764565Sgshapiro ** failures. This results in the delivery being reattempted later 41864565Sgshapiro ** at the expense of repeated failures and multiple deliveries. 41964565Sgshapiro */ 42073191Sgshapiro 42190795Sgshapiro HoldErrs = true; 42290795Sgshapiro fd = store(from, NULL); 42390795Sgshapiro HoldErrs = false; 42473191Sgshapiro if (fd < 0) 42573191Sgshapiro { 42673191Sgshapiro flush_error(); 427141862Sgshapiro sm_exit(ExitVal); 42873191Sgshapiro } 42973191Sgshapiro for (; *argv != NULL; ++argv) 43073191Sgshapiro deliver(fd, *argv); 431141862Sgshapiro sm_exit(ExitVal); 43264565Sgshapiro /* NOTREACHED */ 43364565Sgshapiro return ExitVal; 43438032Speter} 43538032Speter 43638032Speterchar * 43764565Sgshapiroparseaddr(s, rcpt) 43838032Speter char *s; 43964565Sgshapiro bool rcpt; 44038032Speter{ 44138032Speter char *p; 44264565Sgshapiro int l; 44338032Speter 44438032Speter if (*s++ != '<') 44538032Speter return NULL; 44638032Speter 44738032Speter p = s; 44838032Speter 44938032Speter /* at-domain-list */ 45064565Sgshapiro while (*p == '@') 45164565Sgshapiro { 45238032Speter p++; 45364565Sgshapiro while (*p != ',' && *p != ':' && *p != '\0') 45438032Speter p++; 45564565Sgshapiro if (*p == '\0') 45638032Speter return NULL; 45764565Sgshapiro 45864565Sgshapiro /* Skip over , or : */ 45964565Sgshapiro p++; 46038032Speter } 46138032Speter 46242580Speter s = p; 46342580Speter 46438032Speter /* local-part */ 46564565Sgshapiro while (*p != '\0' && *p != '@' && *p != '>') 46664565Sgshapiro { 46764565Sgshapiro if (*p == '\\') 46864565Sgshapiro { 46964565Sgshapiro if (*++p == '\0') 47064565Sgshapiro return NULL; 47164565Sgshapiro } 47264565Sgshapiro else if (*p == '\"') 47364565Sgshapiro { 47464565Sgshapiro p++; 47564565Sgshapiro while (*p != '\0' && *p != '\"') 47664565Sgshapiro { 47764565Sgshapiro if (*p == '\\') 47864565Sgshapiro { 47964565Sgshapiro if (*++p == '\0') 48064565Sgshapiro return NULL; 48164565Sgshapiro } 48264565Sgshapiro p++; 48338032Speter } 48464565Sgshapiro if (*p == '\0' || *(p + 1) == '\0') 48538032Speter return NULL; 48638032Speter } 48764565Sgshapiro /* +detail ? */ 48864565Sgshapiro if (*p == '+' && rcpt) 48964565Sgshapiro *p = '\0'; 49064565Sgshapiro p++; 49138032Speter } 49238032Speter 49338032Speter /* @domain */ 49464565Sgshapiro if (*p == '@') 49564565Sgshapiro { 49664565Sgshapiro if (rcpt) 49764565Sgshapiro *p++ = '\0'; 49864565Sgshapiro while (*p != '\0' && *p != '>') 49938032Speter p++; 50038032Speter } 50138032Speter 50264565Sgshapiro if (*p != '>') 50338032Speter return NULL; 50464565Sgshapiro else 50564565Sgshapiro *p = '\0'; 50664565Sgshapiro p++; 50764565Sgshapiro 50864565Sgshapiro if (*p != '\0' && *p != ' ') 50938032Speter return NULL; 51064565Sgshapiro 51164565Sgshapiro if (*s == '\0') 51242580Speter s = MAILER_DAEMON; 51338032Speter 51464565Sgshapiro l = strlen(s) + 1; 51590795Sgshapiro if (l < 0) 51690795Sgshapiro return NULL; 51764565Sgshapiro p = malloc(l); 51864565Sgshapiro if (p == NULL) 51964565Sgshapiro { 52073191Sgshapiro mailerr("421 4.3.0", "Memory exhausted"); 521141862Sgshapiro sm_exit(EX_TEMPFAIL); 52238032Speter } 52338032Speter 52490795Sgshapiro (void) sm_strlcpy(p, s, l); 52538032Speter return p; 52638032Speter} 52738032Speter 52838032Speterchar * 52938032Speterprocess_recipient(addr) 53038032Speter char *addr; 53138032Speter{ 53290795Sgshapiro SM_MBDB_T user; 53390795Sgshapiro 53490795Sgshapiro switch (sm_mbdb_lookup(addr, &user)) 53590795Sgshapiro { 53690795Sgshapiro case EX_OK: 53790795Sgshapiro return NULL; 53890795Sgshapiro 53990795Sgshapiro case EX_NOUSER: 54073191Sgshapiro return "550 5.1.1 User unknown"; 54190795Sgshapiro 54290795Sgshapiro case EX_TEMPFAIL: 54390795Sgshapiro return "451 4.3.0 User database failure; retry later"; 54490795Sgshapiro 54590795Sgshapiro default: 54690795Sgshapiro return "550 5.3.0 User database failure"; 54790795Sgshapiro } 54838032Speter} 54938032Speter 55038032Speter#define RCPT_GROW 30 55138032Speter 55238032Spetervoid 55373191Sgshapirodolmtp() 55438032Speter{ 55538032Speter char *return_path = NULL; 55638032Speter char **rcpt_addr = NULL; 55738032Speter int rcpt_num = 0; 55838032Speter int rcpt_alloc = 0; 55990795Sgshapiro bool gotlhlo = false; 56038032Speter char *err; 56138032Speter int msgfd; 56238032Speter char *p; 56338032Speter int i; 56464565Sgshapiro char myhostname[1024]; 56564565Sgshapiro char buf[4096]; 56638032Speter 56773191Sgshapiro memset(myhostname, '\0', sizeof myhostname); 56864565Sgshapiro (void) gethostname(myhostname, sizeof myhostname - 1); 56973191Sgshapiro if (myhostname[0] == '\0') 57090795Sgshapiro sm_strlcpy(myhostname, "localhost", sizeof myhostname); 57138032Speter 57238032Speter printf("220 %s LMTP ready\r\n", myhostname); 57364565Sgshapiro for (;;) 57464565Sgshapiro { 57564565Sgshapiro (void) fflush(stdout); 57664565Sgshapiro if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) 577141862Sgshapiro sm_exit(EX_OK); 57838032Speter p = buf + strlen(buf) - 1; 57938032Speter if (p >= buf && *p == '\n') 58038032Speter *p-- = '\0'; 58138032Speter if (p >= buf && *p == '\r') 58238032Speter *p-- = '\0'; 58338032Speter 58464565Sgshapiro switch (buf[0]) 58564565Sgshapiro { 58664565Sgshapiro case 'd': 58764565Sgshapiro case 'D': 58890795Sgshapiro if (sm_strcasecmp(buf, "data") == 0) 58964565Sgshapiro { 59090795Sgshapiro bool inbody = false; 59173191Sgshapiro 59264565Sgshapiro if (rcpt_num == 0) 59364565Sgshapiro { 59473191Sgshapiro mailerr("503 5.5.1", "No recipients"); 59538032Speter continue; 59638032Speter } 59790795Sgshapiro HoldErrs = true; 59890795Sgshapiro msgfd = store(return_path, &inbody); 59990795Sgshapiro HoldErrs = false; 60073191Sgshapiro if (msgfd < 0 && !inbody) 60173191Sgshapiro { 60273191Sgshapiro flush_error(); 60338032Speter continue; 60473191Sgshapiro } 60538032Speter 60664565Sgshapiro for (i = 0; i < rcpt_num; i++) 60764565Sgshapiro { 60873191Sgshapiro if (msgfd < 0) 60973191Sgshapiro { 61073191Sgshapiro /* print error for rcpt */ 61173191Sgshapiro flush_error(); 61273191Sgshapiro continue; 61373191Sgshapiro } 61438032Speter p = strchr(rcpt_addr[i], '+'); 61538032Speter if (p != NULL) 61673191Sgshapiro *p = '\0'; 61773191Sgshapiro deliver(msgfd, rcpt_addr[i]); 61838032Speter } 61973191Sgshapiro if (msgfd >= 0) 62073191Sgshapiro (void) close(msgfd); 62138032Speter goto rset; 62238032Speter } 62338032Speter goto syntaxerr; 62464565Sgshapiro /* NOTREACHED */ 62564565Sgshapiro break; 62638032Speter 62764565Sgshapiro case 'l': 62864565Sgshapiro case 'L': 62990795Sgshapiro if (sm_strncasecmp(buf, "lhlo ", 5) == 0) 63064565Sgshapiro { 63164565Sgshapiro /* check for duplicate per RFC 1651 4.2 */ 63264565Sgshapiro if (gotlhlo) 63364565Sgshapiro { 63473191Sgshapiro mailerr("503", "%s Duplicate LHLO", 63564565Sgshapiro myhostname); 63664565Sgshapiro continue; 63764565Sgshapiro } 63890795Sgshapiro gotlhlo = true; 63964565Sgshapiro printf("250-%s\r\n", myhostname); 64064565Sgshapiro if (EightBitMime) 64164565Sgshapiro printf("250-8BITMIME\r\n"); 64264565Sgshapiro printf("250-ENHANCEDSTATUSCODES\r\n"); 64364565Sgshapiro printf("250 PIPELINING\r\n"); 64438032Speter continue; 64538032Speter } 64638032Speter goto syntaxerr; 64764565Sgshapiro /* NOTREACHED */ 64864565Sgshapiro break; 64938032Speter 65064565Sgshapiro case 'm': 65164565Sgshapiro case 'M': 65290795Sgshapiro if (sm_strncasecmp(buf, "mail ", 5) == 0) 65364565Sgshapiro { 65464565Sgshapiro if (return_path != NULL) 65564565Sgshapiro { 65673191Sgshapiro mailerr("503 5.5.1", 65773191Sgshapiro "Nested MAIL command"); 65838032Speter continue; 65938032Speter } 66098125Sgshapiro if (sm_strncasecmp(buf + 5, "from:", 5) != 0 || 66164565Sgshapiro ((return_path = parseaddr(buf + 10, 66290795Sgshapiro false)) == NULL)) 66364565Sgshapiro { 66473191Sgshapiro mailerr("501 5.5.4", 66573191Sgshapiro "Syntax error in parameters"); 66638032Speter continue; 66738032Speter } 66873191Sgshapiro printf("250 2.5.0 Ok\r\n"); 66938032Speter continue; 67038032Speter } 67138032Speter goto syntaxerr; 67264565Sgshapiro /* NOTREACHED */ 67364565Sgshapiro break; 67438032Speter 67564565Sgshapiro case 'n': 67664565Sgshapiro case 'N': 67790795Sgshapiro if (sm_strcasecmp(buf, "noop") == 0) 67864565Sgshapiro { 67973191Sgshapiro printf("250 2.0.0 Ok\r\n"); 68038032Speter continue; 68138032Speter } 68238032Speter goto syntaxerr; 68364565Sgshapiro /* NOTREACHED */ 68464565Sgshapiro break; 68538032Speter 68664565Sgshapiro case 'q': 68764565Sgshapiro case 'Q': 68890795Sgshapiro if (sm_strcasecmp(buf, "quit") == 0) 68964565Sgshapiro { 69073191Sgshapiro printf("221 2.0.0 Bye\r\n"); 691141862Sgshapiro sm_exit(EX_OK); 69238032Speter } 69338032Speter goto syntaxerr; 69464565Sgshapiro /* NOTREACHED */ 69564565Sgshapiro break; 69638032Speter 69764565Sgshapiro case 'r': 69864565Sgshapiro case 'R': 69990795Sgshapiro if (sm_strncasecmp(buf, "rcpt ", 5) == 0) 70064565Sgshapiro { 70164565Sgshapiro if (return_path == NULL) 70264565Sgshapiro { 70373191Sgshapiro mailerr("503 5.5.1", 70473191Sgshapiro "Need MAIL command"); 70538032Speter continue; 70638032Speter } 70764565Sgshapiro if (rcpt_num >= rcpt_alloc) 70864565Sgshapiro { 70938032Speter rcpt_alloc += RCPT_GROW; 71038032Speter rcpt_addr = (char **) 71166497Sgshapiro REALLOC((char *) rcpt_addr, 71264565Sgshapiro rcpt_alloc * 71364565Sgshapiro sizeof(char **)); 71464565Sgshapiro if (rcpt_addr == NULL) 71564565Sgshapiro { 71673191Sgshapiro mailerr("421 4.3.0", 71773191Sgshapiro "Memory exhausted"); 718141862Sgshapiro sm_exit(EX_TEMPFAIL); 71938032Speter } 72038032Speter } 72190795Sgshapiro if (sm_strncasecmp(buf + 5, "to:", 3) != 0 || 72264565Sgshapiro ((rcpt_addr[rcpt_num] = parseaddr(buf + 8, 723132946Sgshapiro StripRcptDomain)) == NULL)) 72464565Sgshapiro { 72573191Sgshapiro mailerr("501 5.5.4", 72673191Sgshapiro "Syntax error in parameters"); 72738032Speter continue; 72838032Speter } 72973191Sgshapiro err = process_recipient(rcpt_addr[rcpt_num]); 73073191Sgshapiro if (err != NULL) 73164565Sgshapiro { 73273191Sgshapiro mailerr(NULL, "%s", err); 73338032Speter continue; 73438032Speter } 73538032Speter rcpt_num++; 73673191Sgshapiro printf("250 2.1.5 Ok\r\n"); 73738032Speter continue; 73838032Speter } 73990795Sgshapiro else if (sm_strcasecmp(buf, "rset") == 0) 74064565Sgshapiro { 74173191Sgshapiro printf("250 2.0.0 Ok\r\n"); 74238032Speter 74364565Sgshapirorset: 74471348Sgshapiro while (rcpt_num > 0) 74538032Speter free(rcpt_addr[--rcpt_num]); 74638032Speter if (return_path != NULL) 74738032Speter free(return_path); 74838032Speter return_path = NULL; 74938032Speter continue; 75038032Speter } 75138032Speter goto syntaxerr; 75264565Sgshapiro /* NOTREACHED */ 75364565Sgshapiro break; 75438032Speter 75564565Sgshapiro case 'v': 75664565Sgshapiro case 'V': 75790795Sgshapiro if (sm_strncasecmp(buf, "vrfy ", 5) == 0) 75864565Sgshapiro { 75973191Sgshapiro printf("252 2.3.3 Try RCPT to attempt delivery\r\n"); 76038032Speter continue; 76138032Speter } 76238032Speter goto syntaxerr; 76364565Sgshapiro /* NOTREACHED */ 76464565Sgshapiro break; 76538032Speter 76664565Sgshapiro default: 76738032Speter syntaxerr: 76873191Sgshapiro mailerr("500 5.5.2", "Syntax error"); 76938032Speter continue; 77064565Sgshapiro /* NOTREACHED */ 77164565Sgshapiro break; 77238032Speter } 77338032Speter } 77438032Speter} 77538032Speter 77638032Speterint 77790795Sgshapirostore(from, inbody) 77838032Speter char *from; 77973191Sgshapiro bool *inbody; 78038032Speter{ 78142580Speter FILE *fp = NULL; 78238032Speter time_t tval; 78390795Sgshapiro bool eline; /* previous line was empty */ 78490795Sgshapiro bool fullline = true; /* current line is terminated */ 78564565Sgshapiro bool prevfl; /* previous line was terminated */ 78638032Speter char line[2048]; 78764565Sgshapiro int fd; 78838032Speter char tmpbuf[sizeof _PATH_LOCTMP + 1]; 78938032Speter 79073191Sgshapiro if (inbody != NULL) 79190795Sgshapiro *inbody = false; 79273191Sgshapiro 79364565Sgshapiro (void) umask(0077); 79490795Sgshapiro (void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf); 79573191Sgshapiro if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL) 79664565Sgshapiro { 797110563Sgshapiro if (fd >= 0) 798110563Sgshapiro (void) close(fd); 79973191Sgshapiro mailerr("451 4.3.0", "Unable to open temporary file"); 80073191Sgshapiro return -1; 80138032Speter } 80264565Sgshapiro (void) unlink(tmpbuf); 80338032Speter 80464565Sgshapiro if (LMTPMode) 80564565Sgshapiro { 80673191Sgshapiro printf("354 Go ahead\r\n"); 80764565Sgshapiro (void) fflush(stdout); 80838032Speter } 80973191Sgshapiro if (inbody != NULL) 81090795Sgshapiro *inbody = true; 81138032Speter 81264565Sgshapiro (void) time(&tval); 81364565Sgshapiro (void) fprintf(fp, "From %s %s", from, ctime(&tval)); 81438032Speter 81564565Sgshapiro#ifdef CONTENTLENGTH 81664565Sgshapiro HeaderLength = 0; 81764565Sgshapiro BodyLength = -1; 81864565Sgshapiro#endif /* CONTENTLENGTH */ 81964565Sgshapiro 82038032Speter line[0] = '\0'; 82190795Sgshapiro eline = true; 82273191Sgshapiro while (fgets(line, sizeof(line), stdin) != (char *) NULL) 82364565Sgshapiro { 82464565Sgshapiro size_t line_len = 0; 82564565Sgshapiro int peek; 82642580Speter 82764565Sgshapiro prevfl = fullline; /* preserve state of previous line */ 82864565Sgshapiro while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 82964565Sgshapiro line_len++; 83064565Sgshapiro line_len++; 83142580Speter 83264565Sgshapiro /* Check for dot-stuffing */ 83373191Sgshapiro if (prevfl && LMTPMode && line[0] == '.') 83464565Sgshapiro { 83564565Sgshapiro if (line[1] == '\n' || 83664565Sgshapiro (line[1] == '\r' && line[2] == '\n')) 83738032Speter goto lmtpdot; 83864565Sgshapiro memcpy(line, line + 1, line_len); 83964565Sgshapiro line_len--; 84038032Speter } 84164565Sgshapiro 84264565Sgshapiro /* Check to see if we have the full line from fgets() */ 84390795Sgshapiro fullline = false; 84464565Sgshapiro if (line_len > 0) 84564565Sgshapiro { 84664565Sgshapiro if (line[line_len - 1] == '\n') 84764565Sgshapiro { 84864565Sgshapiro if (line_len >= 2 && 84964565Sgshapiro line[line_len - 2] == '\r') 85064565Sgshapiro { 85164565Sgshapiro line[line_len - 2] = '\n'; 85264565Sgshapiro line[line_len - 1] = '\0'; 85364565Sgshapiro line_len--; 85464565Sgshapiro } 85590795Sgshapiro fullline = true; 85664565Sgshapiro } 85764565Sgshapiro else if (line[line_len - 1] == '\r') 85864565Sgshapiro { 85964565Sgshapiro /* Did we just miss the CRLF? */ 86064565Sgshapiro peek = fgetc(stdin); 86164565Sgshapiro if (peek == '\n') 86264565Sgshapiro { 86364565Sgshapiro line[line_len - 1] = '\n'; 86490795Sgshapiro fullline = true; 86564565Sgshapiro } 86664565Sgshapiro else 86764565Sgshapiro (void) ungetc(peek, stdin); 86864565Sgshapiro } 86964565Sgshapiro } 87064565Sgshapiro else 87190795Sgshapiro fullline = true; 87264565Sgshapiro 87364565Sgshapiro#ifdef CONTENTLENGTH 87464565Sgshapiro if (prevfl && line[0] == '\n' && HeaderLength == 0) 87564565Sgshapiro { 87690795Sgshapiro eline = false; 87773191Sgshapiro if (fp != NULL) 87873191Sgshapiro HeaderLength = ftell(fp); 87964565Sgshapiro if (HeaderLength <= 0) 88064565Sgshapiro { 88164565Sgshapiro /* 88264565Sgshapiro ** shouldn't happen, unless ftell() is 88364565Sgshapiro ** badly broken 88464565Sgshapiro */ 88564565Sgshapiro 88664565Sgshapiro HeaderLength = -1; 88764565Sgshapiro } 88864565Sgshapiro } 88964565Sgshapiro#else /* CONTENTLENGTH */ 89064565Sgshapiro if (prevfl && line[0] == '\n') 89190795Sgshapiro eline = true; 89264565Sgshapiro#endif /* CONTENTLENGTH */ 89364565Sgshapiro else 89464565Sgshapiro { 89538032Speter if (eline && line[0] == 'F' && 89673191Sgshapiro fp != NULL && 89738032Speter !memcmp(line, "From ", 5)) 89866497Sgshapiro (void) putc('>', fp); 89990795Sgshapiro eline = false; 90064565Sgshapiro#ifdef CONTENTLENGTH 90164565Sgshapiro /* discard existing "Content-Length:" headers */ 90264565Sgshapiro if (prevfl && HeaderLength == 0 && 90364565Sgshapiro (line[0] == 'C' || line[0] == 'c') && 90490795Sgshapiro sm_strncasecmp(line, ContentHdr, 15) == 0) 90564565Sgshapiro { 90664565Sgshapiro /* 90764565Sgshapiro ** be paranoid: clear the line 90864565Sgshapiro ** so no "wrong matches" may occur later 90964565Sgshapiro */ 91064565Sgshapiro line[0] = '\0'; 91164565Sgshapiro continue; 91264565Sgshapiro } 91364565Sgshapiro#endif /* CONTENTLENGTH */ 91464565Sgshapiro 91538032Speter } 91673191Sgshapiro if (fp != NULL) 91764565Sgshapiro { 91873191Sgshapiro (void) fwrite(line, sizeof(char), line_len, fp); 91973191Sgshapiro if (ferror(fp)) 92064565Sgshapiro { 92142580Speter mailerr("451 4.3.0", 92273191Sgshapiro "Temporary file write error"); 92364565Sgshapiro (void) fclose(fp); 92473191Sgshapiro fp = NULL; 92573191Sgshapiro continue; 92638032Speter } 92738032Speter } 92838032Speter } 92938032Speter 93073191Sgshapiro /* check if an error occurred */ 93173191Sgshapiro if (fp == NULL) 93273191Sgshapiro return -1; 93373191Sgshapiro 93473191Sgshapiro if (LMTPMode) 93564565Sgshapiro { 93638032Speter /* Got a premature EOF -- toss message and exit */ 937141862Sgshapiro sm_exit(EX_OK); 93838032Speter } 93938032Speter 94038032Speter /* If message not newline terminated, need an extra. */ 94173191Sgshapiro if (fp != NULL && strchr(line, '\n') == NULL) 94264565Sgshapiro (void) putc('\n', fp); 94338032Speter 94438032Speter lmtpdot: 94538032Speter 94664565Sgshapiro#ifdef CONTENTLENGTH 94773191Sgshapiro if (fp != NULL) 94873191Sgshapiro BodyLength = ftell(fp); 94964565Sgshapiro if (HeaderLength == 0 && BodyLength > 0) /* empty body */ 95064565Sgshapiro { 95164565Sgshapiro HeaderLength = BodyLength; 95264565Sgshapiro BodyLength = 0; 95364565Sgshapiro } 95464565Sgshapiro else 95564565Sgshapiro BodyLength = BodyLength - HeaderLength - 1 ; 95664565Sgshapiro 95764565Sgshapiro if (HeaderLength > 0 && BodyLength >= 0) 95864565Sgshapiro { 95990795Sgshapiro (void) sm_snprintf(line, sizeof line, "%lld\n", 96090795Sgshapiro (LONGLONG_T) BodyLength); 96190795Sgshapiro (void) sm_strlcpy(&ContentHdr[16], line, 96290795Sgshapiro sizeof(ContentHdr) - 16); 96364565Sgshapiro } 96464565Sgshapiro else 96564565Sgshapiro BodyLength = -1; /* Something is wrong here */ 96664565Sgshapiro#endif /* CONTENTLENGTH */ 96764565Sgshapiro 96838032Speter /* Output a newline; note, empty messages are allowed. */ 96973191Sgshapiro if (fp != NULL) 97073191Sgshapiro (void) putc('\n', fp); 97138032Speter 97273191Sgshapiro if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0) 97364565Sgshapiro { 97473191Sgshapiro mailerr("451 4.3.0", "Temporary file write error"); 97573191Sgshapiro if (fp != NULL) 97664565Sgshapiro (void) fclose(fp); 97773191Sgshapiro return -1; 97838032Speter } 97964565Sgshapiro return fd; 98038032Speter} 98138032Speter 98238032Spetervoid 98373191Sgshapirodeliver(fd, name) 98438032Speter int fd; 98538032Speter char *name; 98638032Speter{ 98764565Sgshapiro struct stat fsb; 98864565Sgshapiro struct stat sb; 98964565Sgshapiro char path[MAXPATHLEN]; 99071348Sgshapiro int mbfd = -1, nr = 0, nw, off; 99190795Sgshapiro int exitval; 99238032Speter char *p; 99373191Sgshapiro char *errcode; 994120259Sgshapiro off_t curoff, cursize; 99564565Sgshapiro#ifdef CONTENTLENGTH 99664565Sgshapiro off_t headerbytes; 99764565Sgshapiro int readamount; 99864565Sgshapiro#endif /* CONTENTLENGTH */ 99998125Sgshapiro char biffmsg[100], buf[8 * 1024]; 100090795Sgshapiro SM_MBDB_T user; 100138032Speter 100238032Speter /* 100364565Sgshapiro ** Disallow delivery to unknown names -- special mailboxes can be 100464565Sgshapiro ** handled in the sendmail aliases file. 100564565Sgshapiro */ 100673191Sgshapiro 100790795Sgshapiro exitval = sm_mbdb_lookup(name, &user); 100890795Sgshapiro switch (exitval) 100964565Sgshapiro { 101090795Sgshapiro case EX_OK: 101190795Sgshapiro break; 101290795Sgshapiro 101390795Sgshapiro case EX_NOUSER: 101490795Sgshapiro exitval = EX_UNAVAILABLE; 101590795Sgshapiro mailerr("550 5.1.1", "%s: User unknown", name); 101690795Sgshapiro break; 101790795Sgshapiro 101890795Sgshapiro case EX_TEMPFAIL: 101990795Sgshapiro mailerr("451 4.3.0", "%s: User database failure; retry later", 102090795Sgshapiro name); 102190795Sgshapiro break; 102290795Sgshapiro 102390795Sgshapiro default: 102490795Sgshapiro exitval = EX_UNAVAILABLE; 102590795Sgshapiro mailerr("550 5.3.0", "%s: User database failure", name); 102690795Sgshapiro break; 102790795Sgshapiro } 102890795Sgshapiro 102990795Sgshapiro if (exitval != EX_OK) 103090795Sgshapiro { 103190795Sgshapiro if (ExitVal != EX_TEMPFAIL) 103290795Sgshapiro ExitVal = exitval; 103338032Speter return; 103438032Speter } 103590795Sgshapiro 103638032Speter endpwent(); 103738032Speter 103838032Speter /* 103964565Sgshapiro ** Keep name reasonably short to avoid buffer overruns. 104064565Sgshapiro ** This isn't necessary on BSD because of the proper 104164565Sgshapiro ** definition of snprintf(), but it can cause problems 104264565Sgshapiro ** on other systems. 104364565Sgshapiro ** Also, clear out any bogus characters. 104464565Sgshapiro */ 104538032Speter 1046132946Sgshapiro#if !HASHSPOOL 104738032Speter if (strlen(name) > 40) 104838032Speter name[40] = '\0'; 104938032Speter for (p = name; *p != '\0'; p++) 105038032Speter { 105138032Speter if (!isascii(*p)) 105238032Speter *p &= 0x7f; 105338032Speter else if (!isprint(*p)) 105438032Speter *p = '.'; 105538032Speter } 1056132946Sgshapiro#endif /* !HASHSPOOL */ 105738032Speter 105838032Speter 105990795Sgshapiro if (HomeMailFile == NULL) 106090795Sgshapiro { 1061132946Sgshapiro if (sm_strlcpyn(path, sizeof(path), 1062132946Sgshapiro#if HASHSPOOL 1063132946Sgshapiro 4, 1064132946Sgshapiro#else /* HASHSPOOL */ 1065132946Sgshapiro 3, 1066132946Sgshapiro#endif /* HASHSPOOL */ 1067132946Sgshapiro SpoolPath, "/", 1068132946Sgshapiro#if HASHSPOOL 1069132946Sgshapiro hashname(name), 1070132946Sgshapiro#endif /* HASHSPOOL */ 1071132946Sgshapiro name) >= sizeof(path)) 107290795Sgshapiro { 107390795Sgshapiro exitval = EX_UNAVAILABLE; 107490795Sgshapiro mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 107590795Sgshapiro return; 107690795Sgshapiro } 107790795Sgshapiro } 107890795Sgshapiro else if (*user.mbdb_homedir == '\0') 107990795Sgshapiro { 108090795Sgshapiro exitval = EX_UNAVAILABLE; 108190795Sgshapiro mailerr("550 5.1.1", "%s: User missing home directory", name); 108290795Sgshapiro return; 108390795Sgshapiro } 108490795Sgshapiro else if (sm_snprintf(path, sizeof(path), "%s/%s", 108590795Sgshapiro user.mbdb_homedir, HomeMailFile) >= sizeof(path)) 108690795Sgshapiro { 108790795Sgshapiro exitval = EX_UNAVAILABLE; 108890795Sgshapiro mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 108990795Sgshapiro return; 109090795Sgshapiro } 109164565Sgshapiro 109264565Sgshapiro 109338032Speter /* 109464565Sgshapiro ** If the mailbox is linked or a symlink, fail. There's an obvious 109564565Sgshapiro ** race here, that the file was replaced with a symbolic link after 109664565Sgshapiro ** the lstat returned, but before the open. We attempt to detect 109764565Sgshapiro ** this by comparing the original stat information and information 109864565Sgshapiro ** returned by an fstat of the file descriptor returned by the open. 109964565Sgshapiro ** 110064565Sgshapiro ** NB: this is a symptom of a larger problem, that the mail spooling 110164565Sgshapiro ** directory is writeable by the wrong users. If that directory is 110264565Sgshapiro ** writeable, system security is compromised for other reasons, and 110364565Sgshapiro ** it cannot be fixed here. 110464565Sgshapiro ** 110564565Sgshapiro ** If we created the mailbox, set the owner/group. If that fails, 110664565Sgshapiro ** just return. Another process may have already opened it, so we 110764565Sgshapiro ** can't unlink it. Historically, binmail set the owner/group at 110864565Sgshapiro ** each mail delivery. We no longer do this, assuming that if the 110964565Sgshapiro ** ownership or permissions were changed there was a reason. 111064565Sgshapiro ** 111164565Sgshapiro ** XXX 111264565Sgshapiro ** open(2) should support flock'ing the file. 111364565Sgshapiro */ 111464565Sgshapiro 111538032Spetertryagain: 111664565Sgshapiro#ifdef MAILLOCK 111764565Sgshapiro p = name; 111864565Sgshapiro#else /* MAILLOCK */ 111964565Sgshapiro p = path; 112064565Sgshapiro#endif /* MAILLOCK */ 112164565Sgshapiro if ((off = lockmbox(p)) != 0) 112264565Sgshapiro { 112364565Sgshapiro if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL) 112464565Sgshapiro { 112564565Sgshapiro ExitVal = EX_TEMPFAIL; 112673191Sgshapiro errcode = "451 4.3.0"; 112764565Sgshapiro } 112864565Sgshapiro else 112973191Sgshapiro errcode = "551 5.3.0"; 113073191Sgshapiro 113173191Sgshapiro mailerr(errcode, "lockmailbox %s failed; error code %d %s", 113290795Sgshapiro p, off, errno > 0 ? sm_errstring(errno) : ""); 113364565Sgshapiro return; 113464565Sgshapiro } 113564565Sgshapiro 113664565Sgshapiro if (lstat(path, &sb) < 0) 113764565Sgshapiro { 113864565Sgshapiro int save_errno; 113964565Sgshapiro int mode = S_IRUSR|S_IWUSR; 114090795Sgshapiro gid_t gid = user.mbdb_gid; 114164565Sgshapiro 114264565Sgshapiro#ifdef MAILGID 114364565Sgshapiro (void) umask(0007); 114464565Sgshapiro gid = MAILGID; 114564565Sgshapiro mode |= S_IRGRP|S_IWGRP; 114664565Sgshapiro#endif /* MAILGID */ 114764565Sgshapiro 1148120259Sgshapiro mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, 114966497Sgshapiro mode); 115064565Sgshapiro save_errno = errno; 115164565Sgshapiro 115238032Speter if (lstat(path, &sb) < 0) 115338032Speter { 115464565Sgshapiro ExitVal = EX_CANTCREAT; 115542580Speter mailerr("550 5.2.0", 115642580Speter "%s: lstat: file changed after open", path); 115738032Speter goto err1; 115838032Speter } 115973191Sgshapiro if (mbfd < 0) 116064565Sgshapiro { 116164565Sgshapiro if (save_errno == EEXIST) 116238032Speter goto tryagain; 116366497Sgshapiro 116466497Sgshapiro /* open failed, don't try again */ 116566497Sgshapiro mailerr("450 4.2.0", "%s: %s", path, 116690795Sgshapiro sm_errstring(save_errno)); 116766497Sgshapiro goto err0; 116864565Sgshapiro } 116990795Sgshapiro else if (fchown(mbfd, user.mbdb_uid, gid) < 0) 117064565Sgshapiro { 117138032Speter mailerr("451 4.3.0", "chown %u.%u: %s", 117290795Sgshapiro user.mbdb_uid, gid, name); 117338032Speter goto err1; 117438032Speter } 117566497Sgshapiro else 117666497Sgshapiro { 117766497Sgshapiro /* 117866497Sgshapiro ** open() was successful, now close it so can 117966497Sgshapiro ** be opened as the right owner again. 118066497Sgshapiro ** Paranoia: reset mbdf since the file descriptor 118166497Sgshapiro ** is no longer valid; better safe than sorry. 118266497Sgshapiro */ 118366497Sgshapiro 118490795Sgshapiro sb.st_uid = user.mbdb_uid; 118566497Sgshapiro (void) close(mbfd); 118666497Sgshapiro mbfd = -1; 118766497Sgshapiro } 118864565Sgshapiro } 1189111826Sgshapiro else if (sb.st_nlink != 1) 119064565Sgshapiro { 1191111826Sgshapiro mailerr("550 5.2.0", "%s: too many links", path); 1192111826Sgshapiro goto err0; 1193111826Sgshapiro } 1194111826Sgshapiro else if (!S_ISREG(sb.st_mode)) 1195111826Sgshapiro { 119638032Speter mailerr("550 5.2.0", "%s: irregular file", path); 119738032Speter goto err0; 119864565Sgshapiro } 119990795Sgshapiro else if (sb.st_uid != user.mbdb_uid) 120064565Sgshapiro { 120164565Sgshapiro ExitVal = EX_CANTCREAT; 120238032Speter mailerr("550 5.2.0", "%s: wrong ownership (%d)", 120390795Sgshapiro path, (int) sb.st_uid); 120438032Speter goto err0; 120564565Sgshapiro } 120638032Speter 120766497Sgshapiro /* change UID for quota checks */ 120890795Sgshapiro if (setreuid(0, user.mbdb_uid) < 0) 120964565Sgshapiro { 121066497Sgshapiro mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", 121190795Sgshapiro (int) user.mbdb_uid, sm_errstring(errno), 121290795Sgshapiro (int) getuid(), (int) geteuid()); 121366497Sgshapiro goto err1; 121466497Sgshapiro } 121566497Sgshapiro#ifdef DEBUG 121690795Sgshapiro fprintf(stderr, "new euid = %d\n", (int) geteuid()); 121766497Sgshapiro#endif /* DEBUG */ 1218120259Sgshapiro mbfd = open(path, O_APPEND|O_WRONLY, 0); 121966497Sgshapiro if (mbfd < 0) 122066497Sgshapiro { 122190795Sgshapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 122238032Speter goto err0; 122364565Sgshapiro } 122464565Sgshapiro else if (fstat(mbfd, &fsb) < 0 || 122564565Sgshapiro fsb.st_nlink != 1 || 122664565Sgshapiro sb.st_nlink != 1 || 122764565Sgshapiro !S_ISREG(fsb.st_mode) || 122864565Sgshapiro sb.st_dev != fsb.st_dev || 122964565Sgshapiro sb.st_ino != fsb.st_ino || 123066497Sgshapiro# if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 123164565Sgshapiro sb.st_gen != fsb.st_gen || 123266497Sgshapiro# endif /* HAS_ST_GEN && 0 */ 123364565Sgshapiro sb.st_uid != fsb.st_uid) 123464565Sgshapiro { 123564565Sgshapiro ExitVal = EX_TEMPFAIL; 123642580Speter mailerr("550 5.2.0", "%s: fstat: file changed after open", 123742580Speter path); 123838032Speter goto err1; 123938032Speter } 124038032Speter 124190795Sgshapiro#if 0 124290795Sgshapiro /* 124390795Sgshapiro ** This code could be reused if we decide to add a 124490795Sgshapiro ** per-user quota field to the sm_mbdb interface. 124590795Sgshapiro */ 124664565Sgshapiro 124790795Sgshapiro /* 124890795Sgshapiro ** Fail if the user has a quota specified, and delivery of this 124990795Sgshapiro ** message would exceed that quota. We bounce such failures using 125090795Sgshapiro ** EX_UNAVAILABLE, unless there were internal problems, since 125190795Sgshapiro ** storing immense messages for later retries can cause queueing 125290795Sgshapiro ** issues. 125390795Sgshapiro */ 125490795Sgshapiro 125590795Sgshapiro if (ui.quota > 0) 125690795Sgshapiro { 125790795Sgshapiro struct stat dsb; 125890795Sgshapiro 125990795Sgshapiro if (fstat(fd, &dsb) < 0) 126090795Sgshapiro { 126190795Sgshapiro ExitVal = EX_TEMPFAIL; 126290795Sgshapiro mailerr("451 4.3.0", 126390795Sgshapiro "%s: fstat: can't stat temporary storage: %s", 126490795Sgshapiro ui.mailspool, sm_errstring(errno)); 126590795Sgshapiro goto err1; 126690795Sgshapiro } 126790795Sgshapiro 126890795Sgshapiro if (dsb.st_size + sb.st_size + 1 > ui.quota) 126990795Sgshapiro { 127090795Sgshapiro ExitVal = EX_UNAVAILABLE; 127190795Sgshapiro mailerr("551 5.2.2", 127290795Sgshapiro "%s: Mailbox full or quota exceeded", 127390795Sgshapiro ui.mailspool); 127490795Sgshapiro goto err1; 127590795Sgshapiro } 127690795Sgshapiro } 127790795Sgshapiro#endif /* 0 */ 127890795Sgshapiro 127938032Speter /* Wait until we can get a lock on the file. */ 128064565Sgshapiro if (flock(mbfd, LOCK_EX) < 0) 128164565Sgshapiro { 128290795Sgshapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 128338032Speter goto err1; 128438032Speter } 128538032Speter 1286112813Sgshapiro /* Get the starting offset of the new message */ 1287110553Sgshapiro curoff = lseek(mbfd, (off_t) 0, SEEK_END); 1288110553Sgshapiro 128964565Sgshapiro if (!nobiff) 129064565Sgshapiro { 129190795Sgshapiro (void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n", 129290795Sgshapiro name, (LONGLONG_T) curoff); 129338084Speter } 129438032Speter 129538032Speter /* Copy the message into the file. */ 129673191Sgshapiro if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1) 129764565Sgshapiro { 129873191Sgshapiro mailerr("450 4.2.0", "Temporary file: %s", 129990795Sgshapiro sm_errstring(errno)); 130038032Speter goto err1; 130138032Speter } 130238032Speter#ifdef DEBUG 130390795Sgshapiro fprintf(stderr, "before writing: euid = %d\n", (int) geteuid()); 130464565Sgshapiro#endif /* DEBUG */ 130564565Sgshapiro#ifdef CONTENTLENGTH 130664565Sgshapiro headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; 130764565Sgshapiro for (;;) 130864565Sgshapiro { 130964565Sgshapiro if (headerbytes == 0) 131064565Sgshapiro { 131190795Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr); 131264565Sgshapiro nr = strlen(buf); 131364565Sgshapiro headerbytes = -1; 131464565Sgshapiro readamount = 0; 131564565Sgshapiro } 131664565Sgshapiro else if (headerbytes > sizeof(buf) || headerbytes < 0) 131764565Sgshapiro readamount = sizeof(buf); 131864565Sgshapiro else 131964565Sgshapiro readamount = headerbytes; 132064565Sgshapiro if (readamount != 0) 132164565Sgshapiro nr = read(fd, buf, readamount); 132264565Sgshapiro if (nr <= 0) 132364565Sgshapiro break; 132464565Sgshapiro if (headerbytes > 0) 132564565Sgshapiro headerbytes -= nr ; 132664565Sgshapiro 132764565Sgshapiro#else /* CONTENTLENGTH */ 132838032Speter while ((nr = read(fd, buf, sizeof(buf))) > 0) 132964565Sgshapiro { 133064565Sgshapiro#endif /* CONTENTLENGTH */ 133138032Speter for (off = 0; off < nr; off += nw) 133264565Sgshapiro { 133364565Sgshapiro if ((nw = write(mbfd, buf + off, nr - off)) < 0) 133464565Sgshapiro { 133573191Sgshapiro errcode = "450 4.2.0"; 133664565Sgshapiro#ifdef EDQUOT 133773191Sgshapiro if (errno == EDQUOT && BounceQuota) 133873191Sgshapiro errcode = "552 5.2.2"; 133964565Sgshapiro#endif /* EDQUOT */ 134073191Sgshapiro mailerr(errcode, "%s: %s", 134190795Sgshapiro path, sm_errstring(errno)); 134238032Speter goto err3; 134338032Speter } 134464565Sgshapiro } 134564565Sgshapiro } 134664565Sgshapiro if (nr < 0) 134764565Sgshapiro { 134873191Sgshapiro mailerr("450 4.2.0", "Temporary file: %s", 134990795Sgshapiro sm_errstring(errno)); 135038032Speter goto err3; 135138032Speter } 135238032Speter 135338032Speter /* Flush to disk, don't wait for update. */ 135464565Sgshapiro if (!nofsync && fsync(mbfd) < 0) 135564565Sgshapiro { 135690795Sgshapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 135738032Spetererr3: 135838032Speter#ifdef DEBUG 135990795Sgshapiro fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 136064565Sgshapiro#endif /* DEBUG */ 1361110563Sgshapiro if (mbfd >= 0) 1362110563Sgshapiro (void) ftruncate(mbfd, curoff); 136366497Sgshapiroerr1: if (mbfd >= 0) 136466497Sgshapiro (void) close(mbfd); 1365120259Sgshapiroerr0: (void) setreuid(0, 0); 1366120259Sgshapiro unlockmbox(); 136738032Speter return; 136838032Speter } 136938032Speter 1370120259Sgshapiro /* 1371120259Sgshapiro ** Save the current size so if the close() fails below 1372120259Sgshapiro ** we can make sure no other process has changed the mailbox 1373120259Sgshapiro ** between the failed close and the re-open()/re-lock(). 1374120259Sgshapiro ** If something else has changed the size, we shouldn't 1375120259Sgshapiro ** try to truncate it as we may do more harm then good 1376120259Sgshapiro ** (e.g., truncate a later message delivery). 1377120259Sgshapiro */ 1378120259Sgshapiro 1379120259Sgshapiro if (fstat(mbfd, &sb) < 0) 1380120259Sgshapiro cursize = 0; 1381120259Sgshapiro else 1382120259Sgshapiro cursize = sb.st_size; 1383120259Sgshapiro 1384120259Sgshapiro 138538032Speter /* Close and check -- NFS doesn't write until the close. */ 138664565Sgshapiro if (close(mbfd)) 138764565Sgshapiro { 138873191Sgshapiro errcode = "450 4.2.0"; 138964565Sgshapiro#ifdef EDQUOT 139073191Sgshapiro if (errno == EDQUOT && BounceQuota) 139173191Sgshapiro errcode = "552 5.2.2"; 139264565Sgshapiro#endif /* EDQUOT */ 139390795Sgshapiro mailerr(errcode, "%s: %s", path, sm_errstring(errno)); 1394120259Sgshapiro mbfd = open(path, O_WRONLY, 0); 1395120259Sgshapiro if (mbfd < 0 || 1396120259Sgshapiro cursize == 0 1397120259Sgshapiro || flock(mbfd, LOCK_EX) < 0 || 1398120259Sgshapiro fstat(mbfd, &sb) < 0 || 1399120259Sgshapiro sb.st_size != cursize || 1400110563Sgshapiro sb.st_nlink != 1 || 1401110563Sgshapiro !S_ISREG(sb.st_mode) || 1402110563Sgshapiro sb.st_dev != fsb.st_dev || 1403110563Sgshapiro sb.st_ino != fsb.st_ino || 1404110563Sgshapiro# if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 1405110563Sgshapiro sb.st_gen != fsb.st_gen || 1406110563Sgshapiro# endif /* HAS_ST_GEN && 0 */ 1407110563Sgshapiro sb.st_uid != fsb.st_uid 1408110563Sgshapiro ) 1409110563Sgshapiro { 1410110563Sgshapiro /* Don't use a bogus file */ 1411110563Sgshapiro if (mbfd >= 0) 1412110563Sgshapiro { 1413110563Sgshapiro (void) close(mbfd); 1414110563Sgshapiro mbfd = -1; 1415110563Sgshapiro } 1416110563Sgshapiro } 1417110563Sgshapiro 1418110563Sgshapiro /* Attempt to truncate back to pre-write size */ 1419110563Sgshapiro goto err3; 142064565Sgshapiro } 142164565Sgshapiro else if (!nobiff) 142238032Speter notifybiff(biffmsg); 142338032Speter 142464565Sgshapiro if (setreuid(0, 0) < 0) 142564565Sgshapiro { 142638032Speter mailerr("450 4.2.0", "setreuid(0, 0): %s", 142790795Sgshapiro sm_errstring(errno)); 142838032Speter goto err0; 142938032Speter } 143038032Speter#ifdef DEBUG 143190795Sgshapiro fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 143264565Sgshapiro#endif /* DEBUG */ 143338032Speter unlockmbox(); 143464565Sgshapiro if (LMTPMode) 143573191Sgshapiro printf("250 2.1.5 %s Ok\r\n", name); 143638032Speter} 143738032Speter 143838032Speter/* 143964565Sgshapiro** user.lock files are necessary for compatibility with other 144064565Sgshapiro** systems, e.g., when the mail spool file is NFS exported. 144164565Sgshapiro** Alas, mailbox locking is more than just a local matter. 144264565Sgshapiro** EPA 11/94. 144364565Sgshapiro*/ 144438032Speter 144590795Sgshapirobool Locked = false; 144638032Speter 144764565Sgshapiro#ifdef MAILLOCK 144864565Sgshapiroint 144964565Sgshapirolockmbox(name) 145064565Sgshapiro char *name; 145164565Sgshapiro{ 145266497Sgshapiro int r = 0; 145364565Sgshapiro 145464565Sgshapiro if (Locked) 145564565Sgshapiro return 0; 145664565Sgshapiro if ((r = maillock(name, 15)) == L_SUCCESS) 145764565Sgshapiro { 145890795Sgshapiro Locked = true; 145964565Sgshapiro return 0; 146064565Sgshapiro } 146164565Sgshapiro switch (r) 146264565Sgshapiro { 146364565Sgshapiro case L_TMPLOCK: /* Can't create tmp file */ 146464565Sgshapiro case L_TMPWRITE: /* Can't write pid into lockfile */ 146564565Sgshapiro case L_MAXTRYS: /* Failed after retrycnt attempts */ 146664565Sgshapiro errno = 0; 146764565Sgshapiro r = EX_TEMPFAIL; 146864565Sgshapiro break; 146964565Sgshapiro case L_ERROR: /* Check errno for reason */ 147064565Sgshapiro r = errno; 147164565Sgshapiro break; 147264565Sgshapiro default: /* other permanent errors */ 147364565Sgshapiro errno = 0; 147464565Sgshapiro r = EX_UNAVAILABLE; 147564565Sgshapiro break; 147664565Sgshapiro } 147764565Sgshapiro return r; 147864565Sgshapiro} 147964565Sgshapiro 148038032Spetervoid 148164565Sgshapirounlockmbox() 148264565Sgshapiro{ 148364565Sgshapiro if (Locked) 148464565Sgshapiro mailunlock(); 148590795Sgshapiro Locked = false; 148664565Sgshapiro} 148764565Sgshapiro#else /* MAILLOCK */ 148864565Sgshapiro 148964565Sgshapirochar LockName[MAXPATHLEN]; 149064565Sgshapiro 149164565Sgshapiroint 149238032Speterlockmbox(path) 149338032Speter char *path; 149438032Speter{ 149538032Speter int statfailed = 0; 149664565Sgshapiro time_t start; 149738032Speter 149864565Sgshapiro if (Locked) 149964565Sgshapiro return 0; 150064565Sgshapiro if (strlen(path) + 6 > sizeof LockName) 150164565Sgshapiro return EX_SOFTWARE; 150290795Sgshapiro (void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path); 150364565Sgshapiro (void) time(&start); 150464565Sgshapiro for (; ; sleep(5)) 150564565Sgshapiro { 150638032Speter int fd; 150738032Speter struct stat st; 150838032Speter time_t now; 150938032Speter 151064565Sgshapiro /* global timeout */ 151164565Sgshapiro (void) time(&now); 151264565Sgshapiro if (now > start + LOCKTO_GLOB) 151364565Sgshapiro { 151464565Sgshapiro errno = 0; 151564565Sgshapiro return EX_TEMPFAIL; 151664565Sgshapiro } 1517120259Sgshapiro fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, LOCKFILE_PMODE); 151864565Sgshapiro if (fd >= 0) 151964565Sgshapiro { 152038032Speter /* defeat lock checking programs which test pid */ 152164565Sgshapiro (void) write(fd, "0", 2); 152290795Sgshapiro Locked = true; 152364565Sgshapiro (void) close(fd); 152464565Sgshapiro return 0; 152538032Speter } 152664565Sgshapiro if (stat(LockName, &st) < 0) 152764565Sgshapiro { 152838032Speter if (statfailed++ > 5) 152964565Sgshapiro { 153064565Sgshapiro errno = 0; 153164565Sgshapiro return EX_TEMPFAIL; 153264565Sgshapiro } 153338032Speter continue; 153438032Speter } 153538032Speter statfailed = 0; 153664565Sgshapiro (void) time(&now); 153764565Sgshapiro if (now < st.st_ctime + LOCKTO_RM) 153838032Speter continue; 153964565Sgshapiro 154064565Sgshapiro /* try to remove stale lockfile */ 154164565Sgshapiro if (unlink(LockName) < 0) 154264565Sgshapiro return errno; 154338032Speter } 154438032Speter} 154538032Speter 154638032Spetervoid 154738032Speterunlockmbox() 154838032Speter{ 154964565Sgshapiro if (!Locked) 155038032Speter return; 155164565Sgshapiro (void) unlink(LockName); 155290795Sgshapiro Locked = false; 155338032Speter} 155464565Sgshapiro#endif /* MAILLOCK */ 155538032Speter 155638032Spetervoid 155738032Speternotifybiff(msg) 155838032Speter char *msg; 155938032Speter{ 156090795Sgshapiro static bool initialized = false; 156138032Speter static int f = -1; 156238032Speter struct hostent *hp; 156338032Speter struct servent *sp; 156438032Speter int len; 156564565Sgshapiro static struct sockaddr_in addr; 156638032Speter 156764565Sgshapiro if (!initialized) 156864565Sgshapiro { 156990795Sgshapiro initialized = true; 157064565Sgshapiro 157138032Speter /* Be silent if biff service not available. */ 157264565Sgshapiro if ((sp = getservbyname("biff", "udp")) == NULL || 157364565Sgshapiro (hp = gethostbyname("localhost")) == NULL || 157464565Sgshapiro hp->h_length != INADDRSZ) 157538032Speter return; 157664565Sgshapiro 157738032Speter addr.sin_family = hp->h_addrtype; 157864565Sgshapiro memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ); 157938032Speter addr.sin_port = sp->s_port; 158038032Speter } 158164565Sgshapiro 158264565Sgshapiro /* No message, just return */ 158364565Sgshapiro if (msg == NULL) 158438032Speter return; 158564565Sgshapiro 158664565Sgshapiro /* Couldn't initialize addr struct */ 158764565Sgshapiro if (addr.sin_family == AF_UNSPEC) 158864565Sgshapiro return; 158964565Sgshapiro 159073191Sgshapiro if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 159164565Sgshapiro return; 159238032Speter len = strlen(msg) + 1; 159364565Sgshapiro (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr)); 159438032Speter} 159538032Speter 159638032Spetervoid 159738032Speterusage() 159838032Speter{ 159964565Sgshapiro ExitVal = EX_USAGE; 1600182352Sgshapiro# if _FFR_SPOOL_PATH 1601182352Sgshapiro mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] [-p path] user ..."); 1602182352Sgshapiro# else /* _FFR_SPOOL_PATH */ 160390795Sgshapiro mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ..."); 1604182352Sgshapiro# endif /* _FFR_SPOOL_PATH */ 1605141862Sgshapiro sm_exit(ExitVal); 160638032Speter} 160738032Speter 160838032Spetervoid 160990795Sgshapiro/*VARARGS2*/ 161038032Speter#ifdef __STDC__ 161138032Spetermailerr(const char *hdr, const char *fmt, ...) 161264565Sgshapiro#else /* __STDC__ */ 161338032Spetermailerr(hdr, fmt, va_alist) 161438032Speter const char *hdr; 161538032Speter const char *fmt; 161638032Speter va_dcl 161764565Sgshapiro#endif /* __STDC__ */ 161838032Speter{ 161973191Sgshapiro size_t len = 0; 162090795Sgshapiro SM_VA_LOCAL_DECL 162138032Speter 162273191Sgshapiro (void) e_to_sys(errno); 162373191Sgshapiro 162490795Sgshapiro SM_VA_START(ap, fmt); 162573191Sgshapiro 162690795Sgshapiro if (LMTPMode && hdr != NULL) 162738032Speter { 162890795Sgshapiro sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); 162990795Sgshapiro len = strlen(ErrBuf); 163038032Speter } 163190795Sgshapiro (void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap); 163290795Sgshapiro SM_VA_END(ap); 163373191Sgshapiro 163473191Sgshapiro if (!HoldErrs) 163573191Sgshapiro flush_error(); 163673191Sgshapiro 163773191Sgshapiro /* Log the message to syslog. */ 163873191Sgshapiro if (!LMTPMode) 163973191Sgshapiro syslog(LOG_ERR, "%s", ErrBuf); 164038032Speter} 164138032Speter 164238032Spetervoid 164373191Sgshapiroflush_error() 164438032Speter{ 164573191Sgshapiro if (LMTPMode) 164673191Sgshapiro printf("%s\r\n", ErrBuf); 164773191Sgshapiro else 164838032Speter { 164973191Sgshapiro if (ExitVal != EX_USAGE) 165073191Sgshapiro (void) fprintf(stderr, "mail.local: "); 165173191Sgshapiro fprintf(stderr, "%s\n", ErrBuf); 165238032Speter } 165338032Speter} 165438032Speter 1655132946Sgshapiro#if HASHSPOOL 1656132946Sgshapiroconst char * 1657132946Sgshapirohashname(name) 1658132946Sgshapiro char *name; 1659132946Sgshapiro{ 1660132946Sgshapiro static char p[MAXPATHLEN]; 1661132946Sgshapiro int i; 1662132946Sgshapiro int len; 1663132946Sgshapiro char *str; 1664132946Sgshapiro# if HASHSPOOLMD5 1665132946Sgshapiro char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_"; 1666132946Sgshapiro MD5_CTX ctx; 1667132946Sgshapiro unsigned char md5[18]; 1668132946Sgshapiro# if MAXPATHLEN <= 24 1669132946Sgshapiro ERROR _MAXPATHLEN <= 24 1670132946Sgshapiro# endif /* MAXPATHLEN <= 24 */ 1671132946Sgshapiro char b64[24]; 1672132946Sgshapiro MD5_LONG bits; 1673132946Sgshapiro int j; 1674132946Sgshapiro# endif /* HASHSPOOLMD5 */ 1675132946Sgshapiro 1676132946Sgshapiro if (HashType == HASH_NONE || HashDepth * 2 >= MAXPATHLEN) 1677132946Sgshapiro { 1678132946Sgshapiro p[0] = '\0'; 1679132946Sgshapiro return p; 1680132946Sgshapiro } 1681132946Sgshapiro 1682132946Sgshapiro switch(HashType) 1683132946Sgshapiro { 1684132946Sgshapiro case HASH_USER: 1685132946Sgshapiro str = name; 1686132946Sgshapiro break; 1687132946Sgshapiro 1688132946Sgshapiro# if HASHSPOOLMD5 1689132946Sgshapiro case HASH_MD5: 1690132946Sgshapiro MD5_Init(&ctx); 1691132946Sgshapiro MD5_Update(&ctx, name, strlen(name)); 1692132946Sgshapiro MD5_Final(md5, &ctx); 1693132946Sgshapiro md5[16] = 0; 1694132946Sgshapiro md5[17] = 0; 1695132946Sgshapiro 1696132946Sgshapiro for (i = 0; i < 6; i++) 1697132946Sgshapiro { 1698132946Sgshapiro bits = (unsigned) md5[(3 * i)] << 16; 1699132946Sgshapiro bits |= (unsigned) md5[(3 * i) + 1] << 8; 1700132946Sgshapiro bits |= (unsigned) md5[(3 * i) + 2]; 1701132946Sgshapiro 1702132946Sgshapiro for (j = 3; j >= 0; j--) 1703132946Sgshapiro { 1704132946Sgshapiro b64[(4 * i) + j] = Base64[(bits & 0x3f)]; 1705132946Sgshapiro bits >>= 6; 1706132946Sgshapiro } 1707132946Sgshapiro } 1708132946Sgshapiro b64[22] = '\0'; 1709132946Sgshapiro str = b64; 1710132946Sgshapiro break; 1711132946Sgshapiro# endif /* HASHSPOOLMD5 */ 1712132946Sgshapiro } 1713132946Sgshapiro 1714132946Sgshapiro len = strlen(str); 1715132946Sgshapiro for (i = 0; i < HashDepth; i++) 1716132946Sgshapiro { 1717132946Sgshapiro if (i < len) 1718132946Sgshapiro p[i * 2] = str[i]; 1719132946Sgshapiro else 1720132946Sgshapiro p[i * 2] = '_'; 1721132946Sgshapiro p[(i * 2) + 1] = '/'; 1722132946Sgshapiro } 1723132946Sgshapiro p[HashDepth * 2] = '\0'; 1724132946Sgshapiro return p; 1725132946Sgshapiro} 1726132946Sgshapiro#endif /* HASHSPOOL */ 1727132946Sgshapiro 172838032Speter/* 172938032Speter * e_to_sys -- 173038032Speter * Guess which errno's are temporary. Gag me. 173138032Speter */ 173273191Sgshapiro 173364565Sgshapiroint 173438032Spetere_to_sys(num) 173538032Speter int num; 173638032Speter{ 173738032Speter /* Temporary failures override hard errors. */ 173864565Sgshapiro if (ExitVal == EX_TEMPFAIL) 173964565Sgshapiro return ExitVal; 174038032Speter 174164565Sgshapiro switch (num) /* Hopefully temporary errors. */ 174264565Sgshapiro { 174364565Sgshapiro#ifdef EDQUOT 174464565Sgshapiro case EDQUOT: /* Disc quota exceeded */ 174573191Sgshapiro if (BounceQuota) 174664565Sgshapiro { 174764565Sgshapiro ExitVal = EX_UNAVAILABLE; 174864565Sgshapiro break; 174964565Sgshapiro } 175064565Sgshapiro /* FALLTHROUGH */ 175164565Sgshapiro#endif /* EDQUOT */ 175238032Speter#ifdef EAGAIN 175364565Sgshapiro case EAGAIN: /* Resource temporarily unavailable */ 175464565Sgshapiro#endif /* EAGAIN */ 175538032Speter#ifdef EBUSY 175664565Sgshapiro case EBUSY: /* Device busy */ 175764565Sgshapiro#endif /* EBUSY */ 175838032Speter#ifdef EPROCLIM 175964565Sgshapiro case EPROCLIM: /* Too many processes */ 176064565Sgshapiro#endif /* EPROCLIM */ 176138032Speter#ifdef EUSERS 176264565Sgshapiro case EUSERS: /* Too many users */ 176364565Sgshapiro#endif /* EUSERS */ 176438032Speter#ifdef ECONNABORTED 176564565Sgshapiro case ECONNABORTED: /* Software caused connection abort */ 176664565Sgshapiro#endif /* ECONNABORTED */ 176738032Speter#ifdef ECONNREFUSED 176864565Sgshapiro case ECONNREFUSED: /* Connection refused */ 176964565Sgshapiro#endif /* ECONNREFUSED */ 177038032Speter#ifdef ECONNRESET 177164565Sgshapiro case ECONNRESET: /* Connection reset by peer */ 177264565Sgshapiro#endif /* ECONNRESET */ 177338032Speter#ifdef EDEADLK 177464565Sgshapiro case EDEADLK: /* Resource deadlock avoided */ 177564565Sgshapiro#endif /* EDEADLK */ 177638032Speter#ifdef EFBIG 177764565Sgshapiro case EFBIG: /* File too large */ 177864565Sgshapiro#endif /* EFBIG */ 177938032Speter#ifdef EHOSTDOWN 178064565Sgshapiro case EHOSTDOWN: /* Host is down */ 178164565Sgshapiro#endif /* EHOSTDOWN */ 178238032Speter#ifdef EHOSTUNREACH 178364565Sgshapiro case EHOSTUNREACH: /* No route to host */ 178464565Sgshapiro#endif /* EHOSTUNREACH */ 178538032Speter#ifdef EMFILE 178664565Sgshapiro case EMFILE: /* Too many open files */ 178764565Sgshapiro#endif /* EMFILE */ 178838032Speter#ifdef ENETDOWN 178964565Sgshapiro case ENETDOWN: /* Network is down */ 179064565Sgshapiro#endif /* ENETDOWN */ 179138032Speter#ifdef ENETRESET 179264565Sgshapiro case ENETRESET: /* Network dropped connection on reset */ 179364565Sgshapiro#endif /* ENETRESET */ 179438032Speter#ifdef ENETUNREACH 179564565Sgshapiro case ENETUNREACH: /* Network is unreachable */ 179664565Sgshapiro#endif /* ENETUNREACH */ 179738032Speter#ifdef ENFILE 179864565Sgshapiro case ENFILE: /* Too many open files in system */ 179964565Sgshapiro#endif /* ENFILE */ 180038032Speter#ifdef ENOBUFS 180164565Sgshapiro case ENOBUFS: /* No buffer space available */ 180264565Sgshapiro#endif /* ENOBUFS */ 180338032Speter#ifdef ENOMEM 180464565Sgshapiro case ENOMEM: /* Cannot allocate memory */ 180564565Sgshapiro#endif /* ENOMEM */ 180638032Speter#ifdef ENOSPC 180764565Sgshapiro case ENOSPC: /* No space left on device */ 180864565Sgshapiro#endif /* ENOSPC */ 180938032Speter#ifdef EROFS 181064565Sgshapiro case EROFS: /* Read-only file system */ 181164565Sgshapiro#endif /* EROFS */ 181238032Speter#ifdef ESTALE 181364565Sgshapiro case ESTALE: /* Stale NFS file handle */ 181464565Sgshapiro#endif /* ESTALE */ 181538032Speter#ifdef ETIMEDOUT 181664565Sgshapiro case ETIMEDOUT: /* Connection timed out */ 181764565Sgshapiro#endif /* ETIMEDOUT */ 181838032Speter#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK 181964565Sgshapiro case EWOULDBLOCK: /* Operation would block. */ 182064565Sgshapiro#endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */ 182164565Sgshapiro ExitVal = EX_TEMPFAIL; 182238032Speter break; 182364565Sgshapiro 182464565Sgshapiro default: 182564565Sgshapiro ExitVal = EX_UNAVAILABLE; 182638032Speter break; 182738032Speter } 182864565Sgshapiro return ExitVal; 182938032Speter} 183038032Speter 183138032Speter#if defined(ultrix) || defined(_CRAY) 183238032Speter/* 183338032Speter * Copyright (c) 1987, 1993 183438032Speter * The Regents of the University of California. All rights reserved. 183538032Speter * 183638032Speter * Redistribution and use in source and binary forms, with or without 183738032Speter * modification, are permitted provided that the following conditions 183838032Speter * are met: 183938032Speter * 1. Redistributions of source code must retain the above copyright 184038032Speter * notice, this list of conditions and the following disclaimer. 184138032Speter * 2. Redistributions in binary form must reproduce the above copyright 184238032Speter * notice, this list of conditions and the following disclaimer in the 184338032Speter * documentation and/or other materials provided with the distribution. 184438032Speter * 3. All advertising materials mentioning features or use of this software 184538032Speter * must display the following acknowledgement: 184638032Speter * This product includes software developed by the University of 184738032Speter * California, Berkeley and its contributors. 184838032Speter * 4. Neither the name of the University nor the names of its contributors 184938032Speter * may be used to endorse or promote products derived from this software 185038032Speter * without specific prior written permission. 185138032Speter * 185238032Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 185338032Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 185438032Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 185538032Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 185638032Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 185738032Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 185838032Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 185938032Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 186038032Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 186138032Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 186238032Speter * SUCH DAMAGE. 186338032Speter */ 186438032Speter 186564565Sgshapiro# if defined(LIBC_SCCS) && !defined(lint) 186638032Speterstatic char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; 186764565Sgshapiro# endif /* defined(LIBC_SCCS) && !defined(lint) */ 186838032Speter 186964565Sgshapiro# include <sys/types.h> 187064565Sgshapiro# include <sys/stat.h> 187164565Sgshapiro# include <fcntl.h> 187264565Sgshapiro# include <errno.h> 187364565Sgshapiro# include <stdio.h> 187464565Sgshapiro# include <ctype.h> 187538032Speter 187638032Speterstatic int _gettemp(); 187738032Speter 187838032Spetermkstemp(path) 187938032Speter char *path; 188038032Speter{ 188138032Speter int fd; 188238032Speter 188338032Speter return (_gettemp(path, &fd) ? fd : -1); 188438032Speter} 188538032Speter 188638032Speterstatic 188738032Speter_gettemp(path, doopen) 188838032Speter char *path; 188938032Speter register int *doopen; 189038032Speter{ 189138032Speter extern int errno; 189238032Speter register char *start, *trv; 189338032Speter struct stat sbuf; 189490795Sgshapiro unsigned int pid; 189538032Speter 189638032Speter pid = getpid(); 189738032Speter for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ 189864565Sgshapiro while (*--trv == 'X') 189964565Sgshapiro { 190038032Speter *trv = (pid % 10) + '0'; 190138032Speter pid /= 10; 190238032Speter } 190338032Speter 190438032Speter /* 190538032Speter * check the target directory; if you have six X's and it 190638032Speter * doesn't exist this runs for a *very* long time. 190738032Speter */ 190864565Sgshapiro for (start = trv + 1;; --trv) 190964565Sgshapiro { 191038032Speter if (trv <= path) 191138032Speter break; 191264565Sgshapiro if (*trv == '/') 191364565Sgshapiro { 191438032Speter *trv = '\0'; 191538032Speter if (stat(path, &sbuf) < 0) 191638032Speter return(0); 191764565Sgshapiro if (!S_ISDIR(sbuf.st_mode)) 191864565Sgshapiro { 191938032Speter errno = ENOTDIR; 192038032Speter return(0); 192138032Speter } 192238032Speter *trv = '/'; 192338032Speter break; 192438032Speter } 192538032Speter } 192638032Speter 192764565Sgshapiro for (;;) 192864565Sgshapiro { 192964565Sgshapiro if (doopen) 193064565Sgshapiro { 193166497Sgshapiro if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 193266497Sgshapiro 0600)) >= 0) 193338032Speter return(1); 193438032Speter if (errno != EEXIST) 193538032Speter return(0); 193638032Speter } 193738032Speter else if (stat(path, &sbuf) < 0) 193838032Speter return(errno == ENOENT ? 1 : 0); 193938032Speter 194038032Speter /* tricky little algorithm for backward compatibility */ 194164565Sgshapiro for (trv = start;;) 194264565Sgshapiro { 194338032Speter if (!*trv) 194438032Speter return(0); 194538032Speter if (*trv == 'z') 194638032Speter *trv++ = 'a'; 194764565Sgshapiro else 194864565Sgshapiro { 194938032Speter if (isascii(*trv) && isdigit(*trv)) 195038032Speter *trv = 'a'; 195138032Speter else 195238032Speter ++*trv; 195338032Speter break; 195438032Speter } 195538032Speter } 195638032Speter } 195764565Sgshapiro /* NOTREACHED */ 195838032Speter} 195964565Sgshapiro#endif /* defined(ultrix) || defined(_CRAY) */ 1960