xref: /netbsd-src/lib/libwrap/options.c (revision 4cd46b4ac8ae91572b68912742234cbd34f9906e)
1*4cd46b4aSjoerg /*	$NetBSD: options.c,v 1.16 2012/03/22 22:59:43 joerg Exp $	*/
2d6ddaab4Schristos 
3541be36cSmrg  /*
4541be36cSmrg   * General skeleton for adding options to the access control language. The
5541be36cSmrg   * features offered by this module are documented in the hosts_options(5)
6541be36cSmrg   * manual page (source file: hosts_options.5, "nroff -man" format).
7541be36cSmrg   *
8541be36cSmrg   * Notes and warnings for those who want to add features:
9541be36cSmrg   *
10541be36cSmrg   * In case of errors, abort options processing and deny access. There are too
11541be36cSmrg   * many irreversible side effects to make error recovery feasible. For
12541be36cSmrg   * example, it makes no sense to continue after we have already changed the
13541be36cSmrg   * userid.
14541be36cSmrg   *
15541be36cSmrg   * In case of errors, do not terminate the process: the routines might be
16541be36cSmrg   * called from a long-running daemon that should run forever. Instead, call
17541be36cSmrg   * tcpd_jump() which does a non-local goto back into the hosts_access()
18541be36cSmrg   * routine.
19541be36cSmrg   *
20541be36cSmrg   * In case of severe errors, use clean_exit() instead of directly calling
21541be36cSmrg   * exit(), or the inetd may loop on an UDP request.
22541be36cSmrg   *
23541be36cSmrg   * In verification mode (for example, with the "tcpdmatch" command) the
24541be36cSmrg   * "dry_run" flag is set. In this mode, an option function should just "say"
25541be36cSmrg   * what it is going to do instead of really doing it.
26541be36cSmrg   *
27541be36cSmrg   * Some option functions do not return (for example, the twist option passes
28541be36cSmrg   * control to another program). In verification mode (dry_run flag is set)
29541be36cSmrg   * such options should clear the "dry_run" flag to inform the caller of this
30541be36cSmrg   * course of action.
31541be36cSmrg   */
32541be36cSmrg 
33d6ddaab4Schristos #include <sys/cdefs.h>
34541be36cSmrg #ifndef lint
35d6ddaab4Schristos #if 0
36541be36cSmrg static char sccsid[] = "@(#) options.c 1.17 96/02/11 17:01:31";
37d6ddaab4Schristos #else
38*4cd46b4aSjoerg __RCSID("$NetBSD: options.c,v 1.16 2012/03/22 22:59:43 joerg Exp $");
39d6ddaab4Schristos #endif
40541be36cSmrg #endif
41541be36cSmrg 
42541be36cSmrg /* System libraries. */
43541be36cSmrg 
44541be36cSmrg #include <sys/types.h>
45541be36cSmrg #include <sys/param.h>
46541be36cSmrg #include <sys/socket.h>
47541be36cSmrg #include <sys/stat.h>
48541be36cSmrg #include <netinet/in.h>
49541be36cSmrg #include <netdb.h>
50541be36cSmrg #include <stdio.h>
51541be36cSmrg #include <syslog.h>
52d6ddaab4Schristos #include <unistd.h>
53d6ddaab4Schristos #include <stdlib.h>
54541be36cSmrg #include <pwd.h>
55541be36cSmrg #include <grp.h>
56541be36cSmrg #include <ctype.h>
57541be36cSmrg #include <setjmp.h>
58541be36cSmrg #include <string.h>
59541be36cSmrg 
60541be36cSmrg /* Local stuff. */
61541be36cSmrg 
62541be36cSmrg #include "tcpd.h"
63541be36cSmrg 
64541be36cSmrg /* Options runtime support. */
65541be36cSmrg 
66541be36cSmrg int     dry_run = 0;			/* flag set in verification mode */
67541be36cSmrg extern jmp_buf tcpd_buf;		/* tcpd_jump() support */
68541be36cSmrg 
69541be36cSmrg /* Options parser support. */
70541be36cSmrg 
71541be36cSmrg static char whitespace_eq[] = "= \t\r\n";
72541be36cSmrg #define whitespace (whitespace_eq + 1)
73541be36cSmrg 
74d6ddaab4Schristos static char *get_field			/* chew :-delimited field off string */
75e1a2f47fSmatt 		(char *);
76d6ddaab4Schristos static char *chop_string		/* strip leading and trailing blanks */
77e1a2f47fSmatt 		(char *);
78d6ddaab4Schristos struct syslog_names;
79d6ddaab4Schristos static int severity_map
80e1a2f47fSmatt 		(const struct syslog_names *, char *);
81541be36cSmrg 
82541be36cSmrg /* List of functions that implement the options. Add yours here. */
83541be36cSmrg 
84d6ddaab4Schristos static void user_option			/* execute "user name.group" option */
85e1a2f47fSmatt 		(char *, struct request_info *);
86d6ddaab4Schristos static void group_option		/* execute "group name" option */
87e1a2f47fSmatt 		(char *, struct request_info *);
88d6ddaab4Schristos static void umask_option		/* execute "umask mask" option */
89e1a2f47fSmatt 		(char *, struct request_info *);
90d6ddaab4Schristos static void linger_option		/* execute "linger time" option */
91e1a2f47fSmatt 		(char *, struct request_info *);
92d6ddaab4Schristos static void keepalive_option		/* execute "keepalive" option */
93e1a2f47fSmatt 		(char *, struct request_info *);
94d6ddaab4Schristos static void spawn_option		/* execute "spawn command" option */
95e1a2f47fSmatt 		(char *, struct request_info *);
96d6ddaab4Schristos static void twist_option		/* execute "twist command" option */
97e1a2f47fSmatt 		(char *, struct request_info *);
98d6ddaab4Schristos static void rfc931_option		/* execute "rfc931" option */
99e1a2f47fSmatt 		(char *, struct request_info *);
100d6ddaab4Schristos static void setenv_option		/* execute "setenv name value" */
101e1a2f47fSmatt 		(char *, struct request_info *);
102d6ddaab4Schristos static void nice_option			/* execute "nice" option */
103e1a2f47fSmatt 		(char *, struct request_info *);
104d6ddaab4Schristos static void severity_option		/* execute "severity value" */
105e1a2f47fSmatt 		(char *, struct request_info *);
106*4cd46b4aSjoerg __dead static void allow_option		/* execute "allow" option */
107e1a2f47fSmatt 		(char *, struct request_info *);
108*4cd46b4aSjoerg __dead static void deny_option			/* execute "deny" option */
109e1a2f47fSmatt 		(char *, struct request_info *);
110d6ddaab4Schristos static void banners_option		/* execute "banners path" option */
111e1a2f47fSmatt 		(char *, struct request_info *);
112541be36cSmrg 
113541be36cSmrg /* Structure of the options table. */
114541be36cSmrg 
115cbfdcd8cSbillc struct option {
116e1a2f47fSmatt     const char *name;			/* keyword name, case is ignored */
117d6ddaab4Schristos     void  (*func)			/* function that does the real work */
118e1a2f47fSmatt 		(char *, struct request_info *);
119541be36cSmrg     int     flags;			/* see below... */
120541be36cSmrg };
121541be36cSmrg 
122541be36cSmrg #define NEED_ARG	(1<<1)		/* option requires argument */
123541be36cSmrg #define USE_LAST	(1<<2)		/* option must be last */
124541be36cSmrg #define OPT_ARG		(1<<3)		/* option has optional argument */
125541be36cSmrg #define EXPAND_ARG	(1<<4)		/* do %x expansion on argument */
126541be36cSmrg 
127541be36cSmrg #define need_arg(o)	((o)->flags & NEED_ARG)
128541be36cSmrg #define opt_arg(o)	((o)->flags & OPT_ARG)
129541be36cSmrg #define permit_arg(o)	((o)->flags & (NEED_ARG | OPT_ARG))
130541be36cSmrg #define use_last(o)	((o)->flags & USE_LAST)
131541be36cSmrg #define expand_arg(o)	((o)->flags & EXPAND_ARG)
132541be36cSmrg 
133541be36cSmrg /* List of known keywords. Add yours here. */
134541be36cSmrg 
135cbfdcd8cSbillc static struct option option_table[] = {
136d6ddaab4Schristos     { "user", user_option, NEED_ARG },
137d6ddaab4Schristos     { "group", group_option, NEED_ARG },
138d6ddaab4Schristos     { "umask", umask_option, NEED_ARG },
139d6ddaab4Schristos     { "linger", linger_option, NEED_ARG },
140d6ddaab4Schristos     { "keepalive", keepalive_option, 0 },
141d6ddaab4Schristos     { "spawn", spawn_option, NEED_ARG | EXPAND_ARG },
142d6ddaab4Schristos     { "twist", twist_option, NEED_ARG | EXPAND_ARG | USE_LAST },
143d6ddaab4Schristos     { "rfc931", rfc931_option, OPT_ARG },
144d6ddaab4Schristos     { "setenv", setenv_option, NEED_ARG | EXPAND_ARG },
145d6ddaab4Schristos     { "nice", nice_option, OPT_ARG },
146d6ddaab4Schristos     { "severity", severity_option, NEED_ARG },
147d6ddaab4Schristos     { "allow", allow_option, USE_LAST },
148d6ddaab4Schristos     { "deny", deny_option, USE_LAST },
149d6ddaab4Schristos     { "banners", banners_option, NEED_ARG },
150d6ddaab4Schristos     { NULL, NULL, 0 }
151541be36cSmrg };
152541be36cSmrg 
153541be36cSmrg /* process_options - process access control options */
154541be36cSmrg 
155e1a2f47fSmatt void
process_options(char * options,struct request_info * request)156e1a2f47fSmatt process_options(char *options, struct request_info *request)
157541be36cSmrg {
158541be36cSmrg     char   *key;
159541be36cSmrg     char   *value;
160541be36cSmrg     char   *curr_opt;
161541be36cSmrg     char   *next_opt;
162cbfdcd8cSbillc     struct option *op;
163541be36cSmrg     char    bf[BUFSIZ];
164541be36cSmrg 
165541be36cSmrg     for (curr_opt = get_field(options); curr_opt; curr_opt = next_opt) {
166541be36cSmrg 	next_opt = get_field((char *) 0);
167541be36cSmrg 
168541be36cSmrg 	/*
169541be36cSmrg 	 * Separate the option into name and value parts. For backwards
170541be36cSmrg 	 * compatibility we ignore exactly one '=' between name and value.
171541be36cSmrg 	 */
172541be36cSmrg 	curr_opt = chop_string(curr_opt);
173541be36cSmrg 	if (*(value = curr_opt + strcspn(curr_opt, whitespace_eq))) {
174541be36cSmrg 	    if (*value != '=') {
175541be36cSmrg 		*value++ = 0;
176541be36cSmrg 		value += strspn(value, whitespace);
177541be36cSmrg 	    }
178541be36cSmrg 	    if (*value == '=') {
179541be36cSmrg 		*value++ = 0;
180541be36cSmrg 		value += strspn(value, whitespace);
181541be36cSmrg 	    }
182541be36cSmrg 	}
183541be36cSmrg 	if (*value == 0)
184541be36cSmrg 	    value = 0;
185541be36cSmrg 	key = curr_opt;
186541be36cSmrg 
187541be36cSmrg 	/*
188541be36cSmrg 	 * Disallow missing option names (and empty option fields).
189541be36cSmrg 	 */
190541be36cSmrg 	if (*key == 0)
191541be36cSmrg 	    tcpd_jump("missing option name");
192541be36cSmrg 
193541be36cSmrg 	/*
194541be36cSmrg 	 * Lookup the option-specific info and do some common error checks.
195541be36cSmrg 	 * Delegate option-specific processing to the specific functions.
196541be36cSmrg 	 */
197541be36cSmrg 
198541be36cSmrg 	for (op = option_table; op->name && STR_NE(op->name, key); op++)
199541be36cSmrg 	     /* VOID */ ;
200541be36cSmrg 	if (op->name == 0)
201541be36cSmrg 	    tcpd_jump("bad option name: \"%s\"", key);
202541be36cSmrg 	if (!value && need_arg(op))
203541be36cSmrg 	    tcpd_jump("option \"%s\" requires value", key);
204541be36cSmrg 	if (value && !permit_arg(op))
205541be36cSmrg 	    tcpd_jump("option \"%s\" requires no value", key);
206541be36cSmrg 	if (next_opt && use_last(op))
207541be36cSmrg 	    tcpd_jump("option \"%s\" must be at end", key);
208541be36cSmrg 	if (value && expand_arg(op))
209541be36cSmrg 	    value = chop_string(percent_x(bf, sizeof(bf), value, request));
210541be36cSmrg 	if (hosts_access_verbose)
211541be36cSmrg 	    syslog(LOG_DEBUG, "option:   %s %s", key, value ? value : "");
212541be36cSmrg 	(*(op->func)) (value, request);
213541be36cSmrg     }
214541be36cSmrg }
215541be36cSmrg 
216541be36cSmrg /* allow_option - grant access */
217541be36cSmrg 
218541be36cSmrg /* ARGSUSED */
219541be36cSmrg 
220e1a2f47fSmatt static void
allow_option(char * value,struct request_info * request)221e1a2f47fSmatt allow_option(char *value, struct request_info *request)
222541be36cSmrg {
223541be36cSmrg     longjmp(tcpd_buf, AC_PERMIT);
224541be36cSmrg }
225541be36cSmrg 
226541be36cSmrg /* deny_option - deny access */
227541be36cSmrg 
228541be36cSmrg /* ARGSUSED */
229541be36cSmrg 
230e1a2f47fSmatt static void
deny_option(char * value,struct request_info * request)231e1a2f47fSmatt deny_option(char *value, struct request_info *request)
232541be36cSmrg {
233541be36cSmrg     longjmp(tcpd_buf, AC_DENY);
234541be36cSmrg }
235541be36cSmrg 
236541be36cSmrg /* banners_option - expand %<char>, terminate each line with CRLF */
237541be36cSmrg 
238e1a2f47fSmatt static void
banners_option(char * value,struct request_info * request)239e1a2f47fSmatt banners_option(char *value, struct request_info *request)
240541be36cSmrg {
2412ef04ff6Sitojun     char    path[MAXPATHLEN];
242541be36cSmrg     char    ibuf[BUFSIZ];
243541be36cSmrg     char    obuf[2 * BUFSIZ];
244541be36cSmrg     struct stat st;
245541be36cSmrg     int     ch;
246541be36cSmrg     FILE   *fp;
247541be36cSmrg 
2489cd5492cSmrg     (void)snprintf(path, sizeof path, "%s/%s", value, eval_daemon(request));
249541be36cSmrg     if ((fp = fopen(path, "r")) != 0) {
250541be36cSmrg 	while ((ch = fgetc(fp)) == 0)
251541be36cSmrg 	    write(request->fd, "", 1);
252541be36cSmrg 	ungetc(ch, fp);
2539cd5492cSmrg 	while (fgets(ibuf, sizeof(ibuf) - 2, fp)) {
254541be36cSmrg 	    if (split_at(ibuf, '\n'))
2559cd5492cSmrg 		strcat(ibuf, "\r\n");	/* XXX strcat is safe */
256541be36cSmrg 	    percent_x(obuf, sizeof(obuf), ibuf, request);
257541be36cSmrg 	    write(request->fd, obuf, strlen(obuf));
258541be36cSmrg 	}
259541be36cSmrg 	fclose(fp);
260541be36cSmrg     } else if (stat(value, &st) < 0) {
261541be36cSmrg 	tcpd_warn("%s: %m", value);
262541be36cSmrg     }
263541be36cSmrg }
264541be36cSmrg 
265541be36cSmrg /* group_option - switch group id */
266541be36cSmrg 
267541be36cSmrg /* ARGSUSED */
268541be36cSmrg 
269e1a2f47fSmatt static void
group_option(char * value,struct request_info * request)270e1a2f47fSmatt group_option(char *value, struct request_info *request)
271541be36cSmrg {
27267c3cbddSchristos     struct group grs, *grp;
27367c3cbddSchristos     char grbuf[1024];
274541be36cSmrg 
27567c3cbddSchristos     (void)getgrnam_r(value, &grs, grbuf, sizeof(grbuf), &grp);
27667c3cbddSchristos     if (grp == NULL)
277541be36cSmrg 	tcpd_jump("unknown group: \"%s\"", value);
278541be36cSmrg 
279541be36cSmrg     if (dry_run == 0 && setgid(grp->gr_gid))
280541be36cSmrg 	tcpd_jump("setgid(%s): %m", value);
281541be36cSmrg }
282541be36cSmrg 
283541be36cSmrg /* user_option - switch user id */
284541be36cSmrg 
285541be36cSmrg /* ARGSUSED */
286541be36cSmrg 
287e1a2f47fSmatt static void
user_option(char * value,struct request_info * request)288e1a2f47fSmatt user_option(char *value, struct request_info *request)
289541be36cSmrg {
290c4402ab0Schristos     struct passwd *pwd, pws;
291541be36cSmrg     char   *group;
292c4402ab0Schristos     char   pwbuf[1024];
293541be36cSmrg 
294541be36cSmrg     if ((group = split_at(value, '.')) != 0)
295541be36cSmrg 	group_option(group, request);
29667c3cbddSchristos     (void)getpwnam_r(value, &pws, pwbuf, sizeof(pwbuf), &pwd);
29767c3cbddSchristos     if (pwd == NULL)
298541be36cSmrg 	tcpd_jump("unknown user: \"%s\"", value);
299541be36cSmrg 
300541be36cSmrg     if (dry_run == 0 && setuid(pwd->pw_uid))
301541be36cSmrg 	tcpd_jump("setuid(%s): %m", value);
302541be36cSmrg }
303541be36cSmrg 
304541be36cSmrg /* umask_option - set file creation mask */
305541be36cSmrg 
306541be36cSmrg /* ARGSUSED */
307541be36cSmrg 
308e1a2f47fSmatt static void
umask_option(char * value,struct request_info * request)309e1a2f47fSmatt umask_option(char *value, struct request_info *request)
310541be36cSmrg {
311541be36cSmrg     unsigned mask;
312541be36cSmrg     char    junk;
313541be36cSmrg 
314541be36cSmrg     if (sscanf(value, "%o%c", &mask, &junk) != 1 || (mask & 0777) != mask)
315541be36cSmrg 	tcpd_jump("bad umask value: \"%s\"", value);
316541be36cSmrg     (void) umask(mask);
317541be36cSmrg }
318541be36cSmrg 
319541be36cSmrg /* spawn_option - spawn a shell command and wait */
320541be36cSmrg 
321541be36cSmrg /* ARGSUSED */
322541be36cSmrg 
323e1a2f47fSmatt static void
spawn_option(char * value,struct request_info * request)324e1a2f47fSmatt spawn_option(char *value, struct request_info *request)
325541be36cSmrg {
326541be36cSmrg     if (dry_run == 0)
327541be36cSmrg 	shell_cmd(value);
328541be36cSmrg }
329541be36cSmrg 
330541be36cSmrg /* linger_option - set the socket linger time (Marc Boucher <marc@cam.org>) */
331541be36cSmrg 
332541be36cSmrg /* ARGSUSED */
333541be36cSmrg 
334e1a2f47fSmatt static void
linger_option(char * value,struct request_info * request)335e1a2f47fSmatt linger_option(char *value, struct request_info *request)
336541be36cSmrg {
337541be36cSmrg     struct linger linger;
338541be36cSmrg     char    junk;
339541be36cSmrg 
340541be36cSmrg     if (sscanf(value, "%d%c", &linger.l_linger, &junk) != 1
341541be36cSmrg 	|| linger.l_linger < 0)
342541be36cSmrg 	tcpd_jump("bad linger value: \"%s\"", value);
343541be36cSmrg     if (dry_run == 0) {
344541be36cSmrg 	linger.l_onoff = (linger.l_linger != 0);
345541be36cSmrg 	if (setsockopt(request->fd, SOL_SOCKET, SO_LINGER, (char *) &linger,
346541be36cSmrg 		       sizeof(linger)) < 0)
347541be36cSmrg 	    tcpd_warn("setsockopt SO_LINGER %d: %m", linger.l_linger);
348541be36cSmrg     }
349541be36cSmrg }
350541be36cSmrg 
351541be36cSmrg /* keepalive_option - set the socket keepalive option */
352541be36cSmrg 
353541be36cSmrg /* ARGSUSED */
354541be36cSmrg 
355e1a2f47fSmatt static void
keepalive_option(char * value,struct request_info * request)356e1a2f47fSmatt keepalive_option(char *value, struct request_info *request)
357541be36cSmrg {
358541be36cSmrg     static int on = 1;
359541be36cSmrg 
360541be36cSmrg     if (dry_run == 0 && setsockopt(request->fd, SOL_SOCKET, SO_KEEPALIVE,
361541be36cSmrg 				   (char *) &on, sizeof(on)) < 0)
362541be36cSmrg 	tcpd_warn("setsockopt SO_KEEPALIVE: %m");
363541be36cSmrg }
364541be36cSmrg 
365541be36cSmrg /* nice_option - set nice value */
366541be36cSmrg 
367541be36cSmrg /* ARGSUSED */
368541be36cSmrg 
369e1a2f47fSmatt static void
nice_option(char * value,struct request_info * request)370e1a2f47fSmatt nice_option(char *value, struct request_info *request)
371541be36cSmrg {
372541be36cSmrg     int     niceval = 10;
373541be36cSmrg     char    junk;
374541be36cSmrg 
375541be36cSmrg     if (value != 0 && sscanf(value, "%d%c", &niceval, &junk) != 1)
376541be36cSmrg 	tcpd_jump("bad nice value: \"%s\"", value);
377541be36cSmrg     if (dry_run == 0 && nice(niceval) < 0)
378541be36cSmrg 	tcpd_warn("nice(%d): %m", niceval);
379541be36cSmrg }
380541be36cSmrg 
381541be36cSmrg /* twist_option - replace process by shell command */
382541be36cSmrg 
383e1a2f47fSmatt static void
twist_option(char * value,struct request_info * request)384e1a2f47fSmatt twist_option(char *value, struct request_info *request)
385541be36cSmrg {
386541be36cSmrg     if (dry_run != 0) {
387541be36cSmrg 	dry_run = 0;
388541be36cSmrg     } else {
389541be36cSmrg 	if (resident > 0)
390541be36cSmrg 	    tcpd_jump("twist option in resident process");
391541be36cSmrg 
392541be36cSmrg 	syslog(deny_severity, "twist %s to %s", eval_client(request), value);
393541be36cSmrg 
394541be36cSmrg 	/* Before switching to the shell, set up stdin, stdout and stderr. */
395541be36cSmrg 
396541be36cSmrg #define maybe_dup2(from, to) ((from == to) ? to : (close(to), dup(from)))
397541be36cSmrg 
398541be36cSmrg 	if (maybe_dup2(request->fd, 0) != 0 ||
399541be36cSmrg 	    maybe_dup2(request->fd, 1) != 1 ||
400541be36cSmrg 	    maybe_dup2(request->fd, 2) != 2) {
4018aefd973Ssommerfeld 	    tcpd_warn("twist_option: dup: %m");
402541be36cSmrg 	} else {
403541be36cSmrg 	    if (request->fd > 2)
404541be36cSmrg 		close(request->fd);
405541be36cSmrg 	    (void) execl("/bin/sh", "sh", "-c", value, (char *) 0);
4068aefd973Ssommerfeld 	    tcpd_warn("twist_option: /bin/sh: %m");
407541be36cSmrg 	}
408541be36cSmrg 
409541be36cSmrg 	/* Something went wrong: we MUST terminate the process. */
410541be36cSmrg 	clean_exit(request);
411541be36cSmrg     }
412541be36cSmrg }
413541be36cSmrg 
414541be36cSmrg /* rfc931_option - look up remote user name */
415541be36cSmrg 
416e1a2f47fSmatt static void
rfc931_option(char * value,struct request_info * request)417e1a2f47fSmatt rfc931_option(char *value, struct request_info *request)
418541be36cSmrg {
419541be36cSmrg     int     timeout;
420541be36cSmrg     char    junk;
421541be36cSmrg 
422541be36cSmrg     if (value != 0) {
423541be36cSmrg 	if (sscanf(value, "%d%c", &timeout, &junk) != 1 || timeout <= 0)
424541be36cSmrg 	    tcpd_jump("bad rfc931 timeout: \"%s\"", value);
425541be36cSmrg 	rfc931_timeout = timeout;
426541be36cSmrg     }
427541be36cSmrg     (void) eval_user(request);
428541be36cSmrg }
429541be36cSmrg 
430541be36cSmrg /* setenv_option - set environment variable */
431541be36cSmrg 
432541be36cSmrg /* ARGSUSED */
433541be36cSmrg 
434e1a2f47fSmatt static void
setenv_option(char * value,struct request_info * request)435e1a2f47fSmatt setenv_option(char *value, struct request_info *request)
436541be36cSmrg {
437541be36cSmrg     char   *var_value;
438541be36cSmrg 
439541be36cSmrg     if (*(var_value = value + strcspn(value, whitespace)))
440541be36cSmrg 	*var_value++ = 0;
441541be36cSmrg     if (setenv(chop_string(value), chop_string(var_value), 1))
442541be36cSmrg 	tcpd_jump("memory allocation failure");
443541be36cSmrg }
444541be36cSmrg 
445541be36cSmrg  /*
446541be36cSmrg   * The severity option goes last because it comes with a huge amount of ugly
447541be36cSmrg   * #ifdefs and tables.
448541be36cSmrg   */
449541be36cSmrg 
450541be36cSmrg struct syslog_names {
451e1a2f47fSmatt     const char *name;
452541be36cSmrg     int value;
453541be36cSmrg };
454541be36cSmrg 
455e1a2f47fSmatt static const struct syslog_names log_fac[] = {
456541be36cSmrg #ifdef LOG_KERN
457d6ddaab4Schristos     { "kern", LOG_KERN },
458541be36cSmrg #endif
459541be36cSmrg #ifdef LOG_USER
460d6ddaab4Schristos     { "user", LOG_USER },
461541be36cSmrg #endif
462541be36cSmrg #ifdef LOG_MAIL
463d6ddaab4Schristos     { "mail", LOG_MAIL },
464541be36cSmrg #endif
465541be36cSmrg #ifdef LOG_DAEMON
466d6ddaab4Schristos     { "daemon", LOG_DAEMON },
467541be36cSmrg #endif
468541be36cSmrg #ifdef LOG_AUTH
469d6ddaab4Schristos     { "auth", LOG_AUTH },
470541be36cSmrg #endif
471541be36cSmrg #ifdef LOG_LPR
472d6ddaab4Schristos     { "lpr", LOG_LPR },
473541be36cSmrg #endif
474541be36cSmrg #ifdef LOG_NEWS
475d6ddaab4Schristos     { "news", LOG_NEWS },
476541be36cSmrg #endif
477541be36cSmrg #ifdef LOG_UUCP
478d6ddaab4Schristos     { "uucp", LOG_UUCP },
479541be36cSmrg #endif
480541be36cSmrg #ifdef LOG_CRON
481d6ddaab4Schristos     { "cron", LOG_CRON },
482541be36cSmrg #endif
483fefb03d5Stv #ifdef LOG_AUTHPRIV
484fefb03d5Stv     { "authpriv", LOG_AUTHPRIV },
485fefb03d5Stv #endif
486fefb03d5Stv #ifdef LOG_FTP
487fefb03d5Stv     { "ftp", LOG_FTP },
488fefb03d5Stv #endif
489541be36cSmrg #ifdef LOG_LOCAL0
490d6ddaab4Schristos     { "local0", LOG_LOCAL0 },
491541be36cSmrg #endif
492541be36cSmrg #ifdef LOG_LOCAL1
493d6ddaab4Schristos     { "local1", LOG_LOCAL1 },
494541be36cSmrg #endif
495541be36cSmrg #ifdef LOG_LOCAL2
496d6ddaab4Schristos     { "local2", LOG_LOCAL2 },
497541be36cSmrg #endif
498541be36cSmrg #ifdef LOG_LOCAL3
499d6ddaab4Schristos     { "local3", LOG_LOCAL3 },
500541be36cSmrg #endif
501541be36cSmrg #ifdef LOG_LOCAL4
502d6ddaab4Schristos     { "local4", LOG_LOCAL4 },
503541be36cSmrg #endif
504541be36cSmrg #ifdef LOG_LOCAL5
505d6ddaab4Schristos     { "local5", LOG_LOCAL5 },
506541be36cSmrg #endif
507541be36cSmrg #ifdef LOG_LOCAL6
508d6ddaab4Schristos     { "local6", LOG_LOCAL6 },
509541be36cSmrg #endif
510541be36cSmrg #ifdef LOG_LOCAL7
511d6ddaab4Schristos     { "local7", LOG_LOCAL7 },
512541be36cSmrg #endif
513d6ddaab4Schristos     { NULL, 0 }
514541be36cSmrg };
515541be36cSmrg 
516e1a2f47fSmatt static const struct syslog_names log_sev[] = {
517541be36cSmrg #ifdef LOG_EMERG
518d6ddaab4Schristos     { "emerg", LOG_EMERG },
519541be36cSmrg #endif
520541be36cSmrg #ifdef LOG_ALERT
521d6ddaab4Schristos     { "alert", LOG_ALERT },
522541be36cSmrg #endif
523541be36cSmrg #ifdef LOG_CRIT
524d6ddaab4Schristos     { "crit", LOG_CRIT },
525541be36cSmrg #endif
526541be36cSmrg #ifdef LOG_ERR
527d6ddaab4Schristos     { "err", LOG_ERR },
528541be36cSmrg #endif
529541be36cSmrg #ifdef LOG_WARNING
530d6ddaab4Schristos     { "warning", LOG_WARNING },
531541be36cSmrg #endif
532541be36cSmrg #ifdef LOG_NOTICE
533d6ddaab4Schristos     { "notice", LOG_NOTICE },
534541be36cSmrg #endif
535541be36cSmrg #ifdef LOG_INFO
536d6ddaab4Schristos     { "info", LOG_INFO },
537541be36cSmrg #endif
538541be36cSmrg #ifdef LOG_DEBUG
539d6ddaab4Schristos     { "debug", LOG_DEBUG },
540541be36cSmrg #endif
541d6ddaab4Schristos     { NULL, 0 }
542541be36cSmrg };
543541be36cSmrg 
544541be36cSmrg /* severity_map - lookup facility or severity value */
545541be36cSmrg 
546e1a2f47fSmatt static int
severity_map(const struct syslog_names * table,char * name)547e1a2f47fSmatt severity_map(const struct syslog_names *table, char *name)
548541be36cSmrg {
549e1a2f47fSmatt     const struct syslog_names *t;
550541be36cSmrg 
551541be36cSmrg     for (t = table; t->name; t++)
552541be36cSmrg 	if (STR_EQ(t->name, name))
553541be36cSmrg 	    return (t->value);
554541be36cSmrg     tcpd_jump("bad syslog facility or severity: \"%s\"", name);
555541be36cSmrg     /* NOTREACHED */
556d6ddaab4Schristos     return -1;
557541be36cSmrg }
558541be36cSmrg 
559541be36cSmrg /* severity_option - change logging severity for this event (Dave Mitchell) */
560541be36cSmrg 
561541be36cSmrg /* ARGSUSED */
562541be36cSmrg 
563e1a2f47fSmatt static void
severity_option(char * value,struct request_info * request)564e1a2f47fSmatt severity_option(char *value, struct request_info *request)
565541be36cSmrg {
566541be36cSmrg     char   *level = split_at(value, '.');
567541be36cSmrg 
568541be36cSmrg     allow_severity = deny_severity = level ?
569541be36cSmrg 	severity_map(log_fac, value) | severity_map(log_sev, level) :
570541be36cSmrg 	severity_map(log_sev, value);
571541be36cSmrg }
572541be36cSmrg 
573541be36cSmrg /* get_field - return pointer to next field in string */
574541be36cSmrg 
575e1a2f47fSmatt static char *
get_field(char * string)576e1a2f47fSmatt get_field(char *string)
577541be36cSmrg {
578e1a2f47fSmatt     static char nul = '\0';
579e1a2f47fSmatt     static char *last = &nul;
580541be36cSmrg     char   *src;
581541be36cSmrg     char   *dst;
582541be36cSmrg     char   *ret;
583541be36cSmrg     int     ch;
584541be36cSmrg 
585541be36cSmrg     /*
586541be36cSmrg      * This function returns pointers to successive fields within a given
587541be36cSmrg      * string. ":" is the field separator; warn if the rule ends in one. It
588541be36cSmrg      * replaces a "\:" sequence by ":", without treating the result of
589541be36cSmrg      * substitution as field terminator. A null argument means resume search
590541be36cSmrg      * where the previous call terminated. This function destroys its
591541be36cSmrg      * argument.
592541be36cSmrg      *
593541be36cSmrg      * Work from explicit source or from memory. While processing \: we
594541be36cSmrg      * overwrite the input. This way we do not have to maintain buffers for
595541be36cSmrg      * copies of input fields.
596541be36cSmrg      */
597541be36cSmrg 
598541be36cSmrg     src = dst = ret = (string ? string : last);
599541be36cSmrg     if (src[0] == 0)
600541be36cSmrg 	return (0);
601541be36cSmrg 
602d6ddaab4Schristos     while ((ch = *src) != '\0') {
603541be36cSmrg 	if (ch == ':') {
604541be36cSmrg 	    if (*++src == 0)
605541be36cSmrg 		tcpd_warn("rule ends in \":\"");
606541be36cSmrg 	    break;
607541be36cSmrg 	}
608541be36cSmrg 	if (ch == '\\' && src[1] == ':')
609541be36cSmrg 	    src++;
610541be36cSmrg 	*dst++ = *src++;
611541be36cSmrg     }
612541be36cSmrg     last = src;
613541be36cSmrg     *dst = 0;
614541be36cSmrg     return (ret);
615541be36cSmrg }
616541be36cSmrg 
617541be36cSmrg /* chop_string - strip leading and trailing blanks from string */
618541be36cSmrg 
619e1a2f47fSmatt static char *
chop_string(register char * string)620e1a2f47fSmatt chop_string(register char *string)
621541be36cSmrg {
622d6ddaab4Schristos     char   *start = NULL;
623d6ddaab4Schristos     char   *end = NULL;
624541be36cSmrg     char   *cp;
625541be36cSmrg 
626541be36cSmrg     for (cp = string; *cp; cp++) {
6279bdf22f6Sitohy 	if (!isspace((unsigned char) *cp)) {
628541be36cSmrg 	    if (start == 0)
629541be36cSmrg 		start = cp;
630541be36cSmrg 	    end = cp;
631541be36cSmrg 	}
632541be36cSmrg     }
633541be36cSmrg     return (start ? (end[1] = 0, start) : cp);
634541be36cSmrg }
635