From a6f1137bf4d974192903cde234bccbe5bed3a56c Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Mon, 27 Nov 2000 00:00:00 -0500 Subject: [PATCH] snapshot-20001127 --- postfix/.indent.pro | 1 + postfix/src/global/mail_version.h | 2 +- postfix/src/global/mbox_open.c | 86 +++++++++++++++++++++---------- postfix/src/global/mbox_open.h | 9 +++- postfix/src/local/file.c | 19 +++---- postfix/src/local/mailbox.c | 13 ++--- postfix/src/postlock/postlock.c | 15 +++--- 7 files changed, 91 insertions(+), 54 deletions(-) diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 8c52cbf79..dd75ae0bd 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -68,6 +68,7 @@ -TMASTER_SERV -TMASTER_STATUS -TMBLOCK +-TMBOX -TMKMAP -TMKMAP_OPEN_INFO -TMULTI_SERVER diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index e0067e4af..ea69a239b 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -15,7 +15,7 @@ * Version of this program. */ #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "Snapshot-20001126" +#define DEF_MAIL_VERSION "Snapshot-20001127" extern char *var_mail_version; /* LICENSE diff --git a/postfix/src/global/mbox_open.c b/postfix/src/global/mbox_open.c index 36474482f..6b512e662 100644 --- a/postfix/src/global/mbox_open.c +++ b/postfix/src/global/mbox_open.c @@ -6,7 +6,14 @@ /* SYNOPSIS /* #include /* -/* int mbox_open(path, flags, mode, st, user, group, lock_style, why) +/* typedef struct { +/* .in +4 +/* /* public members... */ +/* VSTREAM *fp; +/* .in -4 +/* } MBOX; +/* +/* MBOX *mbox_open(path, flags, mode, st, user, group, lock_style, why) /* const char *path; /* int flags; /* int mode; @@ -16,9 +23,8 @@ /* int lock_style; /* VSTRING *why; /* -/* void mbox_release(path, lock_style) -/* const char *path; -/* int lock_style; +/* void mbox_release(mbox) +/* MBOX *mbox; /* DESCRIPTION /* This module manages access to UNIX mailbox-style files. /* @@ -29,9 +35,10 @@ /* adequate effective privileges. /* The \fBlock_style\fR argument specifies a lock style from /* mbox_lock_mask(). Kernel locks are applied to regular files only. +/* The result is a handle that must be destroyed by mbox_release(). /* /* mbox_release() releases the named mailbox. It is up to the -/* application to close the file. +/* application to close the stream. /* DIAGNOSTICS /* mbox_open() returns a null pointer in case of problems, and /* sets errno to EAGAIN if someone else has exclusive access. @@ -59,6 +66,8 @@ #include #include #include +#include +#include /* Global library. */ @@ -69,43 +78,58 @@ /* mbox_open - open mailbox-style file for exclusive access */ -VSTREAM *mbox_open(const char *path, int flags, int mode, struct stat * st, - uid_t chown_uid, gid_t chown_gid, - int lock_style, VSTRING *why) +MBOX *mbox_open(const char *path, int flags, int mode, struct stat * st, + uid_t chown_uid, gid_t chown_gid, + int lock_style, VSTRING *why) { struct stat local_statbuf; + MBOX *mp; + int locked = 0; VSTREAM *fp; /* * Create dotlock file. This locking method does not work well over NFS: * creating files atomically is a problem, and a successful operation can * fail with EEXIST. + * + * If file.lock can't be created, ignore the problem if the application says + * so. We need this so that Postfix can deliver as unprivileged user to + * /dev/null style aliases. Alternatively, we could open the file first, + * and dot-lock the file only if it is a regular file, just like we do + * with kernel locks. */ - if ((lock_style & MBOX_DOT_LOCK) && dot_lockfile(path, why) < 0) { - if (errno == EEXIST) { - errno = EAGAIN; - return (0); + if (lock_style & MBOX_DOT_LOCK) { + if (dot_lockfile(path, why) == 0) { + locked |= MBOX_DOT_LOCK; + } else { + if (errno == EEXIST) { + errno = EAGAIN; + return (0); + } + if ((lock_style & MBOX_DOT_LOCK_MAY_FAIL) == 0) { + return (0); + } } - - /* - * If file.lock can't be created, ignore the problem. We need this so - * that Postfix can deliver as unprivileged user to /dev/null - * aliases. - */ - if ((lock_style & MBOX_DOT_LOCK_MAY_FAIL) == 0) - return (0); } /* - * Open or create the target file. + * Open or create the target file. In case of a privileged open, the + * privileged user may be attacked through an unsafe parent directory. In + * case of an unprivileged open, the mail system may be attacked by a + * malicious user-specified path, and the unprivileged user may be + * attacked through an unsafe parent directory. Open non-blocking to fend + * off attacks involving FIFOs and other weird targets. */ if (st == 0) st = &local_statbuf; - if ((fp = safe_open(path, flags, mode, st, chown_uid, chown_gid, why)) == 0) { - if (lock_style & MBOX_DOT_LOCK) + if ((fp = safe_open(path, flags, mode | O_NONBLOCK, st, + chown_uid, chown_gid, why)) == 0) { + if (locked & MBOX_DOT_LOCK) dot_unlockfile(path); return (0); } + non_blocking(vstream_fileno(fp), BLOCKING); + close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC); /* * Acquire kernel locks, but only if the target is a regular file, in @@ -121,17 +145,23 @@ VSTREAM *mbox_open(const char *path, int flags, int mode, struct stat * st, || LOCK_FAIL(MBOX_FCNTL_LOCK, MYFLOCK_STYLE_FCNTL))) { if (myflock_locked(vstream_fileno(fp))) errno = EAGAIN; - if (lock_style & MBOX_DOT_LOCK) + if (locked & MBOX_DOT_LOCK) dot_unlockfile(path); return (0); } - return (fp); + mp = (MBOX *) mymalloc(sizeof(*mp)); + mp->path = mystrdup(path); + mp->fp = fp; + mp->locked = locked; + return (mp); } /* mbox_release - release mailbox exclusive access */ -void mbox_release(const char *path, int lock_style) +void mbox_release(MBOX *mp) { - if (lock_style & MBOX_DOT_LOCK) - dot_unlockfile(path); + if (mp->locked & MBOX_DOT_LOCK) + dot_unlockfile(mp->path); + myfree(mp->path); + myfree((char *) mp); } diff --git a/postfix/src/global/mbox_open.h b/postfix/src/global/mbox_open.h index 7e98772ab..940f04b52 100644 --- a/postfix/src/global/mbox_open.h +++ b/postfix/src/global/mbox_open.h @@ -21,8 +21,13 @@ /* * External interface. */ -extern VSTREAM *mbox_open(const char *, int, int, struct stat *, uid_t, gid_t, int, VSTRING *); -extern void mbox_release(const char *, int); +typedef struct { + char *path; /* saved path, for dot_unlock */ + VSTREAM *fp; /* open stream or null */ + int locked; /* what locks were set */ +} MBOX; +extern MBOX *mbox_open(const char *, int, int, struct stat *, uid_t, gid_t, int, VSTRING *); +extern void mbox_release(MBOX *); /* LICENSE /* .ad diff --git a/postfix/src/local/file.c b/postfix/src/local/file.c index f3b0af1c1..75588ea61 100644 --- a/postfix/src/local/file.c +++ b/postfix/src/local/file.c @@ -84,7 +84,7 @@ int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path) { char *myname = "deliver_file"; struct stat st; - VSTREAM *dst; + MBOX *mp; VSTRING *why; int status; int copy_flags; @@ -148,26 +148,27 @@ int deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path) copy_flags &= ~MAIL_COPY_DELIVERED; set_eugid(usr_attr.uid, usr_attr.gid); - dst = mbox_open(path, O_APPEND | O_CREAT | O_WRONLY, - S_IRUSR | S_IWUSR, &st, -1, -1, - local_mbox_lock_mask | MBOX_DOT_LOCK_MAY_FAIL, why); - if (dst == 0) { + mp = mbox_open(path, O_APPEND | O_CREAT | O_WRONLY, + S_IRUSR | S_IWUSR, &st, -1, -1, + local_mbox_lock_mask | MBOX_DOT_LOCK_MAY_FAIL, why); + if (mp == 0) { status = (errno == EAGAIN ? defer_append : bounce_append) (BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), "cannot access destination file %s: %s", path, STR(why)); } else if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { - vstream_fclose(dst); + vstream_fclose(mp->fp); status = bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), "executable destination file %s", path); - } else if (mail_copy(COPY_ATTR(state.msg_attr), dst, S_ISREG(st.st_mode) ? - copy_flags : (copy_flags & ~MAIL_COPY_TOFILE), "\n", why)) { + } else if (mail_copy(COPY_ATTR(state.msg_attr), mp->fp, + S_ISREG(st.st_mode) ? copy_flags : (copy_flags & ~MAIL_COPY_TOFILE), + "\n", why)) { status = defer_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), "cannot append destination file %s: %s", path, STR(why)); } else { status = sent(SENT_ATTR(state.msg_attr), "%s", path); } - mbox_release(path, local_mbox_lock_mask); + mbox_release(mp); set_eugid(var_owner_uid, var_owner_gid); /* diff --git a/postfix/src/local/mailbox.c b/postfix/src/local/mailbox.c index cc8267a07..30d2ad80d 100644 --- a/postfix/src/local/mailbox.c +++ b/postfix/src/local/mailbox.c @@ -96,7 +96,7 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) char *spool_dir; char *mailbox; VSTRING *why; - VSTREAM *dst; + MBOX *mp; int status; int copy_flags; VSTRING *biff; @@ -180,23 +180,24 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) copy_flags &= ~MAIL_COPY_DELIVERED; set_eugid(spool_uid, spool_gid); - dst = mbox_open(mailbox, O_APPEND | O_WRONLY | O_CREAT, + mp = mbox_open(mailbox, O_APPEND | O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR, &st, chown_uid, chown_gid, local_mbox_lock_mask, why); - if (dst != 0) { + if (mp != 0) { if (spool_uid != usr_attr.uid || spool_gid != usr_attr.gid) set_eugid(usr_attr.uid, usr_attr.gid); if (S_ISREG(st.st_mode) == 0) { + vstream_fclose(mp->fp); vstring_sprintf(why, "file %s should be a regular file", mailbox); errno = 0; } else { - end = vstream_fseek(dst, (off_t) 0, SEEK_END); - status = mail_copy(COPY_ATTR(state.msg_attr), dst, + end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END); + status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp, copy_flags, "\n", why); } if (spool_uid != usr_attr.uid || spool_gid != usr_attr.gid) set_eugid(spool_uid, spool_gid); - mbox_release(mailbox, local_mbox_lock_mask); + mbox_release(mp); } set_eugid(var_owner_uid, var_owner_gid); diff --git a/postfix/src/postlock/postlock.c b/postfix/src/postlock/postlock.c index c721db84d..44d7ecdea 100644 --- a/postfix/src/postlock/postlock.c +++ b/postfix/src/postlock/postlock.c @@ -142,9 +142,9 @@ int main(int argc, char **argv) int count; WAIT_STATUS_T status; pid_t pid; - int mbox_lock; + int lock_mask; char *lock_style = 0; - VSTREAM *fp; + MBOX *mp; /* * Be consistent with file permissions. @@ -204,7 +204,7 @@ int main(int argc, char **argv) * Read the config file. */ mail_conf_read(); - mbox_lock = mbox_lock_mask(lock_style ? lock_style : + lock_mask = mbox_lock_mask(lock_style ? lock_style : get_mail_conf_str(VAR_MAILBOX_LOCK, DEF_MAILBOX_LOCK, 1, 0)); /* @@ -212,11 +212,10 @@ int main(int argc, char **argv) * command is not supposed to disappear into the background. */ why = vstring_alloc(1); - if ((fp = mbox_open(folder, O_APPEND | O_WRONLY | O_CREAT, + if ((mp = mbox_open(folder, O_APPEND | O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR, (struct stat *) 0, - -1, -1, mbox_lock, why)) == 0) + -1, -1, lock_mask, why)) == 0) msg_fatal("%s", vstring_str(why)); - close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC); /* * Run the command. Remove the lock after completion. @@ -232,12 +231,12 @@ int main(int argc, char **argv) default: if (waitpid(pid, &status, 0) < 0) msg_fatal("waitpid: %m"); - mbox_release(folder, mbox_lock); + mbox_release(mp); exit(WIFEXITED(status) ? WEXITSTATUS(status) : 1); } if (count + 1 < var_fork_tries) sleep(var_fork_delay); } - mbox_release(folder, mbox_lock); + mbox_release(mp); exit(EX_TEMPFAIL); }