summaryrefslogtreecommitdiffstats
path: root/src/local/include.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/local/include.c')
-rw-r--r--src/local/include.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/src/local/include.c b/src/local/include.c
new file mode 100644
index 0000000..a213d3c
--- /dev/null
+++ b/src/local/include.c
@@ -0,0 +1,223 @@
+/*++
+/* NAME
+/* deliver_include 3
+/* SUMMARY
+/* deliver to addresses listed in include file
+/* SYNOPSIS
+/* #include "local.h"
+/*
+/* int deliver_include(state, usr_attr, path)
+/* LOCAL_STATE state;
+/* USER_ATTR usr_attr;
+/* char *path;
+/* DESCRIPTION
+/* deliver_include() processes the contents of the named include
+/* file and delivers to each address listed. Some sanity checks
+/* are done on the include file permissions and type.
+/*
+/* 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.
+/* A table with delivered-to: addresses taken from the message.
+/* .IP usr_attr
+/* Attributes describing user rights and environment.
+/* .IP path
+/* Pathname of the include file.
+/* DIAGNOSTICS
+/* Fatal errors: out of memory. Warnings: bad include file type
+/* or permissions. The result is non-zero when delivery should be
+/* tried again.
+/* SEE ALSO
+/* token(3) tokenize list
+/* 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 <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <htable.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <open_as.h>
+#include <stat_as.h>
+#include <iostuff.h>
+#include <mypwd.h>
+
+/* Global library. */
+
+#include <bounce.h>
+#include <defer.h>
+#include <been_here.h>
+#include <mail_params.h>
+#include <ext_prop.h>
+#include <sent.h>
+
+/* Application-specific. */
+
+#include "local.h"
+
+/* deliver_include - open include file and deliver */
+
+int deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
+{
+ const char *myname = "deliver_include";
+ struct stat st;
+ struct mypasswd *file_pwd = 0;
+ int status;
+ VSTREAM *fp;
+ int fd;
+
+ /*
+ * Make verbose logging easier to understand.
+ */
+ state.level++;
+ if (msg_verbose)
+ MSG_LOG_STATE(myname, state);
+
+ /*
+ * DUPLICATE ELIMINATION
+ *
+ * Don't process this include file more than once as this particular user.
+ */
+ if (been_here(state.dup_filter, "include %ld %s", (long) usr_attr.uid, path))
+ return (0);
+ state.msg_attr.exp_from = state.msg_attr.local;
+
+ /*
+ * Can of worms. Allow this include file to be symlinked, but disallow
+ * inclusion of special files or of files with world write permission
+ * enabled.
+ */
+ if (*path != '/') {
+ msg_warn(":include:%s uses a relative path", path);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ if (stat_as(path, &st, usr_attr.uid, usr_attr.gid) < 0) {
+ msg_warn("unable to lookup :include: file %s: %m", path);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ if (S_ISREG(st.st_mode) == 0) {
+ msg_warn(":include: file %s is not a regular file", path);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ if (st.st_mode & S_IWOTH) {
+ msg_warn(":include: file %s is world writable", path);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+
+ /*
+ * DELIVERY POLICY
+ *
+ * Set the expansion type attribute so that we can decide if destinations
+ * such as /file/name and |command are allowed at all.
+ */
+ state.msg_attr.exp_type = EXPAND_TYPE_INCL;
+
+ /*
+ * DELIVERY RIGHTS
+ *
+ * When a non-root include file is listed in a root-owned alias, use the
+ * rights of the include file owner. We do not want to give the include
+ * file owner control of the default account.
+ *
+ * When an include file is listed in a user-owned alias or .forward file,
+ * leave the delivery rights alone. Users should not be able to make
+ * things happen with someone else's rights just by including some file
+ * that is owned by their victim.
+ */
+ if (usr_attr.uid == 0) {
+ if ((errno = mypwuid_err(st.st_uid, &file_pwd)) != 0 || file_pwd == 0) {
+ msg_warn(errno ? "cannot find username for uid %ld: %m" :
+ "cannot find username for uid %ld", (long) st.st_uid);
+ msg_warn("%s: cannot find :include: file owner username", path);
+ dsb_simple(state.msg_attr.why, "4.3.5",
+ "mail system configuration error");
+ return (defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+ if (file_pwd->pw_uid != 0)
+ SET_USER_ATTR(usr_attr, file_pwd, state.level);
+ }
+
+ /*
+ * MESSAGE FORWARDING
+ *
+ * When no owner attribute is set (either via an owner- alias, or as part of
+ * .forward file processing), set the owner attribute, to disable direct
+ * delivery of local recipients. By now it is clear that the owner
+ * attribute should have been called forwarder instead.
+ */
+ if (state.msg_attr.owner == 0)
+ state.msg_attr.owner = state.msg_attr.rcpt.address;
+
+ /*
+ * From here on no early returns or we have a memory leak.
+ *
+ * FILE OPEN RIGHTS
+ *
+ * Use the delivery rights to open the include file. When no delivery rights
+ * were established sofar, the file containing the :include: is owned by
+ * root, so it should be OK to open any file that is accessible to root.
+ * The command and file delivery routines are responsible for setting the
+ * proper delivery rights. These are the rights of the default user, in
+ * case the :include: is in a root-owned alias.
+ *
+ * Don't propagate unmatched extensions unless permitted to do so.
+ */
+#define FOPEN_AS(p,u,g) ((fd = open_as(p,O_RDONLY,0,u,g)) >= 0 ? \
+ vstream_fdopen(fd,O_RDONLY) : 0)
+
+ if ((fp = FOPEN_AS(path, usr_attr.uid, usr_attr.gid)) == 0) {
+ msg_warn("cannot open include file %s: %m", path);
+ dsb_simple(state.msg_attr.why, "5.3.5",
+ "mail system configuration error");
+ status = bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr));
+ } else {
+ if ((local_ext_prop_mask & EXT_PROP_INCLUDE) == 0)
+ state.msg_attr.unmatched = 0;
+ close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC);
+ status = deliver_token_stream(state, usr_attr, fp, (int *) 0);
+ if (vstream_fclose(fp))
+ msg_warn("close %s: %m", path);
+ }
+
+ /*
+ * Cleanup.
+ */
+ if (file_pwd)
+ mypwfree(file_pwd);
+
+ return (status);
+}