-TMASTER_SERV
-TMASTER_STATUS
-TMBLOCK
+-TMBOX
-TMKMAP
-TMKMAP_OPEN_INFO
-TMULTI_SERVER
* 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
/* SYNOPSIS
/* #include <mbox_open.h>
/*
-/* 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;
/* 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.
/*
/* 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.
#include <vstream.h>
#include <vstring.h>
#include <safe_open.h>
+#include <iostuff.h>
+#include <mymalloc.h>
/* Global library. */
/* 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
|| 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);
}
/*
* 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
{
char *myname = "deliver_file";
struct stat st;
- VSTREAM *dst;
+ MBOX *mp;
VSTRING *why;
int status;
int copy_flags;
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);
/*
char *spool_dir;
char *mailbox;
VSTRING *why;
- VSTREAM *dst;
+ MBOX *mp;
int status;
int copy_flags;
VSTRING *biff;
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);
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.
* 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));
/*
* 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.
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);
}