176b7bf71SPeter Wemm /* 2f9218d3dSGregory Neil Shapiro * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 33299c2f1SGregory Neil Shapiro * All rights reserved. 4c2aa98e2SPeter Wemm * Copyright (c) 1990, 1993, 1994 5c2aa98e2SPeter Wemm * The Regents of the University of California. All rights reserved. 6c2aa98e2SPeter Wemm * 7c2aa98e2SPeter Wemm * By using this file, you agree to the terms and conditions set 8c2aa98e2SPeter Wemm * forth in the LICENSE file which can be found at the top level of 9c2aa98e2SPeter Wemm * the sendmail distribution. 10c2aa98e2SPeter Wemm * 113edfa581SGregory Neil Shapiro * $FreeBSD$ 123edfa581SGregory Neil Shapiro * 13c2aa98e2SPeter Wemm */ 14c2aa98e2SPeter Wemm 1512ed1c7cSGregory Neil Shapiro #include <sm/gen.h> 1612ed1c7cSGregory Neil Shapiro 1712ed1c7cSGregory Neil Shapiro SM_IDSTR(copyright, 18b4662009SGregory Neil Shapiro "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ 193299c2f1SGregory Neil Shapiro All rights reserved.\n\ 203299c2f1SGregory Neil Shapiro Copyright (c) 1990, 1993, 1994\n\ 2112ed1c7cSGregory Neil Shapiro The Regents of the University of California. All rights reserved.\n") 22c2aa98e2SPeter Wemm 237660b554SGregory Neil Shapiro SM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.239.2.11 2003/09/01 01:49:46 gshapiro Exp $") 2412ed1c7cSGregory Neil Shapiro 2512ed1c7cSGregory Neil Shapiro #include <stdlib.h> 2612ed1c7cSGregory Neil Shapiro #include <sm/errstring.h> 2712ed1c7cSGregory Neil Shapiro #include <sm/io.h> 2812ed1c7cSGregory Neil Shapiro #include <sm/limits.h> 2912ed1c7cSGregory Neil Shapiro # include <unistd.h> 3012ed1c7cSGregory Neil Shapiro # ifdef EX_OK 3112ed1c7cSGregory Neil Shapiro # undef EX_OK /* unistd.h may have another use for this */ 3212ed1c7cSGregory Neil Shapiro # endif /* EX_OK */ 337660b554SGregory Neil Shapiro # define LOCKFILE_PMODE 0 3412ed1c7cSGregory Neil Shapiro #include <sm/mbdb.h> 3512ed1c7cSGregory Neil Shapiro #include <sm/sysexits.h> 363299c2f1SGregory Neil Shapiro 37c2aa98e2SPeter Wemm /* 383299c2f1SGregory Neil Shapiro ** This is not intended to work on System V derived systems 393299c2f1SGregory Neil Shapiro ** such as Solaris or HP-UX, since they use a totally different 4012ed1c7cSGregory Neil Shapiro ** approach to mailboxes (essentially, they have a set-group-ID program 4112ed1c7cSGregory Neil Shapiro ** rather than set-user-ID, and they rely on the ability to "give away" 423299c2f1SGregory Neil Shapiro ** files to do their work). IT IS NOT A BUG that this doesn't 433299c2f1SGregory Neil Shapiro ** work on such architectures. 44c2aa98e2SPeter Wemm */ 45c2aa98e2SPeter Wemm 463299c2f1SGregory Neil Shapiro 4712ed1c7cSGregory Neil Shapiro #include <stdio.h> 4812ed1c7cSGregory Neil Shapiro #include <errno.h> 4912ed1c7cSGregory Neil Shapiro #include <fcntl.h> 5012ed1c7cSGregory Neil Shapiro #include <sys/types.h> 5112ed1c7cSGregory Neil Shapiro #include <sys/stat.h> 5212ed1c7cSGregory Neil Shapiro #include <time.h> 5312ed1c7cSGregory Neil Shapiro #include <stdlib.h> 5412ed1c7cSGregory Neil Shapiro # include <sys/socket.h> 5512ed1c7cSGregory Neil Shapiro # include <sys/file.h> 5612ed1c7cSGregory Neil Shapiro # include <netinet/in.h> 5712ed1c7cSGregory Neil Shapiro # include <arpa/nameser.h> 5812ed1c7cSGregory Neil Shapiro # include <netdb.h> 5912ed1c7cSGregory Neil Shapiro # include <pwd.h> 6012ed1c7cSGregory Neil Shapiro 6112ed1c7cSGregory Neil Shapiro #include <sm/string.h> 6212ed1c7cSGregory Neil Shapiro #include <syslog.h> 6312ed1c7cSGregory Neil Shapiro #include <ctype.h> 6412ed1c7cSGregory Neil Shapiro 6512ed1c7cSGregory Neil Shapiro #include <sm/conf.h> 6612ed1c7cSGregory Neil Shapiro #include <sendmail/pathnames.h> 6712ed1c7cSGregory Neil Shapiro 6812ed1c7cSGregory Neil Shapiro 693299c2f1SGregory Neil Shapiro #ifndef LOCKTO_RM 703299c2f1SGregory Neil Shapiro # define LOCKTO_RM 300 /* timeout for stale lockfile removal */ 71d995d2baSGregory Neil Shapiro #endif /* ! LOCKTO_RM */ 723299c2f1SGregory Neil Shapiro #ifndef LOCKTO_GLOB 733299c2f1SGregory Neil Shapiro # define LOCKTO_GLOB 400 /* global timeout for lockfile creation */ 74d995d2baSGregory Neil Shapiro #endif /* ! LOCKTO_GLOB */ 753299c2f1SGregory Neil Shapiro 763299c2f1SGregory Neil Shapiro /* define a realloc() which works for NULL pointers */ 773299c2f1SGregory Neil Shapiro #define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size)) 78c2aa98e2SPeter Wemm 79c2aa98e2SPeter Wemm /* 8012ed1c7cSGregory Neil Shapiro ** If you don't have flock, you could try using lockf instead. 81c2aa98e2SPeter Wemm */ 82c2aa98e2SPeter Wemm 8312ed1c7cSGregory Neil Shapiro #ifdef LDA_USE_LOCKF 84c2aa98e2SPeter Wemm # define flock(a, b) lockf(a, b, 0) 853299c2f1SGregory Neil Shapiro # ifdef LOCK_EX 863299c2f1SGregory Neil Shapiro # undef LOCK_EX 873299c2f1SGregory Neil Shapiro # endif /* LOCK_EX */ 88c2aa98e2SPeter Wemm # define LOCK_EX F_LOCK 8912ed1c7cSGregory Neil Shapiro #endif /* LDA_USE_LOCKF */ 90c2aa98e2SPeter Wemm 91c2aa98e2SPeter Wemm #ifndef LOCK_EX 92c2aa98e2SPeter Wemm # include <sys/file.h> 933299c2f1SGregory Neil Shapiro #endif /* ! LOCK_EX */ 94c2aa98e2SPeter Wemm 95c2aa98e2SPeter Wemm /* 963299c2f1SGregory Neil Shapiro ** If you don't have setreuid, and you have saved uids, and you have 973299c2f1SGregory Neil Shapiro ** a seteuid() call that doesn't try to emulate using setuid(), then 9812ed1c7cSGregory Neil Shapiro ** you can try defining LDA_USE_SETEUID. 99c2aa98e2SPeter Wemm */ 100b4662009SGregory Neil Shapiro 10112ed1c7cSGregory Neil Shapiro #ifdef LDA_USE_SETEUID 102c2aa98e2SPeter Wemm # define setreuid(r, e) seteuid(e) 10312ed1c7cSGregory Neil Shapiro #endif /* LDA_USE_SETEUID */ 104c2aa98e2SPeter Wemm 10512ed1c7cSGregory Neil Shapiro #ifdef LDA_CONTENTLENGTH 10612ed1c7cSGregory Neil Shapiro # define CONTENTLENGTH 1 10712ed1c7cSGregory Neil Shapiro #endif /* LDA_CONTENTLENGTH */ 108d995d2baSGregory Neil Shapiro 1093299c2f1SGregory Neil Shapiro #ifndef INADDRSZ 1103299c2f1SGregory Neil Shapiro # define INADDRSZ 4 /* size of an IPv4 address in bytes */ 1113299c2f1SGregory Neil Shapiro #endif /* ! INADDRSZ */ 112c2aa98e2SPeter Wemm 11312ed1c7cSGregory Neil Shapiro #ifdef MAILLOCK 11412ed1c7cSGregory Neil Shapiro # include <maillock.h> 11512ed1c7cSGregory Neil Shapiro #endif /* MAILLOCK */ 11612ed1c7cSGregory Neil Shapiro 11776b7bf71SPeter Wemm #ifndef MAILER_DAEMON 11876b7bf71SPeter Wemm # define MAILER_DAEMON "MAILER-DAEMON" 1193299c2f1SGregory Neil Shapiro #endif /* ! MAILER_DAEMON */ 12076b7bf71SPeter Wemm 1213299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 1223299c2f1SGregory Neil Shapiro char ContentHdr[40] = "Content-Length: "; 1233299c2f1SGregory Neil Shapiro off_t HeaderLength; 1243299c2f1SGregory Neil Shapiro off_t BodyLength; 1253299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 126c2aa98e2SPeter Wemm 12712ed1c7cSGregory Neil Shapiro bool EightBitMime = true; /* advertise 8BITMIME in LMTP */ 128b4662009SGregory Neil Shapiro char ErrBuf[10240]; /* error buffer */ 1293299c2f1SGregory Neil Shapiro int ExitVal = EX_OK; /* sysexits.h error value. */ 13012ed1c7cSGregory Neil Shapiro bool nobiff = false; 13112ed1c7cSGregory Neil Shapiro bool nofsync = false; 13212ed1c7cSGregory Neil Shapiro bool HoldErrs = false; /* Hold errors in ErrBuf */ 13312ed1c7cSGregory Neil Shapiro bool LMTPMode = false; 13412ed1c7cSGregory Neil Shapiro bool BounceQuota = false; /* permanent error when over quota */ 13512ed1c7cSGregory Neil Shapiro char *HomeMailFile = NULL; /* store mail in homedir */ 1363299c2f1SGregory Neil Shapiro 137b4662009SGregory Neil Shapiro void deliver __P((int, char *)); 1383299c2f1SGregory Neil Shapiro int e_to_sys __P((int)); 139c2aa98e2SPeter Wemm void notifybiff __P((char *)); 14012ed1c7cSGregory Neil Shapiro int store __P((char *, bool *)); 141c2aa98e2SPeter Wemm void usage __P((void)); 1423299c2f1SGregory Neil Shapiro int lockmbox __P((char *)); 143c2aa98e2SPeter Wemm void unlockmbox __P((void)); 144c2aa98e2SPeter Wemm void mailerr __P((const char *, const char *, ...)); 145b4662009SGregory Neil Shapiro void flush_error __P((void)); 1463299c2f1SGregory Neil Shapiro 147c2aa98e2SPeter Wemm 148c2aa98e2SPeter Wemm int 149c2aa98e2SPeter Wemm main(argc, argv) 150c2aa98e2SPeter Wemm int argc; 151c2aa98e2SPeter Wemm char *argv[]; 152c2aa98e2SPeter Wemm { 153c2aa98e2SPeter Wemm struct passwd *pw; 1543299c2f1SGregory Neil Shapiro int ch, fd; 155c2aa98e2SPeter Wemm uid_t uid; 156c2aa98e2SPeter Wemm char *from; 15712ed1c7cSGregory Neil Shapiro char *mbdbname = "pw"; 15812ed1c7cSGregory Neil Shapiro int err; 159c2aa98e2SPeter Wemm extern char *optarg; 160c2aa98e2SPeter Wemm extern int optind; 1613299c2f1SGregory Neil Shapiro 162c2aa98e2SPeter Wemm 163c2aa98e2SPeter Wemm /* make sure we have some open file descriptors */ 164c2aa98e2SPeter Wemm for (fd = 10; fd < 30; fd++) 165c2aa98e2SPeter Wemm (void) close(fd); 166c2aa98e2SPeter Wemm 167c2aa98e2SPeter Wemm /* use a reasonable umask */ 168c2aa98e2SPeter Wemm (void) umask(0077); 169c2aa98e2SPeter Wemm 170c2aa98e2SPeter Wemm # ifdef LOG_MAIL 171c2aa98e2SPeter Wemm openlog("mail.local", 0, LOG_MAIL); 1723299c2f1SGregory Neil Shapiro # else /* LOG_MAIL */ 173c2aa98e2SPeter Wemm openlog("mail.local", 0); 1743299c2f1SGregory Neil Shapiro # endif /* LOG_MAIL */ 175c2aa98e2SPeter Wemm 176c2aa98e2SPeter Wemm from = NULL; 17712ed1c7cSGregory Neil Shapiro while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1) 1783299c2f1SGregory Neil Shapiro { 1793299c2f1SGregory Neil Shapiro switch(ch) 1803299c2f1SGregory Neil Shapiro { 1813299c2f1SGregory Neil Shapiro case '7': /* Do not advertise 8BITMIME */ 18212ed1c7cSGregory Neil Shapiro EightBitMime = false; 183d615a192SPeter Wemm break; 1843299c2f1SGregory Neil Shapiro 1853299c2f1SGregory Neil Shapiro case 'B': 18639e37e72SGregory Neil Shapiro nobiff = true; 1873299c2f1SGregory Neil Shapiro break; 1883299c2f1SGregory Neil Shapiro 1893299c2f1SGregory Neil Shapiro case 'b': /* bounce mail when over quota. */ 19012ed1c7cSGregory Neil Shapiro BounceQuota = true; 1913299c2f1SGregory Neil Shapiro break; 1923299c2f1SGregory Neil Shapiro 193c2aa98e2SPeter Wemm case 'd': /* Backward compatible. */ 194c2aa98e2SPeter Wemm break; 1953299c2f1SGregory Neil Shapiro 19612ed1c7cSGregory Neil Shapiro case 'D': /* mailbox database type */ 19712ed1c7cSGregory Neil Shapiro mbdbname = optarg; 19812ed1c7cSGregory Neil Shapiro break; 19912ed1c7cSGregory Neil Shapiro 200c2aa98e2SPeter Wemm case 'f': 201c2aa98e2SPeter Wemm case 'r': /* Backward compatible. */ 2023299c2f1SGregory Neil Shapiro if (from != NULL) 2033299c2f1SGregory Neil Shapiro { 204b4662009SGregory Neil Shapiro mailerr(NULL, "Multiple -f options"); 205c2aa98e2SPeter Wemm usage(); 206c2aa98e2SPeter Wemm } 207c2aa98e2SPeter Wemm from = optarg; 208c2aa98e2SPeter Wemm break; 2093299c2f1SGregory Neil Shapiro 21012ed1c7cSGregory Neil Shapiro case 'h': 21112ed1c7cSGregory Neil Shapiro if (optarg != NULL || *optarg != '\0') 21212ed1c7cSGregory Neil Shapiro HomeMailFile = optarg; 21312ed1c7cSGregory Neil Shapiro else 21412ed1c7cSGregory Neil Shapiro { 21512ed1c7cSGregory Neil Shapiro mailerr(NULL, "-h: missing filename"); 21612ed1c7cSGregory Neil Shapiro usage(); 21712ed1c7cSGregory Neil Shapiro } 21812ed1c7cSGregory Neil Shapiro break; 21912ed1c7cSGregory Neil Shapiro 220c2aa98e2SPeter Wemm case 'l': 22112ed1c7cSGregory Neil Shapiro LMTPMode = true; 222c2aa98e2SPeter Wemm break; 2233299c2f1SGregory Neil Shapiro 224d615a192SPeter Wemm case 's': 22505b73c60SPeter Wemm nofsync++; 226d615a192SPeter Wemm break; 2273299c2f1SGregory Neil Shapiro 228c2aa98e2SPeter Wemm case '?': 229c2aa98e2SPeter Wemm default: 230c2aa98e2SPeter Wemm usage(); 231c2aa98e2SPeter Wemm } 2323299c2f1SGregory Neil Shapiro } 233c2aa98e2SPeter Wemm argc -= optind; 234c2aa98e2SPeter Wemm argv += optind; 235c2aa98e2SPeter Wemm 2363299c2f1SGregory Neil Shapiro /* initialize biff structures */ 2373299c2f1SGregory Neil Shapiro if (!nobiff) 2383299c2f1SGregory Neil Shapiro notifybiff(NULL); 239c2aa98e2SPeter Wemm 24012ed1c7cSGregory Neil Shapiro err = sm_mbdb_initialize(mbdbname); 24112ed1c7cSGregory Neil Shapiro if (err != EX_OK) 24212ed1c7cSGregory Neil Shapiro { 24312ed1c7cSGregory Neil Shapiro char *errcode = "521"; 24412ed1c7cSGregory Neil Shapiro 24512ed1c7cSGregory Neil Shapiro if (err == EX_TEMPFAIL) 24612ed1c7cSGregory Neil Shapiro errcode = "421"; 24712ed1c7cSGregory Neil Shapiro 24812ed1c7cSGregory Neil Shapiro mailerr(errcode, "Can not open mailbox database %s: %s", 24912ed1c7cSGregory Neil Shapiro mbdbname, sm_strexit(err)); 25012ed1c7cSGregory Neil Shapiro exit(err); 25112ed1c7cSGregory Neil Shapiro } 25212ed1c7cSGregory Neil Shapiro 2533299c2f1SGregory Neil Shapiro if (LMTPMode) 254b4662009SGregory Neil Shapiro { 255b4662009SGregory Neil Shapiro extern void dolmtp __P((void)); 2563299c2f1SGregory Neil Shapiro 257b4662009SGregory Neil Shapiro if (argc > 0) 258b4662009SGregory Neil Shapiro { 259b4662009SGregory Neil Shapiro mailerr("421", "Users should not be specified in command line if LMTP required"); 260b4662009SGregory Neil Shapiro exit(EX_TEMPFAIL); 261b4662009SGregory Neil Shapiro } 262b4662009SGregory Neil Shapiro 263b4662009SGregory Neil Shapiro dolmtp(); 264b4662009SGregory Neil Shapiro /* NOTREACHED */ 265b4662009SGregory Neil Shapiro exit(EX_OK); 266b4662009SGregory Neil Shapiro } 267b4662009SGregory Neil Shapiro 268b4662009SGregory Neil Shapiro /* Non-LMTP from here on out */ 2693299c2f1SGregory Neil Shapiro if (*argv == '\0') 270c2aa98e2SPeter Wemm usage(); 271c2aa98e2SPeter Wemm 272c2aa98e2SPeter Wemm /* 2733299c2f1SGregory Neil Shapiro ** If from not specified, use the name from getlogin() if the 2743299c2f1SGregory Neil Shapiro ** uid matches, otherwise, use the name from the password file 2753299c2f1SGregory Neil Shapiro ** corresponding to the uid. 276c2aa98e2SPeter Wemm */ 277b4662009SGregory Neil Shapiro 278c2aa98e2SPeter Wemm uid = getuid(); 2793299c2f1SGregory Neil Shapiro if (from == NULL && ((from = getlogin()) == NULL || 2803299c2f1SGregory Neil Shapiro (pw = getpwnam(from)) == NULL || 2813299c2f1SGregory Neil Shapiro pw->pw_uid != uid)) 2823299c2f1SGregory Neil Shapiro from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???"; 283c2aa98e2SPeter Wemm 284c2aa98e2SPeter Wemm /* 2853299c2f1SGregory Neil Shapiro ** There is no way to distinguish the error status of one delivery 2863299c2f1SGregory Neil Shapiro ** from the rest of the deliveries. So, if we failed hard on one 2873299c2f1SGregory Neil Shapiro ** or more deliveries, but had no failures on any of the others, we 2883299c2f1SGregory Neil Shapiro ** return a hard failure. If we failed temporarily on one or more 2893299c2f1SGregory Neil Shapiro ** deliveries, we return a temporary failure regardless of the other 2903299c2f1SGregory Neil Shapiro ** failures. This results in the delivery being reattempted later 2913299c2f1SGregory Neil Shapiro ** at the expense of repeated failures and multiple deliveries. 292c2aa98e2SPeter Wemm */ 293b4662009SGregory Neil Shapiro 29412ed1c7cSGregory Neil Shapiro HoldErrs = true; 29512ed1c7cSGregory Neil Shapiro fd = store(from, NULL); 29612ed1c7cSGregory Neil Shapiro HoldErrs = false; 297b4662009SGregory Neil Shapiro if (fd < 0) 298b4662009SGregory Neil Shapiro { 299b4662009SGregory Neil Shapiro flush_error(); 300b4662009SGregory Neil Shapiro exit(ExitVal); 301b4662009SGregory Neil Shapiro } 302b4662009SGregory Neil Shapiro for (; *argv != NULL; ++argv) 303b4662009SGregory Neil Shapiro deliver(fd, *argv); 3043299c2f1SGregory Neil Shapiro exit(ExitVal); 3053299c2f1SGregory Neil Shapiro /* NOTREACHED */ 3063299c2f1SGregory Neil Shapiro return ExitVal; 307c2aa98e2SPeter Wemm } 308c2aa98e2SPeter Wemm 309c2aa98e2SPeter Wemm char * 3103299c2f1SGregory Neil Shapiro parseaddr(s, rcpt) 311c2aa98e2SPeter Wemm char *s; 3123299c2f1SGregory Neil Shapiro bool rcpt; 313c2aa98e2SPeter Wemm { 314c2aa98e2SPeter Wemm char *p; 3153299c2f1SGregory Neil Shapiro int l; 316c2aa98e2SPeter Wemm 317c2aa98e2SPeter Wemm if (*s++ != '<') 318c2aa98e2SPeter Wemm return NULL; 319c2aa98e2SPeter Wemm 320c2aa98e2SPeter Wemm p = s; 321c2aa98e2SPeter Wemm 322c2aa98e2SPeter Wemm /* at-domain-list */ 3233299c2f1SGregory Neil Shapiro while (*p == '@') 3243299c2f1SGregory Neil Shapiro { 325c2aa98e2SPeter Wemm p++; 3263299c2f1SGregory Neil Shapiro while (*p != ',' && *p != ':' && *p != '\0') 327c2aa98e2SPeter Wemm p++; 3283299c2f1SGregory Neil Shapiro if (*p == '\0') 329c2aa98e2SPeter Wemm return NULL; 3303299c2f1SGregory Neil Shapiro 3313299c2f1SGregory Neil Shapiro /* Skip over , or : */ 332c2aa98e2SPeter Wemm p++; 333c2aa98e2SPeter Wemm } 334c2aa98e2SPeter Wemm 33576b7bf71SPeter Wemm s = p; 33676b7bf71SPeter Wemm 337c2aa98e2SPeter Wemm /* local-part */ 3383299c2f1SGregory Neil Shapiro while (*p != '\0' && *p != '@' && *p != '>') 3393299c2f1SGregory Neil Shapiro { 3403299c2f1SGregory Neil Shapiro if (*p == '\\') 3413299c2f1SGregory Neil Shapiro { 3423299c2f1SGregory Neil Shapiro if (*++p == '\0') 3433299c2f1SGregory Neil Shapiro return NULL; 3443299c2f1SGregory Neil Shapiro } 3453299c2f1SGregory Neil Shapiro else if (*p == '\"') 3463299c2f1SGregory Neil Shapiro { 347c2aa98e2SPeter Wemm p++; 3483299c2f1SGregory Neil Shapiro while (*p != '\0' && *p != '\"') 3493299c2f1SGregory Neil Shapiro { 3503299c2f1SGregory Neil Shapiro if (*p == '\\') 3513299c2f1SGregory Neil Shapiro { 3523299c2f1SGregory Neil Shapiro if (*++p == '\0') 353c2aa98e2SPeter Wemm return NULL; 354c2aa98e2SPeter Wemm } 355c2aa98e2SPeter Wemm p++; 356c2aa98e2SPeter Wemm } 3573299c2f1SGregory Neil Shapiro if (*p == '\0' || *(p + 1) == '\0') 358c2aa98e2SPeter Wemm return NULL; 359c2aa98e2SPeter Wemm } 3603299c2f1SGregory Neil Shapiro /* +detail ? */ 3613299c2f1SGregory Neil Shapiro if (*p == '+' && rcpt) 3623299c2f1SGregory Neil Shapiro *p = '\0'; 363c2aa98e2SPeter Wemm p++; 364c2aa98e2SPeter Wemm } 365c2aa98e2SPeter Wemm 366c2aa98e2SPeter Wemm /* @domain */ 3673299c2f1SGregory Neil Shapiro if (*p == '@') 36876b7bf71SPeter Wemm { 3693299c2f1SGregory Neil Shapiro if (rcpt) 3703299c2f1SGregory Neil Shapiro *p++ = '\0'; 3713299c2f1SGregory Neil Shapiro while (*p != '\0' && *p != '>') 3723299c2f1SGregory Neil Shapiro p++; 37376b7bf71SPeter Wemm } 374c2aa98e2SPeter Wemm 3753299c2f1SGregory Neil Shapiro if (*p != '>') 3763299c2f1SGregory Neil Shapiro return NULL; 3773299c2f1SGregory Neil Shapiro else 3783299c2f1SGregory Neil Shapiro *p = '\0'; 3793299c2f1SGregory Neil Shapiro p++; 3803299c2f1SGregory Neil Shapiro 3813299c2f1SGregory Neil Shapiro if (*p != '\0' && *p != ' ') 3823299c2f1SGregory Neil Shapiro return NULL; 3833299c2f1SGregory Neil Shapiro 3843299c2f1SGregory Neil Shapiro if (*s == '\0') 3853299c2f1SGregory Neil Shapiro s = MAILER_DAEMON; 3863299c2f1SGregory Neil Shapiro 3873299c2f1SGregory Neil Shapiro l = strlen(s) + 1; 38812ed1c7cSGregory Neil Shapiro if (l < 0) 38912ed1c7cSGregory Neil Shapiro return NULL; 3903299c2f1SGregory Neil Shapiro p = malloc(l); 3913299c2f1SGregory Neil Shapiro if (p == NULL) 3923299c2f1SGregory Neil Shapiro { 393b4662009SGregory Neil Shapiro mailerr("421 4.3.0", "Memory exhausted"); 394c2aa98e2SPeter Wemm exit(EX_TEMPFAIL); 395c2aa98e2SPeter Wemm } 396c2aa98e2SPeter Wemm 39712ed1c7cSGregory Neil Shapiro (void) sm_strlcpy(p, s, l); 398c2aa98e2SPeter Wemm return p; 399c2aa98e2SPeter Wemm } 400c2aa98e2SPeter Wemm 401c2aa98e2SPeter Wemm char * 402c2aa98e2SPeter Wemm process_recipient(addr) 403c2aa98e2SPeter Wemm char *addr; 404c2aa98e2SPeter Wemm { 40512ed1c7cSGregory Neil Shapiro SM_MBDB_T user; 40612ed1c7cSGregory Neil Shapiro 40712ed1c7cSGregory Neil Shapiro switch (sm_mbdb_lookup(addr, &user)) 40812ed1c7cSGregory Neil Shapiro { 40912ed1c7cSGregory Neil Shapiro case EX_OK: 410c2aa98e2SPeter Wemm return NULL; 41112ed1c7cSGregory Neil Shapiro 41212ed1c7cSGregory Neil Shapiro case EX_NOUSER: 41312ed1c7cSGregory Neil Shapiro return "550 5.1.1 User unknown"; 41412ed1c7cSGregory Neil Shapiro 41512ed1c7cSGregory Neil Shapiro case EX_TEMPFAIL: 41612ed1c7cSGregory Neil Shapiro return "451 4.3.0 User database failure; retry later"; 41712ed1c7cSGregory Neil Shapiro 41812ed1c7cSGregory Neil Shapiro default: 41912ed1c7cSGregory Neil Shapiro return "550 5.3.0 User database failure"; 42012ed1c7cSGregory Neil Shapiro } 421c2aa98e2SPeter Wemm } 422c2aa98e2SPeter Wemm 423c2aa98e2SPeter Wemm #define RCPT_GROW 30 424c2aa98e2SPeter Wemm 425c2aa98e2SPeter Wemm void 426b4662009SGregory Neil Shapiro dolmtp() 427c2aa98e2SPeter Wemm { 428c2aa98e2SPeter Wemm char *return_path = NULL; 429c2aa98e2SPeter Wemm char **rcpt_addr = NULL; 430c2aa98e2SPeter Wemm int rcpt_num = 0; 431c2aa98e2SPeter Wemm int rcpt_alloc = 0; 43212ed1c7cSGregory Neil Shapiro bool gotlhlo = false; 433c2aa98e2SPeter Wemm char *err; 434c2aa98e2SPeter Wemm int msgfd; 435c2aa98e2SPeter Wemm char *p; 436c2aa98e2SPeter Wemm int i; 4373299c2f1SGregory Neil Shapiro char myhostname[1024]; 4383299c2f1SGregory Neil Shapiro char buf[4096]; 439c2aa98e2SPeter Wemm 440b4662009SGregory Neil Shapiro memset(myhostname, '\0', sizeof myhostname); 4413299c2f1SGregory Neil Shapiro (void) gethostname(myhostname, sizeof myhostname - 1); 442b4662009SGregory Neil Shapiro if (myhostname[0] == '\0') 44312ed1c7cSGregory Neil Shapiro sm_strlcpy(myhostname, "localhost", sizeof myhostname); 444c2aa98e2SPeter Wemm 445c2aa98e2SPeter Wemm printf("220 %s LMTP ready\r\n", myhostname); 4463299c2f1SGregory Neil Shapiro for (;;) 4473299c2f1SGregory Neil Shapiro { 4483299c2f1SGregory Neil Shapiro (void) fflush(stdout); 4493299c2f1SGregory Neil Shapiro if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) 450c2aa98e2SPeter Wemm exit(EX_OK); 451c2aa98e2SPeter Wemm p = buf + strlen(buf) - 1; 452c2aa98e2SPeter Wemm if (p >= buf && *p == '\n') 453c2aa98e2SPeter Wemm *p-- = '\0'; 454c2aa98e2SPeter Wemm if (p >= buf && *p == '\r') 455c2aa98e2SPeter Wemm *p-- = '\0'; 456c2aa98e2SPeter Wemm 4573299c2f1SGregory Neil Shapiro switch (buf[0]) 4583299c2f1SGregory Neil Shapiro { 459c2aa98e2SPeter Wemm case 'd': 460c2aa98e2SPeter Wemm case 'D': 46112ed1c7cSGregory Neil Shapiro if (sm_strcasecmp(buf, "data") == 0) 4623299c2f1SGregory Neil Shapiro { 46312ed1c7cSGregory Neil Shapiro bool inbody = false; 464b4662009SGregory Neil Shapiro 4653299c2f1SGregory Neil Shapiro if (rcpt_num == 0) 4663299c2f1SGregory Neil Shapiro { 467b4662009SGregory Neil Shapiro mailerr("503 5.5.1", "No recipients"); 468c2aa98e2SPeter Wemm continue; 469c2aa98e2SPeter Wemm } 47012ed1c7cSGregory Neil Shapiro HoldErrs = true; 47112ed1c7cSGregory Neil Shapiro msgfd = store(return_path, &inbody); 47212ed1c7cSGregory Neil Shapiro HoldErrs = false; 473b4662009SGregory Neil Shapiro if (msgfd < 0 && !inbody) 474b4662009SGregory Neil Shapiro { 475b4662009SGregory Neil Shapiro flush_error(); 476c2aa98e2SPeter Wemm continue; 477b4662009SGregory Neil Shapiro } 478c2aa98e2SPeter Wemm 4793299c2f1SGregory Neil Shapiro for (i = 0; i < rcpt_num; i++) 4803299c2f1SGregory Neil Shapiro { 481b4662009SGregory Neil Shapiro if (msgfd < 0) 482b4662009SGregory Neil Shapiro { 483b4662009SGregory Neil Shapiro /* print error for rcpt */ 484b4662009SGregory Neil Shapiro flush_error(); 485b4662009SGregory Neil Shapiro continue; 486b4662009SGregory Neil Shapiro } 487c2aa98e2SPeter Wemm p = strchr(rcpt_addr[i], '+'); 488c2aa98e2SPeter Wemm if (p != NULL) 489b4662009SGregory Neil Shapiro *p = '\0'; 490b4662009SGregory Neil Shapiro deliver(msgfd, rcpt_addr[i]); 491c2aa98e2SPeter Wemm } 492b4662009SGregory Neil Shapiro if (msgfd >= 0) 4933299c2f1SGregory Neil Shapiro (void) close(msgfd); 494c2aa98e2SPeter Wemm goto rset; 495c2aa98e2SPeter Wemm } 496c2aa98e2SPeter Wemm goto syntaxerr; 4973299c2f1SGregory Neil Shapiro /* NOTREACHED */ 4983299c2f1SGregory Neil Shapiro break; 499c2aa98e2SPeter Wemm 500c2aa98e2SPeter Wemm case 'l': 501c2aa98e2SPeter Wemm case 'L': 50212ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf, "lhlo ", 5) == 0) 5033299c2f1SGregory Neil Shapiro { 5043299c2f1SGregory Neil Shapiro /* check for duplicate per RFC 1651 4.2 */ 5053299c2f1SGregory Neil Shapiro if (gotlhlo) 5063299c2f1SGregory Neil Shapiro { 507b4662009SGregory Neil Shapiro mailerr("503", "%s Duplicate LHLO", 508c2aa98e2SPeter Wemm myhostname); 509c2aa98e2SPeter Wemm continue; 510c2aa98e2SPeter Wemm } 51112ed1c7cSGregory Neil Shapiro gotlhlo = true; 5123299c2f1SGregory Neil Shapiro printf("250-%s\r\n", myhostname); 5133299c2f1SGregory Neil Shapiro if (EightBitMime) 5143299c2f1SGregory Neil Shapiro printf("250-8BITMIME\r\n"); 5153299c2f1SGregory Neil Shapiro printf("250-ENHANCEDSTATUSCODES\r\n"); 5163299c2f1SGregory Neil Shapiro printf("250 PIPELINING\r\n"); 5173299c2f1SGregory Neil Shapiro continue; 5183299c2f1SGregory Neil Shapiro } 519c2aa98e2SPeter Wemm goto syntaxerr; 5203299c2f1SGregory Neil Shapiro /* NOTREACHED */ 5213299c2f1SGregory Neil Shapiro break; 522c2aa98e2SPeter Wemm 523c2aa98e2SPeter Wemm case 'm': 524c2aa98e2SPeter Wemm case 'M': 52512ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf, "mail ", 5) == 0) 5263299c2f1SGregory Neil Shapiro { 5273299c2f1SGregory Neil Shapiro if (return_path != NULL) 5283299c2f1SGregory Neil Shapiro { 529b4662009SGregory Neil Shapiro mailerr("503 5.5.1", 530b4662009SGregory Neil Shapiro "Nested MAIL command"); 531c2aa98e2SPeter Wemm continue; 532c2aa98e2SPeter Wemm } 53312ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf + 5, "from:", 5) != 0 || 5343299c2f1SGregory Neil Shapiro ((return_path = parseaddr(buf + 10, 53512ed1c7cSGregory Neil Shapiro false)) == NULL)) 5363299c2f1SGregory Neil Shapiro { 537b4662009SGregory Neil Shapiro mailerr("501 5.5.4", 538b4662009SGregory Neil Shapiro "Syntax error in parameters"); 539c2aa98e2SPeter Wemm continue; 540c2aa98e2SPeter Wemm } 541b4662009SGregory Neil Shapiro printf("250 2.5.0 Ok\r\n"); 542c2aa98e2SPeter Wemm continue; 543c2aa98e2SPeter Wemm } 544c2aa98e2SPeter Wemm goto syntaxerr; 5453299c2f1SGregory Neil Shapiro /* NOTREACHED */ 5463299c2f1SGregory Neil Shapiro break; 547c2aa98e2SPeter Wemm 548c2aa98e2SPeter Wemm case 'n': 549c2aa98e2SPeter Wemm case 'N': 55012ed1c7cSGregory Neil Shapiro if (sm_strcasecmp(buf, "noop") == 0) 5513299c2f1SGregory Neil Shapiro { 552b4662009SGregory Neil Shapiro printf("250 2.0.0 Ok\r\n"); 553c2aa98e2SPeter Wemm continue; 554c2aa98e2SPeter Wemm } 555c2aa98e2SPeter Wemm goto syntaxerr; 5563299c2f1SGregory Neil Shapiro /* NOTREACHED */ 5573299c2f1SGregory Neil Shapiro break; 558c2aa98e2SPeter Wemm 559c2aa98e2SPeter Wemm case 'q': 560c2aa98e2SPeter Wemm case 'Q': 56112ed1c7cSGregory Neil Shapiro if (sm_strcasecmp(buf, "quit") == 0) 5623299c2f1SGregory Neil Shapiro { 563b4662009SGregory Neil Shapiro printf("221 2.0.0 Bye\r\n"); 564c2aa98e2SPeter Wemm exit(EX_OK); 565c2aa98e2SPeter Wemm } 566c2aa98e2SPeter Wemm goto syntaxerr; 5673299c2f1SGregory Neil Shapiro /* NOTREACHED */ 5683299c2f1SGregory Neil Shapiro break; 569c2aa98e2SPeter Wemm 570c2aa98e2SPeter Wemm case 'r': 571c2aa98e2SPeter Wemm case 'R': 57212ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf, "rcpt ", 5) == 0) 5733299c2f1SGregory Neil Shapiro { 5743299c2f1SGregory Neil Shapiro if (return_path == NULL) 5753299c2f1SGregory Neil Shapiro { 576b4662009SGregory Neil Shapiro mailerr("503 5.5.1", 577b4662009SGregory Neil Shapiro "Need MAIL command"); 578c2aa98e2SPeter Wemm continue; 579c2aa98e2SPeter Wemm } 5803299c2f1SGregory Neil Shapiro if (rcpt_num >= rcpt_alloc) 5813299c2f1SGregory Neil Shapiro { 582c2aa98e2SPeter Wemm rcpt_alloc += RCPT_GROW; 583c2aa98e2SPeter Wemm rcpt_addr = (char **) 5843299c2f1SGregory Neil Shapiro REALLOC((char *) rcpt_addr, 5853299c2f1SGregory Neil Shapiro rcpt_alloc * 5863299c2f1SGregory Neil Shapiro sizeof(char **)); 5873299c2f1SGregory Neil Shapiro if (rcpt_addr == NULL) 5883299c2f1SGregory Neil Shapiro { 589b4662009SGregory Neil Shapiro mailerr("421 4.3.0", 590b4662009SGregory Neil Shapiro "Memory exhausted"); 591c2aa98e2SPeter Wemm exit(EX_TEMPFAIL); 592c2aa98e2SPeter Wemm } 593c2aa98e2SPeter Wemm } 59412ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf + 5, "to:", 3) != 0 || 5953299c2f1SGregory Neil Shapiro ((rcpt_addr[rcpt_num] = parseaddr(buf + 8, 59612ed1c7cSGregory Neil Shapiro true)) == NULL)) 5973299c2f1SGregory Neil Shapiro { 598b4662009SGregory Neil Shapiro mailerr("501 5.5.4", 599b4662009SGregory Neil Shapiro "Syntax error in parameters"); 600c2aa98e2SPeter Wemm continue; 601c2aa98e2SPeter Wemm } 602b4662009SGregory Neil Shapiro err = process_recipient(rcpt_addr[rcpt_num]); 603b4662009SGregory Neil Shapiro if (err != NULL) 6043299c2f1SGregory Neil Shapiro { 605b4662009SGregory Neil Shapiro mailerr(NULL, "%s", err); 606c2aa98e2SPeter Wemm continue; 607c2aa98e2SPeter Wemm } 608c2aa98e2SPeter Wemm rcpt_num++; 609b4662009SGregory Neil Shapiro printf("250 2.1.5 Ok\r\n"); 610c2aa98e2SPeter Wemm continue; 611c2aa98e2SPeter Wemm } 61212ed1c7cSGregory Neil Shapiro else if (sm_strcasecmp(buf, "rset") == 0) 6133299c2f1SGregory Neil Shapiro { 614b4662009SGregory Neil Shapiro printf("250 2.0.0 Ok\r\n"); 615c2aa98e2SPeter Wemm 616c2aa98e2SPeter Wemm rset: 617c46d91b7SGregory Neil Shapiro while (rcpt_num > 0) 618c2aa98e2SPeter Wemm free(rcpt_addr[--rcpt_num]); 619c2aa98e2SPeter Wemm if (return_path != NULL) 620c2aa98e2SPeter Wemm free(return_path); 621c2aa98e2SPeter Wemm return_path = NULL; 622c2aa98e2SPeter Wemm continue; 623c2aa98e2SPeter Wemm } 624c2aa98e2SPeter Wemm goto syntaxerr; 6253299c2f1SGregory Neil Shapiro /* NOTREACHED */ 6263299c2f1SGregory Neil Shapiro break; 627c2aa98e2SPeter Wemm 628c2aa98e2SPeter Wemm case 'v': 629c2aa98e2SPeter Wemm case 'V': 63012ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf, "vrfy ", 5) == 0) 6313299c2f1SGregory Neil Shapiro { 632b4662009SGregory Neil Shapiro printf("252 2.3.3 Try RCPT to attempt delivery\r\n"); 633c2aa98e2SPeter Wemm continue; 634c2aa98e2SPeter Wemm } 635c2aa98e2SPeter Wemm goto syntaxerr; 6363299c2f1SGregory Neil Shapiro /* NOTREACHED */ 6373299c2f1SGregory Neil Shapiro break; 638c2aa98e2SPeter Wemm 639c2aa98e2SPeter Wemm default: 640c2aa98e2SPeter Wemm syntaxerr: 641b4662009SGregory Neil Shapiro mailerr("500 5.5.2", "Syntax error"); 642c2aa98e2SPeter Wemm continue; 6433299c2f1SGregory Neil Shapiro /* NOTREACHED */ 6443299c2f1SGregory Neil Shapiro break; 645c2aa98e2SPeter Wemm } 646c2aa98e2SPeter Wemm } 647c2aa98e2SPeter Wemm } 648c2aa98e2SPeter Wemm 649c2aa98e2SPeter Wemm int 65012ed1c7cSGregory Neil Shapiro store(from, inbody) 651c2aa98e2SPeter Wemm char *from; 652b4662009SGregory Neil Shapiro bool *inbody; 653c2aa98e2SPeter Wemm { 65476b7bf71SPeter Wemm FILE *fp = NULL; 655c2aa98e2SPeter Wemm time_t tval; 65612ed1c7cSGregory Neil Shapiro bool eline; /* previous line was empty */ 65712ed1c7cSGregory Neil Shapiro bool fullline = true; /* current line is terminated */ 6583299c2f1SGregory Neil Shapiro bool prevfl; /* previous line was terminated */ 659c2aa98e2SPeter Wemm char line[2048]; 6603299c2f1SGregory Neil Shapiro int fd; 661c2aa98e2SPeter Wemm char tmpbuf[sizeof _PATH_LOCTMP + 1]; 662c2aa98e2SPeter Wemm 663b4662009SGregory Neil Shapiro if (inbody != NULL) 66412ed1c7cSGregory Neil Shapiro *inbody = false; 665b4662009SGregory Neil Shapiro 6663299c2f1SGregory Neil Shapiro (void) umask(0077); 66712ed1c7cSGregory Neil Shapiro (void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf); 668b4662009SGregory Neil Shapiro if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL) 6693299c2f1SGregory Neil Shapiro { 6702ef40764SGregory Neil Shapiro if (fd >= 0) 6712ef40764SGregory Neil Shapiro (void) close(fd); 672b4662009SGregory Neil Shapiro mailerr("451 4.3.0", "Unable to open temporary file"); 673c2aa98e2SPeter Wemm return -1; 6743299c2f1SGregory Neil Shapiro } 675c2aa98e2SPeter Wemm (void) unlink(tmpbuf); 676c2aa98e2SPeter Wemm 6773299c2f1SGregory Neil Shapiro if (LMTPMode) 6783299c2f1SGregory Neil Shapiro { 679b4662009SGregory Neil Shapiro printf("354 Go ahead\r\n"); 6803299c2f1SGregory Neil Shapiro (void) fflush(stdout); 681c2aa98e2SPeter Wemm } 682b4662009SGregory Neil Shapiro if (inbody != NULL) 68312ed1c7cSGregory Neil Shapiro *inbody = true; 684c2aa98e2SPeter Wemm 685c2aa98e2SPeter Wemm (void) time(&tval); 686c2aa98e2SPeter Wemm (void) fprintf(fp, "From %s %s", from, ctime(&tval)); 687c2aa98e2SPeter Wemm 6883299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 6893299c2f1SGregory Neil Shapiro HeaderLength = 0; 6903299c2f1SGregory Neil Shapiro BodyLength = -1; 6913299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 6923299c2f1SGregory Neil Shapiro 693c2aa98e2SPeter Wemm line[0] = '\0'; 69412ed1c7cSGregory Neil Shapiro eline = true; 6953299c2f1SGregory Neil Shapiro while (fgets(line, sizeof(line), stdin) != (char *) NULL) 6963299c2f1SGregory Neil Shapiro { 6973299c2f1SGregory Neil Shapiro size_t line_len = 0; 6983299c2f1SGregory Neil Shapiro int peek; 69976b7bf71SPeter Wemm 7003299c2f1SGregory Neil Shapiro prevfl = fullline; /* preserve state of previous line */ 7013299c2f1SGregory Neil Shapiro while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 7023299c2f1SGregory Neil Shapiro line_len++; 7033299c2f1SGregory Neil Shapiro line_len++; 70476b7bf71SPeter Wemm 7053299c2f1SGregory Neil Shapiro /* Check for dot-stuffing */ 706b4662009SGregory Neil Shapiro if (prevfl && LMTPMode && line[0] == '.') 7073299c2f1SGregory Neil Shapiro { 7083299c2f1SGregory Neil Shapiro if (line[1] == '\n' || 7093299c2f1SGregory Neil Shapiro (line[1] == '\r' && line[2] == '\n')) 710c2aa98e2SPeter Wemm goto lmtpdot; 7113299c2f1SGregory Neil Shapiro memcpy(line, line + 1, line_len); 7123299c2f1SGregory Neil Shapiro line_len--; 713c2aa98e2SPeter Wemm } 7143299c2f1SGregory Neil Shapiro 7153299c2f1SGregory Neil Shapiro /* Check to see if we have the full line from fgets() */ 71612ed1c7cSGregory Neil Shapiro fullline = false; 7173299c2f1SGregory Neil Shapiro if (line_len > 0) 7183299c2f1SGregory Neil Shapiro { 7193299c2f1SGregory Neil Shapiro if (line[line_len - 1] == '\n') 7203299c2f1SGregory Neil Shapiro { 7213299c2f1SGregory Neil Shapiro if (line_len >= 2 && 7223299c2f1SGregory Neil Shapiro line[line_len - 2] == '\r') 7233299c2f1SGregory Neil Shapiro { 7243299c2f1SGregory Neil Shapiro line[line_len - 2] = '\n'; 7253299c2f1SGregory Neil Shapiro line[line_len - 1] = '\0'; 7263299c2f1SGregory Neil Shapiro line_len--; 7273299c2f1SGregory Neil Shapiro } 72812ed1c7cSGregory Neil Shapiro fullline = true; 7293299c2f1SGregory Neil Shapiro } 7303299c2f1SGregory Neil Shapiro else if (line[line_len - 1] == '\r') 7313299c2f1SGregory Neil Shapiro { 7323299c2f1SGregory Neil Shapiro /* Did we just miss the CRLF? */ 7333299c2f1SGregory Neil Shapiro peek = fgetc(stdin); 7343299c2f1SGregory Neil Shapiro if (peek == '\n') 7353299c2f1SGregory Neil Shapiro { 7363299c2f1SGregory Neil Shapiro line[line_len - 1] = '\n'; 73712ed1c7cSGregory Neil Shapiro fullline = true; 7383299c2f1SGregory Neil Shapiro } 7393299c2f1SGregory Neil Shapiro else 7403299c2f1SGregory Neil Shapiro (void) ungetc(peek, stdin); 7413299c2f1SGregory Neil Shapiro } 7423299c2f1SGregory Neil Shapiro } 7433299c2f1SGregory Neil Shapiro else 74412ed1c7cSGregory Neil Shapiro fullline = true; 7453299c2f1SGregory Neil Shapiro 7463299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 7473299c2f1SGregory Neil Shapiro if (prevfl && line[0] == '\n' && HeaderLength == 0) 7483299c2f1SGregory Neil Shapiro { 74912ed1c7cSGregory Neil Shapiro eline = false; 750b4662009SGregory Neil Shapiro if (fp != NULL) 7513299c2f1SGregory Neil Shapiro HeaderLength = ftell(fp); 7523299c2f1SGregory Neil Shapiro if (HeaderLength <= 0) 7533299c2f1SGregory Neil Shapiro { 7543299c2f1SGregory Neil Shapiro /* 7553299c2f1SGregory Neil Shapiro ** shouldn't happen, unless ftell() is 7563299c2f1SGregory Neil Shapiro ** badly broken 7573299c2f1SGregory Neil Shapiro */ 7583299c2f1SGregory Neil Shapiro 7593299c2f1SGregory Neil Shapiro HeaderLength = -1; 7603299c2f1SGregory Neil Shapiro } 7613299c2f1SGregory Neil Shapiro } 7623299c2f1SGregory Neil Shapiro #else /* CONTENTLENGTH */ 7633299c2f1SGregory Neil Shapiro if (prevfl && line[0] == '\n') 76412ed1c7cSGregory Neil Shapiro eline = true; 7653299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 7663299c2f1SGregory Neil Shapiro else 7673299c2f1SGregory Neil Shapiro { 768c2aa98e2SPeter Wemm if (eline && line[0] == 'F' && 769b4662009SGregory Neil Shapiro fp != NULL && 770c2aa98e2SPeter Wemm !memcmp(line, "From ", 5)) 771c2aa98e2SPeter Wemm (void) putc('>', fp); 77212ed1c7cSGregory Neil Shapiro eline = false; 7733299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 7743299c2f1SGregory Neil Shapiro /* discard existing "Content-Length:" headers */ 7753299c2f1SGregory Neil Shapiro if (prevfl && HeaderLength == 0 && 7763299c2f1SGregory Neil Shapiro (line[0] == 'C' || line[0] == 'c') && 77712ed1c7cSGregory Neil Shapiro sm_strncasecmp(line, ContentHdr, 15) == 0) 7783299c2f1SGregory Neil Shapiro { 7793299c2f1SGregory Neil Shapiro /* 7803299c2f1SGregory Neil Shapiro ** be paranoid: clear the line 7813299c2f1SGregory Neil Shapiro ** so no "wrong matches" may occur later 7823299c2f1SGregory Neil Shapiro */ 7833299c2f1SGregory Neil Shapiro line[0] = '\0'; 7843299c2f1SGregory Neil Shapiro continue; 785c2aa98e2SPeter Wemm } 7863299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 7873299c2f1SGregory Neil Shapiro 7883299c2f1SGregory Neil Shapiro } 789b4662009SGregory Neil Shapiro if (fp != NULL) 790b4662009SGregory Neil Shapiro { 7913299c2f1SGregory Neil Shapiro (void) fwrite(line, sizeof(char), line_len, fp); 7923299c2f1SGregory Neil Shapiro if (ferror(fp)) 7933299c2f1SGregory Neil Shapiro { 79476b7bf71SPeter Wemm mailerr("451 4.3.0", 795b4662009SGregory Neil Shapiro "Temporary file write error"); 7963299c2f1SGregory Neil Shapiro (void) fclose(fp); 797b4662009SGregory Neil Shapiro fp = NULL; 798b4662009SGregory Neil Shapiro continue; 799c2aa98e2SPeter Wemm } 800c2aa98e2SPeter Wemm } 801c2aa98e2SPeter Wemm } 802c2aa98e2SPeter Wemm 803b4662009SGregory Neil Shapiro /* check if an error occurred */ 804b4662009SGregory Neil Shapiro if (fp == NULL) 805b4662009SGregory Neil Shapiro return -1; 806b4662009SGregory Neil Shapiro 807b4662009SGregory Neil Shapiro if (LMTPMode) 8083299c2f1SGregory Neil Shapiro { 809c2aa98e2SPeter Wemm /* Got a premature EOF -- toss message and exit */ 810c2aa98e2SPeter Wemm exit(EX_OK); 811c2aa98e2SPeter Wemm } 812c2aa98e2SPeter Wemm 813c2aa98e2SPeter Wemm /* If message not newline terminated, need an extra. */ 814b4662009SGregory Neil Shapiro if (fp != NULL && strchr(line, '\n') == NULL) 815c2aa98e2SPeter Wemm (void) putc('\n', fp); 816c2aa98e2SPeter Wemm 817c2aa98e2SPeter Wemm lmtpdot: 818c2aa98e2SPeter Wemm 8193299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 820b4662009SGregory Neil Shapiro if (fp != NULL) 8213299c2f1SGregory Neil Shapiro BodyLength = ftell(fp); 8223299c2f1SGregory Neil Shapiro if (HeaderLength == 0 && BodyLength > 0) /* empty body */ 8233299c2f1SGregory Neil Shapiro { 8243299c2f1SGregory Neil Shapiro HeaderLength = BodyLength; 8253299c2f1SGregory Neil Shapiro BodyLength = 0; 8263299c2f1SGregory Neil Shapiro } 8273299c2f1SGregory Neil Shapiro else 8283299c2f1SGregory Neil Shapiro BodyLength = BodyLength - HeaderLength - 1 ; 8293299c2f1SGregory Neil Shapiro 8303299c2f1SGregory Neil Shapiro if (HeaderLength > 0 && BodyLength >= 0) 8313299c2f1SGregory Neil Shapiro { 83212ed1c7cSGregory Neil Shapiro (void) sm_snprintf(line, sizeof line, "%lld\n", 83312ed1c7cSGregory Neil Shapiro (LONGLONG_T) BodyLength); 83412ed1c7cSGregory Neil Shapiro (void) sm_strlcpy(&ContentHdr[16], line, 83512ed1c7cSGregory Neil Shapiro sizeof(ContentHdr) - 16); 8363299c2f1SGregory Neil Shapiro } 8373299c2f1SGregory Neil Shapiro else 8383299c2f1SGregory Neil Shapiro BodyLength = -1; /* Something is wrong here */ 8393299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 8403299c2f1SGregory Neil Shapiro 841c2aa98e2SPeter Wemm /* Output a newline; note, empty messages are allowed. */ 842b4662009SGregory Neil Shapiro if (fp != NULL) 843c2aa98e2SPeter Wemm (void) putc('\n', fp); 844c2aa98e2SPeter Wemm 845b4662009SGregory Neil Shapiro if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0) 8463299c2f1SGregory Neil Shapiro { 847b4662009SGregory Neil Shapiro mailerr("451 4.3.0", "Temporary file write error"); 848b4662009SGregory Neil Shapiro if (fp != NULL) 8493299c2f1SGregory Neil Shapiro (void) fclose(fp); 850c2aa98e2SPeter Wemm return -1; 8513299c2f1SGregory Neil Shapiro } 8523299c2f1SGregory Neil Shapiro return fd; 853c2aa98e2SPeter Wemm } 854c2aa98e2SPeter Wemm 855c2aa98e2SPeter Wemm void 856b4662009SGregory Neil Shapiro deliver(fd, name) 857c2aa98e2SPeter Wemm int fd; 858c2aa98e2SPeter Wemm char *name; 859c2aa98e2SPeter Wemm { 8603299c2f1SGregory Neil Shapiro struct stat fsb; 8613299c2f1SGregory Neil Shapiro struct stat sb; 8623299c2f1SGregory Neil Shapiro char path[MAXPATHLEN]; 863c46d91b7SGregory Neil Shapiro int mbfd = -1, nr = 0, nw, off; 86412ed1c7cSGregory Neil Shapiro int exitval; 865c2aa98e2SPeter Wemm char *p; 866b4662009SGregory Neil Shapiro char *errcode; 8677660b554SGregory Neil Shapiro off_t curoff, cursize; 8683299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 8693299c2f1SGregory Neil Shapiro off_t headerbytes; 8703299c2f1SGregory Neil Shapiro int readamount; 8713299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 8723299c2f1SGregory Neil Shapiro char biffmsg[100], buf[8 * 1024]; 87312ed1c7cSGregory Neil Shapiro SM_MBDB_T user; 8743299c2f1SGregory Neil Shapiro 875c2aa98e2SPeter Wemm /* 8763299c2f1SGregory Neil Shapiro ** Disallow delivery to unknown names -- special mailboxes can be 8773299c2f1SGregory Neil Shapiro ** handled in the sendmail aliases file. 878c2aa98e2SPeter Wemm */ 879b4662009SGregory Neil Shapiro 88012ed1c7cSGregory Neil Shapiro exitval = sm_mbdb_lookup(name, &user); 88112ed1c7cSGregory Neil Shapiro switch (exitval) 8823299c2f1SGregory Neil Shapiro { 88312ed1c7cSGregory Neil Shapiro case EX_OK: 88412ed1c7cSGregory Neil Shapiro break; 88512ed1c7cSGregory Neil Shapiro 88612ed1c7cSGregory Neil Shapiro case EX_NOUSER: 88712ed1c7cSGregory Neil Shapiro exitval = EX_UNAVAILABLE; 88812ed1c7cSGregory Neil Shapiro mailerr("550 5.1.1", "%s: User unknown", name); 88912ed1c7cSGregory Neil Shapiro break; 89012ed1c7cSGregory Neil Shapiro 89112ed1c7cSGregory Neil Shapiro case EX_TEMPFAIL: 89212ed1c7cSGregory Neil Shapiro mailerr("451 4.3.0", "%s: User database failure; retry later", 89312ed1c7cSGregory Neil Shapiro name); 89412ed1c7cSGregory Neil Shapiro break; 89512ed1c7cSGregory Neil Shapiro 89612ed1c7cSGregory Neil Shapiro default: 89712ed1c7cSGregory Neil Shapiro exitval = EX_UNAVAILABLE; 89812ed1c7cSGregory Neil Shapiro mailerr("550 5.3.0", "%s: User database failure", name); 89912ed1c7cSGregory Neil Shapiro break; 900c2aa98e2SPeter Wemm } 90112ed1c7cSGregory Neil Shapiro 90212ed1c7cSGregory Neil Shapiro if (exitval != EX_OK) 90312ed1c7cSGregory Neil Shapiro { 90412ed1c7cSGregory Neil Shapiro if (ExitVal != EX_TEMPFAIL) 90512ed1c7cSGregory Neil Shapiro ExitVal = exitval; 906c2aa98e2SPeter Wemm return; 907c2aa98e2SPeter Wemm } 90812ed1c7cSGregory Neil Shapiro 909c2aa98e2SPeter Wemm endpwent(); 910c2aa98e2SPeter Wemm 911c2aa98e2SPeter Wemm /* 9123299c2f1SGregory Neil Shapiro ** Keep name reasonably short to avoid buffer overruns. 9133299c2f1SGregory Neil Shapiro ** This isn't necessary on BSD because of the proper 9143299c2f1SGregory Neil Shapiro ** definition of snprintf(), but it can cause problems 9153299c2f1SGregory Neil Shapiro ** on other systems. 9163299c2f1SGregory Neil Shapiro ** Also, clear out any bogus characters. 917c2aa98e2SPeter Wemm */ 918c2aa98e2SPeter Wemm 919c2aa98e2SPeter Wemm if (strlen(name) > 40) 920c2aa98e2SPeter Wemm name[40] = '\0'; 921c2aa98e2SPeter Wemm for (p = name; *p != '\0'; p++) 922c2aa98e2SPeter Wemm { 923c2aa98e2SPeter Wemm if (!isascii(*p)) 924c2aa98e2SPeter Wemm *p &= 0x7f; 925c2aa98e2SPeter Wemm else if (!isprint(*p)) 926c2aa98e2SPeter Wemm *p = '.'; 927c2aa98e2SPeter Wemm } 928c2aa98e2SPeter Wemm 9293299c2f1SGregory Neil Shapiro 93012ed1c7cSGregory Neil Shapiro if (HomeMailFile == NULL) 93112ed1c7cSGregory Neil Shapiro { 93212ed1c7cSGregory Neil Shapiro if (sm_snprintf(path, sizeof(path), "%s/%s", 93312ed1c7cSGregory Neil Shapiro _PATH_MAILDIR, name) >= sizeof(path)) 93412ed1c7cSGregory Neil Shapiro { 93512ed1c7cSGregory Neil Shapiro exitval = EX_UNAVAILABLE; 93612ed1c7cSGregory Neil Shapiro mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 93712ed1c7cSGregory Neil Shapiro return; 93812ed1c7cSGregory Neil Shapiro } 93912ed1c7cSGregory Neil Shapiro } 94012ed1c7cSGregory Neil Shapiro else if (*user.mbdb_homedir == '\0') 94112ed1c7cSGregory Neil Shapiro { 94212ed1c7cSGregory Neil Shapiro exitval = EX_UNAVAILABLE; 94312ed1c7cSGregory Neil Shapiro mailerr("550 5.1.1", "%s: User missing home directory", name); 94412ed1c7cSGregory Neil Shapiro return; 94512ed1c7cSGregory Neil Shapiro } 94612ed1c7cSGregory Neil Shapiro else if (sm_snprintf(path, sizeof(path), "%s/%s", 94712ed1c7cSGregory Neil Shapiro user.mbdb_homedir, HomeMailFile) >= sizeof(path)) 94812ed1c7cSGregory Neil Shapiro { 94912ed1c7cSGregory Neil Shapiro exitval = EX_UNAVAILABLE; 95012ed1c7cSGregory Neil Shapiro mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 95112ed1c7cSGregory Neil Shapiro return; 95212ed1c7cSGregory Neil Shapiro } 953c2aa98e2SPeter Wemm 9543299c2f1SGregory Neil Shapiro 955c2aa98e2SPeter Wemm /* 9563299c2f1SGregory Neil Shapiro ** If the mailbox is linked or a symlink, fail. There's an obvious 9573299c2f1SGregory Neil Shapiro ** race here, that the file was replaced with a symbolic link after 9583299c2f1SGregory Neil Shapiro ** the lstat returned, but before the open. We attempt to detect 9593299c2f1SGregory Neil Shapiro ** this by comparing the original stat information and information 9603299c2f1SGregory Neil Shapiro ** returned by an fstat of the file descriptor returned by the open. 9613299c2f1SGregory Neil Shapiro ** 9623299c2f1SGregory Neil Shapiro ** NB: this is a symptom of a larger problem, that the mail spooling 9633299c2f1SGregory Neil Shapiro ** directory is writeable by the wrong users. If that directory is 9643299c2f1SGregory Neil Shapiro ** writeable, system security is compromised for other reasons, and 9653299c2f1SGregory Neil Shapiro ** it cannot be fixed here. 9663299c2f1SGregory Neil Shapiro ** 9673299c2f1SGregory Neil Shapiro ** If we created the mailbox, set the owner/group. If that fails, 9683299c2f1SGregory Neil Shapiro ** just return. Another process may have already opened it, so we 9693299c2f1SGregory Neil Shapiro ** can't unlink it. Historically, binmail set the owner/group at 9703299c2f1SGregory Neil Shapiro ** each mail delivery. We no longer do this, assuming that if the 9713299c2f1SGregory Neil Shapiro ** ownership or permissions were changed there was a reason. 9723299c2f1SGregory Neil Shapiro ** 9733299c2f1SGregory Neil Shapiro ** XXX 9743299c2f1SGregory Neil Shapiro ** open(2) should support flock'ing the file. 975c2aa98e2SPeter Wemm */ 9763299c2f1SGregory Neil Shapiro 977c2aa98e2SPeter Wemm tryagain: 9783299c2f1SGregory Neil Shapiro #ifdef MAILLOCK 9793299c2f1SGregory Neil Shapiro p = name; 9803299c2f1SGregory Neil Shapiro #else /* MAILLOCK */ 9813299c2f1SGregory Neil Shapiro p = path; 9823299c2f1SGregory Neil Shapiro #endif /* MAILLOCK */ 9833299c2f1SGregory Neil Shapiro if ((off = lockmbox(p)) != 0) 9843299c2f1SGregory Neil Shapiro { 9853299c2f1SGregory Neil Shapiro if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL) 9863299c2f1SGregory Neil Shapiro { 9873299c2f1SGregory Neil Shapiro ExitVal = EX_TEMPFAIL; 988b4662009SGregory Neil Shapiro errcode = "451 4.3.0"; 9893299c2f1SGregory Neil Shapiro } 9903299c2f1SGregory Neil Shapiro else 991b4662009SGregory Neil Shapiro errcode = "551 5.3.0"; 992b4662009SGregory Neil Shapiro 993b4662009SGregory Neil Shapiro mailerr(errcode, "lockmailbox %s failed; error code %d %s", 99412ed1c7cSGregory Neil Shapiro p, off, errno > 0 ? sm_errstring(errno) : ""); 9953299c2f1SGregory Neil Shapiro return; 9963299c2f1SGregory Neil Shapiro } 9973299c2f1SGregory Neil Shapiro 998c2aa98e2SPeter Wemm if (lstat(path, &sb) < 0) 999c2aa98e2SPeter Wemm { 10003299c2f1SGregory Neil Shapiro int save_errno; 10013299c2f1SGregory Neil Shapiro int mode = S_IRUSR|S_IWUSR; 100212ed1c7cSGregory Neil Shapiro gid_t gid = user.mbdb_gid; 10033299c2f1SGregory Neil Shapiro 10043299c2f1SGregory Neil Shapiro #ifdef MAILGID 10053299c2f1SGregory Neil Shapiro (void) umask(0007); 10063299c2f1SGregory Neil Shapiro gid = MAILGID; 10073299c2f1SGregory Neil Shapiro mode |= S_IRGRP|S_IWGRP; 10083299c2f1SGregory Neil Shapiro #endif /* MAILGID */ 10093299c2f1SGregory Neil Shapiro 10107660b554SGregory Neil Shapiro mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, 1011d995d2baSGregory Neil Shapiro mode); 10123299c2f1SGregory Neil Shapiro save_errno = errno; 10133299c2f1SGregory Neil Shapiro 10143299c2f1SGregory Neil Shapiro if (lstat(path, &sb) < 0) 10153299c2f1SGregory Neil Shapiro { 10163299c2f1SGregory Neil Shapiro ExitVal = EX_CANTCREAT; 101776b7bf71SPeter Wemm mailerr("550 5.2.0", 101876b7bf71SPeter Wemm "%s: lstat: file changed after open", path); 1019c2aa98e2SPeter Wemm goto err1; 1020c2aa98e2SPeter Wemm } 1021b4662009SGregory Neil Shapiro if (mbfd < 0) 10223299c2f1SGregory Neil Shapiro { 10233299c2f1SGregory Neil Shapiro if (save_errno == EEXIST) 1024c2aa98e2SPeter Wemm goto tryagain; 1025d995d2baSGregory Neil Shapiro 1026d995d2baSGregory Neil Shapiro /* open failed, don't try again */ 1027d995d2baSGregory Neil Shapiro mailerr("450 4.2.0", "%s: %s", path, 102812ed1c7cSGregory Neil Shapiro sm_errstring(save_errno)); 1029d995d2baSGregory Neil Shapiro goto err0; 10303299c2f1SGregory Neil Shapiro } 103112ed1c7cSGregory Neil Shapiro else if (fchown(mbfd, user.mbdb_uid, gid) < 0) 10323299c2f1SGregory Neil Shapiro { 1033c2aa98e2SPeter Wemm mailerr("451 4.3.0", "chown %u.%u: %s", 103412ed1c7cSGregory Neil Shapiro user.mbdb_uid, gid, name); 1035c2aa98e2SPeter Wemm goto err1; 1036c2aa98e2SPeter Wemm } 1037d995d2baSGregory Neil Shapiro else 1038d995d2baSGregory Neil Shapiro { 1039d995d2baSGregory Neil Shapiro /* 1040d995d2baSGregory Neil Shapiro ** open() was successful, now close it so can 1041d995d2baSGregory Neil Shapiro ** be opened as the right owner again. 1042d995d2baSGregory Neil Shapiro ** Paranoia: reset mbdf since the file descriptor 1043d995d2baSGregory Neil Shapiro ** is no longer valid; better safe than sorry. 1044d995d2baSGregory Neil Shapiro */ 1045d995d2baSGregory Neil Shapiro 104612ed1c7cSGregory Neil Shapiro sb.st_uid = user.mbdb_uid; 1047d995d2baSGregory Neil Shapiro (void) close(mbfd); 1048d995d2baSGregory Neil Shapiro mbfd = -1; 1049d995d2baSGregory Neil Shapiro } 10503299c2f1SGregory Neil Shapiro } 1051f9218d3dSGregory Neil Shapiro else if (sb.st_nlink != 1) 1052f9218d3dSGregory Neil Shapiro { 1053f9218d3dSGregory Neil Shapiro mailerr("550 5.2.0", "%s: too many links", path); 1054f9218d3dSGregory Neil Shapiro goto err0; 1055f9218d3dSGregory Neil Shapiro } 1056f9218d3dSGregory Neil Shapiro else if (!S_ISREG(sb.st_mode)) 10573299c2f1SGregory Neil Shapiro { 1058c2aa98e2SPeter Wemm mailerr("550 5.2.0", "%s: irregular file", path); 1059c2aa98e2SPeter Wemm goto err0; 10603299c2f1SGregory Neil Shapiro } 106112ed1c7cSGregory Neil Shapiro else if (sb.st_uid != user.mbdb_uid) 10623299c2f1SGregory Neil Shapiro { 10633299c2f1SGregory Neil Shapiro ExitVal = EX_CANTCREAT; 1064c2aa98e2SPeter Wemm mailerr("550 5.2.0", "%s: wrong ownership (%d)", 106512ed1c7cSGregory Neil Shapiro path, (int) sb.st_uid); 1066c2aa98e2SPeter Wemm goto err0; 1067c2aa98e2SPeter Wemm } 1068c2aa98e2SPeter Wemm 1069d995d2baSGregory Neil Shapiro /* change UID for quota checks */ 107012ed1c7cSGregory Neil Shapiro if (setreuid(0, user.mbdb_uid) < 0) 1071d995d2baSGregory Neil Shapiro { 1072d995d2baSGregory Neil Shapiro mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", 107312ed1c7cSGregory Neil Shapiro (int) user.mbdb_uid, sm_errstring(errno), 107412ed1c7cSGregory Neil Shapiro (int) getuid(), (int) geteuid()); 1075d995d2baSGregory Neil Shapiro goto err1; 1076d995d2baSGregory Neil Shapiro } 1077d995d2baSGregory Neil Shapiro #ifdef DEBUG 107812ed1c7cSGregory Neil Shapiro fprintf(stderr, "new euid = %d\n", (int) geteuid()); 1079d995d2baSGregory Neil Shapiro #endif /* DEBUG */ 10807660b554SGregory Neil Shapiro mbfd = open(path, O_APPEND|O_WRONLY, 0); 1081d995d2baSGregory Neil Shapiro if (mbfd < 0) 10823299c2f1SGregory Neil Shapiro { 108312ed1c7cSGregory Neil Shapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1084c2aa98e2SPeter Wemm goto err0; 10853299c2f1SGregory Neil Shapiro } 10863299c2f1SGregory Neil Shapiro else if (fstat(mbfd, &fsb) < 0 || 1087c2aa98e2SPeter Wemm fsb.st_nlink != 1 || 1088c2aa98e2SPeter Wemm sb.st_nlink != 1 || 1089c2aa98e2SPeter Wemm !S_ISREG(fsb.st_mode) || 1090c2aa98e2SPeter Wemm sb.st_dev != fsb.st_dev || 1091c2aa98e2SPeter Wemm sb.st_ino != fsb.st_ino || 1092c2aa98e2SPeter Wemm # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 1093c2aa98e2SPeter Wemm sb.st_gen != fsb.st_gen || 10943299c2f1SGregory Neil Shapiro # endif /* HAS_ST_GEN && 0 */ 10953299c2f1SGregory Neil Shapiro sb.st_uid != fsb.st_uid) 10963299c2f1SGregory Neil Shapiro { 10973299c2f1SGregory Neil Shapiro ExitVal = EX_TEMPFAIL; 109876b7bf71SPeter Wemm mailerr("550 5.2.0", "%s: fstat: file changed after open", 109976b7bf71SPeter Wemm path); 1100c2aa98e2SPeter Wemm goto err1; 1101c2aa98e2SPeter Wemm } 1102c2aa98e2SPeter Wemm 110312ed1c7cSGregory Neil Shapiro #if 0 110412ed1c7cSGregory Neil Shapiro /* 110512ed1c7cSGregory Neil Shapiro ** This code could be reused if we decide to add a 110612ed1c7cSGregory Neil Shapiro ** per-user quota field to the sm_mbdb interface. 110712ed1c7cSGregory Neil Shapiro */ 110812ed1c7cSGregory Neil Shapiro 110912ed1c7cSGregory Neil Shapiro /* 111012ed1c7cSGregory Neil Shapiro ** Fail if the user has a quota specified, and delivery of this 111112ed1c7cSGregory Neil Shapiro ** message would exceed that quota. We bounce such failures using 111212ed1c7cSGregory Neil Shapiro ** EX_UNAVAILABLE, unless there were internal problems, since 111312ed1c7cSGregory Neil Shapiro ** storing immense messages for later retries can cause queueing 111412ed1c7cSGregory Neil Shapiro ** issues. 111512ed1c7cSGregory Neil Shapiro */ 111612ed1c7cSGregory Neil Shapiro 111712ed1c7cSGregory Neil Shapiro if (ui.quota > 0) 111812ed1c7cSGregory Neil Shapiro { 111912ed1c7cSGregory Neil Shapiro struct stat dsb; 112012ed1c7cSGregory Neil Shapiro 112112ed1c7cSGregory Neil Shapiro if (fstat(fd, &dsb) < 0) 112212ed1c7cSGregory Neil Shapiro { 112312ed1c7cSGregory Neil Shapiro ExitVal = EX_TEMPFAIL; 112412ed1c7cSGregory Neil Shapiro mailerr("451 4.3.0", 112512ed1c7cSGregory Neil Shapiro "%s: fstat: can't stat temporary storage: %s", 112612ed1c7cSGregory Neil Shapiro ui.mailspool, sm_errstring(errno)); 112712ed1c7cSGregory Neil Shapiro goto err1; 112812ed1c7cSGregory Neil Shapiro } 112912ed1c7cSGregory Neil Shapiro 113012ed1c7cSGregory Neil Shapiro if (dsb.st_size + sb.st_size + 1 > ui.quota) 113112ed1c7cSGregory Neil Shapiro { 113212ed1c7cSGregory Neil Shapiro ExitVal = EX_UNAVAILABLE; 113312ed1c7cSGregory Neil Shapiro mailerr("551 5.2.2", 113412ed1c7cSGregory Neil Shapiro "%s: Mailbox full or quota exceeded", 113512ed1c7cSGregory Neil Shapiro ui.mailspool); 113612ed1c7cSGregory Neil Shapiro goto err1; 113712ed1c7cSGregory Neil Shapiro } 113812ed1c7cSGregory Neil Shapiro } 113912ed1c7cSGregory Neil Shapiro #endif /* 0 */ 11403299c2f1SGregory Neil Shapiro 1141c2aa98e2SPeter Wemm /* Wait until we can get a lock on the file. */ 11423299c2f1SGregory Neil Shapiro if (flock(mbfd, LOCK_EX) < 0) 11433299c2f1SGregory Neil Shapiro { 114412ed1c7cSGregory Neil Shapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1145c2aa98e2SPeter Wemm goto err1; 1146c2aa98e2SPeter Wemm } 1147c2aa98e2SPeter Wemm 114872936242SGregory Neil Shapiro /* Get the starting offset of the new message */ 1149c2aa98e2SPeter Wemm curoff = lseek(mbfd, (off_t) 0, SEEK_END); 1150518536daSGregory Neil Shapiro 1151518536daSGregory Neil Shapiro if (!nobiff) 1152518536daSGregory Neil Shapiro { 115312ed1c7cSGregory Neil Shapiro (void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n", 115412ed1c7cSGregory Neil Shapiro name, (LONGLONG_T) curoff); 1155d615a192SPeter Wemm } 1156c2aa98e2SPeter Wemm 1157c2aa98e2SPeter Wemm /* Copy the message into the file. */ 11583299c2f1SGregory Neil Shapiro if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1) 11593299c2f1SGregory Neil Shapiro { 1160b4662009SGregory Neil Shapiro mailerr("450 4.2.0", "Temporary file: %s", 116112ed1c7cSGregory Neil Shapiro sm_errstring(errno)); 1162c2aa98e2SPeter Wemm goto err1; 1163c2aa98e2SPeter Wemm } 1164c2aa98e2SPeter Wemm #ifdef DEBUG 116512ed1c7cSGregory Neil Shapiro fprintf(stderr, "before writing: euid = %d\n", (int) geteuid()); 11663299c2f1SGregory Neil Shapiro #endif /* DEBUG */ 11673299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 11683299c2f1SGregory Neil Shapiro headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; 11693299c2f1SGregory Neil Shapiro for (;;) 11703299c2f1SGregory Neil Shapiro { 11713299c2f1SGregory Neil Shapiro if (headerbytes == 0) 11723299c2f1SGregory Neil Shapiro { 117312ed1c7cSGregory Neil Shapiro (void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr); 11743299c2f1SGregory Neil Shapiro nr = strlen(buf); 11753299c2f1SGregory Neil Shapiro headerbytes = -1; 11763299c2f1SGregory Neil Shapiro readamount = 0; 11773299c2f1SGregory Neil Shapiro } 11783299c2f1SGregory Neil Shapiro else if (headerbytes > sizeof(buf) || headerbytes < 0) 11793299c2f1SGregory Neil Shapiro readamount = sizeof(buf); 11803299c2f1SGregory Neil Shapiro else 11813299c2f1SGregory Neil Shapiro readamount = headerbytes; 11823299c2f1SGregory Neil Shapiro if (readamount != 0) 11833299c2f1SGregory Neil Shapiro nr = read(fd, buf, readamount); 11843299c2f1SGregory Neil Shapiro if (nr <= 0) 11853299c2f1SGregory Neil Shapiro break; 11863299c2f1SGregory Neil Shapiro if (headerbytes > 0) 11873299c2f1SGregory Neil Shapiro headerbytes -= nr ; 11883299c2f1SGregory Neil Shapiro 11893299c2f1SGregory Neil Shapiro #else /* CONTENTLENGTH */ 1190c2aa98e2SPeter Wemm while ((nr = read(fd, buf, sizeof(buf))) > 0) 11913299c2f1SGregory Neil Shapiro { 11923299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 1193c2aa98e2SPeter Wemm for (off = 0; off < nr; off += nw) 11943299c2f1SGregory Neil Shapiro { 11953299c2f1SGregory Neil Shapiro if ((nw = write(mbfd, buf + off, nr - off)) < 0) 11963299c2f1SGregory Neil Shapiro { 1197b4662009SGregory Neil Shapiro errcode = "450 4.2.0"; 11983299c2f1SGregory Neil Shapiro #ifdef EDQUOT 1199b4662009SGregory Neil Shapiro if (errno == EDQUOT && BounceQuota) 1200b4662009SGregory Neil Shapiro errcode = "552 5.2.2"; 12013299c2f1SGregory Neil Shapiro #endif /* EDQUOT */ 1202b4662009SGregory Neil Shapiro mailerr(errcode, "%s: %s", 120312ed1c7cSGregory Neil Shapiro path, sm_errstring(errno)); 1204c2aa98e2SPeter Wemm goto err3; 1205c2aa98e2SPeter Wemm } 12063299c2f1SGregory Neil Shapiro } 12073299c2f1SGregory Neil Shapiro } 12083299c2f1SGregory Neil Shapiro if (nr < 0) 12093299c2f1SGregory Neil Shapiro { 1210b4662009SGregory Neil Shapiro mailerr("450 4.2.0", "Temporary file: %s", 121112ed1c7cSGregory Neil Shapiro sm_errstring(errno)); 1212c2aa98e2SPeter Wemm goto err3; 1213c2aa98e2SPeter Wemm } 1214c2aa98e2SPeter Wemm 1215c2aa98e2SPeter Wemm /* Flush to disk, don't wait for update. */ 12163299c2f1SGregory Neil Shapiro if (!nofsync && fsync(mbfd) < 0) 12173299c2f1SGregory Neil Shapiro { 121812ed1c7cSGregory Neil Shapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1219c2aa98e2SPeter Wemm err3: 1220c2aa98e2SPeter Wemm #ifdef DEBUG 122112ed1c7cSGregory Neil Shapiro fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 12223299c2f1SGregory Neil Shapiro #endif /* DEBUG */ 12232ef40764SGregory Neil Shapiro if (mbfd >= 0) 1224c2aa98e2SPeter Wemm (void) ftruncate(mbfd, curoff); 1225d995d2baSGregory Neil Shapiro err1: if (mbfd >= 0) 1226d995d2baSGregory Neil Shapiro (void) close(mbfd); 12277660b554SGregory Neil Shapiro err0: (void) setreuid(0, 0); 12287660b554SGregory Neil Shapiro unlockmbox(); 1229c2aa98e2SPeter Wemm return; 1230c2aa98e2SPeter Wemm } 1231c2aa98e2SPeter Wemm 12327660b554SGregory Neil Shapiro /* 12337660b554SGregory Neil Shapiro ** Save the current size so if the close() fails below 12347660b554SGregory Neil Shapiro ** we can make sure no other process has changed the mailbox 12357660b554SGregory Neil Shapiro ** between the failed close and the re-open()/re-lock(). 12367660b554SGregory Neil Shapiro ** If something else has changed the size, we shouldn't 12377660b554SGregory Neil Shapiro ** try to truncate it as we may do more harm then good 12387660b554SGregory Neil Shapiro ** (e.g., truncate a later message delivery). 12397660b554SGregory Neil Shapiro */ 12407660b554SGregory Neil Shapiro 12417660b554SGregory Neil Shapiro if (fstat(mbfd, &sb) < 0) 12427660b554SGregory Neil Shapiro cursize = 0; 12437660b554SGregory Neil Shapiro else 12447660b554SGregory Neil Shapiro cursize = sb.st_size; 12457660b554SGregory Neil Shapiro 12467660b554SGregory Neil Shapiro 1247c2aa98e2SPeter Wemm /* Close and check -- NFS doesn't write until the close. */ 12483299c2f1SGregory Neil Shapiro if (close(mbfd)) 12493299c2f1SGregory Neil Shapiro { 1250b4662009SGregory Neil Shapiro errcode = "450 4.2.0"; 12513299c2f1SGregory Neil Shapiro #ifdef EDQUOT 1252b4662009SGregory Neil Shapiro if (errno == EDQUOT && BounceQuota) 1253b4662009SGregory Neil Shapiro errcode = "552 5.2.2"; 12543299c2f1SGregory Neil Shapiro #endif /* EDQUOT */ 125512ed1c7cSGregory Neil Shapiro mailerr(errcode, "%s: %s", path, sm_errstring(errno)); 12567660b554SGregory Neil Shapiro mbfd = open(path, O_WRONLY, 0); 12577660b554SGregory Neil Shapiro if (mbfd < 0 || 12587660b554SGregory Neil Shapiro cursize == 0 12597660b554SGregory Neil Shapiro || flock(mbfd, LOCK_EX) < 0 || 12607660b554SGregory Neil Shapiro fstat(mbfd, &sb) < 0 || 12617660b554SGregory Neil Shapiro sb.st_size != cursize || 12622ef40764SGregory Neil Shapiro sb.st_nlink != 1 || 12632ef40764SGregory Neil Shapiro !S_ISREG(sb.st_mode) || 12642ef40764SGregory Neil Shapiro sb.st_dev != fsb.st_dev || 12652ef40764SGregory Neil Shapiro sb.st_ino != fsb.st_ino || 12662ef40764SGregory Neil Shapiro # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 12672ef40764SGregory Neil Shapiro sb.st_gen != fsb.st_gen || 12682ef40764SGregory Neil Shapiro # endif /* HAS_ST_GEN && 0 */ 12692ef40764SGregory Neil Shapiro sb.st_uid != fsb.st_uid 12702ef40764SGregory Neil Shapiro ) 12712ef40764SGregory Neil Shapiro { 12722ef40764SGregory Neil Shapiro /* Don't use a bogus file */ 12732ef40764SGregory Neil Shapiro if (mbfd >= 0) 12742ef40764SGregory Neil Shapiro { 12752ef40764SGregory Neil Shapiro (void) close(mbfd); 12762ef40764SGregory Neil Shapiro mbfd = -1; 12772ef40764SGregory Neil Shapiro } 12782ef40764SGregory Neil Shapiro } 12792ef40764SGregory Neil Shapiro 12802ef40764SGregory Neil Shapiro /* Attempt to truncate back to pre-write size */ 12812ef40764SGregory Neil Shapiro goto err3; 12823299c2f1SGregory Neil Shapiro } 12833299c2f1SGregory Neil Shapiro else if (!nobiff) 1284c2aa98e2SPeter Wemm notifybiff(biffmsg); 1285c2aa98e2SPeter Wemm 12863299c2f1SGregory Neil Shapiro if (setreuid(0, 0) < 0) 12873299c2f1SGregory Neil Shapiro { 1288c2aa98e2SPeter Wemm mailerr("450 4.2.0", "setreuid(0, 0): %s", 128912ed1c7cSGregory Neil Shapiro sm_errstring(errno)); 1290c2aa98e2SPeter Wemm goto err0; 1291c2aa98e2SPeter Wemm } 1292c2aa98e2SPeter Wemm #ifdef DEBUG 129312ed1c7cSGregory Neil Shapiro fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 12943299c2f1SGregory Neil Shapiro #endif /* DEBUG */ 1295c2aa98e2SPeter Wemm unlockmbox(); 12963299c2f1SGregory Neil Shapiro if (LMTPMode) 1297b4662009SGregory Neil Shapiro printf("250 2.1.5 %s Ok\r\n", name); 1298c2aa98e2SPeter Wemm } 1299c2aa98e2SPeter Wemm 1300c2aa98e2SPeter Wemm /* 13013299c2f1SGregory Neil Shapiro ** user.lock files are necessary for compatibility with other 13023299c2f1SGregory Neil Shapiro ** systems, e.g., when the mail spool file is NFS exported. 13033299c2f1SGregory Neil Shapiro ** Alas, mailbox locking is more than just a local matter. 13043299c2f1SGregory Neil Shapiro ** EPA 11/94. 1305c2aa98e2SPeter Wemm */ 1306c2aa98e2SPeter Wemm 130712ed1c7cSGregory Neil Shapiro bool Locked = false; 13083299c2f1SGregory Neil Shapiro 13093299c2f1SGregory Neil Shapiro #ifdef MAILLOCK 13103299c2f1SGregory Neil Shapiro int 13113299c2f1SGregory Neil Shapiro lockmbox(name) 13123299c2f1SGregory Neil Shapiro char *name; 13133299c2f1SGregory Neil Shapiro { 1314d995d2baSGregory Neil Shapiro int r = 0; 13153299c2f1SGregory Neil Shapiro 13163299c2f1SGregory Neil Shapiro if (Locked) 13173299c2f1SGregory Neil Shapiro return 0; 13183299c2f1SGregory Neil Shapiro if ((r = maillock(name, 15)) == L_SUCCESS) 13193299c2f1SGregory Neil Shapiro { 132012ed1c7cSGregory Neil Shapiro Locked = true; 13213299c2f1SGregory Neil Shapiro return 0; 13223299c2f1SGregory Neil Shapiro } 13233299c2f1SGregory Neil Shapiro switch (r) 13243299c2f1SGregory Neil Shapiro { 13253299c2f1SGregory Neil Shapiro case L_TMPLOCK: /* Can't create tmp file */ 13263299c2f1SGregory Neil Shapiro case L_TMPWRITE: /* Can't write pid into lockfile */ 13273299c2f1SGregory Neil Shapiro case L_MAXTRYS: /* Failed after retrycnt attempts */ 13283299c2f1SGregory Neil Shapiro errno = 0; 13293299c2f1SGregory Neil Shapiro r = EX_TEMPFAIL; 13303299c2f1SGregory Neil Shapiro break; 13313299c2f1SGregory Neil Shapiro case L_ERROR: /* Check errno for reason */ 13323299c2f1SGregory Neil Shapiro r = errno; 13333299c2f1SGregory Neil Shapiro break; 13343299c2f1SGregory Neil Shapiro default: /* other permanent errors */ 13353299c2f1SGregory Neil Shapiro errno = 0; 13363299c2f1SGregory Neil Shapiro r = EX_UNAVAILABLE; 13373299c2f1SGregory Neil Shapiro break; 13383299c2f1SGregory Neil Shapiro } 13393299c2f1SGregory Neil Shapiro return r; 13403299c2f1SGregory Neil Shapiro } 1341c2aa98e2SPeter Wemm 1342c2aa98e2SPeter Wemm void 13433299c2f1SGregory Neil Shapiro unlockmbox() 13443299c2f1SGregory Neil Shapiro { 13453299c2f1SGregory Neil Shapiro if (Locked) 13463299c2f1SGregory Neil Shapiro mailunlock(); 134712ed1c7cSGregory Neil Shapiro Locked = false; 13483299c2f1SGregory Neil Shapiro } 13493299c2f1SGregory Neil Shapiro #else /* MAILLOCK */ 13503299c2f1SGregory Neil Shapiro 13513299c2f1SGregory Neil Shapiro char LockName[MAXPATHLEN]; 13523299c2f1SGregory Neil Shapiro 13533299c2f1SGregory Neil Shapiro int 1354c2aa98e2SPeter Wemm lockmbox(path) 1355c2aa98e2SPeter Wemm char *path; 1356c2aa98e2SPeter Wemm { 1357c2aa98e2SPeter Wemm int statfailed = 0; 13583299c2f1SGregory Neil Shapiro time_t start; 1359c2aa98e2SPeter Wemm 13603299c2f1SGregory Neil Shapiro if (Locked) 13613299c2f1SGregory Neil Shapiro return 0; 13623299c2f1SGregory Neil Shapiro if (strlen(path) + 6 > sizeof LockName) 13633299c2f1SGregory Neil Shapiro return EX_SOFTWARE; 136412ed1c7cSGregory Neil Shapiro (void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path); 13653299c2f1SGregory Neil Shapiro (void) time(&start); 13663299c2f1SGregory Neil Shapiro for (; ; sleep(5)) 13673299c2f1SGregory Neil Shapiro { 1368c2aa98e2SPeter Wemm int fd; 1369c2aa98e2SPeter Wemm struct stat st; 1370c2aa98e2SPeter Wemm time_t now; 1371c2aa98e2SPeter Wemm 13723299c2f1SGregory Neil Shapiro /* global timeout */ 13733299c2f1SGregory Neil Shapiro (void) time(&now); 13743299c2f1SGregory Neil Shapiro if (now > start + LOCKTO_GLOB) 13753299c2f1SGregory Neil Shapiro { 13763299c2f1SGregory Neil Shapiro errno = 0; 13773299c2f1SGregory Neil Shapiro return EX_TEMPFAIL; 1378c2aa98e2SPeter Wemm } 13797660b554SGregory Neil Shapiro fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, LOCKFILE_PMODE); 13803299c2f1SGregory Neil Shapiro if (fd >= 0) 13813299c2f1SGregory Neil Shapiro { 13823299c2f1SGregory Neil Shapiro /* defeat lock checking programs which test pid */ 13833299c2f1SGregory Neil Shapiro (void) write(fd, "0", 2); 138412ed1c7cSGregory Neil Shapiro Locked = true; 13853299c2f1SGregory Neil Shapiro (void) close(fd); 13863299c2f1SGregory Neil Shapiro return 0; 13873299c2f1SGregory Neil Shapiro } 13883299c2f1SGregory Neil Shapiro if (stat(LockName, &st) < 0) 13893299c2f1SGregory Neil Shapiro { 1390c2aa98e2SPeter Wemm if (statfailed++ > 5) 13913299c2f1SGregory Neil Shapiro { 13923299c2f1SGregory Neil Shapiro errno = 0; 13933299c2f1SGregory Neil Shapiro return EX_TEMPFAIL; 13943299c2f1SGregory Neil Shapiro } 1395c2aa98e2SPeter Wemm continue; 1396c2aa98e2SPeter Wemm } 1397c2aa98e2SPeter Wemm statfailed = 0; 13983299c2f1SGregory Neil Shapiro (void) time(&now); 13993299c2f1SGregory Neil Shapiro if (now < st.st_ctime + LOCKTO_RM) 1400c2aa98e2SPeter Wemm continue; 14013299c2f1SGregory Neil Shapiro 14023299c2f1SGregory Neil Shapiro /* try to remove stale lockfile */ 14033299c2f1SGregory Neil Shapiro if (unlink(LockName) < 0) 14043299c2f1SGregory Neil Shapiro return errno; 1405c2aa98e2SPeter Wemm } 1406c2aa98e2SPeter Wemm } 1407c2aa98e2SPeter Wemm 1408c2aa98e2SPeter Wemm void 1409c2aa98e2SPeter Wemm unlockmbox() 1410c2aa98e2SPeter Wemm { 14113299c2f1SGregory Neil Shapiro if (!Locked) 1412c2aa98e2SPeter Wemm return; 14133299c2f1SGregory Neil Shapiro (void) unlink(LockName); 141412ed1c7cSGregory Neil Shapiro Locked = false; 1415c2aa98e2SPeter Wemm } 14163299c2f1SGregory Neil Shapiro #endif /* MAILLOCK */ 1417c2aa98e2SPeter Wemm 1418c2aa98e2SPeter Wemm void 1419c2aa98e2SPeter Wemm notifybiff(msg) 1420c2aa98e2SPeter Wemm char *msg; 1421c2aa98e2SPeter Wemm { 142212ed1c7cSGregory Neil Shapiro static bool initialized = false; 1423c2aa98e2SPeter Wemm static int f = -1; 1424c2aa98e2SPeter Wemm struct hostent *hp; 1425c2aa98e2SPeter Wemm struct servent *sp; 1426c2aa98e2SPeter Wemm int len; 14273299c2f1SGregory Neil Shapiro static struct sockaddr_in addr; 1428c2aa98e2SPeter Wemm 14293299c2f1SGregory Neil Shapiro if (!initialized) 14303299c2f1SGregory Neil Shapiro { 143112ed1c7cSGregory Neil Shapiro initialized = true; 14323299c2f1SGregory Neil Shapiro 1433c2aa98e2SPeter Wemm /* Be silent if biff service not available. */ 14343299c2f1SGregory Neil Shapiro if ((sp = getservbyname("biff", "udp")) == NULL || 14353299c2f1SGregory Neil Shapiro (hp = gethostbyname("localhost")) == NULL || 14363299c2f1SGregory Neil Shapiro hp->h_length != INADDRSZ) 1437c2aa98e2SPeter Wemm return; 14383299c2f1SGregory Neil Shapiro 1439c2aa98e2SPeter Wemm addr.sin_family = hp->h_addrtype; 14403299c2f1SGregory Neil Shapiro memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ); 1441c2aa98e2SPeter Wemm addr.sin_port = sp->s_port; 1442c2aa98e2SPeter Wemm } 14433299c2f1SGregory Neil Shapiro 14443299c2f1SGregory Neil Shapiro /* No message, just return */ 14453299c2f1SGregory Neil Shapiro if (msg == NULL) 1446c2aa98e2SPeter Wemm return; 14473299c2f1SGregory Neil Shapiro 14483299c2f1SGregory Neil Shapiro /* Couldn't initialize addr struct */ 14493299c2f1SGregory Neil Shapiro if (addr.sin_family == AF_UNSPEC) 14503299c2f1SGregory Neil Shapiro return; 14513299c2f1SGregory Neil Shapiro 1452b4662009SGregory Neil Shapiro if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 14533299c2f1SGregory Neil Shapiro return; 1454c2aa98e2SPeter Wemm len = strlen(msg) + 1; 145576b7bf71SPeter Wemm (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr)); 1456c2aa98e2SPeter Wemm } 1457c2aa98e2SPeter Wemm 1458c2aa98e2SPeter Wemm void 1459c2aa98e2SPeter Wemm usage() 1460c2aa98e2SPeter Wemm { 14613299c2f1SGregory Neil Shapiro ExitVal = EX_USAGE; 146212ed1c7cSGregory Neil Shapiro mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ..."); 14633299c2f1SGregory Neil Shapiro exit(ExitVal); 1464c2aa98e2SPeter Wemm } 1465c2aa98e2SPeter Wemm 1466c2aa98e2SPeter Wemm void 146712ed1c7cSGregory Neil Shapiro /*VARARGS2*/ 1468c2aa98e2SPeter Wemm #ifdef __STDC__ 1469c2aa98e2SPeter Wemm mailerr(const char *hdr, const char *fmt, ...) 14703299c2f1SGregory Neil Shapiro #else /* __STDC__ */ 1471c2aa98e2SPeter Wemm mailerr(hdr, fmt, va_alist) 1472c2aa98e2SPeter Wemm const char *hdr; 1473c2aa98e2SPeter Wemm const char *fmt; 1474c2aa98e2SPeter Wemm va_dcl 14753299c2f1SGregory Neil Shapiro #endif /* __STDC__ */ 1476c2aa98e2SPeter Wemm { 1477b4662009SGregory Neil Shapiro size_t len = 0; 147812ed1c7cSGregory Neil Shapiro SM_VA_LOCAL_DECL 1479c2aa98e2SPeter Wemm 1480b4662009SGregory Neil Shapiro (void) e_to_sys(errno); 1481b4662009SGregory Neil Shapiro 148212ed1c7cSGregory Neil Shapiro SM_VA_START(ap, fmt); 1483b4662009SGregory Neil Shapiro 148412ed1c7cSGregory Neil Shapiro if (LMTPMode && hdr != NULL) 1485c2aa98e2SPeter Wemm { 148612ed1c7cSGregory Neil Shapiro sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); 1487b4662009SGregory Neil Shapiro len = strlen(ErrBuf); 1488c2aa98e2SPeter Wemm } 148912ed1c7cSGregory Neil Shapiro (void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap); 149012ed1c7cSGregory Neil Shapiro SM_VA_END(ap); 1491b4662009SGregory Neil Shapiro 1492b4662009SGregory Neil Shapiro if (!HoldErrs) 1493b4662009SGregory Neil Shapiro flush_error(); 1494b4662009SGregory Neil Shapiro 1495b4662009SGregory Neil Shapiro /* Log the message to syslog. */ 1496b4662009SGregory Neil Shapiro if (!LMTPMode) 1497b4662009SGregory Neil Shapiro syslog(LOG_ERR, "%s", ErrBuf); 1498b4662009SGregory Neil Shapiro } 1499c2aa98e2SPeter Wemm 1500c2aa98e2SPeter Wemm void 1501b4662009SGregory Neil Shapiro flush_error() 1502c2aa98e2SPeter Wemm { 1503b4662009SGregory Neil Shapiro if (LMTPMode) 1504b4662009SGregory Neil Shapiro printf("%s\r\n", ErrBuf); 1505b4662009SGregory Neil Shapiro else 1506b4662009SGregory Neil Shapiro { 15073299c2f1SGregory Neil Shapiro if (ExitVal != EX_USAGE) 1508c2aa98e2SPeter Wemm (void) fprintf(stderr, "mail.local: "); 1509b4662009SGregory Neil Shapiro fprintf(stderr, "%s\n", ErrBuf); 1510c2aa98e2SPeter Wemm } 1511c2aa98e2SPeter Wemm } 1512c2aa98e2SPeter Wemm 1513c2aa98e2SPeter Wemm /* 1514c2aa98e2SPeter Wemm * e_to_sys -- 1515c2aa98e2SPeter Wemm * Guess which errno's are temporary. Gag me. 1516c2aa98e2SPeter Wemm */ 1517b4662009SGregory Neil Shapiro 15183299c2f1SGregory Neil Shapiro int 1519c2aa98e2SPeter Wemm e_to_sys(num) 1520c2aa98e2SPeter Wemm int num; 1521c2aa98e2SPeter Wemm { 1522c2aa98e2SPeter Wemm /* Temporary failures override hard errors. */ 15233299c2f1SGregory Neil Shapiro if (ExitVal == EX_TEMPFAIL) 15243299c2f1SGregory Neil Shapiro return ExitVal; 1525c2aa98e2SPeter Wemm 15263299c2f1SGregory Neil Shapiro switch (num) /* Hopefully temporary errors. */ 15273299c2f1SGregory Neil Shapiro { 1528c2aa98e2SPeter Wemm #ifdef EDQUOT 1529c2aa98e2SPeter Wemm case EDQUOT: /* Disc quota exceeded */ 1530b4662009SGregory Neil Shapiro if (BounceQuota) 15313299c2f1SGregory Neil Shapiro { 15323299c2f1SGregory Neil Shapiro ExitVal = EX_UNAVAILABLE; 15333299c2f1SGregory Neil Shapiro break; 15343299c2f1SGregory Neil Shapiro } 15353299c2f1SGregory Neil Shapiro /* FALLTHROUGH */ 15363299c2f1SGregory Neil Shapiro #endif /* EDQUOT */ 15373299c2f1SGregory Neil Shapiro #ifdef EAGAIN 15383299c2f1SGregory Neil Shapiro case EAGAIN: /* Resource temporarily unavailable */ 15393299c2f1SGregory Neil Shapiro #endif /* EAGAIN */ 1540c2aa98e2SPeter Wemm #ifdef EBUSY 1541c2aa98e2SPeter Wemm case EBUSY: /* Device busy */ 15423299c2f1SGregory Neil Shapiro #endif /* EBUSY */ 1543c2aa98e2SPeter Wemm #ifdef EPROCLIM 1544c2aa98e2SPeter Wemm case EPROCLIM: /* Too many processes */ 15453299c2f1SGregory Neil Shapiro #endif /* EPROCLIM */ 1546c2aa98e2SPeter Wemm #ifdef EUSERS 1547c2aa98e2SPeter Wemm case EUSERS: /* Too many users */ 15483299c2f1SGregory Neil Shapiro #endif /* EUSERS */ 1549c2aa98e2SPeter Wemm #ifdef ECONNABORTED 1550c2aa98e2SPeter Wemm case ECONNABORTED: /* Software caused connection abort */ 15513299c2f1SGregory Neil Shapiro #endif /* ECONNABORTED */ 1552c2aa98e2SPeter Wemm #ifdef ECONNREFUSED 1553c2aa98e2SPeter Wemm case ECONNREFUSED: /* Connection refused */ 15543299c2f1SGregory Neil Shapiro #endif /* ECONNREFUSED */ 1555c2aa98e2SPeter Wemm #ifdef ECONNRESET 1556c2aa98e2SPeter Wemm case ECONNRESET: /* Connection reset by peer */ 15573299c2f1SGregory Neil Shapiro #endif /* ECONNRESET */ 1558c2aa98e2SPeter Wemm #ifdef EDEADLK 1559c2aa98e2SPeter Wemm case EDEADLK: /* Resource deadlock avoided */ 15603299c2f1SGregory Neil Shapiro #endif /* EDEADLK */ 1561c2aa98e2SPeter Wemm #ifdef EFBIG 1562c2aa98e2SPeter Wemm case EFBIG: /* File too large */ 15633299c2f1SGregory Neil Shapiro #endif /* EFBIG */ 1564c2aa98e2SPeter Wemm #ifdef EHOSTDOWN 1565c2aa98e2SPeter Wemm case EHOSTDOWN: /* Host is down */ 15663299c2f1SGregory Neil Shapiro #endif /* EHOSTDOWN */ 1567c2aa98e2SPeter Wemm #ifdef EHOSTUNREACH 1568c2aa98e2SPeter Wemm case EHOSTUNREACH: /* No route to host */ 15693299c2f1SGregory Neil Shapiro #endif /* EHOSTUNREACH */ 1570c2aa98e2SPeter Wemm #ifdef EMFILE 1571c2aa98e2SPeter Wemm case EMFILE: /* Too many open files */ 15723299c2f1SGregory Neil Shapiro #endif /* EMFILE */ 1573c2aa98e2SPeter Wemm #ifdef ENETDOWN 1574c2aa98e2SPeter Wemm case ENETDOWN: /* Network is down */ 15753299c2f1SGregory Neil Shapiro #endif /* ENETDOWN */ 1576c2aa98e2SPeter Wemm #ifdef ENETRESET 1577c2aa98e2SPeter Wemm case ENETRESET: /* Network dropped connection on reset */ 15783299c2f1SGregory Neil Shapiro #endif /* ENETRESET */ 1579c2aa98e2SPeter Wemm #ifdef ENETUNREACH 1580c2aa98e2SPeter Wemm case ENETUNREACH: /* Network is unreachable */ 15813299c2f1SGregory Neil Shapiro #endif /* ENETUNREACH */ 1582c2aa98e2SPeter Wemm #ifdef ENFILE 1583c2aa98e2SPeter Wemm case ENFILE: /* Too many open files in system */ 15843299c2f1SGregory Neil Shapiro #endif /* ENFILE */ 1585c2aa98e2SPeter Wemm #ifdef ENOBUFS 1586c2aa98e2SPeter Wemm case ENOBUFS: /* No buffer space available */ 15873299c2f1SGregory Neil Shapiro #endif /* ENOBUFS */ 1588c2aa98e2SPeter Wemm #ifdef ENOMEM 1589c2aa98e2SPeter Wemm case ENOMEM: /* Cannot allocate memory */ 15903299c2f1SGregory Neil Shapiro #endif /* ENOMEM */ 1591c2aa98e2SPeter Wemm #ifdef ENOSPC 1592c2aa98e2SPeter Wemm case ENOSPC: /* No space left on device */ 15933299c2f1SGregory Neil Shapiro #endif /* ENOSPC */ 1594c2aa98e2SPeter Wemm #ifdef EROFS 1595c2aa98e2SPeter Wemm case EROFS: /* Read-only file system */ 15963299c2f1SGregory Neil Shapiro #endif /* EROFS */ 1597c2aa98e2SPeter Wemm #ifdef ESTALE 1598c2aa98e2SPeter Wemm case ESTALE: /* Stale NFS file handle */ 15993299c2f1SGregory Neil Shapiro #endif /* ESTALE */ 1600c2aa98e2SPeter Wemm #ifdef ETIMEDOUT 1601c2aa98e2SPeter Wemm case ETIMEDOUT: /* Connection timed out */ 16023299c2f1SGregory Neil Shapiro #endif /* ETIMEDOUT */ 1603c2aa98e2SPeter Wemm #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK 1604c2aa98e2SPeter Wemm case EWOULDBLOCK: /* Operation would block. */ 16053299c2f1SGregory Neil Shapiro #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */ 16063299c2f1SGregory Neil Shapiro ExitVal = EX_TEMPFAIL; 1607c2aa98e2SPeter Wemm break; 16083299c2f1SGregory Neil Shapiro 1609c2aa98e2SPeter Wemm default: 16103299c2f1SGregory Neil Shapiro ExitVal = EX_UNAVAILABLE; 1611c2aa98e2SPeter Wemm break; 1612c2aa98e2SPeter Wemm } 16133299c2f1SGregory Neil Shapiro return ExitVal; 1614c2aa98e2SPeter Wemm } 1615c2aa98e2SPeter Wemm 1616c2aa98e2SPeter Wemm #if defined(ultrix) || defined(_CRAY) 1617c2aa98e2SPeter Wemm /* 1618c2aa98e2SPeter Wemm * Copyright (c) 1987, 1993 1619c2aa98e2SPeter Wemm * The Regents of the University of California. All rights reserved. 1620c2aa98e2SPeter Wemm * 1621c2aa98e2SPeter Wemm * Redistribution and use in source and binary forms, with or without 1622c2aa98e2SPeter Wemm * modification, are permitted provided that the following conditions 1623c2aa98e2SPeter Wemm * are met: 1624c2aa98e2SPeter Wemm * 1. Redistributions of source code must retain the above copyright 1625c2aa98e2SPeter Wemm * notice, this list of conditions and the following disclaimer. 1626c2aa98e2SPeter Wemm * 2. Redistributions in binary form must reproduce the above copyright 1627c2aa98e2SPeter Wemm * notice, this list of conditions and the following disclaimer in the 1628c2aa98e2SPeter Wemm * documentation and/or other materials provided with the distribution. 1629c2aa98e2SPeter Wemm * 3. All advertising materials mentioning features or use of this software 1630c2aa98e2SPeter Wemm * must display the following acknowledgement: 1631c2aa98e2SPeter Wemm * This product includes software developed by the University of 1632c2aa98e2SPeter Wemm * California, Berkeley and its contributors. 1633c2aa98e2SPeter Wemm * 4. Neither the name of the University nor the names of its contributors 1634c2aa98e2SPeter Wemm * may be used to endorse or promote products derived from this software 1635c2aa98e2SPeter Wemm * without specific prior written permission. 1636c2aa98e2SPeter Wemm * 1637c2aa98e2SPeter Wemm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1638c2aa98e2SPeter Wemm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1639c2aa98e2SPeter Wemm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1640c2aa98e2SPeter Wemm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1641c2aa98e2SPeter Wemm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1642c2aa98e2SPeter Wemm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1643c2aa98e2SPeter Wemm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1644c2aa98e2SPeter Wemm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1645c2aa98e2SPeter Wemm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1646c2aa98e2SPeter Wemm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1647c2aa98e2SPeter Wemm * SUCH DAMAGE. 1648c2aa98e2SPeter Wemm */ 1649c2aa98e2SPeter Wemm 1650c2aa98e2SPeter Wemm # if defined(LIBC_SCCS) && !defined(lint) 1651c2aa98e2SPeter Wemm static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; 16523299c2f1SGregory Neil Shapiro # endif /* defined(LIBC_SCCS) && !defined(lint) */ 1653c2aa98e2SPeter Wemm 1654c2aa98e2SPeter Wemm # include <sys/types.h> 1655c2aa98e2SPeter Wemm # include <sys/stat.h> 1656c2aa98e2SPeter Wemm # include <fcntl.h> 1657c2aa98e2SPeter Wemm # include <errno.h> 1658c2aa98e2SPeter Wemm # include <stdio.h> 1659c2aa98e2SPeter Wemm # include <ctype.h> 1660c2aa98e2SPeter Wemm 1661c2aa98e2SPeter Wemm static int _gettemp(); 1662c2aa98e2SPeter Wemm 1663c2aa98e2SPeter Wemm mkstemp(path) 1664c2aa98e2SPeter Wemm char *path; 1665c2aa98e2SPeter Wemm { 1666c2aa98e2SPeter Wemm int fd; 1667c2aa98e2SPeter Wemm 1668c2aa98e2SPeter Wemm return (_gettemp(path, &fd) ? fd : -1); 1669c2aa98e2SPeter Wemm } 1670c2aa98e2SPeter Wemm 1671c2aa98e2SPeter Wemm static 1672c2aa98e2SPeter Wemm _gettemp(path, doopen) 1673c2aa98e2SPeter Wemm char *path; 1674c2aa98e2SPeter Wemm register int *doopen; 1675c2aa98e2SPeter Wemm { 1676c2aa98e2SPeter Wemm extern int errno; 1677c2aa98e2SPeter Wemm register char *start, *trv; 1678c2aa98e2SPeter Wemm struct stat sbuf; 167912ed1c7cSGregory Neil Shapiro unsigned int pid; 1680c2aa98e2SPeter Wemm 1681c2aa98e2SPeter Wemm pid = getpid(); 1682c2aa98e2SPeter Wemm for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ 16833299c2f1SGregory Neil Shapiro while (*--trv == 'X') 16843299c2f1SGregory Neil Shapiro { 1685c2aa98e2SPeter Wemm *trv = (pid % 10) + '0'; 1686c2aa98e2SPeter Wemm pid /= 10; 1687c2aa98e2SPeter Wemm } 1688c2aa98e2SPeter Wemm 1689c2aa98e2SPeter Wemm /* 1690c2aa98e2SPeter Wemm * check the target directory; if you have six X's and it 1691c2aa98e2SPeter Wemm * doesn't exist this runs for a *very* long time. 1692c2aa98e2SPeter Wemm */ 16933299c2f1SGregory Neil Shapiro for (start = trv + 1;; --trv) 16943299c2f1SGregory Neil Shapiro { 1695c2aa98e2SPeter Wemm if (trv <= path) 1696c2aa98e2SPeter Wemm break; 16973299c2f1SGregory Neil Shapiro if (*trv == '/') 16983299c2f1SGregory Neil Shapiro { 1699c2aa98e2SPeter Wemm *trv = '\0'; 1700c2aa98e2SPeter Wemm if (stat(path, &sbuf) < 0) 1701c2aa98e2SPeter Wemm return(0); 17023299c2f1SGregory Neil Shapiro if (!S_ISDIR(sbuf.st_mode)) 17033299c2f1SGregory Neil Shapiro { 1704c2aa98e2SPeter Wemm errno = ENOTDIR; 1705c2aa98e2SPeter Wemm return(0); 1706c2aa98e2SPeter Wemm } 1707c2aa98e2SPeter Wemm *trv = '/'; 1708c2aa98e2SPeter Wemm break; 1709c2aa98e2SPeter Wemm } 1710c2aa98e2SPeter Wemm } 1711c2aa98e2SPeter Wemm 17123299c2f1SGregory Neil Shapiro for (;;) 17133299c2f1SGregory Neil Shapiro { 17143299c2f1SGregory Neil Shapiro if (doopen) 17153299c2f1SGregory Neil Shapiro { 1716d995d2baSGregory Neil Shapiro if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 1717d995d2baSGregory Neil Shapiro 0600)) >= 0) 1718c2aa98e2SPeter Wemm return(1); 1719c2aa98e2SPeter Wemm if (errno != EEXIST) 1720c2aa98e2SPeter Wemm return(0); 1721c2aa98e2SPeter Wemm } 1722c2aa98e2SPeter Wemm else if (stat(path, &sbuf) < 0) 1723c2aa98e2SPeter Wemm return(errno == ENOENT ? 1 : 0); 1724c2aa98e2SPeter Wemm 1725c2aa98e2SPeter Wemm /* tricky little algorithm for backward compatibility */ 17263299c2f1SGregory Neil Shapiro for (trv = start;;) 17273299c2f1SGregory Neil Shapiro { 1728c2aa98e2SPeter Wemm if (!*trv) 1729c2aa98e2SPeter Wemm return(0); 1730c2aa98e2SPeter Wemm if (*trv == 'z') 1731c2aa98e2SPeter Wemm *trv++ = 'a'; 17323299c2f1SGregory Neil Shapiro else 17333299c2f1SGregory Neil Shapiro { 1734c2aa98e2SPeter Wemm if (isascii(*trv) && isdigit(*trv)) 1735c2aa98e2SPeter Wemm *trv = 'a'; 1736c2aa98e2SPeter Wemm else 1737c2aa98e2SPeter Wemm ++*trv; 1738c2aa98e2SPeter Wemm break; 1739c2aa98e2SPeter Wemm } 1740c2aa98e2SPeter Wemm } 1741c2aa98e2SPeter Wemm } 1742c2aa98e2SPeter Wemm /* NOTREACHED */ 1743c2aa98e2SPeter Wemm } 17443299c2f1SGregory Neil Shapiro #endif /* defined(ultrix) || defined(_CRAY) */ 1745