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