home *** CD-ROM | disk | FTP | other *** search
- /* $Header: main.c,v 1.5 88/09/14 20:00:03 network Exp $
- *
- * A program to deliver local mail with some flexibility.
- *
- * $Log: main.c,v $
- * Revision 1.5 88/09/14 20:00:03 network
- * Add version string, including patchlevel.
- *
- * Revision 1.4 88/09/14 19:41:54 network
- * Portability to System V and BSD.
- * General fixup.
- *
- * Revision 1.3 88/08/30 16:13:54 network
- * Remove general subroutines to new module, subs.c.
- *
- * Revision 1.2 88/08/25 15:29:59 network
- * Implement -s and -u options and ENV_SYSDEL and ENV_USERDEL environment
- * variables. Tighten up control over effective and real uid/gid.
- * In particular, renounce setuid privileges if the system or user delivery
- * file is specified.
- *
- * Revision 1.1 88/06/06 09:38:54 chip
- * Initial revision
- *
- */
-
- #include "deliver.h"
- #include "patchlevel.h"
- #include <signal.h>
-
- /*
- * External data.
- */
-
- /* Variables set by getopt() [blech] */
-
- extern int optind, opterr;
- extern char *optarg;
-
- /*
- * Global data
- */
-
- int verbose = FALSE;
- int dryrun = FALSE;
- int printaddrs = FALSE;
- int leavetemps = FALSE;
- int boxdelivery = FALSE;
-
- char *progname = "deliver";
- char version[32] = "1.0";
- char *shell = SHELL;
-
- char *sys_deliver = NULL;
- char *user_deliver = NULL;
- char *sender = NULL;
- char *hostname = NULL;
-
- int eff_uid = -1;
- int eff_gid = -1;
- int real_uid = -1;
- int real_gid = -1;
-
- CONTEXT *eff_ct = NULL;
- CONTEXT *real_ct = NULL;
-
- char *ttype[T_MAX] = { "header", "body" };
- char *tfile[T_MAX] = { NULL, NULL };
- int tfd[T_MAX] = { -1, -1 };
-
- /*----------------------------------------------------------------------
- * The Program.
- */
-
- main(argc, argv)
- int argc;
- char **argv;
- {
- char *p;
- int u, c, errcount, insecure;
-
- /* Make sure that stdout and stderr are interleaved correctly */
-
- (void) Linebuf(stdout);
- (void) Linebuf(stderr);
-
- /* Figure out the name used to invoke this program. */
-
- progname = basename(argv[0]);
-
- /* What version of the program is this? */
-
- sprintf(version + strlen(version), ".%02d", PATCHLEVEL);
-
- /* Figure out the name of this host */
-
- if ((hostname = gethost()) == NULL)
- {
- hostname = "unknown";
- error("unable to determine host name; using \"%s\"\n",
- hostname);
- }
-
- /* Process environment: handle recursive invocation */
-
- if ((p = getenv(ENV_DFLAGS)) != NULL)
- {
- while (*p)
- {
- switch (*p++)
- {
- case 'v':
- verbose = TRUE;
- break;
- case 'd':
- verbose = TRUE;
- dryrun = TRUE;
- break;
- case 'A':
- printaddrs = TRUE;
- dryrun = TRUE;
- break;
- case 't':
- leavetemps = TRUE;
- break;
- }
- }
- }
- if ((p = getenv(ENV_SYSDEL)) != NULL)
- sys_deliver = p;
- if ((p = getenv(ENV_USERDEL)) != NULL)
- user_deliver = p;
- if ((p = getenv(ENV_SENDER)) != NULL)
- sender = p;
- if ((p = getenv(ENV_HOSTNAME)) != NULL)
- hostname = p;
-
- /* Parse command line arguments */
-
- while ((c = getopt(argc, argv, "vdAtbs:u:r:h:")) != EOF)
- {
- switch (c)
- {
- case 'v':
- verbose = TRUE;
- break;
- case 'd':
- verbose = TRUE;
- dryrun = TRUE;
- break;
- case 'A':
- printaddrs = TRUE;
- dryrun = TRUE;
- break;
- case 't':
- leavetemps = TRUE;
- break;
- case 'b':
- boxdelivery = TRUE;
- break;
- case 's':
- sys_deliver = optarg;
- break;
- case 'u':
- user_deliver = optarg;
- break;
- case 'r':
- sender = optarg;
- break;
- case 'h':
- hostname = optarg;
- break;
- case '?':
- usage();
- }
- }
-
- /* If no destinations were given, forget it. */
-
- if (optind >= argc)
- {
- message("%s: no recipients specified\n", progname);
- usage();
- }
-
- /* Print a debugging message */
-
- if (verbose)
- {
- message("%s %s running on host %s\n",
- progname, version, hostname);
- if (sender && *sender)
- message("Sender is %s\n", sender);
- }
-
- /* Find effective and real uids and gids. */
-
- eff_uid = geteuid();
- eff_gid = getegid();
- real_uid = getuid();
- real_gid = getgid();
-
- if (eff_uid != real_uid && eff_uid != 0)
- {
- message("%s: if setuid, must be setuid root\n");
- leave(1);
- }
-
- /* Renounce special privileges if something insecure was requested. */
-
- if (sys_deliver || user_deliver)
- {
- if (setgid(eff_gid = real_gid) == -1
- || setuid(eff_uid = real_uid) == -1)
- {
- syserr("%s: can't renounce setuid privileges");
- leave(1);
- }
- }
-
- /* Get the contexts of our effective and real uids. */
-
- if ((eff_ct = uid_context(eff_uid)) == NULL)
- error("invalid effective uid %d!?\n", eff_uid);
-
- if ((real_ct = uid_context(real_uid)) == NULL)
- error("invalid real uid %d!?\n", real_uid);
-
- if (!eff_ct || !real_ct)
- leave(1);
-
- if (verbose)
- {
- message("effective uid = %s (%d/%d); real uid = %s (%d/%d)\n",
- eff_ct->name, eff_ct->uid, eff_ct->gid,
- real_ct->name, real_ct->uid, real_ct->gid);
- }
-
- /* Let's be sane about the file creation mask. */
-
- u = umask(0);
- u &= ~0700; /* Let's not deprive ourselves of permissions. */
- u |= 022; /* Let's be reasonably paranoid about writing. */
- (void) umask(u);
-
- /* Turn off all intrusive signals (unless we're not delivering). */
-
- if (! dryrun)
- {
- (void) signal(SIGHUP, SIG_IGN);
- (void) signal(SIGINT, SIG_IGN);
- (void) signal(SIGQUIT, SIG_IGN);
- }
-
- /*
- * Create the temporary files and write the message to them.
- */
-
- if (copy_message() < 0)
- leave(1);
-
- /*
- * Set up useful environment variables.
- * Note that this must be done _after_ copy_message(),
- * since that's where the temp files are created.
- */
-
- setup_environ();
-
- /*
- * Assign the default delivery file names.
- * Note that this must be after setup_environ(), or else the
- * environment won't reflect specified/unspecified options.
- */
-
- if (!sys_deliver)
- sys_deliver = SYS_DELIVER;
- if (!user_deliver)
- user_deliver = USER_DELIVER;
-
- /*
- * Perhaps we should consider all arguments as mailbox names...
- */
-
- if (boxdelivery)
- {
- int a;
-
- if (verbose)
- message("mailbox delivery as %s\n", real_ct->name);
-
- /*
- * Consider all arguments as mailbox filenames.
- */
-
- for (a = optind; a < argc; ++a)
- (void) dest(real_ct->name, argv[a]);
-
- if (verbose)
- dumpdests("(should all be mailboxes)");
- }
-
- /*
- * They're not mailbox names, so they should be mail addresses.
- */
-
- else
- {
- /*
- * Run all destinations though the system delivery file.
- * If sys_dfile() doesn't find one, it will call dest()
- * on each address.
- */
-
- sys_dfile(argc - optind, argv + optind);
-
- if (verbose)
- dumpdests("after running system delivery file");
-
- /*
- * Run each user destination through his delivery file.
- */
-
- user_dfiles();
-
- if (verbose)
- dumpdests("after running user delivery files");
- }
-
- /*
- * Drop mail in mailbox(es).
- */
-
- mbox_deliver();
-
- if (verbose)
- dumpdests("after delivery to all mailboxes");
-
- /*
- * Send mail to UUCP address(es).
- */
-
- uucp_deliver();
-
- if (verbose)
- dumpdests("after delivery to UUCP addresses");
-
- /*
- * Report any errors, and leave.
- */
-
- errcount = report_errors();
-
- /*
- * All done.
- */
-
- leave(errcount ? 1 : 0);
- /* NOTREACHED */
- }
-
- /*----------------------------------------------------------------------
- * Print a usage message and exit.
- */
-
- usage()
- {
- message("Usage: %s [-b][-A][-d][-v][-t][-r from][-h host] args\n", progname);
- message("-b All arguments are mailbox filenames.\n");
- message(" (Default: arguments are user names.)\n");
- message("-A Resolve addresses but do not deliver.\n");
- message("-d Be verbose but do not deliver.\n");
- message("-v Be verbose and deliver.\n");
- message("-t Do not remote temp files before exiting.\n");
- message("-r from Specify the address to appear in the \"From \" line.\n");
- message("-h host Specify the host name.\n");
- message(" (This option overrides any \"From \" line in the input.)\n");
- message("args Either user addresses or mailboxes (-b).\n");
- leave(1);
- }
-
- /*----------------------------------------------------------------------
- * Clean up and exit.
- */
-
- leave(code)
- int code;
- {
- if (! leavetemps)
- {
- int t;
-
- for (t = 0; t < T_MAX; ++t)
- {
- if (tfile[t] && unlink(tfile[t]) == -1)
- syserr("can't unlink %s", tfile[t]);
- }
- }
-
- exit(code);
- }
-
- /*----------------------------------------------------------------------
- * Report any errors to stderr.
- * Return an error count.
- */
-
- int
- report_errors()
- {
- DEST *d;
- int count = 0;
-
- for (d = first_dest(); d; d = next_dest(d))
- {
- if (d->state != ST_ERROR)
- continue;
-
- if (++count == 1)
- {
- error(
- "delivery to the following address(es) failed on host %s\n",
- hostname);
- }
-
- message("\tuser \"%s\"", d->name);
- if (d->class == CL_MBOX)
- message(", mailbox \"%s\"", d->mailbox);
- message(": %s\n", d->error);
- }
-
- return count;
- }
-
- /*----------------------------------------------------------------------
- * Set up useful environment variables.
- */
-
- setup_environ()
- {
- char flags[8];
- int f = 0;
-
- flags[f++] = '-';
- if (verbose)
- flags[f++] = (dryrun ? 'd' : 'v');
- if (printaddrs)
- flags[f++] = 'A';
- if (leavetemps)
- flags[f++] = 't';
- flags[f] = 0;
-
- alloc_env(ENV_DFLAGS, (f > 1) ? flags : "");
- if (sys_deliver && *sys_deliver)
- alloc_env(ENV_SYSDEL, sys_deliver);
- if (user_deliver && *user_deliver)
- alloc_env(ENV_USERDEL, user_deliver);
- if (hostname && *hostname)
- alloc_env(ENV_HOSTNAME, hostname);
- if (sender && *sender)
- alloc_env(ENV_SENDER, sender);
-
- alloc_env(ENV_HEADER, tfile[T_HEADER]);
- alloc_env(ENV_BODY, tfile[T_BODY]);
-
- alloc_env("IFS", " \t\n");
- }
-