253 lines
7.2 KiB
C
253 lines
7.2 KiB
C
/*++
|
|
/* NAME
|
|
/* dotforward 3
|
|
/* SUMMARY
|
|
/* $HOME/.forward file expansion
|
|
/* SYNOPSIS
|
|
/* #include "local.h"
|
|
/*
|
|
/* int deliver_dotforward(state, usr_attr, statusp)
|
|
/* LOCAL_STATE state;
|
|
/* USER_ATTR usr_attr;
|
|
/* int *statusp;
|
|
/* DESCRIPTION
|
|
/* deliver_dotforward() delivers a message to the destinations
|
|
/* listed in a recipient's .forward file(s) as specified through
|
|
/* the forward_path configuration parameter. The result is
|
|
/* zero when no acceptable .forward file was found, or when
|
|
/* a recipient is listed in her own .forward file. Expansions
|
|
/* are scrutinized with the forward_expansion_filter parameter.
|
|
/*
|
|
/* Arguments:
|
|
/* .IP state
|
|
/* Message delivery attributes (sender, recipient etc.).
|
|
/* Attributes describing alias, include or forward expansion.
|
|
/* A table with the results from expanding aliases or lists.
|
|
/* A table with delivered-to: addresses taken from the message.
|
|
/* .IP usr_attr
|
|
/* Attributes describing user rights and environment.
|
|
/* .IP statusp
|
|
/* Message delivery status. See below.
|
|
/* DIAGNOSTICS
|
|
/* Fatal errors: out of memory. Warnings: bad $HOME/.forward
|
|
/* file type, permissions or ownership. The message delivery
|
|
/* status is non-zero when delivery should be tried again.
|
|
/* SEE ALSO
|
|
/* include(3) include file processor.
|
|
/* 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 <unistd.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#ifdef USE_PATHS_H
|
|
#include <paths.h>
|
|
#endif
|
|
#include <string.h>
|
|
|
|
/* Utility library. */
|
|
|
|
#include <msg.h>
|
|
#include <vstring.h>
|
|
#include <vstream.h>
|
|
#include <htable.h>
|
|
#include <open_as.h>
|
|
#include <lstat_as.h>
|
|
#include <iostuff.h>
|
|
#include <stringops.h>
|
|
#include <mymalloc.h>
|
|
#include <mac_expand.h>
|
|
|
|
/* Global library. */
|
|
|
|
#include <mypwd.h>
|
|
#include <bounce.h>
|
|
#include <been_here.h>
|
|
#include <mail_params.h>
|
|
#include <mail_conf.h>
|
|
#include <ext_prop.h>
|
|
|
|
/* Application-specific. */
|
|
|
|
#include "local.h"
|
|
|
|
#define NO 0
|
|
#define YES 1
|
|
|
|
/* deliver_dotforward - expand contents of .forward file */
|
|
|
|
int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
|
|
{
|
|
char *myname = "deliver_dotforward";
|
|
struct stat st;
|
|
VSTRING *path;
|
|
struct mypasswd *mypwd;
|
|
int fd;
|
|
VSTREAM *fp;
|
|
int status;
|
|
int forward_found = NO;
|
|
int lookup_status;
|
|
int addr_count;
|
|
char *saved_forward_path;
|
|
char *lhs;
|
|
char *next;
|
|
int expand_status;
|
|
|
|
/*
|
|
* Make verbose logging easier to understand.
|
|
*/
|
|
state.level++;
|
|
if (msg_verbose)
|
|
MSG_LOG_STATE(myname, state);
|
|
|
|
/*
|
|
* Skip this module if per-user forwarding is disabled.
|
|
*/
|
|
if (*var_forward_path == 0)
|
|
return (NO);
|
|
|
|
/*
|
|
* Skip non-existing users. The mailbox delivery routine will catch the
|
|
* error.
|
|
*/
|
|
if ((mypwd = mypwnam(state.msg_attr.user)) == 0)
|
|
return (NO);
|
|
|
|
/*
|
|
* From here on no early returns or we have a memory leak.
|
|
*/
|
|
|
|
/*
|
|
* EXTERNAL LOOP CONTROL
|
|
*
|
|
* Set the delivered message attribute to the recipient, so that this
|
|
* message will list the correct forwarding address.
|
|
*/
|
|
state.msg_attr.delivered = state.msg_attr.recipient;
|
|
|
|
/*
|
|
* DELIVERY RIGHTS
|
|
*
|
|
* Do not inherit rights from the .forward file owner. Instead, use the
|
|
* recipient's rights, and insist that the .forward file is owned by the
|
|
* recipient. This is a small but significant difference. Use the
|
|
* recipient's rights for all /file and |command deliveries, and pass on
|
|
* these rights to command/file destinations in included files. When
|
|
* these are the rights of root, the /file and |command delivery routines
|
|
* will use unprivileged default rights instead. Better safe than sorry.
|
|
*/
|
|
SET_USER_ATTR(usr_attr, mypwd, state.level);
|
|
|
|
/*
|
|
* DELIVERY POLICY
|
|
*
|
|
* Update the expansion type attribute so that we can decide if deliveries
|
|
* to |command and /file/name are allowed at all.
|
|
*/
|
|
state.msg_attr.exp_type = EXPAND_TYPE_FWD;
|
|
|
|
/*
|
|
* WHERE TO REPORT DELIVERY PROBLEMS
|
|
*
|
|
* Set the owner attribute so that 1) include files won't set the sender to
|
|
* be this user and 2) mail forwarded to other local users will be
|
|
* resubmitted as a new queue file.
|
|
*/
|
|
state.msg_attr.owner = state.msg_attr.user;
|
|
|
|
/*
|
|
* Search the forward_path for an existing forward file.
|
|
*
|
|
* If unmatched extensions should never be propagated, or if a forward file
|
|
* name includes the address extension, don't propagate the extension to
|
|
* the recipient addresses.
|
|
*/
|
|
#define STR(x) vstring_str(x)
|
|
|
|
status = 0;
|
|
path = vstring_alloc(100);
|
|
saved_forward_path = mystrdup(var_forward_path);
|
|
next = saved_forward_path;
|
|
lookup_status = -1;
|
|
|
|
while ((lhs = mystrtok(&next, ", \t\r\n")) != 0) {
|
|
expand_status = local_expand(path, lhs, &state, &usr_attr,
|
|
var_fwd_exp_filter);
|
|
if ((expand_status & (MAC_PARSE_ERROR | MAC_PARSE_UNDEF)) == 0) {
|
|
lookup_status =
|
|
lstat_as(STR(path), &st, usr_attr.uid, usr_attr.gid);
|
|
if (msg_verbose)
|
|
msg_info("%s: path %s expand_status %d look_status %d", myname,
|
|
STR(path), expand_status, lookup_status);
|
|
if (lookup_status >= 0) {
|
|
if ((expand_status & LOCAL_EXP_EXTENSION_MATCHED) != 0
|
|
|| (local_ext_prop_mask & EXT_PROP_FORWARD) == 0)
|
|
state.msg_attr.unmatched = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process the forward file.
|
|
*
|
|
* Assume that usernames do not have file system meta characters. Open the
|
|
* .forward file as the user. Ignore files that aren't regular files,
|
|
* files that are owned by the wrong user, or files that have world write
|
|
* permission enabled.
|
|
*
|
|
* DUPLICATE/LOOP ELIMINATION
|
|
*
|
|
* If this user includes (an alias of) herself in her own .forward file,
|
|
* deliver to the user instead.
|
|
*/
|
|
if (lookup_status >= 0) {
|
|
if (been_here(state.dup_filter, "forward %s", STR(path)) == 0) {
|
|
state.msg_attr.exp_from = state.msg_attr.local;
|
|
if (S_ISREG(st.st_mode) == 0) {
|
|
msg_warn("file %s is not a regular file", STR(path));
|
|
} else if (st.st_uid != 0 && st.st_uid != usr_attr.uid) {
|
|
msg_warn("file %s has bad owner uid %d", STR(path), st.st_uid);
|
|
} else if (st.st_mode & 002) {
|
|
msg_warn("file %s is world writable", STR(path));
|
|
} else if ((fd = open_as(STR(path), O_RDONLY, 0, usr_attr.uid, usr_attr.gid)) < 0) {
|
|
msg_warn("cannot open file %s: %m", STR(path));
|
|
} else {
|
|
close_on_exec(fd, CLOSE_ON_EXEC);
|
|
addr_count = 0;
|
|
fp = vstream_fdopen(fd, O_RDONLY);
|
|
status = deliver_token_stream(state, usr_attr, fp, &addr_count);
|
|
if (vstream_fclose(fp))
|
|
msg_warn("close file %s: %m", STR(path));
|
|
if (addr_count > 0) {
|
|
forward_found = YES;
|
|
been_here(state.dup_filter, "forward-done %s", STR(path));
|
|
}
|
|
}
|
|
} else if (been_here_check(state.dup_filter, "forward-done %s", STR(path)) != 0)
|
|
forward_found = YES; /* else we're recursive */
|
|
}
|
|
|
|
/*
|
|
* Clean up.
|
|
*/
|
|
vstring_free(path);
|
|
myfree(saved_forward_path);
|
|
mypwfree(mypwd);
|
|
|
|
*statusp = status;
|
|
return (forward_found);
|
|
}
|