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

Defined functions

allow_option defined in line 196; used 2 times
banners_option defined in line 216; used 2 times
chop_string defined in line 600; used 5 times
deny_option defined in line 207; used 2 times
get_field defined in line 557; used 3 times
group_option defined in line 245; used 3 times
keepalive_option defined in line 336; used 2 times
linger_option defined in line 314; used 2 times
nice_option defined in line 351; used 2 times
rfc931_option defined in line 401; used 2 times
setenv_option defined in line 420; used 2 times
severity_map defined in line 527; used 3 times
severity_option defined in line 544; used 2 times
spawn_option defined in line 302; used 2 times
twist_option defined in line 366; used 2 times
umask_option defined in line 286; used 2 times
user_option defined in line 264; used 2 times

Defined variables

dry_run defined in line 61; used 12 times
log_fac defined in line 442; used 1 times
log_sev defined in line 497; used 2 times
option_table defined in line 110; used 1 times
sccsid defined in line 32; never used
whitespace_eq defined in line 66; used 2 times

Defined struct's

option defined in line 91; used 4 times
syslog_names defined in line 437; used 8 times

Defined macros

EXPAND_ARG defined in line 100; used 4 times
MAXPATHNAMELEN defined in line 52; used 2 times
NEED_ARG defined in line 97; used 11 times
OPT_ARG defined in line 99; used 4 times
USE_LAST defined in line 98; used 4 times
expand_arg defined in line 106; used 1 times
maybe_dup2 defined in line 379; used 3 times
need_arg defined in line 102; used 1 times
opt_arg defined in line 103; never used
permit_arg defined in line 104; used 1 times
use_last defined in line 105; used 1 times
whitespace defined in line 67; used 3 times
Last modified: 1995-01-08
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4760
Valid CSS Valid XHTML 1.0 Strict