home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume36 / log_tcp / part03 / options.c < prev   
C/C++ Source or Header  |  1993-03-07  |  10KB  |  361 lines

  1.  /*
  2.   * General skeleton for adding options to the access control language. The
  3.   * Makefile describes how this alternative language is enabled. Shell
  4.   * commands will still be available, be it with a slightly different syntax.
  5.   * 
  6.   * The code uses a slightly different format of access control rules. It
  7.   * assumes that an access control rule looks like this:
  8.   * 
  9.   * daemon_list : client_list : option : option ...
  10.   * 
  11.   * An option is of the form "keyword" or "keyword = value". Option fields are
  12.   * processed from left to right. Blanks around keywords, "="  and values are
  13.   * optional. Blanks within values are left alone.
  14.   * 
  15.   * Diagnostics are reported through syslog(3).
  16.   * 
  17.   * Examples of options that are already implemented by the current skeleton:
  18.   * 
  19.   * user = nobody
  20.   * 
  21.   * Causes the process to switch its user id to that of "nobody". This normally
  22.   * requires root privilege.
  23.   * 
  24.   * group = tty
  25.   * 
  26.   * Causes the process to change its group id to that of the "tty" group. In
  27.   * order to switch both user and group ids you should normally switch the
  28.   * group id before switching the user id.
  29.   * 
  30.   * setenv = name value
  31.   * 
  32.   * places a name,value pair into the environment. The value is subjected to
  33.   * %<character> expansions.
  34.   * 
  35.   * spawn = (/usr/ucb/finger -l @%h | /usr/ucb/mail root) &
  36.   * 
  37.   * Executes (in a background child process) the shell command "finger -l @%h |
  38.   * mail root" after doing the %<character> expansions described in the
  39.   * hosts_access(5) manual page. The command is executed with stdin, stdout
  40.   * and stderr connected to the null device. Because options are processed in
  41.   * order, multiple spawn comands can be specified within the same access
  42.   * control rule, though "spawn = command1; command2" would be more
  43.   * efficient.
  44.   * 
  45.   * in.ftpd : ... : twist = /bin/echo 421 Some customized bounce message
  46.   * 
  47.   * Sends some custmized bounce message to the remote client instead of running
  48.   * the real ftp daemon. The command is subjected to %<character> expansion
  49.   * before execution by /bin/sh. Stdin, stdout and stderr are connected to the
  50.   * remote client process. The twist'ed command overlays the current process;
  51.   * it makes no sense to specify other options on the same line after a
  52.   * "twist". The "twist" option was inspired by Dan Bernstein's shuctl daemon
  53.   * wrapper control language.
  54.   * 
  55.   * umask = value
  56.   * 
  57.   * Sets the process file creation mask. Value must be an octal number.
  58.   * 
  59.   * If you compile with -DRFC_OPTION, code is enabled for the following option
  60.   * that does selective rfc931 lookups.
  61.   * 
  62.   * rfc931
  63.   * 
  64.   * Causes the daemon front ends to look up the remote user name with the RFC
  65.   * 931 protocol.
  66.   * 
  67.   * Warnings:
  68.   * 
  69.   * This module uses the non-reentrant strtok() library routine. The options
  70.   * argument to process_options() is destroyed.
  71.   * 
  72.   * There cannot be a ":" character in keywords or values. Backslash sequences
  73.   * are not yet recognized.
  74.   * 
  75.   * In case of UDP connections, do not "twist" commands that use the standard
  76.   * I/O or read(2)/write(2) routines to communicate with the client process;
  77.   * UDP requires other communications primitives.
  78.   * 
  79.   * In case of errors, use clean_exit() instead of directly calling exit(), or
  80.   * your inetd may loop on an UDP request.
  81.   */
  82.  
  83. /* System libraries. */
  84.  
  85. #include <sys/types.h>
  86. #include <sys/param.h>
  87. #include <sys/socket.h>
  88. #include <sys/stat.h>
  89. #include <netinet/in.h>
  90. #include <netdb.h>
  91. #include <stdio.h>
  92. #include <syslog.h>
  93. #include <pwd.h>
  94. #include <grp.h>
  95. #include <ctype.h>
  96.  
  97. extern char *strtok();
  98. extern char *strchr();
  99. extern void closelog();
  100.  
  101. /* Local stuff. */
  102.  
  103. #include "log_tcp.h"
  104.  
  105. /* List of functions that implement the options. Add yours here. */
  106.  
  107. static void user_option();        /* execute "user=name" option */
  108. static void group_option();        /* execute "group=name" option */
  109. static void umask_option();        /* execute "umask=mask" option */
  110. static void twist_option();        /* execute "twist=command" option */
  111. #ifdef RFC931_OPTION
  112. static void rfc931_option();        /* execute "rfc931" option */
  113. #endif
  114. static void setenv_option();        /* execute "setenv=name value" */
  115.  
  116. static char *chop_string();        /* strip leading and trailing blanks */
  117.  
  118. /* Structure of the options table. */
  119.  
  120. struct option {
  121.     char   *name;            /* keyword name, case does not matter */
  122.     int     need_value;            /* value required or not */
  123.     void    (*func) ();            /* function that does the real work */
  124. };
  125.  
  126. /* List of known keywords. Add yours here. */
  127.  
  128. static struct option option_table[] = {
  129.     "user", 1, user_option,        /* switch user id */
  130.     "group", 1, group_option,        /* switch group id */
  131.     "umask", 1, umask_option,        /* change umask */
  132.     "spawn", 1, shell_cmd,        /* spawn shell command */
  133.     "twist", 1, twist_option,        /* replace current process */
  134. #ifdef RFC931_OPTION
  135.     "rfc931", 0, rfc931_option,        /* do RFC 931 lookup */
  136. #endif
  137.     "setenv", 1, setenv_option,        /* update environment */
  138.     0,
  139. };
  140.  
  141. static char whitespace[] = " \t\r\n";
  142.  
  143. /* process_options - process optional access control information */
  144.  
  145. process_options(options, daemon, client)
  146. char   *options;
  147. char   *daemon;
  148. struct from_host *client;
  149. {
  150.     char   *key;
  151.     char   *value;
  152.     struct option *op;
  153.  
  154.     /*
  155.      * Light-weight parser. Remember, we may be running as root so we need
  156.      * code that is easy to comprehend.
  157.      */
  158.  
  159.     for (key = strtok(options, ":"); key; key = strtok((char *) 0, ":")) {
  160.     if (value = strchr(key, '=')) {        /* keyword=value */
  161.         *value++ = 0;
  162.         value = chop_string(value);        /* strip blanks around value */
  163.         if (*value == 0)
  164.         value = 0;            /* no value left */
  165.     }
  166.     key = chop_string(key);            /* strip blanks around key */
  167.     for (op = option_table; op->name; op++)    /* find keyword */
  168.         if (strcasecmp(op->name, key) == 0)
  169.         break;
  170.     if (op->name == 0) {
  171.         syslog(LOG_ERR, "bad option or syntax: \"%s\"", key);
  172.     } else if (value == 0 && op->need_value) {
  173.         syslog(LOG_ERR, "option \"%s\" requires value", key);
  174.     } else if (value && op->need_value == 0) {
  175.         syslog(LOG_ERR, "option \"%s\" requires no value", key);
  176.     } else {
  177.         (*(op->func)) (value, daemon, client);
  178.     }
  179.     }
  180. }
  181.  
  182. /* user_option - switch user id */
  183.  
  184. /* ARGSUSED */
  185.  
  186. static void user_option(value, daemon, client)
  187. char   *value;
  188. char   *daemon;
  189. struct from_host *client;
  190. {
  191.     struct passwd *pwd;
  192.     struct passwd *getpwnam();
  193.  
  194.     if ((pwd = getpwnam(value)) == 0) {
  195.     syslog(LOG_ERR, "unknown user: \"%s\"", value);
  196.     clean_exit(client);
  197.     } else if (setuid(pwd->pw_uid)) {
  198.     syslog(LOG_ERR, "setuid(%s): %m", value);
  199.     clean_exit(client);
  200.     }
  201. }
  202.  
  203. /* group_option - switch group id */
  204.  
  205. /* ARGSUSED */
  206.  
  207. static void group_option(value, daemon, client)
  208. char   *value;
  209. char   *daemon;
  210. struct from_host *client;
  211. {
  212.     struct group *grp;
  213.     struct group *getgrnam();
  214.  
  215.     if ((grp = getgrnam(value)) == 0) {
  216.     syslog(LOG_ERR, "unknown group: \"%s\"", value);
  217.     clean_exit(client);
  218.     } else if (setgid(grp->gr_gid)) {
  219.     syslog(LOG_ERR, "setgid(%s): %m", value);
  220.     clean_exit(client);
  221.     }
  222. }
  223.  
  224. /* umask_option - set file creation mask */
  225.  
  226. /* ARGSUSED */
  227.  
  228. static void umask_option(value, daemon, client)
  229. char   *value;
  230. char   *daemon;
  231. struct from_host *client;
  232. {
  233.     unsigned mask;
  234.     char    junk;
  235.  
  236.     if (sscanf(value, "%o%c", &mask, &junk) != 1 || (mask & 0777) != mask) {
  237.     syslog(LOG_ERR, "bad umask: \"%s\"", value);
  238.     clean_exit(client);
  239.     }
  240.     (void) umask(mask);
  241. }
  242.  
  243. /* twist_option - replace process by shell command */
  244.  
  245. static void twist_option(value, daemon, client)
  246. char   *value;
  247. char   *daemon;
  248. struct from_host *client;
  249. {
  250.     char    buf[BUFSIZ];
  251.     int     pid = getpid();
  252.     char   *error;
  253.  
  254.     percent_x(buf, sizeof(buf), value, daemon, client, pid);
  255.  
  256.     /* Since we will not be logging in the usual way, do it here and now. */
  257.  
  258.     syslog(SEVERITY, "twist from %s to %s", hosts_info(client), buf);
  259.     closelog();
  260.  
  261.     /*
  262.      * Before switching to the shell, set up stdout and stderr in case the
  263.      * Ultrix inetd didn't.
  264.      */
  265.  
  266.     (void) close(1);
  267.     (void) close(2);
  268.     if (dup(0) != 1 || dup(0) != 2) {
  269.     error = "dup: %m";
  270.     } else {
  271.     (void) execl("/bin/sh", "sh", "-c", buf, (char *) 0);
  272.     error = "/bin/sh: %m";
  273.     }
  274.  
  275.     /* Can get here only in case of errors. */
  276.  
  277. #ifdef LOG_MAIL
  278.     (void) openlog(daemon, LOG_PID, FACILITY);
  279. #else
  280.     (void) openlog(daemon, LOG_PID);
  281. #endif
  282.     syslog(LOG_ERR, error);
  283.     clean_exit(client);
  284. }
  285.  
  286. #ifdef RFC931_OPTION
  287.  
  288. /* rfc931_option - look up remote user name */
  289.  
  290. /* ARGSUSED */
  291.  
  292. static void rfc931_option(value, daemon, client)
  293. char   *value;
  294. char   *daemon;
  295. struct from_host *client;
  296. {
  297.     if (client->sock_type == FROM_CONNECTED) {
  298.     if (client->sin == 0) {
  299.         syslog(LOG_ERR, "no socket info for username lookup");
  300.     } else {
  301.         client->user = rfc931_name(client->sin);
  302.     }
  303.     }
  304. }
  305.  
  306. #endif
  307.  
  308. /* setenv_option - set environment variable */
  309.  
  310. /* ARGSUSED */
  311.  
  312. static void setenv_option(value, daemon, client)
  313. char   *value;
  314. char   *daemon;
  315. struct from_host *client;
  316. {
  317.     char   *var_name;
  318.     char   *var_value;
  319.     char    buf[BUFSIZ];
  320.     int     pid;
  321.  
  322.     /*
  323.      * What we get is one string with the name and the value separated by
  324.      * whitespace. Find the end of the name. If that is also the end of the
  325.      * string, the value is empty.
  326.      */
  327.  
  328.     var_value = value + strcspn(value, whitespace);
  329.  
  330.     if (*var_value == 0) {            /* just a name, that's all */
  331.     var_name = value;
  332.     } else {                    /* expand %stuff in value */
  333.     *var_value++ = 0;
  334.     var_name = chop_string(value);
  335.     pid = getpid();
  336.     percent_x(buf, sizeof(buf), var_value, daemon, client, pid);
  337.     var_value = chop_string(buf);
  338.     }
  339.     if (setenv(var_name, var_value, 1)) {
  340.     syslog(LOG_ERR, "memory allocation failure");
  341.     clean_exit(client);
  342.     }
  343. }
  344.  
  345. /* chop_string - strip leading and trailing blanks from string */
  346.  
  347. static char *chop_string(start)
  348. register char *start;
  349. {
  350.     register char *end;
  351.  
  352.     while (*start && isspace(*start))
  353.     start++;
  354.  
  355.     for (end = start + strlen(start); end > start && isspace(end[-1]); end--)
  356.      /* void */ ;
  357.     *end = 0;
  358.  
  359.     return (start);
  360. }
  361.