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