home *** CD-ROM | disk | FTP | other *** search
- /* $Header: dfile.c,v 1.2 88/08/25 15:23:56 network Exp $
- *
- * Filter destination(s) through delivery file(s).
- *
- * $Log: dfile.c,v $
- * Revision 1.2 88/08/25 15:23:56 network
- * Add third parameter to do_dfile(), so that if the delivery file cannot
- * be executed, the given destination can be recorded as an error.
- *
- * Revision 1.1 88/06/06 09:38:38 chip
- * Initial revision
- *
- */
-
- #include "deliver.h"
- #include <sys/types.h>
- #include <sys/stat.h>
-
- /*----------------------------------------------------------------------
- * Filter all valid destinations through the global delivery file.
- */
-
- sys_dfile(dac, dav)
- int dac;
- char **dav;
- {
- char **fav;
- int fac, a, goodnames;
- struct stat st;
-
- /*
- * If there is no global delivery file, then take all the named
- * addresses verbatim and return.
- */
-
- if (stat(sys_deliver, &st) == -1)
- {
- if (verbose)
- message("%s: no system delivery file\n",
- progname);
-
- for (a = 0; a < dac; ++a)
- (void) dest(dav[a], (char *) NULL);
- return;
- }
-
- /*
- * Collect the arguments for the delivery file.
- */
-
- fav = (char **) zalloc((dac + 3) * sizeof(char **));
- fav[0] = shell;
- fav[1] = sys_deliver;
- fac = 2;
-
- goodnames = 0;
- for (a = 0; a < dac; ++a)
- {
- char *p;
-
- for (p = dav[a]; *p; ++p)
- {
- if (!isalpha(*p)
- && !isdigit(*p)
- && !strchr("#%-+._", *p))
- break;
- }
-
- if (*p)
- {
- /* Invalid name -- note it and go on. */
-
- (void) dest(dav[a], (char *) NULL);
- }
- else
- {
- /* Valid name -- let the delivery file handle it. */
-
- fav[fac++] = dav[a];
- ++goodnames;
- }
- }
-
- fav[fac] = NULL;
-
- /*
- * If there were any good names found, let loose the delivery
- * file. Note the meaning of "good" is "well-formed", not "valid".
- * Thus the system delivery file has control over the handling of
- * all local deliveries, not just those to valid users.
- */
-
- if (goodnames)
- (void) do_dfile(eff_ct, fav, (DEST *)NULL);
-
- free((char *) fav);
- }
-
- /*----------------------------------------------------------------------
- * Filter all user destinations through their local delivery files.
- */
-
- user_dfiles()
- {
- DEST *d;
- int nfound;
-
- /*
- * Continue to loop through all addresses until no destination
- * that needs expanding can be found.
- */
-
- do {
- nfound = 0;
- for (d = first_dest(); d; d = next_dest(d))
- {
- if (d->class == CL_USER
- && d->state == ST_WORKING
- && !d->dfdone)
- {
- one_dfile(d);
- d->dfdone = TRUE;
- }
- }
- } while (nfound > 0);
- }
-
- /*----------------------------------------------------------------------
- * Run the delivery file (if any) for the specified destination.
- */
-
- one_dfile(d)
- DEST *d;
- {
- CONTEXT *ct;
- char *fav[4];
- char udel_path[100];
- struct stat st;
-
- if ((ct = name_context(d->name)) == NULL)
- {
- d->state = ST_ERROR;
- d->error = "Missing context in user_dfile_one()";
- return;
- }
-
- /*
- * If user's home directory is missing, forget it.
- * If user's home directory is writable to the world,
- * executing the delivery file would allow a security breach!
- * Thanks to Jon Zeeff for this hint...
- */
-
- if (stat(ct->home, &st) == -1
- || (st.st_mode & S_IFMT) != S_IFDIR)
- {
- if (verbose)
- message("%s: home directory %s is missing!\n",
- ct->name, ct->home);
- return;
- }
-
- if (st.st_mode & 02)
- {
- if (verbose)
- message("%s: home directory is writable to the world!\n",
- ct->name);
- return;
- }
-
- /*
- * If there is no delivery file to execute, just return.
- */
-
- (void) sprintf(udel_path, "%s/%s", ct->home, user_deliver);
- if (stat(udel_path, &st) == -1)
- {
- if (verbose)
- message("%s has no delivery file\n", d->name);
- return;
- }
-
- /*
- * Time to run the file!
- * We put this dest on hold, so that it will be ignored unless
- * the delivery file names it.
- */
-
- d->state = ST_HOLD;
-
- fav[0] = "sh";
- fav[1] = udel_path;
- fav[2] = d->name;
- fav[3] = NULL;
- (void) do_dfile(ct, fav, d);
- }
-
- /*----------------------------------------------------------------------
- * Process a delivery file.
- */
-
- int
- do_dfile(ct, av, d)
- CONTEXT *ct;
- char **av;
- DEST *d;
- {
- FILE *fp;
- char *name, *mailbox;
-
- if (!ct)
- return -1;
-
- if (! ok_context(ct))
- {
- if (d)
- {
- d->state = ST_ERROR;
- d->error = "No permissions for that context";
- }
- else
- message("No permissions to run as %s\n", ct);
-
- return -1;
- }
-
- /* Make sure that the temp files are readable to the new process. */
-
- give_temps(ct);
-
- /* Here we go! */
-
- if (verbose)
- message("Processing delivery file as %s\n", ct->name);
-
- if ((fp = ct_popenv(ct, "/bin/sh", av, "r")) == NULL)
- {
- error("can't execute delivery file as %s\n", ct->name);
- leave(1);
- }
-
- /*
- * Read the standard output of the delivery file.
- */
-
- while (dfile_gets(fp, &name, &mailbox) >= 0)
- {
- DEST *nd;
-
- nd = dest(name, mailbox);
- if (nd->state == ST_HOLD)
- nd->state = ST_WORKING;
-
- /*
- * If the delivery file specified a mailbox, verify
- * that the user whose delivery file is running has
- * permissions for the requested context.
- */
-
- if ((nd->state == ST_WORKING) && (mailbox != NULL))
- {
- CONTEXT *nct;
-
- if ((nct = name_context(name)) == NULL)
- {
- nd->state = ST_ERROR;
- nd->error = "Lost context in do_dfile()";
- }
- else if (! ok_context(nct))
- {
- nd->state = ST_ERROR;
- nd->error = "No permissions for that context";
- }
- }
- }
-
- return ct_pclose(fp);
- }
-
- /*----------------------------------------------------------------------
- * Get and parse a single delivery file output line.
- */
-
- int
- dfile_gets(fp, namep, mailboxp)
- FILE *fp;
- char **namep;
- char **mailboxp;
- {
- char *p, *q;
- static char buf[BUFSIZ];
-
- if (fgets(buf, GETSIZE(buf), fp) == NULL)
- return -1;
-
- if ((p = strchr(buf, '\n')) != NULL)
- *p = 0;
- else
- {
- int c;
-
- while ((c = fgetc(fp)) != '\n' && c != EOF)
- ; /* keep reading */
-
- error("invalid line from delivery file: '%s'\n", buf);
- return -1;
- }
-
- /* Strip out all whitespace and eliminate duplicated slashes */
-
- p = q = buf;
- while (*p)
- {
- if (! isspace(*p))
- {
- if ((*q++ = *p++) == '/')
- {
- while (*p == '/')
- ++p;
- }
- }
- }
- *q = 0;
-
- /* Debugging message: display input line */
-
- if (verbose)
- message("\t'%s'\n", buf);
-
- if ((p = strchr(buf, ':')) != NULL)
- {
- *p++ = 0;
- if ((q = strchr(p, ':')) != NULL)
- *q = 0;
- }
-
- *namep = buf;
- *mailboxp = p;
- return 0;
- }
-
- /*----------------------------------------------------------------------
- * Make the temp files readable in the given context.
- * This is needed because the temps are readable by owner only.
- */
-
- give_temps(ct)
- CONTEXT *ct;
- {
- int t;
-
- if (!ct)
- return;
-
- for (t = 0; t < T_MAX; ++t)
- {
- if (chown(tfile[t], ct->uid, ct->gid) == -1)
- syserr("can't chown %s", tfile[t]);
- }
- }
-