Path: utzoo!utgpu!attcan!uunet!ateng!chip From: chip@ateng.ateng.com (Chip Salzenberg) Newsgroups: comp.sources.bugs Subject: Deliver 1.00 (patchlevel 8) Part 3/3 Message-ID: <1989Feb23.114812.13897@ateng.ateng.com> Date: 23 Feb 89 16:48:11 GMT Organization: A T Engineering, Tampa, FL Lines: 2026 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'main.c' <<'END_OF_FILE' X/* $Header: main.c,v 1.11 89/02/15 19:11:25 network Exp $ X * X * A program to deliver local mail with some flexibility. X * X * $Log: main.c,v $ X * Revision 1.11 89/02/15 19:11:25 network X * Provide second system-wide delivery file, executed after user delivery X * files but before any deliveries take place. Useful for implementing X * system-wide aliases. X * Also, fix bug in do_dfile() that caused infinite loops if delivery files X * output any lines containing white space. (!) X * X * Revision 1.10 89/02/10 15:46:59 network X * V7 support. X * X * Revision 1.9 88/11/28 18:08:03 network X * patch5: Copy temp files before handing them to delivery files. X * patch5: Introduce "uid" program. X * X * Revision 1.8 88/11/26 13:20:51 network X * patch4: Add return type of signal handlers to config.h. X * patch4: Provide a version of getopt() for systems that lack it. X * patch4: Call va_end() in routines that use varargs. X * patch4: Make consistent checks for valid address strings. X * X * Revision 1.7 88/11/18 12:17:17 network X * patch2: Improved signal handling. X * patch2: Make sure all environment variables are always provided. X * patch2: Some users can be trusted to specify delivery files. X * X * Revision 1.6 88/10/13 12:19:27 network X * patch1: add "-n" option, and general bug fixes. X * X * Revision 1.5 88/09/14 20:00:03 network X * Add version string, including patchlevel. X * X * Revision 1.4 88/09/14 19:41:54 network X * Portability to System V and BSD. X * General fixup. X * X * Revision 1.3 88/08/30 16:13:54 network X * Remove general subroutines to new module, subs.c. X * X * Revision 1.2 88/08/25 15:29:59 network X * Implement -s and -u options and ENV_SYSDEL and ENV_USERDEL environment X * variables. Tighten up control over effective and real uid/gid. X * In particular, renounce setuid privileges if the system or user delivery X * file is specified. X * X * Revision 1.1 88/06/06 09:38:54 chip X * Initial revision X * X */ X X#include "deliver.h" X#include "patchlevel.h" X#include X X/* X * External data. X */ X X/* Variables set by getopt() [blech] */ X Xextern int optind, opterr; Xextern char *optarg; X X/* X * Local data X */ X Xstatic char sys_dfl[] = SYS_DELIVER; Xstatic char post_dfl[] = POST_DELIVER; Xstatic char user_dfl[] = USER_DELIVER; X X/* X * Global data X */ X Xint verbose = FALSE; Xint dryrun = FALSE; Xint rundfiles = TRUE; Xint printaddrs = FALSE; Xint leavetemps = FALSE; Xint boxdelivery = FALSE; X Xchar *progname = "deliver"; Xchar version[32] = "1.0"; Xchar *shell = SHELL; X Xchar *sys_deliver = sys_dfl; Xchar *post_deliver = post_dfl; Xchar *user_deliver = user_dfl; Xchar *sender = NULL; Xchar *hostname = NULL; X Xint eff_uid = -1; Xint eff_gid = -1; Xint real_uid = -1; Xint real_gid = -1; X XCONTEXT *eff_ct = NULL; XCONTEXT *real_ct = NULL; X Xint tty_input = FALSE; XSIGFLAG got_sig = FALSE; X Xint trust_user = FALSE; Xint trust_delfiles = FALSE; X Xchar *ttype[T_MAX] = { "header", "body", "header copy", "body copy" }; Xchar *tfile[T_MAX] = { NULL, NULL, NULL, NULL }; Xchar *tenv[T_MAX] = { NULL, NULL, ENV_HEADER, ENV_BODY }; Xint tfd[T_MAX] = { -1, -1, -1, -1 }; X X/* X * Local functions. X */ X Xstatic SIGTYPE sighup(), sigint(), sigquit(); X X/*---------------------------------------------------------------------- X * The Program. X */ X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X char *p; X int u, c, errcount, copy; X X /* Make sure that stdout and stderr are interleaved correctly */ X X Linebuf(stdout); X Linebuf(stderr); X X /* Figure out the name used to invoke this program. */ X X progname = basename(argv[0]); X X /* What version of the program is this? */ X X (void) sprintf(version + strlen(version), ".%02d", PATCHLEVEL); X X /* Figure out the name of this host */ X X if ((hostname = gethost()) == NULL) X { X hostname = "unknown"; X error("unable to determine host name; using \"%s\"\n", X hostname); X } X X /* Find effective and real uids and gids. */ X X eff_uid = geteuid(); X eff_gid = getegid(); X real_uid = getuid(); X real_gid = getgid(); X X if (eff_uid != real_uid && eff_uid != 0) X { X error("if setuid, must be setuid root\n"); X leave(1); X } X X /* Process environment: handle recursive invocation */ X X if ((p = getenv(ENV_DFLAGS)) != NULL) X { X while (*p) X { X switch (*p++) X { X case 'v': X verbose = TRUE; X break; X case 'd': X verbose = TRUE; X dryrun = TRUE; X break; X case 'A': X printaddrs = TRUE; X dryrun = TRUE; X break; X case 'n': X rundfiles = FALSE; X break; X case 't': X leavetemps = TRUE; X break; X } X } X } X X if ((p = getenv(ENV_SYSDEL)) != NULL && *p) X sys_deliver = p; X if ((p = getenv(ENV_POSTDEL)) != NULL && *p) X post_deliver = p; X if ((p = getenv(ENV_USERDEL)) != NULL && *p) X user_deliver = p; X if ((p = getenv(ENV_SENDER)) != NULL && *p) X sender = p; X if ((p = getenv(ENV_HOSTNAME)) != NULL && *p) X hostname = p; X X /* Parse command line arguments */ X X while ((c = getopt(argc, argv, "vdAntbs:p:u:r:h:")) != EOF) X { X switch (c) X { X case 'v': X verbose = TRUE; X break; X case 'd': X verbose = TRUE; X dryrun = TRUE; X break; X case 'A': X printaddrs = TRUE; X dryrun = TRUE; X break; X case 'n': X rundfiles = FALSE; X break; X case 't': X leavetemps = TRUE; X break; X case 'b': X boxdelivery = TRUE; X break; X case 's': X if (*optarg) X sys_deliver = optarg; X break; X case 'p': X if (*optarg) X post_deliver = optarg; X break; X case 'u': X if (*optarg) X user_deliver = optarg; X break; X case 'r': X if (*optarg) X sender = optarg; X break; X case 'h': X if (*optarg) X hostname = optarg; X break; X case '?': X usage(); X } X } X X /* If no destinations were given, forget it. */ X X if (optind >= argc) X { X error("no recipients specified\n"); X usage(); X } X X /* Print a debugging message */ X X if (verbose) X { X message("%s %s running on host %s\n", X progname, version, hostname); X } X X /* Do we trust our caller? */ X X if (trusted_uid(real_uid)) X trust_user = TRUE; X X /* Do we trust our delivery files? */ X X if (strcmp(sys_dfl, sys_deliver) == 0 X && strcmp(post_dfl, post_deliver) == 0 X && strcmp(user_dfl, user_deliver) == 0) X trust_delfiles = TRUE; X X /* Renounce special privileges if something insecure was requested. */ X X if (!trust_user && !trust_delfiles) X { X if (setgid(eff_gid = real_gid) == -1 X || setuid(eff_uid = real_uid) == -1) X { X syserr("can't renounce setuid privileges"); X leave(1); X } X } X X /* Get the contexts of our effective and real uids. */ X X if ((eff_ct = uid_context(eff_uid)) == NULL) X error("invalid effective uid %d!?\n", eff_uid); X X if ((real_ct = uid_context(real_uid)) == NULL) X error("invalid real uid %d!?\n", real_uid); X X if (!eff_ct || !real_ct) X leave(1); X X if (verbose) X { X message("effective uid = %s (%d/%d); real uid = %s (%d/%d)\n", X eff_ct->ct_name, eff_ct->ct_uid, eff_ct->ct_gid, X real_ct->ct_name, real_ct->ct_uid, real_ct->ct_gid); X } X X /* Let's be sane about the file creation mask. */ X X u = umask(0); X u &= ~0700; /* Let's not deprive ourselves of permissions. */ X u |= 022; /* Let's be reasonably paranoid about writing. */ X (void) umask(u); X X /* X * Where is the message coming from? X */ X X if (isatty(0)) X tty_input = TRUE; X X /* X * If we are not going to deliver, or if we are receiving the X * message from a tty, catch signals so we can remove temp files. X * Otherwise, ignore signals. X */ X X if (dryrun || tty_input) X catch_sigs(); X else X ignore_sigs(); X X /* X * Create the temporary files and write the message to them. X */ X X copy = copy_message(); X X /* X * No more signals... X */ X X ignore_sigs(); X X /* X * ... but if we had already caught a signal, X * or if copy_msg() had a problem, leave. X */ X X if ((copy < 0) || got_sig) X { X if (got_sig) X error("caught signal - exiting\n"); X leave(1); X } X X /* X * Set up useful environment variables. X * Note that this must be done _after_ copy_message(), X * since that's where the temp files are created. X */ X X setup_environ(); X X /* X * Perhaps we should consider all arguments as mailbox names... X */ X X if (boxdelivery) X { X int a; X X if (verbose) X message("mailbox delivery as %s\n", real_ct->ct_name); X X /* X * Consider all arguments as mailbox filenames. X */ X X for (a = optind; a < argc; ++a) X (void) dest(real_ct->ct_name, argv[a]); X X if (verbose) X dumpdests("(should all be mailboxes)"); X } X X /* X * They're not mailbox names, so they should be mail addresses. X */ X X else X { X /* Run all destinations though the system delivery file. */ X X if (sys_dfile(argc - optind, argv + optind) >= 0) X { X if (verbose) X dumpdests("after running system delivery file"); X } X else X { X int a; X X /* X * System delivery file is missing or ignored. X * Use the argument list verbatim. X */ X X for (a = optind; a < argc; ++a) X (void) dest(argv[a], (char *) NULL); X X if (verbose) X dumpdests("as taken from argument list"); X } X X /* X * Run each user destination through his delivery file. X */ X X if (user_dfiles() >= 0) X { X if (verbose) X dumpdests("after running user delivery files"); X } X X /* X * Run each remaining destination though the post-user X * delivery file. X */ X X if (post_dfile() >= 0) X { X if (verbose) X dumpdests("after running post-user delivery file"); X } X } X X /* X * Drop mail in mailbox(es). X */ X X mbox_deliver(); X X if (verbose) X dumpdests("after delivery to all mailboxes"); X X /* X * Send mail to UUCP address(es). X */ X X uucp_deliver(); X X if (verbose) X dumpdests("after delivery to UUCP addresses"); X X /* X * Report any errors, and leave. X */ X X errcount = report_errors(); X X /* X * All done. X */ X X leave(errcount ? 1 : 0); X /* NOTREACHED */ X} X X/*---------------------------------------------------------------------- X * Print a usage message and exit. X */ X Xusage() X{ X message("Usage: %s [-b][-A][-d][-v][-n][-t][-r from][-h host] args\n", progname); X message("-b All arguments are mailbox filenames.\n"); X message(" (Default: arguments are user names.)\n"); X message("-A Resolve addresses but do not deliver.\n"); X message("-d Be verbose but do not deliver.\n"); X message("-v Be verbose and deliver.\n"); X message("-n Do not run any delivery files.\n"); X message("-t Do not remote temp files before exiting.\n"); X message("-s file Specify the system delivery filename.\n"); X message("-p file Specify the post-user delivery filename.\n"); X message("-u file Specify the user delivery filename.\n"); X message("-r from Specify the address to appear in the \"From \" line.\n"); X message("-h host Specify the host name.\n"); X message("args Either user addresses or mailboxes (-b).\n"); X leave(1); X} X X/*---------------------------------------------------------------------- X * Clean up and exit. X */ X Xleave(code) Xint code; X{ X if (! leavetemps) X { X int t; X X for (t = 0; t < T_MAX; ++t) X { X if (tfd[t] != -1) X (void) close(tfd[t]); X if (tfile[t] && unlink(tfile[t]) == -1) X syserr("can't unlink %s", tfile[t]); X } X } X X exit(code); X} X X/*---------------------------------------------------------------------- X * Catch signals. X */ X Xcatch_sigs() X{ X if (signal(SIGHUP, SIG_IGN) != SIG_IGN) X (void) signal(SIGHUP, sighup); X if (signal(SIGINT, SIG_IGN) != SIG_IGN) X (void) signal(SIGINT, sigint); X if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) X (void) signal(SIGQUIT, sigquit); X} X X/*---------------------------------------------------------------------- X * Ignore signals. X */ X Xignore_sigs() X{ X (void) signal(SIGHUP, SIG_IGN); X (void) signal(SIGINT, SIG_IGN); X (void) signal(SIGQUIT, SIG_IGN); X} X Xstatic SIGTYPE Xsighup() X{ X (void) signal(SIGHUP, sighup); X got_sig = TRUE; X} X Xstatic SIGTYPE Xsigint() X{ X (void) signal(SIGINT, sigint); X got_sig = TRUE; X} X Xstatic SIGTYPE Xsigquit() X{ X (void) signal(SIGQUIT, sigquit); X got_sig = TRUE; X} X X/*---------------------------------------------------------------------- X * Report any errors to stderr. X * Return an error count. X */ X Xint Xreport_errors() X{ X DEST *d; X int count = 0; X X for (d = first_dest(); d; d = next_dest(d)) X { X if (d->d_state != ST_ERROR) X continue; X X if (++count == 1) X { X error( X "delivery to the following address(es) failed on host %s\n", X hostname); X } X X message("\t\"%s\"", d->d_name); X if (d->d_class == CL_MBOX) X message(", mailbox \"%s\"", d->d_mailbox); X message(": %s\n", derrmsg(d->d_error)); X } X X return count; X} X X/*---------------------------------------------------------------------- X * Is the given uid trusted? X */ X Xint Xtrusted_uid(uid) Xint uid; X{ X CONTEXT *ct; X char **n; X static char *t[] = { TRUSTED_USERS, 0 }; X X for (n = t; *n; ++n) X { X if ((ct = name_context(*n)) != NULL && uid == ct->ct_uid) X return TRUE; X } X X return FALSE; X} X X/*---------------------------------------------------------------------- X * Set up useful environment variables. X */ X Xsetup_environ() X{ X char flags[8]; X int f = 0; X X flags[f++] = '-'; X if (verbose) X flags[f++] = (dryrun ? 'd' : 'v'); X if (printaddrs) X flags[f++] = 'A'; X if (leavetemps) X flags[f++] = 't'; X flags[f] = 0; X X alloc_env(ENV_DFLAGS, (f > 1) ? flags : ""); X if (sys_deliver && *sys_deliver) X alloc_env(ENV_SYSDEL, sys_deliver); X if (user_deliver && *user_deliver) X alloc_env(ENV_USERDEL, user_deliver); X if (hostname && *hostname) X alloc_env(ENV_HOSTNAME, hostname); X if (sender && *sender) X alloc_env(ENV_SENDER, sender); X X alloc_env("IFS", " \t\n"); X} END_OF_FILE if test 13643 -ne `wc -c <'main.c'`; then echo shar: \"'main.c'\" unpacked with wrong size! fi # end of 'main.c' fi if test -f 'mbox.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mbox.c'\" else echo shar: Extracting \"'mbox.c'\" \(4621 characters\) sed "s/^X//" >'mbox.c' <<'END_OF_FILE' X/* $Header: mbox.c,v 1.4 89/02/10 15:47:10 network Exp $ X * X * Finally! Put the message in the specified mailbox(es). X * X * $Log: mbox.c,v $ X * Revision 1.4 89/02/10 15:47:10 network X * V7 support. X * X * Revision 1.3 88/11/28 18:08:13 network X * patch5: Copy temp files before handing them to delivery files. X * patch5: Introduce "uid" program. X * X * Revision 1.2 88/09/14 19:42:06 network X * Portability to System V and BSD. X * General fixup. X * X * Revision 1.1 88/06/06 09:39:06 chip X * Initial revision X * X */ X X#include "deliver.h" X#include X#include X#include X X/* X * External data. X */ X Xextern int errno; X X/* X * Local functions. X */ X Xstatic mbox_one(); Xstatic int mbox_write(); X X/*---------------------------------------------------------------------- X * Deliver mail to all valid destinations. X */ X Xmbox_deliver() X{ X DEST *d; X X for (d = first_dest(); d; d = next_dest(d)) X { X switch (d->d_class) X { X case CL_USER: X case CL_MBOX: X if (d->d_state == ST_WORKING) X mbox_one(d); X break; X } X } X} X X/*---------------------------------------------------------------------- X * Deliver mail to one destination. X */ X Xstatic Xmbox_one(d) XDEST *d; X{ X CONTEXT *ct; X int ret = 0; X X if (printaddrs) X { X (void) printf("%s", d->d_name); X if (d->d_class == CL_MBOX) X (void) printf(":%s", d->d_mailbox); X (void) printf("\n"); X } X X if (dryrun) X { X d->d_state = ST_DONE; X return; X } X X if ((ct = name_context(d->d_name)) == NULL) X { X dest_err(d, E_CTLOST); X return; X } X X if (! ok_context(ct)) X { X dest_err(d, E_CTPERM); X return; X } X X if (d->d_class == CL_MBOX) X { X if (sfork() == 0) X { X if (become(ct, !boxdelivery) < 0) X exit(1); X if (mbox_write(d->d_mailbox, ct, FALSE) < 0) X exit(1); X exit(0); X } X X if (await_child() != 0) X ret = -1; X } X else X { X char mailbox[100]; X X (void) sprintf(mailbox, "%s/%s", X#ifdef MBX_DIR X MBX_DIR, d->d_name X#else X d->d_home, MBX_NAME X#endif X ); X X if (mbox_write(mailbox, ct, TRUE) < 0) X ret = -1; X } X X if (ret >= 0) X d->d_state = ST_DONE; X else X dest_err(d, E_MBOX); X} X X/*---------------------------------------------------------------------- X * Write mail to the named mailbox. X * If we have to create the mailbox, give it to the specified user. X * If "is_sys" is true, then we're writing to a system mailbox. X */ X Xstatic int Xmbox_write(mailbox, ct, is_sys) Xchar *mailbox; XCONTEXT *ct; Xint is_sys; X{ X struct stat st; X int fd, t, mbox_uid, mbox_gid; X int ret = 0; X X if (verbose) X { X message("As %s, delivering to %s mailbox %s\n", X ct->ct_name, (is_sys ? "system" : "user"), mailbox); X } X X if (name_lock(mailbox) < 0) X return -1; X X while ((fd = open(mailbox, O_WRONLY)) == -1) X { X if (errno != ENOENT) X { X syserr("can't open %s", mailbox); X break; X } X X#ifdef O_CREAT X fd = open(mailbox, O_WRONLY|O_CREAT|O_EXCL, MBX_MODE); X X /* If it exists now, try open() again. */ X if (fd == -1 && errno == EEXIST) X continue; X#else X fd = creat(mailbox, MBX_MODE); X#endif X if (fd == -1) X { X syserr("can't create %s", mailbox); X break; X } X X /* Make sure the mailbox receives the correct modes */ X X mbox_uid = ct->ct_uid; X mbox_gid = ct->ct_gid; X X#ifdef MBX_GROUP X if (is_sys) X { X static int mbox_sv_gid = -2; X X if (mbox_sv_gid == -2) X mbox_sv_gid = group_id(MBX_GROUP); X X if (mbox_sv_gid < 0) X message("%s: no such group\n", MBX_GROUP); X else X mbox_gid = mbox_sv_gid; X } X#endif /* MBX_GROUP */ X X if (fstat(fd, &st) == -1) X { X syserr("can't fstat open mailbox?!"); X (void) close(fd); X fd = -1; X break; X } X X /* Change mailbox ownership if it's not already correct. */ X X if ((st.st_uid != mbox_uid || st.st_gid != mbox_gid) X && chown(mailbox, mbox_uid, mbox_gid) == -1) X { X /* print a message, but that's all. (???) */ X syserr("can't chown %s to %d,%d", X mailbox, mbox_uid, mbox_gid); X } X X /* It's open now, so we can stop looping now. */ X X break; X } X X if (fd == -1) X { X (void) name_unlock(mailbox); X return -1; X } X X if (fd_lock(fd) < 0) X { X (void) close(fd); X (void) name_unlock(mailbox); X return -1; X } X X (void) lseek(fd, 0L, 2); /* No error check: may be a special file */ X X for (t = T_HDR; t <= T_BODY; ++t) X { X if (lseek(tfd[t], 0L, 0) == -1) X { X syserr("lseek in %s file %s", ttype[t], tfile[t]); X ret = -1; X break; X } X X if (copyfd(tfd[t], fd) < 0) X { X ret = -1; X break; X } X } X X if (verbose) X { X if (ret >= 0) X message("wrote message to %s\n", mailbox); X } X X if (fd_unlock(fd) < 0) X ret = -1; X (void) close(fd); X if (name_unlock(mailbox) < 0) X ret = -1; X X return ret; X} END_OF_FILE if test 4621 -ne `wc -c <'mbox.c'`; then echo shar: \"'mbox.c'\" unpacked with wrong size! fi # end of 'mbox.c' fi if test -f 'procs.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'procs.c'\" else echo shar: Extracting \"'procs.c'\" \(5411 characters\) sed "s/^X//" >'procs.c' <<'END_OF_FILE' X/* $Header: procs.c,v 1.4 89/02/10 15:47:31 network Exp $ X * X * Process management and misc support. X * X * $Log: procs.c,v $ X * Revision 1.4 89/02/10 15:47:31 network X * V7 support. X * X * Revision 1.3 88/11/26 13:21:07 network X * patch4: Add return type of signal handlers to config.h. X * patch4: Provide a version of getopt() for systems that lack it. X * patch4: Call va_end() in routines that use varargs. X * patch4: Make consistent checks for valid address strings. X * X * Revision 1.2 88/09/14 19:42:28 network X * Portability to System V and BSD. X * General fixup. X * X * Revision 1.1 88/06/06 09:39:15 chip X * Initial revision X * X */ X X#include "deliver.h" X#include X#include X X/* X * External data. X */ X Xextern int errno; X X/* X * Local data. X */ X Xstatic int child_pid = -1; Xstatic SIGTYPE (*saved_sigpipe)() = SIG_DFL; X X/*---------------------------------------------------------------------- X * Like popen(), but execute the child in a specific context. X * Also, the argument list is already a vector. X */ X XFILE * Xct_popenv(ct, prog, av, mode) XCONTEXT *ct; Xchar *prog; Xchar **av; Xchar *mode; X{ X char ch; X int child, parent; X int pfd[2]; X X if (!ct || !prog || !av || !mode) X return NULL; X X if (mode[0] == 'r' && mode[1] == 0) X child = 1, parent = 0; X else if (mode[0] == 'w' && mode[1] == 0) X child = 0, parent = 1; X else X return NULL; X X /* We can't have more than one child at a time. */ X X if (child_pid >= 0) X { X error("in ct_popen: a process is already open\n"); X return NULL; X } X X /* Make a stab at predicting uid-related failure. */ X X if (! ok_context(ct)) X { X error("in ct_popen: no permissions to become %s\n", X ct->ct_name); X return NULL; X } X X /* Pipes? Like, tubular, fer shur! */ X X if (pipe(pfd) == -1) X { X syserr("can't create a pipe"); X return NULL; X } X X /* Generate a debugging message. */ X X if (verbose) X { X int a; X X message("Spawning"); X for (a = 0; av[a]; ++a) X message(" %s", av[a]); X message("\n"); X } X X /* Handle the child case */ X X if (sfork() == 0) X { X if (child == 0) X { X (void) close(0); X (void) dup(pfd[0]); /* ass_u_me 0 */ X } X else X { X (void) close(0); X if (open("/dev/null", O_RDONLY) != 0) X { X /* This should _never_ happen, but... */ X syserr("can't open /dev/null"); X (void) dup(1); /* ass_u_me 0 */ X } X X (void) close(1); X (void) dup(pfd[1]); /* ass_u_me 1 */ X } X X if (become(ct, TRUE) < 0) X (void) write(pfd[1], "n", 1); X else X { X int t; X X (void) write(pfd[1], "y", 1); X X (void) close(pfd[child]); X (void) close(pfd[parent]); X for (t = 0; t < T_MAX; ++t) X (void) close(tfd[t]); X X (void) execv(prog, av); X syserr("can't execute %s", prog); X } X X exit(127); X } X X /* Make sure that a broken pipe won't kill us */ X X saved_sigpipe = signal(SIGPIPE, SIG_IGN); X X /* The child must report "OK" before we continue. */ X X if ((read(pfd[0], &ch, 1) < 1) || (ch != 'y')) X { X (void) close(pfd[0]); X (void) close(pfd[1]); X (void) await_child(); X return NULL; X } X X (void) close(pfd[child]); X return fdopen(pfd[parent], mode); X} X X/*---------------------------------------------------------------------- X * Close the stream opened by ct_popen(). X */ X Xct_pclose(fp) XFILE *fp; X{ X if (fp) X (void) fclose(fp); X return await_child(); X} X X/*---------------------------------------------------------------------- X * Assume the identity of the given user. X */ X Xint Xbecome(ct, chd) XCONTEXT *ct; Xint chd; X{ X char env_path[32]; X X /* X * Assume a new identity. X * Note the importance of doing the setgid() before the setuid(). X */ X X if (setgid(ct->ct_gid) == -1) X { X syserr("can't setgid to %d", ct->ct_gid); X return -1; X } X if (setuid(ct->ct_uid) == -1) X { X syserr("can't setgid to %u", ct->ct_uid); X return -1; X } X if (chd && chdir(ct->ct_home) == -1) X { X syserr("can't chdir to %s", ct->ct_home); X return -1; X } X X /* Set up the environment */ X X (void) sprintf(env_path, "%s:/bin:/usr/bin", X ((ct->ct_uid == 0) ? "/etc" : ".")); X alloc_env("HOME", ct->ct_home); X alloc_env("PATH", env_path); X X /* I guess it worked. */ X X return 0; X} X X/*---------------------------------------------------------------------- X * Safe fork. If it doesn't work, it exits. X */ X Xint Xsfork() X{ X int tries; X X /* X * A few safety measures. X */ X X (void) await_child(); X (void) fflush(stdout); X (void) fflush(stderr); X X /* X * Be patient in waiting for a fork(). X */ X X for (tries = 0; tries < 10; ++tries) X { X if (tries) X snooze(3); X if ((child_pid = fork()) >= 0) X return child_pid; X if (errno != EAGAIN) X break; X } X X syserr("can't fork"); X leave(1); X /* NOTREACHED */ X} X X/*---------------------------------------------------------------------- X * Wait for our child (if any) to exit. X * Returns child's exit status or -1 if there is a problem. X */ X Xint Xawait_child() X{ X int wpid, st; X X if (child_pid < 0) X return -1; X X while ((wpid = wait(&st)) >= 0) X { X if (wpid == child_pid) X break; X } X X child_pid = -1; X if (wpid == -1) X syserr("waiting for child"); X X (void) signal(SIGPIPE, saved_sigpipe); X saved_sigpipe = SIG_DFL; X X if (wpid == -1) X return -1; X X if (st & 0xFF) X { X error("child process died%s due to signal %d.\n", X ((st & 0x80) ? " and dumped core" : ""), X (st & 0x7F)); X X return -1; X } X X if (verbose) X { X message("child process exited with status %d.\n", X (st >> 8) & 0xFF); X } X X return ((st >> 8) & 0xFF); X} END_OF_FILE if test 5411 -ne `wc -c <'procs.c'`; then echo shar: \"'procs.c'\" unpacked with wrong size! fi # end of 'procs.c' fi if test -f 'subs.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'subs.c'\" else echo shar: Extracting \"'subs.c'\" \(2775 characters\) sed "s/^X//" >'subs.c' <<'END_OF_FILE' X/* $Header: subs.c,v 1.6 89/02/10 15:47:40 network Exp $ X * X * Miscellaneous subroutines. X * X * $Log: subs.c,v $ X * Revision 1.6 89/02/10 15:47:40 network X * V7 support. X * X * Revision 1.5 88/11/26 13:21:11 network X * patch4: Add return type of signal handlers to config.h. X * patch4: Provide a version of getopt() for systems that lack it. X * patch4: Call va_end() in routines that use varargs. X * patch4: Make consistent checks for valid address strings. X * X * Revision 1.4 88/10/13 12:20:34 network X * patch1: add "-n" option, and general bug fixes. X * X * Revision 1.3 88/09/14 19:42:33 network X * Portability to System V and BSD. X * General fixup. X * X * Revision 1.2 88/08/30 16:14:53 network X * New module. Includes routines from main.c. X * Also, new routine savestr(). X * X */ X X#include "deliver.h" X X/*---------------------------------------------------------------------- X * Allocate memory for an environment variable, and putenv() it. X */ X Xalloc_env(name, value) Xchar *name; Xchar *value; X{ X char *s; X X if (!name || !value) X return; X X s = zalloc((unsigned) (strlen(name) + strlen(value) + 2)); X (void) sprintf(s, "%s=%s", name, value); X if (putenv(s)) X nomem(); X} X X/*---------------------------------------------------------------------- X * Allocate and clear. If it fails, it takes the emergency exit. X */ X Xchar * Xzalloc(size) Xunsigned size; X{ X char *p; X X if ((p = malloc(size)) == NULL) X nomem(); X X Zero(p, size); X return p; X} X X/*---------------------------------------------------------------------- X * Reallocate to new size. If it fails, it takes the emergency exit. X */ X Xchar * Xsrealloc(ptr, size) Xchar *ptr; Xunsigned size; X{ X char *p; X X if ((p = realloc(ptr, size)) == NULL) X nomem(); X X return p; X} X X/*---------------------------------------------------------------------- X * Make an allocated copy of a string. X */ X Xchar * Xcopystr(s) Xchar *s; X{ X char *p; X X if (s == NULL) X return NULL; X X if ((p = malloc((unsigned) strlen(s) + 1)) == NULL) X nomem(); X X (void) strcpy(p, s); X return p; X} X X/*---------------------------------------------------------------------- X * Emergency exit for memory loss. X */ X Xnomem() X{ X error("out of memory\n"); X leave(1); X} X X/*---------------------------------------------------------------------- X * Return the last component of the given pathname. X */ X Xchar * Xbasename(name) Xchar *name; X{ X char *b; X X if ((b = strrchr(name, '/')) != NULL) X ++b; X else X b = name; X X return (b); X} X X/*---------------------------------------------------------------------- X * Check an address for validity. X */ X Xvalid_address(addr) Xchar *addr; X{ X char *p; X static char sanitize[] = SANITIZE; X X for (p = addr; *p; ++p) X { X if (strchr(sanitize, *p)) X return FALSE; X } X X return TRUE; X} END_OF_FILE if test 2775 -ne `wc -c <'subs.c'`; then echo shar: \"'subs.c'\" unpacked with wrong size! fi # end of 'subs.c' fi if test -f 'sysdep.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'sysdep.c'\" else echo shar: Extracting \"'sysdep.c'\" \(7306 characters\) sed "s/^X//" >'sysdep.c' <<'END_OF_FILE' X/* $Header: sysdep.c,v 1.7 89/02/10 15:47:44 network Exp $ X * X * Routines which are (or might well be) system-dependant. X * I've put the message routines here since you may need to use X * the ANSI instead of . X * X * $Log: sysdep.c,v $ X * Revision 1.7 89/02/10 15:47:44 network X * V7 support. X * X * Revision 1.6 88/11/30 16:24:56 network X * patch6: Separate getopt() into its own module. X * X * Revision 1.5 88/11/26 13:21:15 network X * patch4: Add return type of signal handlers to config.h. X * patch4: Provide a version of getopt() for systems that lack it. X * patch4: Call va_end() in routines that use varargs. X * patch4: Make consistent checks for valid address strings. X * X * Revision 1.4 88/10/13 12:20:39 network X * patch1: add "-n" option, and general bug fixes. X * X * Revision 1.3 88/09/14 20:00:24 network X * Fix type of gethostname() for BSD. X * X * Revision 1.2 88/09/14 19:42:37 network X * Portability to System V and BSD. X * General fixup. X * X * Revision 1.1 88/06/06 09:39:29 chip X * Initial revision X * X */ X X#include "deliver.h" X#include X#ifdef HAS_STDARG X#include X#else X#ifdef HAS_VARARGS X#include X#else X/* X * Non-portable home-grown varargs. Use at your own risk. X * Especially note that if sizeof(int) > sizeof(short), then X * "va_arg(..,short)" is broken. X */ Xtypedef char *va_list; X#define va_dcl int va_alist; X#define va_start(ap) ap = (char *) &va_alist X#define va_arg(ap,type) *(type *)(ap += sizeof(type), ap - sizeof(type)) X#define va_end(ap) /* nothing */ X#endif X#endif X X#ifdef UNAME X#include X#endif X X/* X * External functions. X */ X X#ifdef M_XENIX Xextern long nap(); X#else Xextern unsigned sleep(); X#endif X X/* X * External data. X */ X Xextern int errno; Xextern int sys_nerr; Xextern char *sys_errlist[]; X X/*---------------------------------------------------------------------- X * Print a message. X */ X X/* VARARGS */ X#ifdef HAS_STDARG Xmessage(char *fmt, ...) X#else Xmessage(va_alist) va_dcl X#endif X{ X va_list ap; X X#ifdef HAS_STDARG X va_start(ap, fmt); X#else X char *fmt; X va_start(ap); X fmt = va_arg(ap, char *); X#endif X X (void) vfprintf(stderr, fmt, ap); X X va_end(ap); X} X X/*---------------------------------------------------------------------- X * Print an error message. X */ X X/* VARARGS */ X#ifdef HAS_STDARG Xerror(char *fmt, ...) X#else Xerror(va_alist) va_dcl X#endif X{ X va_list ap; X X#ifdef HAS_STDARG X va_start(ap, fmt); X#else X char *fmt; X va_start(ap); X fmt = va_arg(ap, char *); X#endif X X (void) fprintf(stderr, "%s: ", progname); X (void) vfprintf(stderr, fmt, ap); X X va_end(ap); X} X X/*---------------------------------------------------------------------- X * Report an error returned from a system call. X */ X X/* VARARGS */ X#ifdef HAS_STDARG Xsyserr(char *fmt, ...) X#else Xsyserr(va_alist) va_dcl X#endif X{ X int e = errno; X va_list ap; X X#ifdef HAS_STDARG X va_start(ap, fmt); X#else X char *fmt; X va_start(ap); X fmt = va_arg(ap, char *); X#endif X X (void) fprintf(stderr, "%s: ", progname); X (void) vfprintf(stderr, fmt, ap); X if (e <= sys_nerr) X (void) fprintf(stderr, ": %s\n", sys_errlist[e]); X else X (void) fprintf(stderr, ": unknown system error %d\n", e); X X va_end(ap); X} X X/*---------------------------------------------------------------------- X * Sleep for the given number of seconds. X */ X Xsnooze(n) Xint n; X{ X#ifdef M_XENIX X (void) nap(n * 1000L); X#else X (void) sleep(n); X#endif X} X X/*---------------------------------------------------------------------- X * Get the host name from HOSTFILE. X */ X X#ifdef HOSTFILE X Xchar * Xgethost() X{ X int fd, rd; X char *p; X static char name[32]; X X if ((fd = open(HOSTFILE, O_RDONLY)) == -1) X return NULL; X rd = read(fd, name, sizeof(name) - 1); X (void) close(fd); X X if (rd < 1) X return NULL; X name[rd] = 0; X if ((p = strchr(name, '\n')) != NULL) X *p = 0; X X return (name[0] ? name : NULL); X} X X#endif /* HOSTFILE */ X X/*---------------------------------------------------------------------- X * Get the host name via the uname() system call. X */ X X#ifdef UNAME X Xchar * Xgethost() X{ X static struct utsname u; X X uname(&u); X return (u.nodename[0] ? u.nodename : NULL); X} X X#endif /* UNAME */ X X/*---------------------------------------------------------------------- X * Get the host name via the gethostname() system call. X */ X X#ifdef GETHOSTNAME X Xchar * Xgethost() X{ X static char hostname[64]; X X if (gethostname(hostname, sizeof(hostname)) == -1) X return NULL; X X return hostname; X} X X#endif /* GETHOSTNAME */ X X/*---------------------------------------------------------------------- X * Return a pre-defined HOSTNAME. X */ X X#ifdef HOSTNAME X Xchar * Xgethost() X{ X return HOSTNAME; X} X X#endif /* HOSTNAME */ X X/*---------------------------------------------------------------------- X * Variable-argument-list output, System V style. X */ X X#ifndef HAS_VPRINTF X Xvprintf(fmt, ap) Xchar *fmt; Xva_list ap; X{ X int a,b,c,d,e,f,g,h; X X a = va_arg(ap, int); X b = va_arg(ap, int); X c = va_arg(ap, int); X d = va_arg(ap, int); X e = va_arg(ap, int); X f = va_arg(ap, int); X g = va_arg(ap, int); X h = va_arg(ap, int); X X (void) printf(fmt, a,b,c,d,e,f,g,h); X} X Xvfprintf(fp, fmt, ap) XFILE *fp; Xchar *fmt; Xva_list ap; X{ X int a,b,c,d,e,f,g,h; X X a = va_arg(ap, int); X b = va_arg(ap, int); X c = va_arg(ap, int); X d = va_arg(ap, int); X e = va_arg(ap, int); X f = va_arg(ap, int); X g = va_arg(ap, int); X h = va_arg(ap, int); X X (void) fprintf(fp, fmt, a,b,c,d,e,f,g,h); X} X Xvsprintf(s, fmt, ap) Xchar *s; Xchar *fmt; Xva_list ap; X{ X int a,b,c,d,e,f,g,h; X X a = va_arg(ap, int); X b = va_arg(ap, int); X c = va_arg(ap, int); X d = va_arg(ap, int); X e = va_arg(ap, int); X f = va_arg(ap, int); X g = va_arg(ap, int); X h = va_arg(ap, int); X X (void) sprintf(s, fmt, a,b,c,d,e,f,g,h); X} X X#endif /* !HAS_VPRINTF */ X X/*---------------------------------------------------------------------- X * Add a new environment variable. X */ X X#ifndef HAS_PUTENV X Xint Xputenv(s) Xchar *s; X{ X static char **env_array; X static int env_size; X char *e; X int i, j; X X if (env_array == NULL) X { X for (i = 0; environ[i]; ++i) X {} X env_size = i + 10; /* arbitrary */ X env_array = (char **) zalloc(env_size * sizeof(char *)); X Copy((char *)env_array, (char *)environ, X (int) ((i + 1) * sizeof(char *))); X environ = env_array; X } X else if (environ != env_array) X message("putenv: warning: someone moved environ!\n"); X X if ((e = strchr(s, '=')) != NULL) X ++e; X else X e = s + strlen(s); X X j = 0; X for (i = 0; env_array[i]; ++i) X { X if (strncmp(env_array[i], s, e - s) != 0) X env_array[j++] = env_array[i]; X } X X if ((j + 1) >= env_size) X { X env_size += 10; /* arbitrary */ X env_array = (char **) srealloc((char *)env_array, X env_size * sizeof(char **)); X } X X env_array[j++] = s; X env_array[j] = NULL; X X environ = env_array; X return 0; X} X X#endif /* !HAS_PUTENV */ X X/*---------------------------------------------------------------------- X * Memory copy. X */ X X#ifdef MEMFUNCS X XCopy(dest, src, len) Xchar *dest; Xchar *src; Xint len; X{ X while (len-- > 0) X *dest++ = *src++; X} X X#endif X X/*---------------------------------------------------------------------- X * Memory clear. X */ X X#ifdef MEMFUNCS X XZero(dest, len) Xchar *dest; Xint len; X{ X while (len-- > 0) X *dest++ = 0; X} X X#endif END_OF_FILE if test 7306 -ne `wc -c <'sysdep.c'`; then echo shar: \"'sysdep.c'\" unpacked with wrong size! fi # end of 'sysdep.c' fi if test -f 'uucp.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'uucp.c'\" else echo shar: Extracting \"'uucp.c'\" \(3269 characters\) sed "s/^X//" >'uucp.c' <<'END_OF_FILE' X/* $Header: uucp.c,v 1.3 89/02/10 15:47:51 network Exp $ X * X * Handle mail destined for other hosts via UUCP. X * Deliver is intended as a very low-level program, so we don't X * do anything fancy here. We just hand the message to uux. X * X * $Log: uucp.c,v $ X * Revision 1.3 89/02/10 15:47:51 network X * V7 support. X * X * Revision 1.2 88/11/28 18:08:23 network X * patch5: Copy temp files before handing them to delivery files. X * patch5: Introduce "uid" program. X * X * Revision 1.1 88/06/06 09:39:42 chip X * Initial revision X * X */ X X#include "deliver.h" X#include X#include X X/* X * Local functions. X */ X Xstatic int uucp_copy(); X X/*---------------------------------------------------------------------- X * Send mail to UUCP addresses (if any). X * This is a simple implementation: invoke uux once per address. X */ X Xuucp_deliver() X{ X struct stat st; X DEST *d; X char *uux; X static char uux1[] = "/bin/uux"; X static char uux2[] = "/usr/bin/uux"; X X if (stat(uux1, &st) == 0) X uux = uux1; X else if (stat(uux2, &st) == 0) X uux = uux2; X else X { X error("can't find uux!?\n"); X return; X } X X for (d = first_dest(); d; d = next_dest(d)) X { X FILE *uux_fp; X char *bang; X char *av[5]; X char rmail[40]; X char who[BUFSIZ]; X X if (d->d_class != CL_UUCP || d->d_state != ST_WORKING) X continue; X X if (printaddrs) X (void) printf("%s\n", d->d_name); X X if (dryrun) X { X d->d_state = ST_DONE; X continue; X } X X bang = strchr(d->d_name, '!'); X *bang = 0; X (void) sprintf(rmail, "%s!rmail", d->d_name); X *bang++ = '!'; X (void) sprintf(who, "(%s)", bang); X X av[0] = "uux"; X av[1] = "-"; X av[2] = rmail; X av[3] = who; X av[4] = NULL; X if ((uux_fp = ct_popenv(eff_ct, uux, av, "w")) == NULL) X continue; X X if (uucp_copy(uux_fp) < 0) X dest_err(d, E_UUX); X X if (ct_pclose(uux_fp)) X { X /* "No such host" overrides piping problems. */ X dest_err(d, E_NSHOST); X } X else X d->d_state = ST_DONE; X } X} X X/*---------------------------------------------------------------------- X * Write the message for UUCP transmission to the given file. X */ X Xstatic int Xuucp_copy(ofp) XFILE *ofp; X{ X FILE *ifp; X char *p; X register int c; X int fd; X char buf[BUFSIZ]; X X if ((fd = dup(tfd[T_HDR])) == -1) X { X syserr("can't dup header fd"); X return -1; X } X (void) lseek(fd, 0L, 0); X if ((ifp = fdopen(fd, "r")) == NULL) X { X error("can't fdopen header fd"); X return -1; X } X X /* X * Copy the header, but tack "remote from" onto the end of the X * From_ line. (If it weren't for dealing with the From_ line, X * I'd skip stream I/O altogether and use read/write. Maybe X * I should save the length of the From_ line when I copy it...) X */ X X (void) fgets(buf, GETSIZE(buf), ifp); X if ((p = strchr(buf, '\n')) != NULL) X *p = 0; X (void) fprintf(ofp, "%s remote from %s\n", buf, hostname); X X while ((c = getc(ifp)) != EOF) X (void) putc(c, ofp); X X (void) fclose(ifp); X X /* X * Copy the body X */ X X if ((fd = dup(tfd[T_BODY])) == -1) X { X syserr("can't dup body fd"); X return -1; X } X (void) lseek(fd, 0L, 0); X if ((ifp = fdopen(fd, "r")) == NULL) X { X error("can't fdopen body fd"); X (void) close(fd); X return -1; X } X X while ((c = getc(ifp)) != EOF) X (void) putc(c, ofp); X X (void) fclose(ifp); X return 0; X} END_OF_FILE if test 3269 -ne `wc -c <'uucp.c'`; then echo shar: \"'uucp.c'\" unpacked with wrong size! fi # end of 'uucp.c' fi echo shar: End of shell archive. exit 0 -- Chip Salzenberg or A T Engineering Me? Speak for my company? Surely you jest! "It's no good. They're tapping the lines."