This is fwdfile.c in view mode; [Download] [Up]
/* @(#)src/directors/fwdfile.c 1.4 23 Feb 1991 01:05:22 */ /* * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll * * See the file COPYING, distributed with smail, for restriction * and warranty information. */ /* * forward.c: * direct mail using forward files. In the future, the name of * the file involved will be configurable. Now, for each addr * structure representing a local user, an attempt is made to * open a .forward file in that user's home directory. If the * attempt succeeds then the contents are read in and transformed * by process_field into a list of addr structures. * * Specifications for the forwardfile directing driver: * * private attribute data: * file (string): specifies the template for the name of the * forward file. expand_string will be used to build the * actual name. If this does not begin with `/', it will * be referenced relative to the smail_lib_dir directory. * * modemask (number): specifies bits that are not allowed * to be set. If some of these bits are set, the * ALIAS_SECURE flag is not set for the resultant * addr structures. * * caution (string): colon-separated list of users and * directories considered not secure. Directory names * can begin with ~user. Secure addresses will not have * the ADDR_CAUTION bit set in the addr structure. * * unsecure (string): colon-separated list of users and * directories considered very unsecure. Unsecure * addresses will have the ADDR_UNSECURE bit set * in the addr structure. * * owners (string): list of possible owners for the file. * For files owned by others, the ADDR_CAUTION bit is * set in the resultant addr structures. * * owngroups (string): like the `owners' attribute except * that it applies to groups. * * prefix (string): a string which must appear at the * beginning of each address to be matched by this * director. The prefix is not included in any * expansions of the $user variable. The prefix is also * compared independently of case. * * suffix (string): a string which must appear at the end of * each address to be matched by this director. The * suffix is not included in any expansions of the $user * variable. The suffix is also compared independently * of case. * * private attribute flags: * checkowner: if set, the user associated with the forward * file (remainder from the addr structure) is allowed * as a possible owner for the file. * * forwardto: if set, this is actually a Forward-to driver. * In this case, the forward file is used only if the * first line begins with "Forward to " and only the * first line is actually used. * * NOTE: if none of the attributes `owners', `owngroups' or * `checkowner' is set, no checks are made for ownership * restrictions. */ #include <stdio.h> #include <ctype.h> #include <pwd.h> #include <grp.h> #include <sys/types.h> #include <sys/stat.h> #include "defs.h" #ifdef UNIX_BSD # include <sys/file.h> #endif #include "../smail.h" #include "../smailconf.h" #include "../parse.h" #include "../addr.h" #include "../field.h" #include "../log.h" #include "../direct.h" #include "../dys.h" #include "../exitcodes.h" #include "fwdfile.h" #ifndef DEPEND # include "../extern.h" # include "../error.h" # include "../debug.h" #endif long atol(); /* * dtd_forwardfile - direct using per-user forward files */ /*ARGSUSED*/ struct addr * dtd_forwardfile(dp, in, out, new, defer, fail) struct director *dp; /* director entry */ struct addr *in; /* input local-form addrs */ struct addr **out; /* output resolved addrs */ struct addr **new; /* output new addrs to resolve */ struct addr **defer; /* addrs to defer to a later time */ struct addr **fail; /* unresolvable addrs */ { register struct addr *cur; /* temp for processing input */ struct addr *pass = NULL; /* addrs to pass to next director */ struct addr *next; /* next value for cur */ DEBUG(DBG_DRIVER_HI, "dtd_forwardfile called\n"); /* loop over all of the input addrs */ for (cur = in; cur; cur = next) { next = cur->succ; if (cur->remainder[0] == '.' || index(cur->remainder, '/')) { /* do not match hidden files or files in subdirectories */ cur->succ = pass; pass = cur; continue; } /* fill in any user information */ director_user_info(cur); /* finish up */ if (finish_forward(dp, cur, new, defer, fail) == FAIL) { /* if there was no forward file, pass it to the next director */ DEBUG2(DBG_DRIVER_MID, "director %s did not match name %s\n", dp->name, cur->remainder); cur->succ = pass; pass = cur; } } return pass; /* return addrs for next director */ } /* * dtv_forwardfile - verify using forward file */ /*ARGSUSED*/ void dtv_forwardfile(dp, in, retry, okay, defer, fail) struct director *dp; /* director entry */ struct addr *in; /* input local-form addrs */ struct addr **retry; /* output list of unmatched addrs */ struct addr **okay; /* output list of verified addrs */ struct addr **defer; /* temporariliy unverifiable addrs */ struct addr **fail; /* unverified addrs */ { register struct addr *cur; /* temp for processing input */ struct addr *next; /* next value for cur */ register struct forwardfile_private *priv; priv = (struct forwardfile_private *)dp->private; DEBUG(DBG_DRIVER_HI, "dtv_forwardfile called\n"); /* loop over all of the input addrs */ for (cur = in; cur; cur = next) { char *fn; /* forward filename */ struct stat statbuf; /* stat of forward file */ char *remainder = cur->remainder; int c_save; /* saved start of suffix */ char *suffixp = NULL; /* start of suffix in remainder */ next = cur->succ; if (cur->remainder[0] == '.' || index(cur->remainder, '/')) { /* do not match hidden files or files in subdirectories */ cur->succ = *retry; *retry = cur; continue; } /* fill in any user information */ director_user_info(cur); /* remove prefix and suffix from the user name */ if (priv->prefix) { int len = strlen(priv->prefix); if (strncmpic(priv->prefix, remainder, len) == 0) { remainder += len; } else { cur->succ = *retry; *retry = cur; continue; } } if (priv->suffix) { int len = strlen(priv->suffix); int rem_len = strlen(remainder); if (rem_len > len && EQIC(remainder + rem_len - len, priv->suffix)) { suffixp = remainder + rem_len - len; c_save = *suffixp; *suffixp = '\0'; } else { cur->succ = *retry; *retry = cur; continue; } } /* compute the filename */ fn = expand_string(priv->file, (struct addr *)NULL, cur->flags&ADDR_ISUSER? cur->home: (char *)NULL, remainder); if (suffixp) { *suffixp = c_save; } if (fn == NULL) { DEBUG2(DBG_DRIVER_MID, "dtv_forwardfile: director %s failed to expand %s\n", dp->name, priv->file); cur->succ = *retry; *retry = cur; continue; } if (fn[0] == '/') { fn = COPY_STRING(fn); } else { fn = make_lib_fn(fn); } if (fn == NULL) { /* * ERR_114 - forward file not an absolute path, and no * smail_lib_dir defined * * DESCRIPTION * A path from / (root) was not returned by expand_string() * for the file attribute, and no smail_lib_dir is defined. * Since the current directory for smail is not generally * useful, this can be considered a configuration error. * * ACTIONS * Defer the address as a configuration error. * * RESOLUTION * The postmaster should check the director to ensure that the * forward-file director will always either fail to expand * (if a $-variable is not defined in some context) or produce * an absolute pathname, or that the smail_lib_dir be defined. */ cur->error = note_error(ERR_CONFERR|ERR_114, xprintf("director %s: %s not an absolute path", dp->name, priv->file)); cur->succ = *defer; *defer = cur; continue; } /* * make sure that the file exists and is a regular non-zero * length file. */ if (stat(fn, &statbuf) < 0 || (statbuf.st_mode&S_IFMT) != S_IFREG || statbuf.st_size == 0) { DEBUG2(DBG_DRIVER_MID, "dtv_forwardfile: director %s does not match %s\n", dp->name, cur->remainder); xfree(fn); cur->succ = *retry; *retry = cur; continue; } xfree(fn); /* address was matched */ cur->succ = *okay; *okay = cur; } } /* * dtb_forwardfile - read the configuration file attributes */ char * dtb_forwardfile(dp, attrs) struct director *dp; /* director entry being defined */ struct attribute *attrs; /* list of per-driver attributes */ { char *error; static struct attr_table forwardfile_attributes[] = { { "file", t_string, NULL, NULL, OFFSET(forwardfile_private, file) }, { "modemask", t_int, NULL, NULL, OFFSET(forwardfile_private, modemask) }, { "caution", t_string, NULL, NULL, OFFSET(forwardfile_private, caution) }, { "unsecure", t_string, NULL, NULL, OFFSET(forwardfile_private, unsecure) }, { "owners", t_string, NULL, NULL, OFFSET(forwardfile_private, owners) }, { "owngroups", t_string, NULL, NULL, OFFSET(forwardfile_private, owngroups) }, { "prefix", t_string, NULL, NULL, OFFSET(forwardfile_private, prefix) }, { "suffix", t_string, NULL, NULL, OFFSET(forwardfile_private, suffix) }, { "checkowner", t_boolean, NULL, NULL, FWD_CHECKOWNER }, { "forwardto", t_boolean, NULL, NULL, FWD_FORWARDTO }, }; static struct attr_table *end_forwardfile_attributes = ENDTABLE(forwardfile_attributes); static struct forwardfile_private forwardfile_template = { NULL, /* file */ 000, /* modemask */ NULL, /* caution */ NULL, /* unsecure */ NULL, /* owners */ NULL, /* owngroups */ }; struct forwardfile_private *priv; /* new forwardfile_private structure */ /* copy the template private data */ priv = (struct forwardfile_private *)xmalloc(sizeof(*priv)); (void) memcpy((char *)priv, (char *)&forwardfile_template, sizeof(*priv)); dp->private = (char *)priv; /* fill in the attributes of the private data */ error = fill_attributes((char *)priv, attrs, &dp->flags, forwardfile_attributes, end_forwardfile_attributes); if (error) { return error; } else { return NULL; } } /* * finish_forward - do the real work of finding and processing forward file * * return SUCCEED, if a forward file was found and processed, * FAIL if it was not found or processed. If SUCCEED, `new' will * be properly filled in. */ static int finish_forward(dp, addr, new, defer, fail) struct director *dp; /* director entry */ struct addr *addr; /* specific addr structure */ struct addr **new; /* output new addrs to resolve */ struct addr **defer; /* addrs to defer to a later time */ struct addr **fail; /* unresolvable addrs */ { register struct forwardfile_private *priv; char *fn; /* name of forward file */ int fd; /* open forward file */ struct stat statbuf; /* buf for fstat call */ char *buf; /* buffer for file contents */ struct addr *cur; /* addrs from process_field */ struct addr *next; /* next addr from process_field */ int flags = ADDR_FWDTYPE; /* addr structure flags value */ char *error; /* error message */ struct str fwdtobuf; char *remainder = addr->remainder; int c_save; /* saved start of suffix */ char *suffixp = NULL; /* start of suffix in remainder */ priv = (struct forwardfile_private *)dp->private; /* remove prefix and suffix from the user name */ if (priv->prefix) { int len = strlen(priv->prefix); if (strncmpic(priv->prefix, remainder, len) == 0) { remainder += len; } else { return FAIL; } } if (priv->suffix) { int len = strlen(priv->suffix); int rem_len = strlen(remainder); if (rem_len > len && EQIC(remainder + rem_len - len, priv->suffix)) { suffixp = remainder + rem_len - len; c_save = *suffixp; *suffixp = '\0'; } else { return FAIL; } } /* compute the file name */ fn = expand_string(priv->file, (struct addr *)NULL, addr->flags&ADDR_ISUSER? addr->home: (char *)NULL, remainder); if (suffixp) { *suffixp = c_save; } if (fn == NULL) { DEBUG2(DBG_DRIVER_MID, "dtd_forwardfile: director %s failed to expand %s\n", dp->name, priv->file); return FAIL; } if (fn[0] == '/') { fn = COPY_STRING(fn); } else { fn = make_lib_fn(fn); } if (fn == NULL) { /* * ERR_114 - forward file not an absolute path, and no * smail_lib_dir defined * * DESCRIPTION * A path from / (root) was not returned by expand_string() * for the file attribute, and no smail_lib_dir is defined. * Since the current directory for smail is not generally * useful, this can be considered a configuration error. * * ACTIONS * Defer the address as a configuration error. * * RESOLUTION * The postmaster should check the director to ensure that the * forward-file director will always either fail to expand * (if a $-variable is not defined in some context) or produce * an absolute pathname, or that the smail_lib_dir be defined. */ addr->error = note_error(ERR_CONFERR|ERR_114, xprintf("director %s: %s not an absolute path", dp->name, priv->file)); addr->succ = *defer; *defer = addr; return SUCCEED; } DEBUG1(DBG_DRIVER_MID, "dtd_forwardfile: opening forward file %s\n", fn); fd = open(fn, 0); if (fd < 0) { xfree(fn); DEBUG1(DBG_DRIVER_HI, "dtd_forwardfile: no forward file for %s\n", addr->remainder); return FAIL; } /* find out how big the file is, malloc that much and read the file */ #ifdef lock_fd_rd_wait lock_fd_rd_wait(fd); #endif (void) fstat(fd, &statbuf); /* ignore non-files and ignore zero-length forward files */ if ((statbuf.st_mode & S_IFMT) != S_IFREG || statbuf.st_size == 0) { (void) close(fd); xfree(fn); return FAIL; } /* set security bits appropriately */ flags |= forward_secure(dp, addr, &statbuf, fn); if (dp->flags & FWD_FORWARDTO) { register FILE *f = fdopen(fd, "r"); register int c; STR_INIT(&fwdtobuf); while ((c = getc(f)) != EOF && c != '\n') { STR_NEXT(&fwdtobuf, c); } if (ferror(f)) { write_log(LOG_SYS, "read failed on %s, director=%s", fn, dp->name); (void) fclose(f); xfree(fn); STR_FREE(&fwdtobuf); return FAIL; } (void) fclose(f); STR_NEXT(&fwdtobuf, '\0'); if (strncmpic(fwdtobuf.p, "forward to ", sizeof("forward to ") - 1) != 0) { STR_FREE(&fwdtobuf); xfree(fn); return FAIL; } buf = fwdtobuf.p + sizeof("forward to ") - 1; } else { buf = xmalloc(statbuf.st_size + 1); /* leave room for trailing nul */ if (read(fd, buf, statbuf.st_size) < statbuf.st_size) { write_log(LOG_SYS, "read failed on %s, director=%s", fn, dp->name); (void) close(fd); xfree(buf); xfree(fn); return FAIL; } (void) close(fd); /* don't need the open file anymore */ buf[statbuf.st_size] = '\0'; /* terminate the buffer */ DEBUG1(DBG_DRIVER_HI, "read %d bytes\n", statbuf.st_size); } /* matched a forward file */ DEBUG2(DBG_DRIVER_LO, "director %s: matched %s", dp->name, addr->remainder); DEBUG1(DBG_DRIVER_HI, ", forwarded to %s", buf); DEBUG(DBG_DRIVER_LO, "\n"); addr->director = dp; /* * extract addr structures from the file contents, then * mark them all as coming from the forward director */ cur = NULL; error = NULL; (void) process_field((char *)NULL, buf, (char *)NULL, (char *)NULL, &cur, F_ALIAS, &error); if (error) { /* * ERR_115 - forward file parse error * * DESCRIPTION * process_field() found an error while parsing a forward file. * The specific error is stored in `error'. * * ACTIONS * Fail the address and send to the owner or to the postmaster. * * RESOLUTION * The postmaster or forward file owner should correct the * forward file. */ cur->error = note_error(ERR_NPOWNER|ERR_115, xprintf("director %s: error parsing %s: %s", dp->name, error)); cur->succ = *fail; *fail = cur; return SUCCEED; } if (dp->flags & FWD_FORWARDTO) { STR_FREE(&fwdtobuf); } else { xfree(buf); /* don't need buf anymore */ } for (; cur; cur = next) { next = cur->succ; cur->parent = addr; cur->flags = flags; cur->home = addr->home; cur->uid = addr->uid; cur->gid = addr->gid; cur->succ = *new; *new = cur; } DEBUG2(DBG_DRIVER_LO, "director <%s> matched <%s>\n", dp->name, addr->remainder); xfree(fn); return SUCCEED; } /* * forward_secure - determine if a forward file is secure * * return ADDR_UNSECURE in the event that the `unsecure' attribute * affects the forward file, unset ADDR_CAUTION if other constraints * do not state that the file is not secure. */ static int forward_secure(dp, addr, statp, fn) struct director *dp; /* source director */ struct addr *addr; /* source addr structure */ struct stat *statp; /* source of file data */ char *fn; /* source of filename data */ { register struct forwardfile_private *priv; priv = (struct forwardfile_private *)dp->private; /* `unsecure' has priority */ if (priv->unsecure) { char *temp = priv->unsecure; int found = FALSE; char *save_dir = rindex(fn, '/'); /* temp put a nul byte here */ *save_dir = '\0'; for (temp = strcolon(temp); temp; temp = strcolon((char *)NULL)) { if (secure_match(temp, addr->remainder, fn)) { found = TRUE; break; } } *save_dir = '/'; if (found) { return ADDR_UNSECURE; } } /* check the mode bits */ if (priv->modemask & statp->st_mode) { return ADDR_CAUTION; /* not secure */ } /* check the list of caution sources */ if (priv->caution) { char *temp = priv->caution; int found = FALSE; char *save_dir = rindex(fn, '/'); /* temp put a nul byte here */ /* pass the directory name to secure_match */ *save_dir = '\0'; for (temp = strcolon(temp); temp; temp = strcolon((char *)NULL)) { if (secure_match(temp, addr->remainder, fn)) { found = TRUE; break; } } *save_dir = '/'; if (found) { return ADDR_CAUTION; /* not secure */ } } if (priv->owners) { if ((dp->flags & FWD_CHECKOWNER) == 0 || addr->uid != statp->st_uid) { /* checkowner fails and owners attribute exists */ char *temp = priv->owners; int found = FALSE; /* check against passwd file uid */ for (temp = strcolon(temp); temp; temp = strcolon((char *)NULL)) { struct passwd *pw = getpwbyname(temp); if (pw == NULL) { write_log(LOG_SYS, "dtd_forwardfile: no such owner %s\n", temp); } else { if (statp->st_uid == pw->pw_uid) { found = TRUE; break; } } } if (!found) { return ADDR_CAUTION; /* not secure */ } } } else if (dp->flags & FWD_CHECKOWNER) { /* no owners attribute, so checkowner attribute is the final word */ if (addr->uid != statp->st_uid) { return ADDR_CAUTION; /* not secure */ } } if (priv->owngroups) { /* verify owning group */ char *temp = priv->owngroups; int found = FALSE; /* check against passwd file uid */ for (temp = strcolon(temp); temp; temp = strcolon((char *)NULL)) { struct group *gr = getgrbyname(temp); if (gr == NULL) { write_log(LOG_SYS, "dtd_forwardfile: no such owngroup %s\n", temp); } else { if (statp->st_gid == gr->gr_gid) { found = TRUE; break; } } } if (!found) { return ADDR_CAUTION; /* not secure */ } } return 0; /* secure by default */ } /* * secure_match - determine if a string matches a user or parent directory * * return TRUE if the given string matches the user associated with the * forward file, or the parent directory of the forward file. */ static int secure_match(string, user, dirn) char *string; /* string to be tested */ char *user; /* associated user name */ char *dirn; /* directory to verify against */ { char *p; char *delim; struct passwd *pw; if (string[0] == '~' || string[0] == '/') { /* hmm, looks sort of like a directory name, expand it */ char *dir = expand_string(string, (struct addr *)NULL, (char *)NULL, (char *)NULL); if (dir == NULL) { /* didn't expand, ignore */ return FALSE; } /* match if string matches directory */ return EQ(dir, dirn); } if (isdigit(string[0])) { for (p = string; *p; p++) { if (*p == '-') { if (delim) goto not_range; delim = p; continue; } if (! isdigit(*p)) goto not_range; } pw = getpwbyname(user); if (pw == NULL) return FALSE; if (delim) { if (atol(string) <= pw->pw_uid && pw->pw_uid <= atol(delim + 1)) return TRUE; } else { if (atol(string) == pw->pw_uid) return TRUE; } return FALSE; } not_range: /* check against the user */ return EQ(string, user); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.