xref: /illumos-gate/usr/src/lib/libwrap/options.c (revision 1da57d55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate  /*
77c478bd9Sstevel@tonic-gate   * General skeleton for adding options to the access control language. The
87c478bd9Sstevel@tonic-gate   * features offered by this module are documented in the hosts_options(5)
97c478bd9Sstevel@tonic-gate   * manual page (source file: hosts_options.5, "nroff -man" format).
10*1da57d55SToomas Soome   *
117c478bd9Sstevel@tonic-gate   * Notes and warnings for those who want to add features:
12*1da57d55SToomas Soome   *
137c478bd9Sstevel@tonic-gate   * In case of errors, abort options processing and deny access. There are too
147c478bd9Sstevel@tonic-gate   * many irreversible side effects to make error recovery feasible. For
157c478bd9Sstevel@tonic-gate   * example, it makes no sense to continue after we have already changed the
167c478bd9Sstevel@tonic-gate   * userid.
17*1da57d55SToomas Soome   *
187c478bd9Sstevel@tonic-gate   * In case of errors, do not terminate the process: the routines might be
197c478bd9Sstevel@tonic-gate   * called from a long-running daemon that should run forever. Instead, call
207c478bd9Sstevel@tonic-gate   * tcpd_jump() which does a non-local goto back into the hosts_access()
217c478bd9Sstevel@tonic-gate   * routine.
22*1da57d55SToomas Soome   *
237c478bd9Sstevel@tonic-gate   * In case of severe errors, use clean_exit() instead of directly calling
247c478bd9Sstevel@tonic-gate   * exit(), or the inetd may loop on an UDP request.
25*1da57d55SToomas Soome   *
267c478bd9Sstevel@tonic-gate   * In verification mode (for example, with the "tcpdmatch" command) the
277c478bd9Sstevel@tonic-gate   * "dry_run" flag is set. In this mode, an option function should just "say"
287c478bd9Sstevel@tonic-gate   * what it is going to do instead of really doing it.
29*1da57d55SToomas Soome   *
307c478bd9Sstevel@tonic-gate   * Some option functions do not return (for example, the twist option passes
317c478bd9Sstevel@tonic-gate   * control to another program). In verification mode (dry_run flag is set)
327c478bd9Sstevel@tonic-gate   * such options should clear the "dry_run" flag to inform the caller of this
337c478bd9Sstevel@tonic-gate   * course of action.
347c478bd9Sstevel@tonic-gate   */
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate #ifndef lint
377c478bd9Sstevel@tonic-gate static char sccsid[] = "@(#) options.c 1.17 96/02/11 17:01:31";
387c478bd9Sstevel@tonic-gate #endif
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate /* System libraries. */
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate #include <sys/types.h>
437c478bd9Sstevel@tonic-gate #include <sys/param.h>
447c478bd9Sstevel@tonic-gate #include <sys/socket.h>
457c478bd9Sstevel@tonic-gate #include <sys/stat.h>
467c478bd9Sstevel@tonic-gate #include <netinet/in.h>
477c478bd9Sstevel@tonic-gate #include <netdb.h>
487c478bd9Sstevel@tonic-gate #include <stdio.h>
497c478bd9Sstevel@tonic-gate #include <stdlib.h>
507c478bd9Sstevel@tonic-gate #include <unistd.h>
517c478bd9Sstevel@tonic-gate #include <syslog.h>
527c478bd9Sstevel@tonic-gate #include <pwd.h>
537c478bd9Sstevel@tonic-gate #include <grp.h>
547c478bd9Sstevel@tonic-gate #include <ctype.h>
557c478bd9Sstevel@tonic-gate #include <setjmp.h>
567c478bd9Sstevel@tonic-gate #include <string.h>
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate #ifndef MAXPATHNAMELEN
597c478bd9Sstevel@tonic-gate #define MAXPATHNAMELEN  BUFSIZ
607c478bd9Sstevel@tonic-gate #endif
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate /* Local stuff. */
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate #include "tcpd.h"
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate /* Options runtime support. */
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate int     dry_run = 0;			/* flag set in verification mode */
697c478bd9Sstevel@tonic-gate extern jmp_buf tcpd_buf;		/* tcpd_jump() support */
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate /* Options parser support. */
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate static char whitespace_eq[] = "= \t\r\n";
747c478bd9Sstevel@tonic-gate #define whitespace (whitespace_eq + 1)
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate static char *get_field();		/* chew :-delimited field off string */
777c478bd9Sstevel@tonic-gate static char *chop_string();		/* strip leading and trailing blanks */
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /* List of functions that implement the options. Add yours here. */
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate static void user_option();		/* execute "user name.group" option */
827c478bd9Sstevel@tonic-gate static void group_option();		/* execute "group name" option */
837c478bd9Sstevel@tonic-gate static void umask_option();		/* execute "umask mask" option */
847c478bd9Sstevel@tonic-gate static void linger_option();		/* execute "linger time" option */
857c478bd9Sstevel@tonic-gate static void keepalive_option();		/* execute "keepalive" option */
867c478bd9Sstevel@tonic-gate static void spawn_option();		/* execute "spawn command" option */
877c478bd9Sstevel@tonic-gate static void twist_option();		/* execute "twist command" option */
887c478bd9Sstevel@tonic-gate static void rfc931_option();		/* execute "rfc931" option */
897c478bd9Sstevel@tonic-gate static void setenv_option();		/* execute "setenv name value" */
907c478bd9Sstevel@tonic-gate static void nice_option();		/* execute "nice" option */
917c478bd9Sstevel@tonic-gate static void severity_option();		/* execute "severity value" */
927c478bd9Sstevel@tonic-gate static void allow_option();		/* execute "allow" option */
937c478bd9Sstevel@tonic-gate static void deny_option();		/* execute "deny" option */
947c478bd9Sstevel@tonic-gate static void banners_option();		/* execute "banners path" option */
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate /* Structure of the options table. */
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate struct option {
997c478bd9Sstevel@tonic-gate     char   *name;			/* keyword name, case is ignored */
1007c478bd9Sstevel@tonic-gate     void  (*func) ();			/* function that does the real work */
1017c478bd9Sstevel@tonic-gate     int     flags;			/* see below... */
1027c478bd9Sstevel@tonic-gate };
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate #define NEED_ARG	(1<<1)		/* option requires argument */
1057c478bd9Sstevel@tonic-gate #define USE_LAST	(1<<2)		/* option must be last */
1067c478bd9Sstevel@tonic-gate #define OPT_ARG		(1<<3)		/* option has optional argument */
1077c478bd9Sstevel@tonic-gate #define EXPAND_ARG	(1<<4)		/* do %x expansion on argument */
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate #define need_arg(o)	((o)->flags & NEED_ARG)
1107c478bd9Sstevel@tonic-gate #define opt_arg(o)	((o)->flags & OPT_ARG)
1117c478bd9Sstevel@tonic-gate #define permit_arg(o)	((o)->flags & (NEED_ARG | OPT_ARG))
1127c478bd9Sstevel@tonic-gate #define use_last(o)	((o)->flags & USE_LAST)
1137c478bd9Sstevel@tonic-gate #define expand_arg(o)	((o)->flags & EXPAND_ARG)
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate /* List of known keywords. Add yours here. */
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate static struct option option_table[] = {
1187c478bd9Sstevel@tonic-gate     "user", user_option, NEED_ARG,
1197c478bd9Sstevel@tonic-gate     "group", group_option, NEED_ARG,
1207c478bd9Sstevel@tonic-gate     "umask", umask_option, NEED_ARG,
1217c478bd9Sstevel@tonic-gate     "linger", linger_option, NEED_ARG,
1227c478bd9Sstevel@tonic-gate     "keepalive", keepalive_option, 0,
1237c478bd9Sstevel@tonic-gate     "spawn", spawn_option, NEED_ARG | EXPAND_ARG,
1247c478bd9Sstevel@tonic-gate     "twist", twist_option, NEED_ARG | EXPAND_ARG | USE_LAST,
1257c478bd9Sstevel@tonic-gate     "rfc931", rfc931_option, OPT_ARG,
1267c478bd9Sstevel@tonic-gate     "setenv", setenv_option, NEED_ARG | EXPAND_ARG,
1277c478bd9Sstevel@tonic-gate     "nice", nice_option, OPT_ARG,
1287c478bd9Sstevel@tonic-gate     "severity", severity_option, NEED_ARG,
1297c478bd9Sstevel@tonic-gate     "allow", allow_option, USE_LAST,
1307c478bd9Sstevel@tonic-gate     "deny", deny_option, USE_LAST,
1317c478bd9Sstevel@tonic-gate     "banners", banners_option, NEED_ARG,
1327c478bd9Sstevel@tonic-gate     0,
1337c478bd9Sstevel@tonic-gate };
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate /* process_options - process access control options */
1367c478bd9Sstevel@tonic-gate 
process_options(options,request)1377c478bd9Sstevel@tonic-gate void    process_options(options, request)
1387c478bd9Sstevel@tonic-gate char   *options;
1397c478bd9Sstevel@tonic-gate struct request_info *request;
1407c478bd9Sstevel@tonic-gate {
1417c478bd9Sstevel@tonic-gate     char   *key;
1427c478bd9Sstevel@tonic-gate     char   *value;
1437c478bd9Sstevel@tonic-gate     char   *curr_opt;
1447c478bd9Sstevel@tonic-gate     char   *next_opt;
1457c478bd9Sstevel@tonic-gate     struct option *op;
1467c478bd9Sstevel@tonic-gate     char    bf[BUFSIZ];
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate     for (curr_opt = get_field(options); curr_opt; curr_opt = next_opt) {
1497c478bd9Sstevel@tonic-gate 	next_opt = get_field((char *) 0);
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 	/*
1527c478bd9Sstevel@tonic-gate 	 * Separate the option into name and value parts. For backwards
1537c478bd9Sstevel@tonic-gate 	 * compatibility we ignore exactly one '=' between name and value.
1547c478bd9Sstevel@tonic-gate 	 */
1557c478bd9Sstevel@tonic-gate 	curr_opt = chop_string(curr_opt);
1567c478bd9Sstevel@tonic-gate 	if (*(value = curr_opt + strcspn(curr_opt, whitespace_eq))) {
1577c478bd9Sstevel@tonic-gate 	    if (*value != '=') {
1587c478bd9Sstevel@tonic-gate 		*value++ = 0;
1597c478bd9Sstevel@tonic-gate 		value += strspn(value, whitespace);
1607c478bd9Sstevel@tonic-gate 	    }
1617c478bd9Sstevel@tonic-gate 	    if (*value == '=') {
1627c478bd9Sstevel@tonic-gate 		*value++ = 0;
1637c478bd9Sstevel@tonic-gate 		value += strspn(value, whitespace);
1647c478bd9Sstevel@tonic-gate 	    }
1657c478bd9Sstevel@tonic-gate 	}
1667c478bd9Sstevel@tonic-gate 	if (*value == 0)
1677c478bd9Sstevel@tonic-gate 	    value = 0;
1687c478bd9Sstevel@tonic-gate 	key = curr_opt;
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	/*
1717c478bd9Sstevel@tonic-gate 	 * Disallow missing option names (and empty option fields).
1727c478bd9Sstevel@tonic-gate 	 */
1737c478bd9Sstevel@tonic-gate 	if (*key == 0)
1747c478bd9Sstevel@tonic-gate 	    tcpd_jump("missing option name");
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	/*
1777c478bd9Sstevel@tonic-gate 	 * Lookup the option-specific info and do some common error checks.
1787c478bd9Sstevel@tonic-gate 	 * Delegate option-specific processing to the specific functions.
1797c478bd9Sstevel@tonic-gate 	 */
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	for (op = option_table; op->name && STR_NE(op->name, key); op++)
1827c478bd9Sstevel@tonic-gate 	     /* VOID */ ;
1837c478bd9Sstevel@tonic-gate 	if (op->name == 0)
1847c478bd9Sstevel@tonic-gate 	    tcpd_jump("bad option name: \"%s\"", key);
1857c478bd9Sstevel@tonic-gate 	if (!value && need_arg(op))
1867c478bd9Sstevel@tonic-gate 	    tcpd_jump("option \"%s\" requires value", key);
1877c478bd9Sstevel@tonic-gate 	if (value && !permit_arg(op))
1887c478bd9Sstevel@tonic-gate 	    tcpd_jump("option \"%s\" requires no value", key);
1897c478bd9Sstevel@tonic-gate 	if (next_opt && use_last(op))
1907c478bd9Sstevel@tonic-gate 	    tcpd_jump("option \"%s\" must be at end", key);
1917c478bd9Sstevel@tonic-gate 	if (value && expand_arg(op))
1927c478bd9Sstevel@tonic-gate 	    value = chop_string(percent_x(bf, sizeof(bf), value, request));
1937c478bd9Sstevel@tonic-gate 	if (hosts_access_verbose)
1947c478bd9Sstevel@tonic-gate 	    syslog(LOG_DEBUG, "option:   %s %s", key, value ? value : "");
1957c478bd9Sstevel@tonic-gate 	(*(op->func)) (value, request);
1967c478bd9Sstevel@tonic-gate     }
1977c478bd9Sstevel@tonic-gate }
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate /* allow_option - grant access */
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate /* ARGSUSED */
2027c478bd9Sstevel@tonic-gate 
allow_option(value,request)2037c478bd9Sstevel@tonic-gate static void allow_option(value, request)
2047c478bd9Sstevel@tonic-gate char   *value;
2057c478bd9Sstevel@tonic-gate struct request_info *request;
2067c478bd9Sstevel@tonic-gate {
2077c478bd9Sstevel@tonic-gate     longjmp(tcpd_buf, AC_PERMIT);
2087c478bd9Sstevel@tonic-gate }
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate /* deny_option - deny access */
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate /* ARGSUSED */
2137c478bd9Sstevel@tonic-gate 
deny_option(value,request)2147c478bd9Sstevel@tonic-gate static void deny_option(value, request)
2157c478bd9Sstevel@tonic-gate char   *value;
2167c478bd9Sstevel@tonic-gate struct request_info *request;
2177c478bd9Sstevel@tonic-gate {
2187c478bd9Sstevel@tonic-gate     longjmp(tcpd_buf, AC_DENY);
2197c478bd9Sstevel@tonic-gate }
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate /* banners_option - expand %<char>, terminate each line with CRLF */
2227c478bd9Sstevel@tonic-gate 
banners_option(value,request)2237c478bd9Sstevel@tonic-gate static void banners_option(value, request)
2247c478bd9Sstevel@tonic-gate char   *value;
2257c478bd9Sstevel@tonic-gate struct request_info *request;
2267c478bd9Sstevel@tonic-gate {
2277c478bd9Sstevel@tonic-gate     char    path[MAXPATHNAMELEN];
2287c478bd9Sstevel@tonic-gate     char    ibuf[BUFSIZ];
2297c478bd9Sstevel@tonic-gate     char    obuf[2 * BUFSIZ];
2307c478bd9Sstevel@tonic-gate     struct stat st;
2317c478bd9Sstevel@tonic-gate     int     ch;
2327c478bd9Sstevel@tonic-gate     FILE   *fp;
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate     sprintf(path, "%s/%s", value, eval_daemon(request));
2357c478bd9Sstevel@tonic-gate     if ((fp = fopen(path, "r")) != 0) {
2367c478bd9Sstevel@tonic-gate 	while ((ch = fgetc(fp)) == 0)
2377c478bd9Sstevel@tonic-gate 	    write(request->fd, "", 1);
2387c478bd9Sstevel@tonic-gate 	ungetc(ch, fp);
2397c478bd9Sstevel@tonic-gate 	while (fgets(ibuf, sizeof(ibuf) - 1, fp)) {
2407c478bd9Sstevel@tonic-gate 	    if (split_at(ibuf, '\n'))
2417c478bd9Sstevel@tonic-gate 		strcat(ibuf, "\r\n");
2427c478bd9Sstevel@tonic-gate 	    percent_x(obuf, sizeof(obuf), ibuf, request);
2437c478bd9Sstevel@tonic-gate 	    write(request->fd, obuf, strlen(obuf));
2447c478bd9Sstevel@tonic-gate 	}
2457c478bd9Sstevel@tonic-gate 	fclose(fp);
2467c478bd9Sstevel@tonic-gate     } else if (stat(value, &st) < 0) {
2477c478bd9Sstevel@tonic-gate 	tcpd_warn("%s: %m", value);
2487c478bd9Sstevel@tonic-gate     }
2497c478bd9Sstevel@tonic-gate }
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate /* group_option - switch group id */
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate /* ARGSUSED */
2547c478bd9Sstevel@tonic-gate 
group_option(value,request)2557c478bd9Sstevel@tonic-gate static void group_option(value, request)
2567c478bd9Sstevel@tonic-gate char   *value;
2577c478bd9Sstevel@tonic-gate struct request_info *request;
2587c478bd9Sstevel@tonic-gate {
2597c478bd9Sstevel@tonic-gate     struct group *grp;
2607c478bd9Sstevel@tonic-gate     struct group *getgrnam();
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate     if ((grp = getgrnam(value)) == 0)
2637c478bd9Sstevel@tonic-gate 	tcpd_jump("unknown group: \"%s\"", value);
2647c478bd9Sstevel@tonic-gate     endgrent();
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate     if (dry_run == 0 && setgid(grp->gr_gid))
2677c478bd9Sstevel@tonic-gate 	tcpd_jump("setgid(%s): %m", value);
2687c478bd9Sstevel@tonic-gate }
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate /* user_option - switch user id */
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate /* ARGSUSED */
2737c478bd9Sstevel@tonic-gate 
user_option(value,request)2747c478bd9Sstevel@tonic-gate static void user_option(value, request)
2757c478bd9Sstevel@tonic-gate char   *value;
2767c478bd9Sstevel@tonic-gate struct request_info *request;
2777c478bd9Sstevel@tonic-gate {
2787c478bd9Sstevel@tonic-gate     struct passwd *pwd;
2797c478bd9Sstevel@tonic-gate     struct passwd *getpwnam();
2807c478bd9Sstevel@tonic-gate     char   *group;
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate     if ((group = split_at(value, '.')) != 0)
2837c478bd9Sstevel@tonic-gate 	group_option(group, request);
2847c478bd9Sstevel@tonic-gate     if ((pwd = getpwnam(value)) == 0)
2857c478bd9Sstevel@tonic-gate 	tcpd_jump("unknown user: \"%s\"", value);
2867c478bd9Sstevel@tonic-gate     endpwent();
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate     if (dry_run == 0 && setuid(pwd->pw_uid))
2897c478bd9Sstevel@tonic-gate 	tcpd_jump("setuid(%s): %m", value);
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate /* umask_option - set file creation mask */
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate /* ARGSUSED */
2957c478bd9Sstevel@tonic-gate 
umask_option(value,request)2967c478bd9Sstevel@tonic-gate static void umask_option(value, request)
2977c478bd9Sstevel@tonic-gate char   *value;
2987c478bd9Sstevel@tonic-gate struct request_info *request;
2997c478bd9Sstevel@tonic-gate {
3007c478bd9Sstevel@tonic-gate     unsigned mask;
3017c478bd9Sstevel@tonic-gate     char    junk;
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate     if (sscanf(value, "%o%c", &mask, &junk) != 1 || (mask & 0777) != mask)
3047c478bd9Sstevel@tonic-gate 	tcpd_jump("bad umask value: \"%s\"", value);
3057c478bd9Sstevel@tonic-gate     (void) umask(mask);
3067c478bd9Sstevel@tonic-gate }
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate /* spawn_option - spawn a shell command and wait */
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate /* ARGSUSED */
3117c478bd9Sstevel@tonic-gate 
spawn_option(value,request)3127c478bd9Sstevel@tonic-gate static void spawn_option(value, request)
3137c478bd9Sstevel@tonic-gate char   *value;
3147c478bd9Sstevel@tonic-gate struct request_info *request;
3157c478bd9Sstevel@tonic-gate {
3167c478bd9Sstevel@tonic-gate     if (dry_run == 0)
3177c478bd9Sstevel@tonic-gate 	shell_cmd(value);
3187c478bd9Sstevel@tonic-gate }
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate /* linger_option - set the socket linger time (Marc Boucher <marc@cam.org>) */
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate /* ARGSUSED */
3237c478bd9Sstevel@tonic-gate 
linger_option(value,request)3247c478bd9Sstevel@tonic-gate static void linger_option(value, request)
3257c478bd9Sstevel@tonic-gate char   *value;
3267c478bd9Sstevel@tonic-gate struct request_info *request;
3277c478bd9Sstevel@tonic-gate {
3287c478bd9Sstevel@tonic-gate     struct linger linger;
3297c478bd9Sstevel@tonic-gate     char    junk;
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate     if (sscanf(value, "%d%c", &linger.l_linger, &junk) != 1
3327c478bd9Sstevel@tonic-gate 	|| linger.l_linger < 0)
3337c478bd9Sstevel@tonic-gate 	tcpd_jump("bad linger value: \"%s\"", value);
3347c478bd9Sstevel@tonic-gate     if (dry_run == 0) {
3357c478bd9Sstevel@tonic-gate 	linger.l_onoff = (linger.l_linger != 0);
3367c478bd9Sstevel@tonic-gate 	if (setsockopt(request->fd, SOL_SOCKET, SO_LINGER, (char *) &linger,
3377c478bd9Sstevel@tonic-gate 		       sizeof(linger)) < 0)
3387c478bd9Sstevel@tonic-gate 	    tcpd_warn("setsockopt SO_LINGER %d: %m", linger.l_linger);
3397c478bd9Sstevel@tonic-gate     }
3407c478bd9Sstevel@tonic-gate }
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate /* keepalive_option - set the socket keepalive option */
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate /* ARGSUSED */
3457c478bd9Sstevel@tonic-gate 
keepalive_option(value,request)3467c478bd9Sstevel@tonic-gate static void keepalive_option(value, request)
3477c478bd9Sstevel@tonic-gate char   *value;
3487c478bd9Sstevel@tonic-gate struct request_info *request;
3497c478bd9Sstevel@tonic-gate {
3507c478bd9Sstevel@tonic-gate     static int on = 1;
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate     if (dry_run == 0 && setsockopt(request->fd, SOL_SOCKET, SO_KEEPALIVE,
3537c478bd9Sstevel@tonic-gate 				   (char *) &on, sizeof(on)) < 0)
3547c478bd9Sstevel@tonic-gate 	tcpd_warn("setsockopt SO_KEEPALIVE: %m");
3557c478bd9Sstevel@tonic-gate }
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate /* nice_option - set nice value */
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate /* ARGSUSED */
3607c478bd9Sstevel@tonic-gate 
nice_option(value,request)3617c478bd9Sstevel@tonic-gate static void nice_option(value, request)
3627c478bd9Sstevel@tonic-gate char   *value;
3637c478bd9Sstevel@tonic-gate struct request_info *request;
3647c478bd9Sstevel@tonic-gate {
3657c478bd9Sstevel@tonic-gate     int     niceval = 10;
3667c478bd9Sstevel@tonic-gate     char    junk;
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate     if (value != 0 && sscanf(value, "%d%c", &niceval, &junk) != 1)
3697c478bd9Sstevel@tonic-gate 	tcpd_jump("bad nice value: \"%s\"", value);
3707c478bd9Sstevel@tonic-gate     if (dry_run == 0 && nice(niceval) < 0)
3717c478bd9Sstevel@tonic-gate 	tcpd_warn("nice(%d): %m", niceval);
3727c478bd9Sstevel@tonic-gate }
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate /* twist_option - replace process by shell command */
3757c478bd9Sstevel@tonic-gate 
twist_option(value,request)3767c478bd9Sstevel@tonic-gate static void twist_option(value, request)
3777c478bd9Sstevel@tonic-gate char   *value;
3787c478bd9Sstevel@tonic-gate struct request_info *request;
3797c478bd9Sstevel@tonic-gate {
3807c478bd9Sstevel@tonic-gate     char   *error;
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate     if (dry_run != 0) {
3837c478bd9Sstevel@tonic-gate 	dry_run = 0;
3847c478bd9Sstevel@tonic-gate     } else {
3857c478bd9Sstevel@tonic-gate 	if (resident > 0)
3867c478bd9Sstevel@tonic-gate 	    tcpd_jump("twist option in resident process");
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	syslog(deny_severity, "twist %s to %s", eval_client(request), value);
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	/* Before switching to the shell, set up stdin, stdout and stderr. */
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate #define maybe_dup2(from, to) ((from == to) ? to : (close(to), dup(from)))
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 	if (maybe_dup2(request->fd, 0) != 0 ||
3957c478bd9Sstevel@tonic-gate 	    maybe_dup2(request->fd, 1) != 1 ||
3967c478bd9Sstevel@tonic-gate 	    maybe_dup2(request->fd, 2) != 2) {
3977c478bd9Sstevel@tonic-gate 	    error = "twist_option: dup: %m";
3987c478bd9Sstevel@tonic-gate 	} else {
3997c478bd9Sstevel@tonic-gate 	    if (request->fd > 2)
4007c478bd9Sstevel@tonic-gate 		close(request->fd);
4017c478bd9Sstevel@tonic-gate 	    (void) execl("/bin/sh", "sh", "-c", value, (char *) 0);
4027c478bd9Sstevel@tonic-gate 	    error = "twist_option: /bin/sh: %m";
4037c478bd9Sstevel@tonic-gate 	}
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	/* Something went wrong: we MUST terminate the process. */
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	tcpd_warn(error);
4087c478bd9Sstevel@tonic-gate 	clean_exit(request);
4097c478bd9Sstevel@tonic-gate     }
4107c478bd9Sstevel@tonic-gate }
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate /* rfc931_option - look up remote user name */
4137c478bd9Sstevel@tonic-gate 
rfc931_option(value,request)4147c478bd9Sstevel@tonic-gate static void rfc931_option(value, request)
4157c478bd9Sstevel@tonic-gate char   *value;
4167c478bd9Sstevel@tonic-gate struct request_info *request;
4177c478bd9Sstevel@tonic-gate {
4187c478bd9Sstevel@tonic-gate     int     timeout;
4197c478bd9Sstevel@tonic-gate     char    junk;
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate     if (value != 0) {
4227c478bd9Sstevel@tonic-gate 	if (sscanf(value, "%d%c", &timeout, &junk) != 1 || timeout <= 0)
4237c478bd9Sstevel@tonic-gate 	    tcpd_jump("bad rfc931 timeout: \"%s\"", value);
4247c478bd9Sstevel@tonic-gate 	rfc931_timeout = timeout;
4257c478bd9Sstevel@tonic-gate     }
4267c478bd9Sstevel@tonic-gate     (void) eval_user(request);
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate /* setenv_option - set environment variable */
4307c478bd9Sstevel@tonic-gate 
4317c478bd9Sstevel@tonic-gate /* ARGSUSED */
4327c478bd9Sstevel@tonic-gate 
setenv_option(value,request)4337c478bd9Sstevel@tonic-gate static void setenv_option(value, request)
4347c478bd9Sstevel@tonic-gate char   *value;
4357c478bd9Sstevel@tonic-gate struct request_info *request;
4367c478bd9Sstevel@tonic-gate {
4377c478bd9Sstevel@tonic-gate     extern int setenv(const char *, const char *, int);
4387c478bd9Sstevel@tonic-gate     char   *var_value;
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate     if (*(var_value = value + strcspn(value, whitespace)))
4417c478bd9Sstevel@tonic-gate 	*var_value++ = 0;
4427c478bd9Sstevel@tonic-gate     if (setenv(chop_string(value), chop_string(var_value), 1))
4437c478bd9Sstevel@tonic-gate 	tcpd_jump("memory allocation failure");
4447c478bd9Sstevel@tonic-gate }
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate  /*
4477c478bd9Sstevel@tonic-gate   * The severity option goes last because it comes with a huge amount of ugly
4487c478bd9Sstevel@tonic-gate   * #ifdefs and tables.
4497c478bd9Sstevel@tonic-gate   */
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate struct syslog_names {
4527c478bd9Sstevel@tonic-gate     char   *name;
4537c478bd9Sstevel@tonic-gate     int     value;
4547c478bd9Sstevel@tonic-gate };
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate static struct syslog_names log_fac[] = {
4577c478bd9Sstevel@tonic-gate #ifdef LOG_KERN
4587c478bd9Sstevel@tonic-gate     "kern", LOG_KERN,
4597c478bd9Sstevel@tonic-gate #endif
4607c478bd9Sstevel@tonic-gate #ifdef LOG_USER
4617c478bd9Sstevel@tonic-gate     "user", LOG_USER,
4627c478bd9Sstevel@tonic-gate #endif
4637c478bd9Sstevel@tonic-gate #ifdef LOG_MAIL
4647c478bd9Sstevel@tonic-gate     "mail", LOG_MAIL,
4657c478bd9Sstevel@tonic-gate #endif
4667c478bd9Sstevel@tonic-gate #ifdef LOG_DAEMON
4677c478bd9Sstevel@tonic-gate     "daemon", LOG_DAEMON,
4687c478bd9Sstevel@tonic-gate #endif
4697c478bd9Sstevel@tonic-gate #ifdef LOG_AUTH
4707c478bd9Sstevel@tonic-gate     "auth", LOG_AUTH,
4717c478bd9Sstevel@tonic-gate #endif
4727c478bd9Sstevel@tonic-gate #ifdef LOG_LPR
4737c478bd9Sstevel@tonic-gate     "lpr", LOG_LPR,
4747c478bd9Sstevel@tonic-gate #endif
4757c478bd9Sstevel@tonic-gate #ifdef LOG_NEWS
4767c478bd9Sstevel@tonic-gate     "news", LOG_NEWS,
4777c478bd9Sstevel@tonic-gate #endif
4787c478bd9Sstevel@tonic-gate #ifdef LOG_UUCP
4797c478bd9Sstevel@tonic-gate     "uucp", LOG_UUCP,
4807c478bd9Sstevel@tonic-gate #endif
4817c478bd9Sstevel@tonic-gate #ifdef LOG_CRON
4827c478bd9Sstevel@tonic-gate     "cron", LOG_CRON,
4837c478bd9Sstevel@tonic-gate #endif
4847c478bd9Sstevel@tonic-gate #ifdef LOG_LOCAL0
4857c478bd9Sstevel@tonic-gate     "local0", LOG_LOCAL0,
4867c478bd9Sstevel@tonic-gate #endif
4877c478bd9Sstevel@tonic-gate #ifdef LOG_LOCAL1
4887c478bd9Sstevel@tonic-gate     "local1", LOG_LOCAL1,
4897c478bd9Sstevel@tonic-gate #endif
4907c478bd9Sstevel@tonic-gate #ifdef LOG_LOCAL2
4917c478bd9Sstevel@tonic-gate     "local2", LOG_LOCAL2,
4927c478bd9Sstevel@tonic-gate #endif
4937c478bd9Sstevel@tonic-gate #ifdef LOG_LOCAL3
4947c478bd9Sstevel@tonic-gate     "local3", LOG_LOCAL3,
4957c478bd9Sstevel@tonic-gate #endif
4967c478bd9Sstevel@tonic-gate #ifdef LOG_LOCAL4
4977c478bd9Sstevel@tonic-gate     "local4", LOG_LOCAL4,
4987c478bd9Sstevel@tonic-gate #endif
4997c478bd9Sstevel@tonic-gate #ifdef LOG_LOCAL5
5007c478bd9Sstevel@tonic-gate     "local5", LOG_LOCAL5,
5017c478bd9Sstevel@tonic-gate #endif
5027c478bd9Sstevel@tonic-gate #ifdef LOG_LOCAL6
5037c478bd9Sstevel@tonic-gate     "local6", LOG_LOCAL6,
5047c478bd9Sstevel@tonic-gate #endif
5057c478bd9Sstevel@tonic-gate #ifdef LOG_LOCAL7
5067c478bd9Sstevel@tonic-gate     "local7", LOG_LOCAL7,
5077c478bd9Sstevel@tonic-gate #endif
5087c478bd9Sstevel@tonic-gate     0,
5097c478bd9Sstevel@tonic-gate };
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate static struct syslog_names log_sev[] = {
5127c478bd9Sstevel@tonic-gate #ifdef LOG_EMERG
5137c478bd9Sstevel@tonic-gate     "emerg", LOG_EMERG,
5147c478bd9Sstevel@tonic-gate #endif
5157c478bd9Sstevel@tonic-gate #ifdef LOG_ALERT
5167c478bd9Sstevel@tonic-gate     "alert", LOG_ALERT,
5177c478bd9Sstevel@tonic-gate #endif
5187c478bd9Sstevel@tonic-gate #ifdef LOG_CRIT
5197c478bd9Sstevel@tonic-gate     "crit", LOG_CRIT,
5207c478bd9Sstevel@tonic-gate #endif
5217c478bd9Sstevel@tonic-gate #ifdef LOG_ERR
5227c478bd9Sstevel@tonic-gate     "err", LOG_ERR,
5237c478bd9Sstevel@tonic-gate #endif
5247c478bd9Sstevel@tonic-gate #ifdef LOG_WARNING
5257c478bd9Sstevel@tonic-gate     "warning", LOG_WARNING,
5267c478bd9Sstevel@tonic-gate #endif
5277c478bd9Sstevel@tonic-gate #ifdef LOG_NOTICE
5287c478bd9Sstevel@tonic-gate     "notice", LOG_NOTICE,
5297c478bd9Sstevel@tonic-gate #endif
5307c478bd9Sstevel@tonic-gate #ifdef LOG_INFO
5317c478bd9Sstevel@tonic-gate     "info", LOG_INFO,
5327c478bd9Sstevel@tonic-gate #endif
5337c478bd9Sstevel@tonic-gate #ifdef LOG_DEBUG
5347c478bd9Sstevel@tonic-gate     "debug", LOG_DEBUG,
5357c478bd9Sstevel@tonic-gate #endif
5367c478bd9Sstevel@tonic-gate     0,
5377c478bd9Sstevel@tonic-gate };
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate /* severity_map - lookup facility or severity value */
5407c478bd9Sstevel@tonic-gate 
severity_map(table,name)5417c478bd9Sstevel@tonic-gate static int severity_map(table, name)
5427c478bd9Sstevel@tonic-gate struct syslog_names *table;
5437c478bd9Sstevel@tonic-gate char   *name;
5447c478bd9Sstevel@tonic-gate {
5457c478bd9Sstevel@tonic-gate     struct syslog_names *t;
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate     for (t = table; t->name; t++)
5487c478bd9Sstevel@tonic-gate 	if (STR_EQ(t->name, name))
5497c478bd9Sstevel@tonic-gate 	    return (t->value);
5507c478bd9Sstevel@tonic-gate     tcpd_jump("bad syslog facility or severity: \"%s\"", name);
5517c478bd9Sstevel@tonic-gate     /* NOTREACHED */
5527c478bd9Sstevel@tonic-gate }
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate /* severity_option - change logging severity for this event (Dave Mitchell) */
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate /* ARGSUSED */
5577c478bd9Sstevel@tonic-gate 
severity_option(value,request)5587c478bd9Sstevel@tonic-gate static void severity_option(value, request)
5597c478bd9Sstevel@tonic-gate char   *value;
5607c478bd9Sstevel@tonic-gate struct request_info *request;
5617c478bd9Sstevel@tonic-gate {
5627c478bd9Sstevel@tonic-gate     char   *level = split_at(value, '.');
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate     allow_severity = deny_severity = level ?
5657c478bd9Sstevel@tonic-gate 	severity_map(log_fac, value) | severity_map(log_sev, level) :
5667c478bd9Sstevel@tonic-gate 	severity_map(log_sev, value);
5677c478bd9Sstevel@tonic-gate }
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate /* get_field - return pointer to next field in string */
5707c478bd9Sstevel@tonic-gate 
get_field(string)5717c478bd9Sstevel@tonic-gate static char *get_field(string)
5727c478bd9Sstevel@tonic-gate char   *string;
5737c478bd9Sstevel@tonic-gate {
5747c478bd9Sstevel@tonic-gate     static char *last = "";
5757c478bd9Sstevel@tonic-gate     char   *src;
5767c478bd9Sstevel@tonic-gate     char   *dst;
5777c478bd9Sstevel@tonic-gate     char   *ret;
5787c478bd9Sstevel@tonic-gate     int     ch;
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate     /*
5817c478bd9Sstevel@tonic-gate      * This function returns pointers to successive fields within a given
5827c478bd9Sstevel@tonic-gate      * string. ":" is the field separator; warn if the rule ends in one. It
5837c478bd9Sstevel@tonic-gate      * replaces a "\:" sequence by ":", without treating the result of
5847c478bd9Sstevel@tonic-gate      * substitution as field terminator. A null argument means resume search
5857c478bd9Sstevel@tonic-gate      * where the previous call terminated. This function destroys its
5867c478bd9Sstevel@tonic-gate      * argument.
587*1da57d55SToomas Soome      *
5887c478bd9Sstevel@tonic-gate      * Work from explicit source or from memory. While processing \: we
5897c478bd9Sstevel@tonic-gate      * overwrite the input. This way we do not have to maintain buffers for
5907c478bd9Sstevel@tonic-gate      * copies of input fields.
5917c478bd9Sstevel@tonic-gate      */
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate     src = dst = ret = (string ? string : last);
5947c478bd9Sstevel@tonic-gate     if (src[0] == 0)
5957c478bd9Sstevel@tonic-gate 	return (0);
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate     while (ch = *src) {
5987c478bd9Sstevel@tonic-gate 	if (ch == ':') {
5997c478bd9Sstevel@tonic-gate 	    if (*++src == 0)
6007c478bd9Sstevel@tonic-gate 		tcpd_warn("rule ends in \":\"");
6017c478bd9Sstevel@tonic-gate 	    break;
6027c478bd9Sstevel@tonic-gate 	}
6037c478bd9Sstevel@tonic-gate 	if (ch == '\\' && src[1] == ':')
6047c478bd9Sstevel@tonic-gate 	    src++;
6057c478bd9Sstevel@tonic-gate 	*dst++ = *src++;
6067c478bd9Sstevel@tonic-gate     }
6077c478bd9Sstevel@tonic-gate     last = src;
6087c478bd9Sstevel@tonic-gate     *dst = 0;
6097c478bd9Sstevel@tonic-gate     return (ret);
6107c478bd9Sstevel@tonic-gate }
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate /* chop_string - strip leading and trailing blanks from string */
6137c478bd9Sstevel@tonic-gate 
chop_string(string)6147c478bd9Sstevel@tonic-gate static char *chop_string(string)
6157c478bd9Sstevel@tonic-gate register char *string;
6167c478bd9Sstevel@tonic-gate {
6177c478bd9Sstevel@tonic-gate     char   *start = 0;
6187c478bd9Sstevel@tonic-gate     char   *end;
6197c478bd9Sstevel@tonic-gate     char   *cp;
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate     for (cp = string; *cp; cp++) {
6227c478bd9Sstevel@tonic-gate 	if (!isspace(*cp)) {
6237c478bd9Sstevel@tonic-gate 	    if (start == 0)
6247c478bd9Sstevel@tonic-gate 		start = cp;
6257c478bd9Sstevel@tonic-gate 	    end = cp;
6267c478bd9Sstevel@tonic-gate 	}
6277c478bd9Sstevel@tonic-gate     }
6287c478bd9Sstevel@tonic-gate     return (start ? (end[1] = 0, start) : cp);
6297c478bd9Sstevel@tonic-gate }
630