This is a discussion on Re: sendmail, mail.local and quota within the mailing.openbsd.tech forums, part of the OpenBSD category; --> On Mon, 13 Jun 2005, Otto Moerbeek wrote: > This new version deals with that by forking an unprivileged ...
| |||||||
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
| ||||
| On Mon, 13 Jun 2005, Otto Moerbeek wrote: > This new version deals with that by forking an unprivileged child, and > doing the locking and unlocking in the parent. This way, even if the > child gets killed, the parent will remove the lock file. New version with more accurate error reporting. -Otto Index: mail.local.8 ================================================== ================= RCS file: /cvs/src/libexec/mail.local/mail.local.8,v retrieving revision 1.25 diff -u -p -r1.25 mail.local.8 --- mail.local.8 20 May 2004 08:46:50 -0000 1.25 +++ mail.local.8 20 Jun 2005 08:02:34 -0000 @@ -36,8 +36,7 @@ .Nd store mail in a mailbox .Sh SYNOPSIS .Nm mail.local -.Op Fl L -.Op Fl l +.Op Fl bLl .Op Fl f Ar from .Ar user ... .Sh DESCRIPTION @@ -52,6 +51,9 @@ must be a valid user name. .Pp The options are as follows: .Bl -tag -width Ds +.It Fl b +Return a permanent failure if the mailbox is over quota, instead +of a temporary failure. .It Fl f Ar from Specify the sender's name. .It Fl l Index: mail.local.c ================================================== ================= RCS file: /cvs/src/libexec/mail.local/mail.local.c,v retrieving revision 1.27 diff -u -p -r1.27 mail.local.c --- mail.local.c 29 May 2005 02:11:49 -0000 1.27 +++ mail.local.c 20 Jun 2005 08:02:34 -0000 @@ -48,9 +48,11 @@ static char rcsid[] = "$OpenBSD: mail.lo #include <sys/param.h> #include <sys/stat.h> #include <sys/socket.h> +#include <sys/wait.h> #include <netinet/in.h> #include <sys/signal.h> #include <syslog.h> +#include <sysexits.h> #include <fcntl.h> #include <netdb.h> #include <pwd.h> @@ -63,6 +65,11 @@ static char rcsid[] = "$OpenBSD: mail.lo #include "pathnames.h" #include "mail.local.h" +int quotafatal; + +int writembox(const char *, const struct stat *, int, const struct passwd *); +__dead int child(const char *, const struct stat *, int, const struct passwd *); + int main(int argc, char *argv[]) { @@ -74,7 +81,7 @@ main(int argc, char *argv[]) openlog("mail.local", LOG_PERROR, LOG_MAIL); from = NULL; - while ((ch = getopt(argc, argv, "lLdf:r:H")) != -1) + while ((ch = getopt(argc, argv, "lLdf:r:Hb")) != -1) switch (ch) { case 'd': /* backward compatible */ break; @@ -93,6 +100,9 @@ main(int argc, char *argv[]) case 'H': holdme=1; break; + case 'b': + quotafatal = 1; + break; case '?': default: usage(); @@ -184,11 +194,11 @@ store(char *from) int deliver(int fd, char *name, int lockfile) { - struct stat sb, fsb; + struct stat sb; struct passwd *pw; - int mbfd=-1, nr, nw, off, rval=1, lfd=-1; - char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; - off_t curoff; + int mbfd=-1, lfd=-1; + int rval = 1; + char path[MAXPATHLEN]; /* * Disallow delivery to unknown names -- special mailboxes can be @@ -235,68 +245,138 @@ retry: pw->pw_uid, pw->pw_gid, name); goto bad; } - } else { - if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) { - merr(NOTFATAL, "%s: linked or special file", path); + if (fstat(mbfd, &sb) < 0) { + merr(NOTFATAL, "fstat %s: %s\n", path, strerror(errno)); goto bad; } - if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK, - S_IRUSR|S_IWUSR)) < 0) { - merr(NOTFATAL, "%s: %s", path, strerror(errno)); - goto bad; - } - if (fstat(mbfd, &fsb)) { - /* relating error to path may be bad style */ - merr(NOTFATAL, "%s: %s", path, strerror(errno)); - goto bad; - } - if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino) { - merr(NOTFATAL, "%s: changed after open", path); - goto bad; - } - /* paranoia? */ - if (fsb.st_nlink != 1 || !S_ISREG(fsb.st_mode)) { - merr(NOTFATAL, "%s: linked or special file", path); - goto bad; + close(mbfd); + } + + + if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) { + merr(NOTFATAL, "%s: linked or special file", path); + goto bad; + } + rval = writembox(path, &sb, fd, pw); + if (rval == EDQUOT) + rval = quotafatal ? EX_UNAVAILABLE : EX_TEMPFAIL; +bad: + + if (lfd != -1) { + rellock(); + close(lfd); + } + + return(rval); +} + +int +writembox(const char *path, const struct stat *sb, int fd, + const struct passwd *pw) +{ + pid_t pid; + int status; + + pid = fork(); + switch (pid) { + case -1: + merr(NOTFATAL, "fork failed: %s\n", errno); + return 1; + case 0: + child(path, sb, fd, pw); + break; + default: + do { + if (waitpid(pid, &status, 0) == -1) { + if (errno == EINTR) + continue; + merr(NOTFATAL, "waitpid: %\n", strerror(errno)); + return 1; + } + break; + } while (1); + if (WIFEXITED(status)) { + status = WEXITSTATUS(status); + if (status == EDQUOT) + merr(NOTFATAL, "%s: %s\n", path, + strerror(EDQUOT)); + else if (status != 0) + merr(NOTFATAL, "child returned %d\n", status); + return status; + } else if (WIFSIGNALED(status)) { + merr(NOTFATAL, "child got signal %d\n", + WTERMSIG(status)); + return 1; + } else { + merr(NOTFATAL, "child died\n"); + return 1; } + break; + } +} + +__dead int +child(const char *path, const struct stat *sb, int fd, const struct passwd *pw) +{ + char buf[8 * 1024], biffmsg[100]; + int mbfd, rval = 1; + struct stat fsb; + off_t curoff; + ssize_t nr, nw; + size_t off; + + (void) setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid); + if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK, + S_IRUSR|S_IWUSR)) < 0) { + merr(NOTFATAL, "%s: %s", path, strerror(errno)); + goto done; + } + if (fstat(mbfd, &fsb)) { + /* relating error to path may be bad style */ + merr(NOTFATAL, "%s: %s", path, strerror(errno)); + goto done; + } + if (sb->st_dev != fsb.st_dev || sb->st_ino != fsb.st_ino) { + merr(NOTFATAL, "%s: changed after open", path); + goto done; + } + /* paranoia? */ + if (fsb.st_nlink != 1 || !S_ISREG(fsb.st_mode)) { + merr(NOTFATAL, "%s: linked or special file", path); + goto done; } curoff = lseek(mbfd, 0, SEEK_END); - (void)snprintf(biffmsg, sizeof biffmsg, "%s@%qd\n", name, curoff); + (void)snprintf(biffmsg, sizeof biffmsg, "%s@%qd\n", + pw->pw_name, curoff); if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { merr(NOTFATAL, "temporary file: %s", strerror(errno)); - goto bad; + goto done; } - while ((nr = read(fd, buf, sizeof(buf))) > 0) - for (off = 0; off < nr; off += nw) + while ((nr = read(fd, buf, sizeof(buf))) > 0) { + for (off = 0; off < nr; off += nw) { if ((nw = write(mbfd, buf + off, nr - off)) < 0) { - merr(NOTFATAL, "%s: %s", path, strerror(errno)); + rval = errno; (void)ftruncate(mbfd, curoff); - goto bad; + goto done; } - - if (nr == 0) { + } + } + if (nr == 0) rval = 0; - } else { + else (void)ftruncate(mbfd, curoff); - merr(FATAL, "temporary file: %s", strerror(errno)); - } - -bad: - if (lfd != -1) { - rellock(); - close(lfd); - } - +done: if (mbfd != -1) { - (void)fsync(mbfd); /* Don't wait for update. */ - (void)close(mbfd); /* Implicit unlock. */ + if (fsync(mbfd) != 0) /* Don't wait for update. */ + merr(NOTFATAL, "%s: %s", path, strerror(errno)); + if (close(mbfd) != 0) /* Implicit unlock. */ + merr(NOTFATAL, "%s: %s", path, strerror(errno)); } - - if (!rval) + if (rval == 0) notifybiff(biffmsg); - return(rval); + exit(rval); } void @@ -334,5 +414,5 @@ notifybiff(char *msg) void usage(void) { - merr(FATAL, "usage: mail.local [-lL] [-f from] user ..."); + merr(FATAL, "usage: mail.local [-blL] [-f from] user ..."); } |