home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume39 / tcp_wrappers / part03 / options.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-29  |  16.6 KB  |  671 lines

  1.  /*
  2.   * General skeleton for adding options to the access control language. The
  3.   * features offered by this module are documented in the hosts_options(5)
  4.   * manual page (source file: hosts_options.5, "nroff -man" format).
  5.   * 
  6.   * Notes and warnings for those who want to add features:
  7.   * 
  8.   * In case of errors, abort options processing and deny access. There are too
  9.   * many irreversible side effects to make error recovery feasible. For example,
  10.   * it makes no sense to continue after we have already changed the userid.
  11.   * 
  12.   * In case of errors, do not terminate the process: the routines might be
  13.   * called from a long-running daemon that should run forever.
  14.   * 
  15.   * In case of fatal errors, use clean_exit() instead of directly calling
  16.   * exit(), or the inetd may loop on an UDP request.
  17.   * 
  18.   * In verification mode (for example, with the "try" command) the "dry_run"
  19.   * flag is set. In this mode, an option function should just "say" what it
  20.   * is going to do instead of really doing it.
  21.   * 
  22.   * Some option functions do not return (for example, the twist option passes
  23.   * control to another program). In verification mode (dry_run flag is set)
  24.   * such options should clear the "dry_run" flag to inform the caller of this
  25.   * course of action.
  26.   */
  27.  
  28. /* System libraries. */
  29.  
  30. #include <sys/types.h>
  31. #include <sys/param.h>
  32. #include <sys/socket.h>
  33. #include <sys/stat.h>
  34. #include <netinet/in.h>
  35. #include <netdb.h>
  36. #include <stdio.h>
  37. #include <syslog.h>
  38. #include <pwd.h>
  39. #include <grp.h>
  40. #include <ctype.h>
  41. #include <setjmp.h>
  42.  
  43. extern char *strchr();
  44. extern void closelog();
  45.  
  46. /* Local stuff. */
  47.  
  48. #include "log_tcp.h"
  49. #include "options.h"
  50.  
  51. int     dry_run = 0;            /* flag set in verification mode */
  52. jmp_buf options_buf;            /* quick way back to hosts_access() */
  53.  
  54. /* List of functions that implement the options. Add yours here. */
  55.  
  56. static void user_option();        /* execute "user=name" option */
  57. static void group_option();        /* execute "group=name" option */
  58. static void umask_option();        /* execute "umask=mask" option */
  59. static void linger_option();        /* execute "linger=time" option */
  60. static void spawn_option();        /* execute "spawn=command" option */
  61. static void twist_option();        /* execute "twist=command" option */
  62. static void rfc931_option();        /* execute "rfc931" option */
  63. static void setenv_option();        /* execute "setenv=name value" */
  64. static void severity_option();        /* execute "severity=value" */
  65. static void allow_option();        /* execute "allow" option */
  66. static void deny_option();        /* execute "deny" option */
  67.  
  68. static char *get_field();        /* chew :-delimited field off string */
  69. static char *chop_string();        /* strip leading and trailing blanks */
  70.  
  71. /* Structure of the options table. */
  72.  
  73. struct option {
  74.     char   *name;            /* keyword name, case is ignored */
  75.     void  (*func) ();            /* function that does the real work */
  76.     int     flags;            /* see below... */
  77. };
  78.  
  79. #define NEED_ARG    (1<<1)        /* option requires argument */
  80. #define USE_LAST    (1<<2)        /* option must be last */
  81.  
  82. #define need_arg(o)    ((o)->flags & NEED_ARG)
  83. #define use_last(o)    ((o)->flags & USE_LAST)
  84.  
  85. /* List of known keywords. Add yours here. */
  86.  
  87. static struct option option_table[] = {
  88.     "user", user_option, NEED_ARG,    /* switch user id */
  89.     "group", group_option, NEED_ARG,    /* switch group id */
  90.     "umask", umask_option, NEED_ARG,    /* change umask */
  91.     "linger", linger_option, NEED_ARG,    /* change socket linger time */
  92.     "spawn", spawn_option, NEED_ARG,    /* spawn shell command */
  93.     "twist", twist_option, NEED_ARG | USE_LAST,    /* replace current process */
  94.     "rfc931", rfc931_option, 0,        /* do RFC 931 lookup */
  95.     "setenv", setenv_option, NEED_ARG,    /* update environment */
  96.     "severity", severity_option, NEED_ARG,    /* adjust logging level */
  97.     "allow", allow_option, USE_LAST,    /* grant access */
  98.     "deny", deny_option, USE_LAST,    /* deny access */
  99.     0,
  100. };
  101.  
  102. static char whitespace[] = " \t\r\n";
  103.  
  104. /* process_options - process access control options */
  105.  
  106. void    process_options(options, daemon, client)
  107. char   *options;
  108. char   *daemon;
  109. struct client_info *client;
  110. {
  111.     char   *key;
  112.     char   *value;
  113.     char   *curr_opt;
  114.     char   *next_opt;
  115.     struct option *op;
  116.  
  117.     /*
  118.      * Light-weight parser. Being easy to comprehend is more important than
  119.      * being smart.
  120.      */
  121.  
  122.     for (curr_opt = get_field(options); curr_opt; curr_opt = next_opt) {
  123.     next_opt = get_field((char *) 0);
  124.  
  125.     /*
  126.      * Separate the option into name and value parts.
  127.      */
  128.  
  129.     if (value = strchr(curr_opt, '=')) {    /* name=value */
  130.         *value++ = 0;
  131.         value = chop_string(value);        /* strip blanks around value */
  132.         if (*value == 0)
  133.         value = 0;            /* no value left */
  134.     }
  135.     key = chop_string(curr_opt);        /* strip blanks around key */
  136.  
  137.     /*
  138.      * Disallow missing option names (and empty option fields).
  139.      */
  140.  
  141.     if (*key == 0) {
  142.         syslog(LOG_ERR, "error: %s, line %d: missing option name",
  143.            hosts_access_file, hosts_access_line);
  144.         longjmp(options_buf, OPT_DENY);
  145.     }
  146.  
  147.     /*
  148.      * Lookup the option-specific info and do some common error checks.
  149.      * Delegate option-specific processing to the specific fuctions.
  150.      */
  151.  
  152.     for (op = option_table; op->name; op++)    /* find keyword */
  153.         if (strcasecmp(op->name, key) == 0)
  154.         break;
  155.     if (op->name == 0) {
  156.         syslog(LOG_ERR, "error: %s, line %d: bad option name or syntax: \"%s\"",
  157.            hosts_access_file, hosts_access_line, key);
  158.         longjmp(options_buf, OPT_DENY);
  159.     } else if (!value && need_arg(op)) {
  160.         syslog(LOG_ERR, "error: %s, line %d: option \"%s\" requires value",
  161.            hosts_access_file, hosts_access_line, key);
  162.         longjmp(options_buf, OPT_DENY);
  163.     } else if (value && !need_arg(op)) {
  164.         syslog(LOG_ERR, "error: %s, line %d: option \"%s\" cannot have value",
  165.            hosts_access_file, hosts_access_line, key);
  166.         longjmp(options_buf, OPT_DENY);
  167.     } else if (next_opt && use_last(op)) {
  168.         syslog(LOG_ERR, "error: %s, line %d: option \"%s\" must be last option",
  169.            hosts_access_file, hosts_access_line, key);
  170.         longjmp(options_buf, OPT_DENY);
  171.     } else {
  172.         (*(op->func)) (value, daemon, client);
  173.     }
  174.     }
  175. }
  176.  
  177. /* allow_option - grant access */
  178.  
  179. /* ARGSUSED */
  180.  
  181. static void allow_option(value, daemon, client)
  182. char   *value;
  183. char   *daemon;
  184. struct client_info *client;
  185. {
  186.     if (dry_run)
  187.     syslog(LOG_DEBUG, "option: allow");
  188.     longjmp(options_buf, OPT_ALLOW);
  189. }
  190.  
  191. /* deny_option - deny access */
  192.  
  193. /* ARGSUSED */
  194.  
  195. static void deny_option(value, daemon, client)
  196. char   *value;
  197. char   *daemon;
  198. struct client_info *client;
  199. {
  200.     if (dry_run)
  201.     syslog(LOG_DEBUG, "option: deny");
  202.     longjmp(options_buf, OPT_DENY);
  203. }
  204.  
  205. /* user_option - switch user id */
  206.  
  207. /* ARGSUSED */
  208.  
  209. static void user_option(value, daemon, client)
  210. char   *value;
  211. char   *daemon;
  212. struct client_info *client;
  213. {
  214.     struct passwd *pwd;
  215.     struct passwd *getpwnam();
  216.  
  217.     if ((pwd = getpwnam(value)) == 0) {
  218.     syslog(LOG_ERR, "error: %s, line %d: unknown user: \"%s\"",
  219.            hosts_access_file, hosts_access_line, value);
  220.     longjmp(options_buf, OPT_DENY);
  221.     }
  222.     endpwent();
  223.  
  224.     if (dry_run) {
  225.     syslog(LOG_DEBUG, "option: user = %s", value);
  226.     return;
  227.     }
  228.     if (setuid(pwd->pw_uid)) {
  229.     syslog(LOG_ERR, "error: %s, line %d: setuid(%s): %m",
  230.            hosts_access_file, hosts_access_line, value);
  231.     longjmp(options_buf, OPT_DENY);
  232.     }
  233. }
  234.  
  235. /* group_option - switch group id */
  236.  
  237. /* ARGSUSED */
  238.  
  239. static void group_option(value, daemon, client)
  240. char   *value;
  241. char   *daemon;
  242. struct client_info *client;
  243. {
  244.     struct group *grp;
  245.     struct group *getgrnam();
  246.  
  247.     if ((grp = getgrnam(value)) == 0) {
  248.     syslog(LOG_ERR, "error: %s, line %d: unknown group: \"%s\"",
  249.            hosts_access_file, hosts_access_line, value);
  250.     longjmp(options_buf, OPT_DENY);
  251.     }
  252.     endgrent();
  253.  
  254.     if (dry_run) {
  255.     syslog(LOG_DEBUG, "option: group = %s", value);
  256.     return;
  257.     }
  258.     if (setgid(grp->gr_gid)) {
  259.     syslog(LOG_ERR, "error: %s, line %d: setgid(%s): %m",
  260.            hosts_access_file, hosts_access_line, value);
  261.     longjmp(options_buf, OPT_DENY);
  262.     }
  263. }
  264.  
  265. /* umask_option - set file creation mask */
  266.  
  267. /* ARGSUSED */
  268.  
  269. static void umask_option(value, daemon, client)
  270. char   *value;
  271. char   *daemon;
  272. struct client_info *client;
  273. {
  274.     unsigned mask;
  275.     char    junk;
  276.  
  277.     if (sscanf(value, "%o%c", &mask, &junk) != 1 || (mask & 0777) != mask) {
  278.     syslog(LOG_ERR, "error: %s, line %d: bad umask value: \"%s\"",
  279.            hosts_access_file, hosts_access_line, value);
  280.     longjmp(options_buf, OPT_DENY);
  281.     }
  282.     if (dry_run) {
  283.     syslog(LOG_DEBUG, "option: umask = %o", mask);
  284.     return;
  285.     }
  286.     (void) umask(mask);
  287. }
  288.  
  289. /* spawn_option - spawn a shell command and wait */
  290.  
  291. static void spawn_option(value, daemon, client)
  292. char   *value;
  293. char   *daemon;
  294. struct client_info *client;
  295. {
  296.     char    buf[BUFSIZ];
  297.     int     pid = getpid();
  298.  
  299.     if (dry_run) {
  300.     percent_x(buf, sizeof(buf), value, daemon, client, pid);
  301.     syslog(LOG_DEBUG, "option: spawn = %s", buf);
  302.     return;
  303.     }
  304.     shell_cmd(value, daemon, client);
  305. }
  306.  
  307. /* linger_option - set the socket linger time (Marc Boucher <marc@cam.org>) */
  308.  
  309. /* ARGSUSED */
  310.  
  311. static void linger_option(value, daemon, client)
  312. char   *value;
  313. char   *daemon;
  314. struct client_info *client;
  315. {
  316. #if defined(SO_LINGER) && !defined(BROKEN_SO_LINGER)    /* broken linux */
  317.     struct linger linger;
  318.     char    junk;
  319.  
  320.     if (sscanf(value, "%d%c", &linger.l_linger, &junk) != 1
  321.     || linger.l_linger < 0) {
  322.     syslog(LOG_ERR, "error: %s, line %d: bad linger value: \"%s\"",
  323.            hosts_access_file, hosts_access_line, value);
  324.     longjmp(options_buf, OPT_DENY);
  325.     }
  326.     if (dry_run) {
  327.     syslog(LOG_DEBUG, "option: linger = %d", linger.l_linger);
  328.     return;
  329.     }
  330.     linger.l_onoff = (linger.l_linger != 0);
  331.     if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *) &linger, sizeof(linger))
  332.     < 0) {
  333.     syslog(LOG_ERR, "error: %s, line %d: setsockopt SO_LINGER %d: %m",
  334.            hosts_access_file, hosts_access_line, linger.l_linger);
  335.     longjmp(options_buf, OPT_DENY);
  336.     }
  337. #else
  338.     syslog(LOG_ERR, "error: %s, line %d: SO_LINGER not supported",
  339.        hosts_access_file, hosts_access_line);
  340.     longjmp(options_buf, OPT_DENY);
  341. #endif
  342. }
  343.  
  344. /* twist_option - replace process by shell command */
  345.  
  346. static void twist_option(value, daemon, client)
  347. char   *value;
  348. char   *daemon;
  349. struct client_info *client;
  350. {
  351.     char    buf[BUFSIZ];
  352.     int     pid = getpid();
  353.     char   *error;
  354.  
  355.     percent_x(buf, sizeof(buf), value, daemon, client, pid);
  356.  
  357.     if (dry_run) {
  358.     syslog(LOG_DEBUG, "option: twist = %s", buf);
  359.     dry_run = 0;
  360.     return;
  361.     }
  362.     syslog(deny_severity, "twist %s to %s", hosts_info(client), buf);
  363.     closelog();
  364.  
  365.     /*
  366.      * Before switching to the shell, set up stdout and stderr in case the
  367.      * Ultrix inetd didn't.
  368.      */
  369.  
  370.     (void) close(1);
  371.     (void) close(2);
  372.     if (dup(0) != 1 || dup(0) != 2) {
  373.     error = "twist_option: dup: %m";
  374.     } else {
  375.     (void) execl("/bin/sh", "sh", "-c", buf, (char *) 0);
  376.     error = "twist_option: /bin/sh: %m";
  377.     }
  378.  
  379.     /* Can get here only in case of errors. */
  380.  
  381. #ifdef LOG_MAIL
  382.     (void) openlog(daemon, LOG_PID, FACILITY);
  383. #else
  384.     (void) openlog(daemon, LOG_PID);
  385. #endif
  386.     syslog(LOG_ERR, error);
  387.  
  388.     /* We MUST terminate the process. */
  389.  
  390.     clean_exit(client);
  391. }
  392.  
  393. /* rfc931_option - look up remote user name */
  394.  
  395. /* ARGSUSED */
  396.  
  397. static void rfc931_option(value, daemon, client)
  398. char   *value;
  399. char   *daemon;
  400. struct client_info *client;
  401. {
  402.     if (dry_run) {
  403.     syslog(LOG_DEBUG, "option: rfc931");
  404.     return;
  405.     }
  406.     if (client->user[0] == 0 && RFC931_POSSIBLE(client))
  407.     client->user = rfc931(client->rmt_sin, client->our_sin);
  408. }
  409.  
  410. /* setenv_option - set environment variable */
  411.  
  412. /* ARGSUSED */
  413.  
  414. static void setenv_option(value, daemon, client)
  415. char   *value;
  416. char   *daemon;
  417. struct client_info *client;
  418. {
  419.     char   *var_name;
  420.     char   *var_value;
  421.     char    buf[BUFSIZ];
  422.     int     pid;
  423.  
  424.     /*
  425.      * Separate the argument into a name and value part.
  426.      */
  427.  
  428.     var_value = value + strcspn(value, whitespace);
  429.  
  430.     if (*var_value == 0) {            /* just a name, that's all */
  431.     var_name = value;
  432.     } else {                    /* expand %stuff in value */
  433.     *var_value++ = 0;
  434.     var_name = chop_string(value);
  435.     pid = getpid();
  436.     percent_x(buf, sizeof(buf), var_value, daemon, client, pid);
  437.     var_value = chop_string(buf);
  438.     }
  439.     if (dry_run) {
  440.     syslog(LOG_DEBUG, "option: setenv = %s %s", var_name, var_value);
  441.     return;
  442.     }
  443.     if (setenv(var_name, var_value, 1)) {
  444.     syslog(LOG_ERR, "setenv_option: memory allocation failure");
  445.     longjmp(options_buf, OPT_DENY);
  446.     }
  447. }
  448.  
  449. /* get_field - return pointer to next field in string */
  450.  
  451. static char *get_field(string)
  452. char   *string;
  453. {
  454.     static char *last = "";
  455.     char   *src;
  456.     char   *dst;
  457.     char   *ret;
  458.  
  459.     /*
  460.      * This function returns pointers to successive fields within a given
  461.      * string. ":" is the field separator; warn if the rule ends in one. It
  462.      * replaces a "\:" sequence by ":", without treating the result of
  463.      * substitution as field terminator. A null argument means resume search
  464.      * where the previous call terminated. This function destroys its
  465.      * argument.
  466.      */
  467.  
  468.     /*
  469.      * Work from explicit source or from memory.
  470.      */
  471.  
  472.     if (string == 0)
  473.     string = last;
  474.     if (string[0] == 0)
  475.     return (0);
  476.  
  477.     /*
  478.      * While processing \: we overwrite the input. This way we do not have to
  479.      * maintain buffers for copies of input fields.
  480.      */
  481.  
  482.     src = dst = ret = string;
  483.  
  484.     for (;;) {
  485.     switch (*src) {
  486.     case '\\':                /* handle escape */
  487.         switch (src[1]) {
  488.         case ':':                /* convert \: to : */
  489.         src++;
  490.         /* FALLTHROUGH */
  491.         case '\0':                /* don't run off end */
  492.         *dst++ = *src++;
  493.         break;
  494.         default:                /* copy \other verbatim */
  495.         *dst++ = *src++, *dst++ = *src++;
  496.         break;
  497.         }
  498.         break;
  499.     case ':':                /* field separator */
  500.         src++;
  501.         if (*src == 0)
  502.         syslog(LOG_WARNING, "warning: %s, line %d: rule ends in \":\"",
  503.                hosts_access_file, hosts_access_line);
  504.         /* FALLTHROUGH */
  505.     case '\0':                /* end of string */
  506.         last = src;
  507.         *dst = 0;
  508.         return (ret);
  509.     default:                /* anything else */
  510.         *dst++ = *src++;
  511.         break;
  512.     }
  513.     }
  514. }
  515.  
  516. /* chop_string - strip leading and trailing blanks from string */
  517.  
  518. static char *chop_string(start)
  519. register char *start;
  520. {
  521.     register char *end;
  522.  
  523.     while (*start && isspace(*start))
  524.     start++;
  525.  
  526.     for (end = start + strlen(start); end > start && isspace(end[-1]); end--)
  527.      /* void */ ;
  528.     *end = 0;
  529.  
  530.     return (start);
  531. }
  532.  
  533.  /*
  534.   * The severity option goes last because it comes with a huge amount of ugly
  535.   * #ifdefs and tables.
  536.   */
  537.  
  538. struct syslog_names {
  539.     char   *name;
  540.     int     value;
  541. };
  542.  
  543. static struct syslog_names log_facilities[] = {
  544. #ifdef LOG_KERN
  545.     "kern", LOG_KERN,
  546. #endif
  547. #ifdef LOG_USER
  548.     "user", LOG_USER,
  549. #endif
  550. #ifdef LOG_MAIL
  551.     "mail", LOG_MAIL,
  552. #endif
  553. #ifdef LOG_DAEMON
  554.     "daemon", LOG_DAEMON,
  555. #endif
  556. #ifdef LOG_AUTH
  557.     "auth", LOG_AUTH,
  558. #endif
  559. #ifdef LOG_LPR
  560.     "lpr", LOG_LPR,
  561. #endif
  562. #ifdef LOG_NEWS
  563.     "news", LOG_NEWS,
  564. #endif
  565. #ifdef LOG_UUCP
  566.     "uucp", LOG_UUCP,
  567. #endif
  568. #ifdef LOG_CRON
  569.     "cron", LOG_CRON,
  570. #endif
  571. #ifdef LOG_LOCAL0
  572.     "local0", LOG_LOCAL0,
  573. #endif
  574. #ifdef LOG_LOCAL1
  575.     "local1", LOG_LOCAL1,
  576. #endif
  577. #ifdef LOG_LOCAL2
  578.     "local2", LOG_LOCAL2,
  579. #endif
  580. #ifdef LOG_LOCAL3
  581.     "local3", LOG_LOCAL3,
  582. #endif
  583. #ifdef LOG_LOCAL4
  584.     "local4", LOG_LOCAL4,
  585. #endif
  586. #ifdef LOG_LOCAL5
  587.     "local5", LOG_LOCAL5,
  588. #endif
  589. #ifdef LOG_LOCAL6
  590.     "local6", LOG_LOCAL6,
  591. #endif
  592. #ifdef LOG_LOCAL7
  593.     "local7", LOG_LOCAL7,
  594. #endif
  595.     0,
  596. };
  597.  
  598. static struct syslog_names log_severities[] = {
  599. #ifdef LOG_EMERG
  600.     "emerg", LOG_EMERG,
  601. #endif
  602. #ifdef LOG_ALERT
  603.     "alert", LOG_ALERT,
  604. #endif
  605. #ifdef LOG_CRIT
  606.     "crit", LOG_CRIT,
  607. #endif
  608. #ifdef LOG_ERR
  609.     "err", LOG_ERR,
  610. #endif
  611. #ifdef LOG_WARNING
  612.     "warning", LOG_WARNING,
  613. #endif
  614. #ifdef LOG_NOTICE
  615.     "notice", LOG_NOTICE,
  616. #endif
  617. #ifdef LOG_INFO
  618.     "info", LOG_INFO,
  619. #endif
  620. #ifdef LOG_DEBUG
  621.     "debug", LOG_DEBUG,
  622. #endif
  623.     0,
  624. };
  625.  
  626. /* severity_map - lookup facility or severity value */
  627.  
  628. static int severity_map(table, name)
  629. struct syslog_names *table;
  630. char   *name;
  631. {
  632.     struct syslog_names *t;
  633.  
  634.     for (t = table; t->name; t++) {
  635.     if (strcasecmp(t->name, name) == 0)
  636.         return (t->value);
  637.     }
  638.     syslog(LOG_ERR,
  639.        "error: %s, line %d: bad syslog facility or severity: \"%s\"",
  640.        hosts_access_file, hosts_access_line, name);
  641.     longjmp(options_buf, OPT_DENY);
  642.     /* NOTREACHED */
  643. }
  644.  
  645. /* severity_option - change logging severity for this event (Dave Mitchell) */
  646.  
  647. /* ARGSUSED */
  648.  
  649. static void severity_option(value, daemon, client)
  650. char   *value;
  651. char   *daemon;
  652. struct client_info *client;
  653. {
  654.     int     new_severity;
  655.     char   *dot;
  656.  
  657.     if (dot = strchr(value, '.')) {        /* facility.level */
  658.     *dot = 0;
  659.     new_severity = severity_map(log_facilities, chop_string(value))
  660.         | severity_map(log_severities, chop_string(dot + 1));
  661.     *dot = '.';
  662.     } else {                    /* no facility, just level */
  663.     new_severity = severity_map(log_severities, chop_string(value));
  664.     }
  665.     if (dry_run) {
  666.     syslog(LOG_DEBUG, "option: severity = %s", value);
  667.     return;
  668.     }
  669.     allow_severity = deny_severity = new_severity;
  670. }
  671.