2 * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
4 * Copyright (c) 1990, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
16 "@(#) Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.\n\
17 All rights reserved.\n\
18 Copyright (c) 1990, 1993, 1994\n\
19 The Regents of the University of California. All rights reserved.\n")
21 SM_IDSTR(id
, "@(#)$Id: mail.local.c,v 8.256 2008/02/19 07:13:30 gshapiro Exp $")
24 #include <sm/errstring.h>
26 #include <sm/limits.h>
29 # undef EX_OK /* unistd.h may have another use for this */
31 # define LOCKFILE_PMODE 0
33 #include <sm/sysexits.h>
37 #endif /* ! HASHSPOOL */
39 # define HASHSPOOLMD5 0
40 #endif /* ! HASHSPOOLMD5 */
43 ** This is not intended to work on System V derived systems
44 ** such as Solaris or HP-UX, since they use a totally different
45 ** approach to mailboxes (essentially, they have a set-group-ID program
46 ** rather than set-user-ID, and they rely on the ability to "give away"
47 ** files to do their work). IT IS NOT A BUG that this doesn't
48 ** work on such architectures.
55 #include <sys/types.h>
59 # include <sys/socket.h>
60 # include <sys/file.h>
61 # include <netinet/in.h>
62 # include <arpa/nameser.h>
66 #include <sm/string.h>
71 #include <sendmail/pathnames.h>
78 # include <openssl/md5.h>
79 # endif /* HASHSPOOLMD5 */
80 #endif /* HASHSPOOL */
84 ** Override path to mail store at run time (using -p).
85 ** From: Eugene Grosbein of Svyaz Service JSC
86 ** See: http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/114195
87 ** NOTE: Update man page before adding this to a release.
89 #endif /* _FFR_SPOOL_PATH */
93 # define LOCKTO_RM 300 /* timeout for stale lockfile removal */
94 #endif /* ! LOCKTO_RM */
96 # define LOCKTO_GLOB 400 /* global timeout for lockfile creation */
97 #endif /* ! LOCKTO_GLOB */
99 /* define a realloc() which works for NULL pointers */
100 #define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size))
103 ** If you don't have flock, you could try using lockf instead.
107 # define flock(a, b) lockf(a, b, 0)
110 # endif /* LOCK_EX */
111 # define LOCK_EX F_LOCK
112 #endif /* LDA_USE_LOCKF */
115 # include <sys/file.h>
116 #endif /* ! LOCK_EX */
119 ** If you don't have setreuid, and you have saved uids, and you have
120 ** a seteuid() call that doesn't try to emulate using setuid(), then
121 ** you can try defining LDA_USE_SETEUID.
124 #ifdef LDA_USE_SETEUID
125 # define setreuid(r, e) seteuid(e)
126 #endif /* LDA_USE_SETEUID */
128 #ifdef LDA_CONTENTLENGTH
129 # define CONTENTLENGTH 1
130 #endif /* LDA_CONTENTLENGTH */
133 # define INADDRSZ 4 /* size of an IPv4 address in bytes */
134 #endif /* ! INADDRSZ */
137 # include <maillock.h>
138 #endif /* MAILLOCK */
140 #ifndef MAILER_DAEMON
141 # define MAILER_DAEMON "MAILER-DAEMON"
142 #endif /* ! MAILER_DAEMON */
145 char ContentHdr
[40] = "Content-Length: ";
148 #endif /* CONTENTLENGTH */
150 bool EightBitMime
= true; /* advertise 8BITMIME in LMTP */
151 char ErrBuf
[10240]; /* error buffer */
152 int ExitVal
= EX_OK
; /* sysexits.h error value. */
153 bool HoldErrs
= false; /* Hold errors in ErrBuf */
154 bool LMTPMode
= false;
155 bool BounceQuota
= false; /* permanent error when over quota */
156 bool CloseMBDB
= false;
157 char *HomeMailFile
= NULL
; /* store mail in homedir */
160 int HashType
= HASH_NONE
;
162 bool StripRcptDomain
= true;
163 #else /* HASHSPOOL */
164 # define StripRcptDomain true
165 #endif /* HASHSPOOL */
166 char SpoolPath
[MAXPATHLEN
];
168 char *parseaddr
__P((char *, bool));
169 char *process_recipient
__P((char *));
170 void dolmtp
__P((void));
171 void deliver
__P((int, char *));
172 int e_to_sys
__P((int));
173 void notifybiff
__P((char *));
174 int store
__P((char *, bool *));
175 void usage
__P((void));
176 int lockmbox
__P((char *));
177 void unlockmbox
__P((void));
178 void mailerr
__P((const char *, const char *, ...));
179 void flush_error
__P((void));
181 const char *hashname
__P((char *));
182 #endif /* HASHSPOOL */
185 static void sm_exit
__P((int));
194 CloseMBDB
= false; /* not really necessary, but ... */
208 char *mbdbname
= "pw";
214 /* make sure we have some open file descriptors */
215 for (fd
= 10; fd
< 30; fd
++)
218 /* use a reasonable umask */
222 openlog("mail.local", 0, LOG_MAIL
);
223 # else /* LOG_MAIL */
224 openlog("mail.local", 0);
225 # endif /* LOG_MAIL */
229 /* XXX can this be converted to a compile time check? */
230 if (sm_strlcpy(SpoolPath
, _PATH_MAILDIR
, sizeof(SpoolPath
)) >=
233 mailerr("421", "Configuration error: _PATH_MAILDIR too large");
237 while ((ch
= getopt(argc
, argv
, "7bdD:f:h:r:lH:p:n")) != -1)
238 #else /* HASHSPOOL */
240 while ((ch
= getopt(argc
, argv
, "7bdD:f:h:r:lp:")) != -1)
241 # else /* _FFR_SPOOL_PATH */
242 while ((ch
= getopt(argc
, argv
, "7bdD:f:h:r:l")) != -1)
243 # endif /* _FFR_SPOOL_PATH */
244 #endif /* HASHSPOOL */
248 case '7': /* Do not advertise 8BITMIME */
249 EightBitMime
= false;
252 case 'b': /* bounce mail when over quota. */
256 case 'd': /* Backward compatible. */
259 case 'D': /* mailbox database type */
264 case 'r': /* Backward compatible. */
267 mailerr(NULL
, "Multiple -f options");
274 if (optarg
!= NULL
|| *optarg
!= '\0')
275 HomeMailFile
= optarg
;
278 mailerr(NULL
, "-h: missing filename");
290 if (optarg
== NULL
|| *optarg
== '\0')
292 mailerr(NULL
, "-H: missing hashinfo");
298 HashType
= HASH_USER
;
305 # endif /* HASHSPOOLMD5 */
308 mailerr(NULL
, "-H: unknown hash type");
311 if (optarg
[1] == '\0')
313 mailerr(NULL
, "-H: invalid hash depth");
316 HashDepth
= atoi(&optarg
[1]);
317 if ((HashDepth
<= 0) || ((HashDepth
* 2) >= MAXPATHLEN
))
319 mailerr(NULL
, "-H: invalid hash depth");
325 StripRcptDomain
= false;
327 #endif /* HASHSPOOL */
329 #if HASHSPOOL || _FFR_SPOOL_PATH
331 if (optarg
== NULL
|| *optarg
== '\0')
333 mailerr(NULL
, "-p: missing spool path");
336 if (sm_strlcpy(SpoolPath
, optarg
, sizeof(SpoolPath
)) >=
339 mailerr(NULL
, "-p: invalid spool path");
343 #endif /* HASHSPOOL || _FFR_SPOOL_PATH */
353 /* initialize biff structures */
356 err
= sm_mbdb_initialize(mbdbname
);
359 char *errcode
= "521";
361 if (err
== EX_TEMPFAIL
)
364 mailerr(errcode
, "Can not open mailbox database %s: %s",
365 mbdbname
, sm_strexit(err
));
374 mailerr("421", "Users should not be specified in command line if LMTP required");
375 sm_exit(EX_TEMPFAIL
);
383 /* Non-LMTP from here on out */
388 ** If from not specified, use the name from getlogin() if the
389 ** uid matches, otherwise, use the name from the password file
390 ** corresponding to the uid.
394 if (from
== NULL
&& ((from
= getlogin()) == NULL
||
395 (pw
= getpwnam(from
)) == NULL
||
397 from
= (pw
= getpwuid(uid
)) != NULL
? pw
->pw_name
: "???";
400 ** There is no way to distinguish the error status of one delivery
401 ** from the rest of the deliveries. So, if we failed hard on one
402 ** or more deliveries, but had no failures on any of the others, we
403 ** return a hard failure. If we failed temporarily on one or more
404 ** deliveries, we return a temporary failure regardless of the other
405 ** failures. This results in the delivery being reattempted later
406 ** at the expense of repeated failures and multiple deliveries.
410 fd
= store(from
, NULL
);
417 for (; *argv
!= NULL
; ++argv
)
441 while (*p
!= ',' && *p
!= ':' && *p
!= '\0')
446 /* Skip over , or : */
453 while (*p
!= '\0' && *p
!= '@' && *p
!= '>')
463 while (*p
!= '\0' && *p
!= '\"')
472 if (*p
== '\0' || *(p
+ 1) == '\0')
476 if (*p
== '+' && rcpt
)
486 while (*p
!= '\0' && *p
!= '>')
496 if (*p
!= '\0' && *p
!= ' ')
508 mailerr("421 4.3.0", "Memory exhausted");
509 sm_exit(EX_TEMPFAIL
);
512 (void) sm_strlcpy(p
, s
, l
);
517 process_recipient(addr
)
522 switch (sm_mbdb_lookup(addr
, &user
))
528 return "550 5.1.1 User unknown";
531 return "451 4.3.0 User database failure; retry later";
534 return "550 5.3.0 User database failure";
543 char *return_path
= NULL
;
544 char **rcpt_addr
= NULL
;
547 bool gotlhlo
= false;
552 char myhostname
[1024];
555 memset(myhostname
, '\0', sizeof myhostname
);
556 (void) gethostname(myhostname
, sizeof myhostname
- 1);
557 if (myhostname
[0] == '\0')
558 sm_strlcpy(myhostname
, "localhost", sizeof myhostname
);
560 printf("220 %s LMTP ready\r\n", myhostname
);
563 (void) fflush(stdout
);
564 if (fgets(buf
, sizeof(buf
) - 1, stdin
) == NULL
)
566 p
= buf
+ strlen(buf
) - 1;
567 if (p
>= buf
&& *p
== '\n')
569 if (p
>= buf
&& *p
== '\r')
576 if (sm_strcasecmp(buf
, "data") == 0)
582 mailerr("503 5.5.1", "No recipients");
586 msgfd
= store(return_path
, &inbody
);
588 if (msgfd
< 0 && !inbody
)
594 for (i
= 0; i
< rcpt_num
; i
++)
598 /* print error for rcpt */
602 p
= strchr(rcpt_addr
[i
], '+');
605 deliver(msgfd
, rcpt_addr
[i
]);
617 if (sm_strncasecmp(buf
, "lhlo ", 5) == 0)
619 /* check for duplicate per RFC 1651 4.2 */
622 mailerr("503", "%s Duplicate LHLO",
627 printf("250-%s\r\n", myhostname
);
629 printf("250-8BITMIME\r\n");
630 printf("250-ENHANCEDSTATUSCODES\r\n");
631 printf("250 PIPELINING\r\n");
640 if (sm_strncasecmp(buf
, "mail ", 5) == 0)
642 if (return_path
!= NULL
)
645 "Nested MAIL command");
648 if (sm_strncasecmp(buf
+ 5, "from:", 5) != 0 ||
649 ((return_path
= parseaddr(buf
+ 10,
653 "Syntax error in parameters");
656 printf("250 2.5.0 Ok\r\n");
665 if (sm_strcasecmp(buf
, "noop") == 0)
667 printf("250 2.0.0 Ok\r\n");
676 if (sm_strcasecmp(buf
, "quit") == 0)
678 printf("221 2.0.0 Bye\r\n");
687 if (sm_strncasecmp(buf
, "rcpt ", 5) == 0)
689 if (return_path
== NULL
)
692 "Need MAIL command");
695 if (rcpt_num
>= rcpt_alloc
)
697 rcpt_alloc
+= RCPT_GROW
;
698 rcpt_addr
= (char **)
699 REALLOC((char *) rcpt_addr
,
702 if (rcpt_addr
== NULL
)
706 sm_exit(EX_TEMPFAIL
);
709 if (sm_strncasecmp(buf
+ 5, "to:", 3) != 0 ||
710 ((rcpt_addr
[rcpt_num
] = parseaddr(buf
+ 8,
711 StripRcptDomain
)) == NULL
))
714 "Syntax error in parameters");
717 err
= process_recipient(rcpt_addr
[rcpt_num
]);
720 mailerr(NULL
, "%s", err
);
724 printf("250 2.1.5 Ok\r\n");
727 else if (sm_strcasecmp(buf
, "rset") == 0)
729 printf("250 2.0.0 Ok\r\n");
733 free(rcpt_addr
[--rcpt_num
]);
734 if (return_path
!= NULL
)
745 if (sm_strncasecmp(buf
, "vrfy ", 5) == 0)
747 printf("252 2.3.3 Try RCPT to attempt delivery\r\n");
756 mailerr("500 5.5.2", "Syntax error");
771 bool eline
; /* previous line was empty */
772 bool fullline
= true; /* current line is terminated */
773 bool prevfl
; /* previous line was terminated */
776 char tmpbuf
[sizeof _PATH_LOCTMP
+ 1];
782 (void) sm_strlcpy(tmpbuf
, _PATH_LOCTMP
, sizeof tmpbuf
);
783 if ((fd
= mkstemp(tmpbuf
)) < 0 || (fp
= fdopen(fd
, "w+")) == NULL
)
787 mailerr("451 4.3.0", "Unable to open temporary file");
790 (void) unlink(tmpbuf
);
794 printf("354 Go ahead\r\n");
795 (void) fflush(stdout
);
801 (void) fprintf(fp
, "From %s %s", from
, ctime(&tval
));
806 #endif /* CONTENTLENGTH */
810 while (fgets(line
, sizeof(line
), stdin
) != (char *) NULL
)
815 prevfl
= fullline
; /* preserve state of previous line */
816 while (line
[line_len
] != '\n' && line_len
< sizeof(line
) - 2)
820 /* Check for dot-stuffing */
821 if (prevfl
&& LMTPMode
&& line
[0] == '.')
823 if (line
[1] == '\n' ||
824 (line
[1] == '\r' && line
[2] == '\n'))
826 memcpy(line
, line
+ 1, line_len
);
830 /* Check to see if we have the full line from fgets() */
834 if (line
[line_len
- 1] == '\n')
837 line
[line_len
- 2] == '\r')
839 line
[line_len
- 2] = '\n';
840 line
[line_len
- 1] = '\0';
845 else if (line
[line_len
- 1] == '\r')
847 /* Did we just miss the CRLF? */
851 line
[line_len
- 1] = '\n';
855 (void) ungetc(peek
, stdin
);
862 if (prevfl
&& line
[0] == '\n' && HeaderLength
== 0)
866 HeaderLength
= ftell(fp
);
867 if (HeaderLength
<= 0)
870 ** shouldn't happen, unless ftell() is
877 #else /* CONTENTLENGTH */
878 if (prevfl
&& line
[0] == '\n')
880 #endif /* CONTENTLENGTH */
883 if (eline
&& line
[0] == 'F' &&
885 !memcmp(line
, "From ", 5))
886 (void) putc('>', fp
);
889 /* discard existing "Content-Length:" headers */
890 if (prevfl
&& HeaderLength
== 0 &&
891 (line
[0] == 'C' || line
[0] == 'c') &&
892 sm_strncasecmp(line
, ContentHdr
, 15) == 0)
895 ** be paranoid: clear the line
896 ** so no "wrong matches" may occur later
901 #endif /* CONTENTLENGTH */
906 (void) fwrite(line
, sizeof(char), line_len
, fp
);
910 "Temporary file write error");
918 /* check if an error occurred */
924 /* Got a premature EOF -- toss message and exit */
928 /* If message not newline terminated, need an extra. */
929 if (fp
!= NULL
&& strchr(line
, '\n') == NULL
)
930 (void) putc('\n', fp
);
936 BodyLength
= ftell(fp
);
937 if (HeaderLength
== 0 && BodyLength
> 0) /* empty body */
939 HeaderLength
= BodyLength
;
943 BodyLength
= BodyLength
- HeaderLength
- 1 ;
945 if (HeaderLength
> 0 && BodyLength
>= 0)
947 (void) sm_snprintf(line
, sizeof line
, "%lld\n",
948 (LONGLONG_T
) BodyLength
);
949 (void) sm_strlcpy(&ContentHdr
[16], line
,
950 sizeof(ContentHdr
) - 16);
953 BodyLength
= -1; /* Something is wrong here */
954 #endif /* CONTENTLENGTH */
956 /* Output a newline; note, empty messages are allowed. */
958 (void) putc('\n', fp
);
960 if (fp
== NULL
|| fflush(fp
) == EOF
|| ferror(fp
) != 0)
962 mailerr("451 4.3.0", "Temporary file write error");
977 char path
[MAXPATHLEN
];
978 int mbfd
= -1, nr
= 0, nw
, off
;
982 off_t curoff
, cursize
;
986 #endif /* CONTENTLENGTH */
987 char biffmsg
[100], buf
[8 * 1024];
991 ** Disallow delivery to unknown names -- special mailboxes can be
992 ** handled in the sendmail aliases file.
995 exitval
= sm_mbdb_lookup(name
, &user
);
1002 exitval
= EX_UNAVAILABLE
;
1003 mailerr("550 5.1.1", "%s: User unknown", name
);
1007 mailerr("451 4.3.0", "%s: User database failure; retry later",
1012 exitval
= EX_UNAVAILABLE
;
1013 mailerr("550 5.3.0", "%s: User database failure", name
);
1017 if (exitval
!= EX_OK
)
1019 if (ExitVal
!= EX_TEMPFAIL
)
1027 ** Keep name reasonably short to avoid buffer overruns.
1028 ** This isn't necessary on BSD because of the proper
1029 ** definition of snprintf(), but it can cause problems
1030 ** on other systems.
1031 ** Also, clear out any bogus characters.
1035 if (strlen(name
) > 40)
1037 for (p
= name
; *p
!= '\0'; p
++)
1041 else if (!isprint(*p
))
1044 #endif /* !HASHSPOOL */
1047 if (HomeMailFile
== NULL
)
1049 if (sm_strlcpyn(path
, sizeof(path
),
1052 #else /* HASHSPOOL */
1054 #endif /* HASHSPOOL */
1058 #endif /* HASHSPOOL */
1059 name
) >= sizeof(path
))
1061 exitval
= EX_UNAVAILABLE
;
1062 mailerr("550 5.1.1", "%s: Invalid mailbox path", name
);
1066 else if (*user
.mbdb_homedir
== '\0')
1068 exitval
= EX_UNAVAILABLE
;
1069 mailerr("550 5.1.1", "%s: User missing home directory", name
);
1072 else if (sm_snprintf(path
, sizeof(path
), "%s/%s",
1073 user
.mbdb_homedir
, HomeMailFile
) >= sizeof(path
))
1075 exitval
= EX_UNAVAILABLE
;
1076 mailerr("550 5.1.1", "%s: Invalid mailbox path", name
);
1082 ** If the mailbox is linked or a symlink, fail. There's an obvious
1083 ** race here, that the file was replaced with a symbolic link after
1084 ** the lstat returned, but before the open. We attempt to detect
1085 ** this by comparing the original stat information and information
1086 ** returned by an fstat of the file descriptor returned by the open.
1088 ** NB: this is a symptom of a larger problem, that the mail spooling
1089 ** directory is writeable by the wrong users. If that directory is
1090 ** writeable, system security is compromised for other reasons, and
1091 ** it cannot be fixed here.
1093 ** If we created the mailbox, set the owner/group. If that fails,
1094 ** just return. Another process may have already opened it, so we
1095 ** can't unlink it. Historically, binmail set the owner/group at
1096 ** each mail delivery. We no longer do this, assuming that if the
1097 ** ownership or permissions were changed there was a reason.
1100 ** open(2) should support flock'ing the file.
1106 #else /* MAILLOCK */
1108 #endif /* MAILLOCK */
1109 if ((off
= lockmbox(p
)) != 0)
1111 if (off
== EX_TEMPFAIL
|| e_to_sys(off
) == EX_TEMPFAIL
)
1113 ExitVal
= EX_TEMPFAIL
;
1114 errcode
= "451 4.3.0";
1117 errcode
= "551 5.3.0";
1119 mailerr(errcode
, "lockmailbox %s failed; error code %d %s",
1120 p
, off
, errno
> 0 ? sm_errstring(errno
) : "");
1124 if (lstat(path
, &sb
) < 0)
1127 int mode
= S_IRUSR
|S_IWUSR
;
1128 gid_t gid
= user
.mbdb_gid
;
1133 mode
|= S_IRGRP
|S_IWGRP
;
1134 #endif /* MAILGID */
1136 mbfd
= open(path
, O_APPEND
|O_CREAT
|O_EXCL
|O_WRONLY
,
1140 if (lstat(path
, &sb
) < 0)
1142 ExitVal
= EX_CANTCREAT
;
1143 mailerr("550 5.2.0",
1144 "%s: lstat: file changed after open", path
);
1149 if (save_errno
== EEXIST
)
1152 /* open failed, don't try again */
1153 mailerr("450 4.2.0", "%s: %s", path
,
1154 sm_errstring(save_errno
));
1157 else if (fchown(mbfd
, user
.mbdb_uid
, gid
) < 0)
1159 mailerr("451 4.3.0", "chown %u.%u: %s",
1160 user
.mbdb_uid
, gid
, name
);
1166 ** open() was successful, now close it so can
1167 ** be opened as the right owner again.
1168 ** Paranoia: reset mbdf since the file descriptor
1169 ** is no longer valid; better safe than sorry.
1172 sb
.st_uid
= user
.mbdb_uid
;
1177 else if (sb
.st_nlink
!= 1)
1179 mailerr("550 5.2.0", "%s: too many links", path
);
1182 else if (!S_ISREG(sb
.st_mode
))
1184 mailerr("550 5.2.0", "%s: irregular file", path
);
1187 else if (sb
.st_uid
!= user
.mbdb_uid
)
1189 ExitVal
= EX_CANTCREAT
;
1190 mailerr("550 5.2.0", "%s: wrong ownership (%d)",
1191 path
, (int) sb
.st_uid
);
1195 /* change UID for quota checks */
1196 if (setreuid(0, user
.mbdb_uid
) < 0)
1198 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
1199 (int) user
.mbdb_uid
, sm_errstring(errno
),
1200 (int) getuid(), (int) geteuid());
1204 fprintf(stderr
, "new euid = %d\n", (int) geteuid());
1206 mbfd
= open(path
, O_APPEND
|O_WRONLY
, 0);
1209 mailerr("450 4.2.0", "%s: %s", path
, sm_errstring(errno
));
1212 else if (fstat(mbfd
, &fsb
) < 0 ||
1213 fsb
.st_nlink
!= 1 ||
1215 !S_ISREG(fsb
.st_mode
) ||
1216 sb
.st_dev
!= fsb
.st_dev
||
1217 sb
.st_ino
!= fsb
.st_ino
||
1218 # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */
1219 sb
.st_gen
!= fsb
.st_gen
||
1220 # endif /* HAS_ST_GEN && 0 */
1221 sb
.st_uid
!= fsb
.st_uid
)
1223 ExitVal
= EX_TEMPFAIL
;
1224 mailerr("550 5.2.0", "%s: fstat: file changed after open",
1231 ** This code could be reused if we decide to add a
1232 ** per-user quota field to the sm_mbdb interface.
1236 ** Fail if the user has a quota specified, and delivery of this
1237 ** message would exceed that quota. We bounce such failures using
1238 ** EX_UNAVAILABLE, unless there were internal problems, since
1239 ** storing immense messages for later retries can cause queueing
1247 if (fstat(fd
, &dsb
) < 0)
1249 ExitVal
= EX_TEMPFAIL
;
1250 mailerr("451 4.3.0",
1251 "%s: fstat: can't stat temporary storage: %s",
1252 ui
.mailspool
, sm_errstring(errno
));
1256 if (dsb
.st_size
+ sb
.st_size
+ 1 > ui
.quota
)
1258 ExitVal
= EX_UNAVAILABLE
;
1259 mailerr("551 5.2.2",
1260 "%s: Mailbox full or quota exceeded",
1267 /* Wait until we can get a lock on the file. */
1268 if (flock(mbfd
, LOCK_EX
) < 0)
1270 mailerr("450 4.2.0", "%s: %s", path
, sm_errstring(errno
));
1274 /* Get the starting offset of the new message */
1275 curoff
= lseek(mbfd
, (off_t
) 0, SEEK_END
);
1276 (void) sm_snprintf(biffmsg
, sizeof(biffmsg
), "%s@%lld\n",
1277 name
, (LONGLONG_T
) curoff
);
1279 /* Copy the message into the file. */
1280 if (lseek(fd
, (off_t
) 0, SEEK_SET
) == (off_t
) -1)
1282 mailerr("450 4.2.0", "Temporary file: %s",
1283 sm_errstring(errno
));
1287 fprintf(stderr
, "before writing: euid = %d\n", (int) geteuid());
1289 #ifdef CONTENTLENGTH
1290 headerbytes
= (BodyLength
>= 0) ? HeaderLength
: -1 ;
1293 if (headerbytes
== 0)
1295 (void) sm_snprintf(buf
, sizeof buf
, "%s", ContentHdr
);
1300 else if (headerbytes
> sizeof(buf
) || headerbytes
< 0)
1301 readamount
= sizeof(buf
);
1303 readamount
= headerbytes
;
1304 if (readamount
!= 0)
1305 nr
= read(fd
, buf
, readamount
);
1308 if (headerbytes
> 0)
1311 #else /* CONTENTLENGTH */
1312 while ((nr
= read(fd
, buf
, sizeof(buf
))) > 0)
1314 #endif /* CONTENTLENGTH */
1315 for (off
= 0; off
< nr
; off
+= nw
)
1317 if ((nw
= write(mbfd
, buf
+ off
, nr
- off
)) < 0)
1319 errcode
= "450 4.2.0";
1321 if (errno
== EDQUOT
&& BounceQuota
)
1322 errcode
= "552 5.2.2";
1324 mailerr(errcode
, "%s: %s",
1325 path
, sm_errstring(errno
));
1332 mailerr("450 4.2.0", "Temporary file: %s",
1333 sm_errstring(errno
));
1337 /* Flush to disk, don't wait for update. */
1338 if (fsync(mbfd
) < 0)
1340 mailerr("450 4.2.0", "%s: %s", path
, sm_errstring(errno
));
1343 fprintf(stderr
, "reset euid = %d\n", (int) geteuid());
1346 (void) ftruncate(mbfd
, curoff
);
1347 err1
: if (mbfd
>= 0)
1349 err0
: (void) setreuid(0, 0);
1355 ** Save the current size so if the close() fails below
1356 ** we can make sure no other process has changed the mailbox
1357 ** between the failed close and the re-open()/re-lock().
1358 ** If something else has changed the size, we shouldn't
1359 ** try to truncate it as we may do more harm then good
1360 ** (e.g., truncate a later message delivery).
1363 if (fstat(mbfd
, &sb
) < 0)
1366 cursize
= sb
.st_size
;
1369 /* Close and check -- NFS doesn't write until the close. */
1372 errcode
= "450 4.2.0";
1374 if (errno
== EDQUOT
&& BounceQuota
)
1375 errcode
= "552 5.2.2";
1377 mailerr(errcode
, "%s: %s", path
, sm_errstring(errno
));
1378 mbfd
= open(path
, O_WRONLY
, 0);
1381 || flock(mbfd
, LOCK_EX
) < 0 ||
1382 fstat(mbfd
, &sb
) < 0 ||
1383 sb
.st_size
!= cursize
||
1385 !S_ISREG(sb
.st_mode
) ||
1386 sb
.st_dev
!= fsb
.st_dev
||
1387 sb
.st_ino
!= fsb
.st_ino
||
1388 # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */
1389 sb
.st_gen
!= fsb
.st_gen
||
1390 # endif /* HAS_ST_GEN && 0 */
1391 sb
.st_uid
!= fsb
.st_uid
1394 /* Don't use a bogus file */
1402 /* Attempt to truncate back to pre-write size */
1406 notifybiff(biffmsg
);
1408 if (setreuid(0, 0) < 0)
1410 mailerr("450 4.2.0", "setreuid(0, 0): %s",
1411 sm_errstring(errno
));
1415 fprintf(stderr
, "reset euid = %d\n", (int) geteuid());
1419 printf("250 2.1.5 %s Ok\r\n", name
);
1423 ** user.lock files are necessary for compatibility with other
1424 ** systems, e.g., when the mail spool file is NFS exported.
1425 ** Alas, mailbox locking is more than just a local matter.
1429 bool Locked
= false;
1440 if ((r
= maillock(name
, 15)) == L_SUCCESS
)
1447 case L_TMPLOCK
: /* Can't create tmp file */
1448 case L_TMPWRITE
: /* Can't write pid into lockfile */
1449 case L_MAXTRYS
: /* Failed after retrycnt attempts */
1453 case L_ERROR
: /* Check errno for reason */
1456 default: /* other permanent errors */
1471 #else /* MAILLOCK */
1473 char LockName
[MAXPATHLEN
];
1484 if (strlen(path
) + 6 > sizeof LockName
)
1486 (void) sm_snprintf(LockName
, sizeof LockName
, "%s.lock", path
);
1487 (void) time(&start
);
1494 /* global timeout */
1496 if (now
> start
+ LOCKTO_GLOB
)
1501 fd
= open(LockName
, O_WRONLY
|O_EXCL
|O_CREAT
, LOCKFILE_PMODE
);
1504 /* defeat lock checking programs which test pid */
1505 (void) write(fd
, "0", 2);
1510 if (stat(LockName
, &st
) < 0)
1512 if (statfailed
++ > 5)
1521 if (now
< st
.st_ctime
+ LOCKTO_RM
)
1524 /* try to remove stale lockfile */
1525 if (unlink(LockName
) < 0)
1535 (void) unlink(LockName
);
1538 #endif /* MAILLOCK */
1544 static bool initialized
= false;
1549 static struct sockaddr_in addr
;
1555 /* Be silent if biff service not available. */
1556 if ((sp
= getservbyname("biff", "udp")) == NULL
||
1557 (hp
= gethostbyname("localhost")) == NULL
||
1558 hp
->h_length
!= INADDRSZ
)
1561 addr
.sin_family
= hp
->h_addrtype
;
1562 memcpy(&addr
.sin_addr
, hp
->h_addr
, INADDRSZ
);
1563 addr
.sin_port
= sp
->s_port
;
1566 /* No message, just return */
1570 /* Couldn't initialize addr struct */
1571 if (addr
.sin_family
== AF_UNSPEC
)
1574 if (f
< 0 && (f
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
1576 len
= strlen(msg
) + 1;
1577 (void) sendto(f
, msg
, len
, 0, (struct sockaddr
*) &addr
, sizeof(addr
));
1584 # if _FFR_SPOOL_PATH
1585 mailerr(NULL
, "usage: mail.local [-7] [-b] [-d] [-l] [-f from|-r from] [-h filename] [-p path] user ...");
1586 # else /* _FFR_SPOOL_PATH */
1587 mailerr(NULL
, "usage: mail.local [-7] [-b] [-d] [-l] [-f from|-r from] [-h filename] user ...");
1588 # endif /* _FFR_SPOOL_PATH */
1595 mailerr(const char *hdr
, const char *fmt
, ...)
1596 #else /* __STDC__ */
1597 mailerr(hdr
, fmt
, va_alist
)
1601 #endif /* __STDC__ */
1606 (void) e_to_sys(errno
);
1608 SM_VA_START(ap
, fmt
);
1610 if (LMTPMode
&& hdr
!= NULL
)
1612 sm_snprintf(ErrBuf
, sizeof ErrBuf
, "%s ", hdr
);
1613 len
= strlen(ErrBuf
);
1615 (void) sm_vsnprintf(&ErrBuf
[len
], sizeof ErrBuf
- len
, fmt
, ap
);
1621 /* Log the message to syslog. */
1623 syslog(LOG_ERR
, "%s", ErrBuf
);
1630 printf("%s\r\n", ErrBuf
);
1633 if (ExitVal
!= EX_USAGE
)
1634 (void) fprintf(stderr
, "mail.local: ");
1635 fprintf(stderr
, "%s\n", ErrBuf
);
1644 static char p
[MAXPATHLEN
];
1649 char Base64
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
1651 unsigned char md5
[18];
1652 # if MAXPATHLEN <= 24
1653 ERROR _MAXPATHLEN
<= 24
1654 # endif /* MAXPATHLEN <= 24 */
1658 # endif /* HASHSPOOLMD5 */
1660 if (HashType
== HASH_NONE
|| HashDepth
* 2 >= MAXPATHLEN
)
1675 MD5_Update(&ctx
, name
, strlen(name
));
1676 MD5_Final(md5
, &ctx
);
1680 for (i
= 0; i
< 6; i
++)
1682 bits
= (unsigned) md5
[(3 * i
)] << 16;
1683 bits
|= (unsigned) md5
[(3 * i
) + 1] << 8;
1684 bits
|= (unsigned) md5
[(3 * i
) + 2];
1686 for (j
= 3; j
>= 0; j
--)
1688 b64
[(4 * i
) + j
] = Base64
[(bits
& 0x3f)];
1695 # endif /* HASHSPOOLMD5 */
1699 for (i
= 0; i
< HashDepth
; i
++)
1705 p
[(i
* 2) + 1] = '/';
1707 p
[HashDepth
* 2] = '\0';
1710 #endif /* HASHSPOOL */
1714 * Guess which errno's are temporary. Gag me.
1721 /* Temporary failures override hard errors. */
1722 if (ExitVal
== EX_TEMPFAIL
)
1725 switch (num
) /* Hopefully temporary errors. */
1728 case EDQUOT
: /* Disc quota exceeded */
1731 ExitVal
= EX_UNAVAILABLE
;
1737 case EAGAIN
: /* Resource temporarily unavailable */
1740 case EBUSY
: /* Device busy */
1743 case EPROCLIM
: /* Too many processes */
1744 #endif /* EPROCLIM */
1746 case EUSERS
: /* Too many users */
1749 case ECONNABORTED
: /* Software caused connection abort */
1750 #endif /* ECONNABORTED */
1752 case ECONNREFUSED
: /* Connection refused */
1753 #endif /* ECONNREFUSED */
1755 case ECONNRESET
: /* Connection reset by peer */
1756 #endif /* ECONNRESET */
1758 case EDEADLK
: /* Resource deadlock avoided */
1759 #endif /* EDEADLK */
1761 case EFBIG
: /* File too large */
1764 case EHOSTDOWN
: /* Host is down */
1765 #endif /* EHOSTDOWN */
1767 case EHOSTUNREACH
: /* No route to host */
1768 #endif /* EHOSTUNREACH */
1770 case EMFILE
: /* Too many open files */
1773 case ENETDOWN
: /* Network is down */
1774 #endif /* ENETDOWN */
1776 case ENETRESET
: /* Network dropped connection on reset */
1777 #endif /* ENETRESET */
1779 case ENETUNREACH
: /* Network is unreachable */
1780 #endif /* ENETUNREACH */
1782 case ENFILE
: /* Too many open files in system */
1785 case ENOBUFS
: /* No buffer space available */
1786 #endif /* ENOBUFS */
1788 case ENOMEM
: /* Cannot allocate memory */
1791 case ENOSPC
: /* No space left on device */
1794 case EROFS
: /* Read-only file system */
1797 case ESTALE
: /* Stale NFS file handle */
1800 case ETIMEDOUT
: /* Connection timed out */
1801 #endif /* ETIMEDOUT */
1802 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
1803 case EWOULDBLOCK
: /* Operation would block. */
1804 #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */
1805 ExitVal
= EX_TEMPFAIL
;
1809 ExitVal
= EX_UNAVAILABLE
;
1815 #if defined(ultrix) || defined(_CRAY)
1817 * Copyright (c) 1987, 1993
1818 * The Regents of the University of California. All rights reserved.
1820 * Redistribution and use in source and binary forms, with or without
1821 * modification, are permitted provided that the following conditions
1823 * 1. Redistributions of source code must retain the above copyright
1824 * notice, this list of conditions and the following disclaimer.
1825 * 2. Redistributions in binary form must reproduce the above copyright
1826 * notice, this list of conditions and the following disclaimer in the
1827 * documentation and/or other materials provided with the distribution.
1828 * 3. All advertising materials mentioning features or use of this software
1829 * must display the following acknowledgement:
1830 * This product includes software developed by the University of
1831 * California, Berkeley and its contributors.
1832 * 4. Neither the name of the University nor the names of its contributors
1833 * may be used to endorse or promote products derived from this software
1834 * without specific prior written permission.
1836 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1837 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1838 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1839 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1840 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1841 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1842 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1843 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1844 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1845 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1849 # if defined(LIBC_SCCS) && !defined(lint)
1850 static char sccsid
[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
1851 # endif /* defined(LIBC_SCCS) && !defined(lint) */
1853 # include <sys/types.h>
1854 # include <sys/stat.h>
1860 static int _gettemp();
1867 return (_gettemp(path
, &fd
) ? fd
: -1);
1871 _gettemp(path
, doopen
)
1873 register int *doopen
;
1876 register char *start
, *trv
;
1881 for (trv
= path
; *trv
; ++trv
); /* extra X's get set to 0's */
1882 while (*--trv
== 'X')
1884 *trv
= (pid
% 10) + '0';
1889 * check the target directory; if you have six X's and it
1890 * doesn't exist this runs for a *very* long time.
1892 for (start
= trv
+ 1;; --trv
)
1899 if (stat(path
, &sbuf
) < 0)
1901 if (!S_ISDIR(sbuf
.st_mode
))
1915 if ((*doopen
= open(path
, O_CREAT
|O_EXCL
|O_RDWR
,
1918 if (errno
!= EEXIST
)
1921 else if (stat(path
, &sbuf
) < 0)
1922 return(errno
== ENOENT
? 1 : 0);
1924 /* tricky little algorithm for backward compatibility */
1933 if (isascii(*trv
) && isdigit(*trv
))
1943 #endif /* defined(ultrix) || defined(_CRAY) */