home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume16 / deliver / part03 / main.c next >
C/C++ Source or Header  |  1988-11-14  |  10KB  |  468 lines

  1. /* $Header: main.c,v 1.5 88/09/14 20:00:03 network Exp $
  2.  *
  3.  * A program to deliver local mail with some flexibility.
  4.  *
  5.  * $Log:    main.c,v $
  6.  * Revision 1.5  88/09/14  20:00:03  network
  7.  * Add version string, including patchlevel.
  8.  * 
  9.  * Revision 1.4  88/09/14  19:41:54  network
  10.  * Portability to System V and BSD.
  11.  * General fixup.
  12.  * 
  13.  * Revision 1.3  88/08/30  16:13:54  network
  14.  * Remove general subroutines to new module, subs.c.
  15.  * 
  16.  * Revision 1.2  88/08/25  15:29:59  network
  17.  * Implement -s and -u options and ENV_SYSDEL and ENV_USERDEL environment
  18.  * variables.  Tighten up control over effective and real uid/gid.
  19.  * In particular, renounce setuid privileges if the system or user delivery
  20.  * file is specified.
  21.  * 
  22.  * Revision 1.1  88/06/06  09:38:54  chip
  23.  * Initial revision
  24.  * 
  25.  */
  26.  
  27. #include "deliver.h"
  28. #include "patchlevel.h"
  29. #include <signal.h>
  30.  
  31. /*
  32.  * External data.
  33.  */
  34.  
  35. /* Variables set by getopt() [blech] */
  36.  
  37. extern  int     optind, opterr;
  38. extern  char    *optarg;
  39.  
  40. /*
  41.  * Global data
  42.  */
  43.  
  44. int     verbose         = FALSE;
  45. int     dryrun          = FALSE;
  46. int     printaddrs      = FALSE;
  47. int     leavetemps      = FALSE;
  48. int     boxdelivery     = FALSE;
  49.  
  50. char    *progname       = "deliver";
  51. char    version[32]     = "1.0";
  52. char    *shell          = SHELL;
  53.  
  54. char    *sys_deliver    = NULL;
  55. char    *user_deliver   = NULL;
  56. char    *sender         = NULL;
  57. char    *hostname       = NULL;
  58.  
  59. int     eff_uid         = -1;
  60. int     eff_gid         = -1;
  61. int     real_uid        = -1;
  62. int     real_gid        = -1;
  63.  
  64. CONTEXT *eff_ct         = NULL;
  65. CONTEXT *real_ct        = NULL;
  66.  
  67. char    *ttype[T_MAX]   = { "header", "body" };
  68. char    *tfile[T_MAX]   = { NULL, NULL };
  69. int     tfd[T_MAX]      = { -1, -1 };
  70.  
  71. /*----------------------------------------------------------------------
  72.  * The Program.
  73.  */
  74.  
  75. main(argc, argv)
  76. int     argc;
  77. char    **argv;
  78. {
  79.     char    *p;
  80.     int     u, c, errcount, insecure;
  81.  
  82.     /* Make sure that stdout and stderr are interleaved correctly */
  83.  
  84.     (void) Linebuf(stdout);
  85.     (void) Linebuf(stderr);
  86.  
  87.     /* Figure out the name used to invoke this program. */
  88.  
  89.     progname = basename(argv[0]);
  90.  
  91.     /* What version of the program is this? */
  92.  
  93.     sprintf(version + strlen(version), ".%02d", PATCHLEVEL);
  94.  
  95.     /* Figure out the name of this host */
  96.  
  97.     if ((hostname = gethost()) == NULL)
  98.     {
  99.         hostname = "unknown";
  100.         error("unable to determine host name; using \"%s\"\n",
  101.               hostname);
  102.     }
  103.  
  104.     /* Process environment: handle recursive invocation */
  105.  
  106.     if ((p = getenv(ENV_DFLAGS)) != NULL)
  107.     {
  108.         while (*p)
  109.         {
  110.             switch (*p++)
  111.             {
  112.             case 'v':
  113.                 verbose = TRUE;
  114.                 break;
  115.             case 'd':
  116.                 verbose = TRUE;
  117.                 dryrun = TRUE;
  118.                 break;
  119.             case 'A':
  120.                 printaddrs = TRUE;
  121.                 dryrun = TRUE;
  122.                 break;
  123.             case 't':
  124.                 leavetemps = TRUE;
  125.                 break;
  126.             }
  127.         }
  128.     }
  129.     if ((p = getenv(ENV_SYSDEL)) != NULL)
  130.         sys_deliver = p;
  131.     if ((p = getenv(ENV_USERDEL)) != NULL)
  132.         user_deliver = p;
  133.     if ((p = getenv(ENV_SENDER)) != NULL)
  134.         sender = p;
  135.     if ((p = getenv(ENV_HOSTNAME)) != NULL)
  136.         hostname = p;
  137.  
  138.     /* Parse command line arguments */
  139.  
  140.     while ((c = getopt(argc, argv, "vdAtbs:u:r:h:")) != EOF)
  141.     {
  142.         switch (c)
  143.         {
  144.         case 'v':
  145.             verbose = TRUE;
  146.             break;
  147.         case 'd':
  148.             verbose = TRUE;
  149.             dryrun = TRUE;
  150.             break;
  151.         case 'A':
  152.             printaddrs = TRUE;
  153.             dryrun = TRUE;
  154.             break;
  155.         case 't':
  156.             leavetemps = TRUE;
  157.             break;
  158.         case 'b':
  159.             boxdelivery = TRUE;
  160.             break;
  161.         case 's':
  162.             sys_deliver = optarg;
  163.             break;
  164.         case 'u':
  165.             user_deliver = optarg;
  166.             break;
  167.         case 'r':
  168.             sender = optarg;
  169.             break;
  170.         case 'h':
  171.             hostname = optarg;
  172.             break;
  173.         case '?':
  174.             usage();
  175.         }
  176.     }
  177.  
  178.     /* If no destinations were given, forget it. */
  179.  
  180.     if (optind >= argc)
  181.     {
  182.         message("%s: no recipients specified\n", progname);
  183.         usage();
  184.     }
  185.  
  186.     /* Print a debugging message */
  187.  
  188.     if (verbose)
  189.     {
  190.         message("%s %s running on host %s\n",
  191.             progname, version, hostname);
  192.         if (sender && *sender)
  193.             message("Sender is %s\n", sender);
  194.     }
  195.  
  196.     /* Find effective and real uids and gids. */
  197.  
  198.     eff_uid = geteuid();
  199.     eff_gid = getegid();
  200.     real_uid = getuid();
  201.     real_gid = getgid();
  202.  
  203.     if (eff_uid != real_uid && eff_uid != 0)
  204.     {
  205.         message("%s: if setuid, must be setuid root\n");
  206.         leave(1);
  207.     }
  208.  
  209.     /* Renounce special privileges if something insecure was requested. */
  210.  
  211.     if (sys_deliver || user_deliver)
  212.     {
  213.         if (setgid(eff_gid = real_gid) == -1
  214.          || setuid(eff_uid = real_uid) == -1)
  215.         {
  216.             syserr("%s: can't renounce setuid privileges");
  217.             leave(1);
  218.         }
  219.     }
  220.  
  221.     /* Get the contexts of our effective and real uids. */
  222.  
  223.     if ((eff_ct = uid_context(eff_uid)) == NULL)
  224.         error("invalid effective uid %d!?\n", eff_uid);
  225.  
  226.     if ((real_ct = uid_context(real_uid)) == NULL)
  227.         error("invalid real uid %d!?\n", real_uid);
  228.  
  229.     if (!eff_ct || !real_ct)
  230.         leave(1);
  231.  
  232.     if (verbose)
  233.     {
  234.         message("effective uid = %s (%d/%d); real uid = %s (%d/%d)\n",
  235.             eff_ct->name, eff_ct->uid, eff_ct->gid,
  236.             real_ct->name, real_ct->uid, real_ct->gid);
  237.     }
  238.  
  239.     /* Let's be sane about the file creation mask. */
  240.  
  241.     u = umask(0);
  242.     u &= ~0700;     /* Let's not deprive ourselves of permissions.  */
  243.     u |= 022;       /* Let's be reasonably paranoid about writing.  */
  244.     (void) umask(u);
  245.  
  246.     /* Turn off all intrusive signals (unless we're not delivering). */
  247.  
  248.     if (! dryrun)
  249.     {
  250.         (void) signal(SIGHUP, SIG_IGN);
  251.         (void) signal(SIGINT, SIG_IGN);
  252.         (void) signal(SIGQUIT, SIG_IGN);
  253.     }
  254.  
  255.     /*
  256.      * Create the temporary files and write the message to them.
  257.      */
  258.  
  259.     if (copy_message() < 0)
  260.         leave(1);
  261.  
  262.     /*
  263.      * Set up useful environment variables.
  264.      * Note that this must be done _after_ copy_message(),
  265.      * since that's where the temp files are created.
  266.      */
  267.  
  268.     setup_environ();
  269.  
  270.     /*
  271.      * Assign the default delivery file names.
  272.      * Note that this must be after setup_environ(), or else the
  273.      * environment won't reflect specified/unspecified options.
  274.      */
  275.  
  276.     if (!sys_deliver)
  277.         sys_deliver = SYS_DELIVER;
  278.     if (!user_deliver)
  279.         user_deliver = USER_DELIVER;
  280.  
  281.     /*
  282.      * Perhaps we should consider all arguments as mailbox names...
  283.      */
  284.  
  285.     if (boxdelivery)
  286.     {
  287.         int     a;
  288.  
  289.         if (verbose)
  290.             message("mailbox delivery as %s\n", real_ct->name);
  291.  
  292.         /*
  293.          * Consider all arguments as mailbox filenames.
  294.          */
  295.  
  296.         for (a = optind; a < argc; ++a)
  297.             (void) dest(real_ct->name, argv[a]);
  298.  
  299.         if (verbose)
  300.             dumpdests("(should all be mailboxes)");
  301.     }
  302.  
  303.     /*
  304.      * They're not mailbox names, so they should be mail addresses.
  305.      */
  306.  
  307.     else
  308.     {
  309.         /*
  310.          * Run all destinations though the system delivery file.
  311.          * If sys_dfile() doesn't find one, it will call dest()
  312.          * on each address.
  313.          */
  314.  
  315.         sys_dfile(argc - optind, argv + optind);
  316.  
  317.         if (verbose)
  318.             dumpdests("after running system delivery file");
  319.  
  320.         /*
  321.          * Run each user destination through his delivery file.
  322.          */
  323.  
  324.         user_dfiles();
  325.  
  326.         if (verbose)
  327.             dumpdests("after running user delivery files");
  328.     }
  329.  
  330.     /*
  331.      * Drop mail in mailbox(es).
  332.      */
  333.  
  334.     mbox_deliver();
  335.  
  336.     if (verbose)
  337.         dumpdests("after delivery to all mailboxes");
  338.  
  339.     /*
  340.      * Send mail to UUCP address(es).
  341.      */
  342.  
  343.     uucp_deliver();
  344.  
  345.     if (verbose)
  346.         dumpdests("after delivery to UUCP addresses");
  347.  
  348.     /*
  349.      * Report any errors, and leave.
  350.      */
  351.  
  352.     errcount = report_errors();
  353.  
  354.     /*
  355.      * All done.
  356.      */
  357.  
  358.     leave(errcount ? 1 : 0);
  359.     /* NOTREACHED */
  360. }
  361.  
  362. /*----------------------------------------------------------------------
  363.  * Print a usage message and exit.
  364.  */
  365.  
  366. usage()
  367. {
  368.     message("Usage: %s [-b][-A][-d][-v][-t][-r from][-h host] args\n", progname);
  369.     message("-b      All arguments are mailbox filenames.\n");
  370.     message("        (Default: arguments are user names.)\n");
  371.     message("-A      Resolve addresses but do not deliver.\n");
  372.     message("-d      Be verbose but do not deliver.\n");
  373.     message("-v      Be verbose and deliver.\n");
  374.     message("-t      Do not remote temp files before exiting.\n");
  375.     message("-r from Specify the address to appear in the \"From \" line.\n");
  376.     message("-h host Specify the host name.\n");
  377.     message("        (This option overrides any \"From \" line in the input.)\n");
  378.     message("args    Either user addresses or mailboxes (-b).\n");
  379.     leave(1);
  380. }
  381.  
  382. /*----------------------------------------------------------------------
  383.  * Clean up and exit.
  384.  */
  385.  
  386. leave(code)
  387. int     code;
  388. {
  389.     if (! leavetemps)
  390.     {
  391.         int     t;
  392.  
  393.         for (t = 0; t < T_MAX; ++t)
  394.         {
  395.             if (tfile[t] && unlink(tfile[t]) == -1)
  396.                 syserr("can't unlink %s", tfile[t]);
  397.         }
  398.     }
  399.  
  400.     exit(code);
  401. }
  402.  
  403. /*----------------------------------------------------------------------
  404.  * Report any errors to stderr.
  405.  * Return an error count.
  406.  */
  407.  
  408. int
  409. report_errors()
  410. {
  411.     DEST    *d;
  412.     int     count = 0;
  413.  
  414.     for (d = first_dest(); d; d = next_dest(d))
  415.     {
  416.         if (d->state != ST_ERROR)
  417.             continue;
  418.  
  419.         if (++count == 1)
  420.         {
  421.             error(
  422.             "delivery to the following address(es) failed on host %s\n",
  423.                 hostname);
  424.         }
  425.  
  426.         message("\tuser \"%s\"", d->name);
  427.         if (d->class == CL_MBOX)
  428.             message(", mailbox \"%s\"", d->mailbox);
  429.         message(": %s\n", d->error);
  430.     }
  431.  
  432.     return count;
  433. }
  434.  
  435. /*----------------------------------------------------------------------
  436.  * Set up useful environment variables.
  437.  */
  438.  
  439. setup_environ()
  440. {
  441.     char    flags[8];
  442.     int     f = 0;
  443.  
  444.     flags[f++] = '-';
  445.     if (verbose)
  446.         flags[f++] = (dryrun ? 'd' : 'v');
  447.     if (printaddrs)
  448.         flags[f++] = 'A';
  449.     if (leavetemps)
  450.         flags[f++] = 't';
  451.     flags[f] = 0;
  452.  
  453.     alloc_env(ENV_DFLAGS, (f > 1) ? flags : "");
  454.     if (sys_deliver && *sys_deliver)
  455.         alloc_env(ENV_SYSDEL, sys_deliver);
  456.     if (user_deliver && *user_deliver)
  457.         alloc_env(ENV_USERDEL, user_deliver);
  458.     if (hostname && *hostname)
  459.         alloc_env(ENV_HOSTNAME, hostname);
  460.     if (sender && *sender)
  461.         alloc_env(ENV_SENDER, sender);
  462.  
  463.     alloc_env(ENV_HEADER, tfile[T_HEADER]);
  464.     alloc_env(ENV_BODY,   tfile[T_BODY]);
  465.  
  466.     alloc_env("IFS", " \t\n");
  467. }
  468.