NetBSD/gnu/dist/postfix/local/mailbox.c

294 lines
7.6 KiB
C
Raw Normal View History

/*++
/* NAME
/* mailbox 3
/* SUMMARY
/* mailbox delivery
/* SYNOPSIS
/* #include "local.h"
/*
/* int deliver_mailbox(state, usr_attr, statusp)
/* LOCAL_STATE state;
/* USER_ATTR usr_attr;
/* int *statusp;
/* DESCRIPTION
/* deliver_mailbox() delivers to mailbox, with duplicate
/* suppression. The default is direct mailbox delivery to
/* /var/[spool/]mail/\fIuser\fR; when a \fIhome_mailbox\fR
/* has been configured, mail is delivered to ~/$\fIhome_mailbox\fR;
/* and when a \fImailbox_command\fR has been configured, the message
/* is piped into the command instead.
/*
/* A zero result means that the named user was not found.
/*
/* Arguments:
/* .IP state
/* The attributes that specify the message, recipient and more.
/* Attributes describing alias, include or forward expansion.
/* A table with the results from expanding aliases or lists.
/* .IP usr_attr
/* Attributes describing user rights and environment.
/* .IP statusp
/* Delivery status: see below.
/* DIAGNOSTICS
/* The message delivery status is non-zero when delivery should be tried
/* again.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
/* Utility library. */
#include <msg.h>
#include <htable.h>
#include <vstring.h>
#include <vstream.h>
#include <mymalloc.h>
#include <stringops.h>
#include <set_eugid.h>
/* Global library. */
#include <mail_copy.h>
#include <safe_open.h>
#include <deliver_flock.h>
#ifdef USE_DOT_LOCK
#include <dot_lockfile.h>
#endif
#include <defer.h>
#include <sent.h>
#include <mypwd.h>
#include <been_here.h>
#include <mail_params.h>
#include <deliver_pass.h>
#ifndef EDQUOT
#define EDQUOT EFBIG
#endif
/* Application-specific. */
#include "local.h"
#include "biff_notify.h"
#define YES 1
#define NO 0
/* deliver_mailbox_file - deliver to recipient mailbox */
static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
{
char *myname = "deliver_mailbox_file";
char *spool_dir;
char *mailbox;
VSTRING *why;
VSTREAM *dst;
int status;
int copy_flags;
VSTRING *biff;
long end;
struct stat st;
uid_t spool_uid;
gid_t spool_gid;
uid_t chown_uid;
gid_t chown_gid;
/*
* Make verbose logging easier to understand.
*/
state.level++;
if (msg_verbose)
MSG_LOG_STATE(myname, state);
/*
* Initialize. Assume the operation will fail. Set the delivered
* attribute to reflect the final recipient.
*/
if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
state.msg_attr.delivered = state.msg_attr.recipient;
status = -1;
why = vstring_alloc(100);
if (*var_home_mailbox) {
spool_dir = 0;
mailbox = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0);
} else {
spool_dir = var_mail_spool_dir;
mailbox = concatenate(spool_dir, "/", state.msg_attr.user, (char *) 0);
}
/*
* Mailbox delivery with least privilege. As long as we do not use root
* privileges this code may also work over NFS.
*
* If delivering to the recipient's home directory, perform all operations
* (including file locking) as that user (Mike Muuss, Army Research
* Laboratory, USA).
*
* If delivering to the mail spool directory, and the spool directory is
* world-writable, deliver as the recipient; if the spool directory is
* group-writable, use the recipient user id and the mail spool group id.
*
* Otherwise, use root privileges and chown the mailbox.
*/
if (spool_dir == 0
|| stat(spool_dir, &st) < 0
|| (st.st_mode & S_IWOTH) != 0) {
spool_uid = usr_attr.uid;
spool_gid = usr_attr.gid;
} else if ((st.st_mode & S_IWGRP) != 0) {
spool_uid = usr_attr.uid;
spool_gid = st.st_gid;
} else {
spool_uid = 0;
spool_gid = 0;
}
if (spool_uid == usr_attr.uid) {
chown_uid = -1;
chown_gid = -1;
} else {
chown_uid = usr_attr.uid;
chown_gid = usr_attr.gid;
}
if (msg_verbose)
msg_info("spool_uid/gid %d/%d chown_uid/gid %d/%d",
spool_uid, spool_gid, chown_uid, chown_gid);
/*
* Lock the mailbox and open/create the mailbox file. Depending on the
* type of locking used, we lock first or we open first.
*
* Write the file as the recipient, so that file quota work.
*/
copy_flags = MAIL_COPY_MBOX;
if ((local_deliver_hdr_mask & DELIVER_HDR_FILE) == 0)
copy_flags &= ~MAIL_COPY_DELIVERED;
set_eugid(spool_uid, spool_gid);
#ifdef USE_DOT_LOCK
if (dot_lockfile(mailbox, why) >= 0) {
#endif
dst = safe_open(mailbox, O_APPEND | O_WRONLY | O_CREAT,
S_IRUSR | S_IWUSR, chown_uid, chown_gid, why);
set_eugid(usr_attr.uid, usr_attr.gid);
if (dst != 0) {
end = vstream_fseek(dst, (off_t) 0, SEEK_END);
if (deliver_flock(vstream_fileno(dst), why) < 0)
vstream_fclose(dst);
else if (mail_copy(COPY_ATTR(state.msg_attr), dst,
copy_flags, why) == 0) {
status = 0;
if (var_biff) {
biff = vstring_alloc(100);
vstring_sprintf(biff, "%s@%ld", usr_attr.logname,
(long) end);
biff_notify(vstring_str(biff), VSTRING_LEN(biff) + 1);
vstring_free(biff);
}
}
}
#ifdef USE_DOT_LOCK
set_eugid(spool_uid, spool_gid);
dot_unlockfile(mailbox);
}
#endif
set_eugid(var_owner_uid, var_owner_gid);
if (status)
status = (errno == EDQUOT ? bounce_append : defer_append)
(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr),
"cannot append to file %s: %s", mailbox, vstring_str(why));
else
sent(SENT_ATTR(state.msg_attr), "mailbox");
myfree(mailbox);
vstring_free(why);
return (status);
}
/* deliver_mailbox - deliver to recipient mailbox */
int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
{
char *myname = "deliver_mailbox";
int status;
struct mypasswd *mbox_pwd;
char *path;
/*
* Make verbose logging easier to understand.
*/
state.level++;
if (msg_verbose)
MSG_LOG_STATE(myname, state);
/*
* DUPLICATE ELIMINATION
*
* Don't come here more than once, whether or not the recipient exists.
*/
if (been_here(state.dup_filter, "mailbox %s", state.msg_attr.user))
return (YES);
/*
* Delegate mailbox delivery to another message transport.
*/
if (*var_mailbox_transport) {
*statusp = deliver_pass(MAIL_CLASS_PRIVATE, var_mailbox_transport,
state.request, state.msg_attr.recipient, -1L);
return (YES);
}
/*
* Skip delivery when this recipient does not exist.
*/
if ((mbox_pwd = mypwnam(state.msg_attr.user)) == 0)
return (NO);
/*
* No early returns or we have a memory leak.
*/
/*
* DELIVERY RIGHTS
*
* Use the rights of the recipient user.
*/
SET_USER_ATTR(usr_attr, mbox_pwd, state.level);
/*
* Deliver to mailbox or to external command.
*/
#define LAST_CHAR(s) (s[strlen(s) - 1])
if (*var_mailbox_command)
status = deliver_command(state, usr_attr, var_mailbox_command);
else if (*var_home_mailbox && LAST_CHAR(var_home_mailbox) == '/') {
path = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0);
status = deliver_maildir(state, usr_attr, path);
myfree(path);
} else
status = deliver_mailbox_file(state, usr_attr);
/*
* Cleanup.
*/
mypwfree(mbox_pwd);
*statusp = status;
return (YES);
}