1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * tcpdmatch - explain what tcpd would do in a specific case 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * usage: tcpdmatch [-d] [-i inet_conf] daemon[@host] [user@]host 5*7c478bd9Sstevel@tonic-gate * 6*7c478bd9Sstevel@tonic-gate * -d: use the access control tables in the current directory. 7*7c478bd9Sstevel@tonic-gate * 8*7c478bd9Sstevel@tonic-gate * -i: location of inetd.conf file. 9*7c478bd9Sstevel@tonic-gate * 10*7c478bd9Sstevel@tonic-gate * All errors are reported to the standard error stream, including the errors 11*7c478bd9Sstevel@tonic-gate * that would normally be reported via the syslog daemon. 12*7c478bd9Sstevel@tonic-gate * 13*7c478bd9Sstevel@tonic-gate * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 14*7c478bd9Sstevel@tonic-gate */ 15*7c478bd9Sstevel@tonic-gate 16*7c478bd9Sstevel@tonic-gate #ifndef lint 17*7c478bd9Sstevel@tonic-gate static char sccsid[] = "@(#) tcpdmatch.c 1.5 96/02/11 17:01:36"; 18*7c478bd9Sstevel@tonic-gate #endif 19*7c478bd9Sstevel@tonic-gate 20*7c478bd9Sstevel@tonic-gate /* System libraries. */ 21*7c478bd9Sstevel@tonic-gate 22*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 23*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> 24*7c478bd9Sstevel@tonic-gate #include <sys/socket.h> 25*7c478bd9Sstevel@tonic-gate #include <netinet/in.h> 26*7c478bd9Sstevel@tonic-gate #include <arpa/inet.h> 27*7c478bd9Sstevel@tonic-gate #include <netdb.h> 28*7c478bd9Sstevel@tonic-gate #include <stdio.h> 29*7c478bd9Sstevel@tonic-gate #include <syslog.h> 30*7c478bd9Sstevel@tonic-gate #include <setjmp.h> 31*7c478bd9Sstevel@tonic-gate #include <string.h> 32*7c478bd9Sstevel@tonic-gate 33*7c478bd9Sstevel@tonic-gate extern void exit(); 34*7c478bd9Sstevel@tonic-gate extern int optind; 35*7c478bd9Sstevel@tonic-gate extern char *optarg; 36*7c478bd9Sstevel@tonic-gate 37*7c478bd9Sstevel@tonic-gate #ifndef INADDR_NONE 38*7c478bd9Sstevel@tonic-gate #define INADDR_NONE (-1) /* XXX should be 0xffffffff */ 39*7c478bd9Sstevel@tonic-gate #endif 40*7c478bd9Sstevel@tonic-gate 41*7c478bd9Sstevel@tonic-gate #ifndef S_ISDIR 42*7c478bd9Sstevel@tonic-gate #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 43*7c478bd9Sstevel@tonic-gate #endif 44*7c478bd9Sstevel@tonic-gate 45*7c478bd9Sstevel@tonic-gate /* Application-specific. */ 46*7c478bd9Sstevel@tonic-gate 47*7c478bd9Sstevel@tonic-gate #include "tcpd.h" 48*7c478bd9Sstevel@tonic-gate #include "inetcf.h" 49*7c478bd9Sstevel@tonic-gate #include "scaffold.h" 50*7c478bd9Sstevel@tonic-gate 51*7c478bd9Sstevel@tonic-gate static void usage(); 52*7c478bd9Sstevel@tonic-gate static void tcpdmatch(); 53*7c478bd9Sstevel@tonic-gate 54*7c478bd9Sstevel@tonic-gate /* The main program */ 55*7c478bd9Sstevel@tonic-gate 56*7c478bd9Sstevel@tonic-gate int main(argc, argv) 57*7c478bd9Sstevel@tonic-gate int argc; 58*7c478bd9Sstevel@tonic-gate char **argv; 59*7c478bd9Sstevel@tonic-gate { 60*7c478bd9Sstevel@tonic-gate struct hostent *hp; 61*7c478bd9Sstevel@tonic-gate char *myname = argv[0]; 62*7c478bd9Sstevel@tonic-gate char *client; 63*7c478bd9Sstevel@tonic-gate char *server; 64*7c478bd9Sstevel@tonic-gate char *addr; 65*7c478bd9Sstevel@tonic-gate char *user; 66*7c478bd9Sstevel@tonic-gate char *daemon; 67*7c478bd9Sstevel@tonic-gate struct request_info request; 68*7c478bd9Sstevel@tonic-gate int ch; 69*7c478bd9Sstevel@tonic-gate char *inetcf = 0; 70*7c478bd9Sstevel@tonic-gate int count; 71*7c478bd9Sstevel@tonic-gate struct sockaddr_gen server_sin; 72*7c478bd9Sstevel@tonic-gate struct sockaddr_gen client_sin; 73*7c478bd9Sstevel@tonic-gate struct stat st; 74*7c478bd9Sstevel@tonic-gate 75*7c478bd9Sstevel@tonic-gate /* 76*7c478bd9Sstevel@tonic-gate * Show what rule actually matched. 77*7c478bd9Sstevel@tonic-gate */ 78*7c478bd9Sstevel@tonic-gate hosts_access_verbose = 2; 79*7c478bd9Sstevel@tonic-gate 80*7c478bd9Sstevel@tonic-gate /* 81*7c478bd9Sstevel@tonic-gate * Parse the JCL. 82*7c478bd9Sstevel@tonic-gate */ 83*7c478bd9Sstevel@tonic-gate while ((ch = getopt(argc, argv, "di:")) != EOF) { 84*7c478bd9Sstevel@tonic-gate switch (ch) { 85*7c478bd9Sstevel@tonic-gate case 'd': 86*7c478bd9Sstevel@tonic-gate hosts_allow_table = "hosts.allow"; 87*7c478bd9Sstevel@tonic-gate hosts_deny_table = "hosts.deny"; 88*7c478bd9Sstevel@tonic-gate break; 89*7c478bd9Sstevel@tonic-gate case 'i': 90*7c478bd9Sstevel@tonic-gate inetcf = optarg; 91*7c478bd9Sstevel@tonic-gate break; 92*7c478bd9Sstevel@tonic-gate default: 93*7c478bd9Sstevel@tonic-gate usage(myname); 94*7c478bd9Sstevel@tonic-gate /* NOTREACHED */ 95*7c478bd9Sstevel@tonic-gate } 96*7c478bd9Sstevel@tonic-gate } 97*7c478bd9Sstevel@tonic-gate if (argc != optind + 2) 98*7c478bd9Sstevel@tonic-gate usage(myname); 99*7c478bd9Sstevel@tonic-gate 100*7c478bd9Sstevel@tonic-gate /* 101*7c478bd9Sstevel@tonic-gate * When confusion really strikes... 102*7c478bd9Sstevel@tonic-gate */ 103*7c478bd9Sstevel@tonic-gate if (check_path(REAL_DAEMON_DIR, &st) < 0) { 104*7c478bd9Sstevel@tonic-gate tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR); 105*7c478bd9Sstevel@tonic-gate } else if (!S_ISDIR(st.st_mode)) { 106*7c478bd9Sstevel@tonic-gate tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR); 107*7c478bd9Sstevel@tonic-gate } 108*7c478bd9Sstevel@tonic-gate 109*7c478bd9Sstevel@tonic-gate /* 110*7c478bd9Sstevel@tonic-gate * Default is to specify a daemon process name. When daemon@host is 111*7c478bd9Sstevel@tonic-gate * specified, separate the two parts. 112*7c478bd9Sstevel@tonic-gate */ 113*7c478bd9Sstevel@tonic-gate if ((server = split_at(argv[optind], '@')) == 0) 114*7c478bd9Sstevel@tonic-gate server = unknown; 115*7c478bd9Sstevel@tonic-gate if (argv[optind][0] == '/') { 116*7c478bd9Sstevel@tonic-gate daemon = strrchr(argv[optind], '/') + 1; 117*7c478bd9Sstevel@tonic-gate tcpd_warn("%s: daemon name normalized to: %s", argv[optind], daemon); 118*7c478bd9Sstevel@tonic-gate } else { 119*7c478bd9Sstevel@tonic-gate daemon = argv[optind]; 120*7c478bd9Sstevel@tonic-gate } 121*7c478bd9Sstevel@tonic-gate 122*7c478bd9Sstevel@tonic-gate /* 123*7c478bd9Sstevel@tonic-gate * Default is to specify a client hostname or address. When user@host is 124*7c478bd9Sstevel@tonic-gate * specified, separate the two parts. 125*7c478bd9Sstevel@tonic-gate */ 126*7c478bd9Sstevel@tonic-gate if ((client = split_at(argv[optind + 1], '@')) != 0) { 127*7c478bd9Sstevel@tonic-gate user = argv[optind + 1]; 128*7c478bd9Sstevel@tonic-gate } else { 129*7c478bd9Sstevel@tonic-gate client = argv[optind + 1]; 130*7c478bd9Sstevel@tonic-gate user = unknown; 131*7c478bd9Sstevel@tonic-gate } 132*7c478bd9Sstevel@tonic-gate 133*7c478bd9Sstevel@tonic-gate /* 134*7c478bd9Sstevel@tonic-gate * Analyze the inetd (or tlid) configuration file, so that we can warn 135*7c478bd9Sstevel@tonic-gate * the user about services that may not be wrapped, services that are not 136*7c478bd9Sstevel@tonic-gate * configured, or services that are wrapped in an incorrect manner. Allow 137*7c478bd9Sstevel@tonic-gate * for services that are not run from inetd, or that have tcpd access 138*7c478bd9Sstevel@tonic-gate * control built into them. 139*7c478bd9Sstevel@tonic-gate */ 140*7c478bd9Sstevel@tonic-gate inetcf = inet_cfg(inetcf); 141*7c478bd9Sstevel@tonic-gate inet_set("portmap", WR_NOT); 142*7c478bd9Sstevel@tonic-gate inet_set("rpcbind", WR_NOT); 143*7c478bd9Sstevel@tonic-gate switch (inet_get(daemon)) { 144*7c478bd9Sstevel@tonic-gate case WR_UNKNOWN: 145*7c478bd9Sstevel@tonic-gate tcpd_warn("%s: no such process name in %s", daemon, inetcf); 146*7c478bd9Sstevel@tonic-gate break; 147*7c478bd9Sstevel@tonic-gate case WR_NOT: 148*7c478bd9Sstevel@tonic-gate tcpd_warn("%s: service possibly not wrapped", daemon); 149*7c478bd9Sstevel@tonic-gate break; 150*7c478bd9Sstevel@tonic-gate } 151*7c478bd9Sstevel@tonic-gate 152*7c478bd9Sstevel@tonic-gate /* 153*7c478bd9Sstevel@tonic-gate * Check accessibility of access control files. 154*7c478bd9Sstevel@tonic-gate */ 155*7c478bd9Sstevel@tonic-gate (void) check_path(hosts_allow_table, &st); 156*7c478bd9Sstevel@tonic-gate (void) check_path(hosts_deny_table, &st); 157*7c478bd9Sstevel@tonic-gate 158*7c478bd9Sstevel@tonic-gate /* 159*7c478bd9Sstevel@tonic-gate * Fill in what we have figured out sofar. Use socket and DNS routines 160*7c478bd9Sstevel@tonic-gate * for address and name conversions. We attach stdout to the request so 161*7c478bd9Sstevel@tonic-gate * that banner messages will become visible. 162*7c478bd9Sstevel@tonic-gate */ 163*7c478bd9Sstevel@tonic-gate request_init(&request, RQ_DAEMON, daemon, RQ_USER, user, RQ_FILE, 1, 0); 164*7c478bd9Sstevel@tonic-gate sock_methods(&request); 165*7c478bd9Sstevel@tonic-gate 166*7c478bd9Sstevel@tonic-gate /* 167*7c478bd9Sstevel@tonic-gate * If a server hostname is specified, insist that the name maps to at 168*7c478bd9Sstevel@tonic-gate * most one address. eval_hostname() warns the user about name server 169*7c478bd9Sstevel@tonic-gate * problems, while using the request.server structure as a cache for host 170*7c478bd9Sstevel@tonic-gate * address and name conversion results. 171*7c478bd9Sstevel@tonic-gate */ 172*7c478bd9Sstevel@tonic-gate if (NOT_INADDR(server) == 0 || HOSTNAME_KNOWN(server)) { 173*7c478bd9Sstevel@tonic-gate if ((hp = find_inet_addr(server)) == 0) 174*7c478bd9Sstevel@tonic-gate exit(1); 175*7c478bd9Sstevel@tonic-gate memset((char *) &server_sin, 0, sizeof(server_sin)); 176*7c478bd9Sstevel@tonic-gate server_sin.sg_family = hp->h_addrtype; 177*7c478bd9Sstevel@tonic-gate request_set(&request, RQ_SERVER_SIN, &server_sin, 0); 178*7c478bd9Sstevel@tonic-gate 179*7c478bd9Sstevel@tonic-gate for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { 180*7c478bd9Sstevel@tonic-gate memcpy((char *) SGADDRP(&server_sin), addr, hp->h_length); 181*7c478bd9Sstevel@tonic-gate 182*7c478bd9Sstevel@tonic-gate /* 183*7c478bd9Sstevel@tonic-gate * Force evaluation of server host name and address. Host name 184*7c478bd9Sstevel@tonic-gate * conflicts will be reported while eval_hostname() does its job. 185*7c478bd9Sstevel@tonic-gate */ 186*7c478bd9Sstevel@tonic-gate request_set(&request, RQ_SERVER_NAME, "", RQ_SERVER_ADDR, "", 0); 187*7c478bd9Sstevel@tonic-gate if (STR_EQ(eval_hostname(request.server), unknown)) 188*7c478bd9Sstevel@tonic-gate tcpd_warn("host address %s->name lookup failed", 189*7c478bd9Sstevel@tonic-gate eval_hostaddr(request.server)); 190*7c478bd9Sstevel@tonic-gate } 191*7c478bd9Sstevel@tonic-gate if (count > 1) { 192*7c478bd9Sstevel@tonic-gate fprintf(stderr, "Error: %s has more than one address\n", server); 193*7c478bd9Sstevel@tonic-gate fprintf(stderr, "Please specify an address instead\n"); 194*7c478bd9Sstevel@tonic-gate exit(1); 195*7c478bd9Sstevel@tonic-gate } 196*7c478bd9Sstevel@tonic-gate free((char *) hp); 197*7c478bd9Sstevel@tonic-gate } else { 198*7c478bd9Sstevel@tonic-gate request_set(&request, RQ_SERVER_NAME, server, 0); 199*7c478bd9Sstevel@tonic-gate } 200*7c478bd9Sstevel@tonic-gate 201*7c478bd9Sstevel@tonic-gate /* 202*7c478bd9Sstevel@tonic-gate * If a client address is specified, we simulate the effect of client 203*7c478bd9Sstevel@tonic-gate * hostname lookup failure. 204*7c478bd9Sstevel@tonic-gate */ 205*7c478bd9Sstevel@tonic-gate if (numeric_addr(client, NULL, NULL, NULL) == 0) { 206*7c478bd9Sstevel@tonic-gate request_set(&request, RQ_CLIENT_ADDR, client, 0); 207*7c478bd9Sstevel@tonic-gate tcpdmatch(&request); 208*7c478bd9Sstevel@tonic-gate exit(0); 209*7c478bd9Sstevel@tonic-gate } 210*7c478bd9Sstevel@tonic-gate 211*7c478bd9Sstevel@tonic-gate /* 212*7c478bd9Sstevel@tonic-gate * Perhaps they are testing special client hostname patterns that aren't 213*7c478bd9Sstevel@tonic-gate * really host names at all. 214*7c478bd9Sstevel@tonic-gate */ 215*7c478bd9Sstevel@tonic-gate if (NOT_INADDR(client) && HOSTNAME_KNOWN(client) == 0) { 216*7c478bd9Sstevel@tonic-gate request_set(&request, RQ_CLIENT_NAME, client, 0); 217*7c478bd9Sstevel@tonic-gate tcpdmatch(&request); 218*7c478bd9Sstevel@tonic-gate exit(0); 219*7c478bd9Sstevel@tonic-gate } 220*7c478bd9Sstevel@tonic-gate 221*7c478bd9Sstevel@tonic-gate /* 222*7c478bd9Sstevel@tonic-gate * Otherwise, assume that a client hostname is specified, and insist that 223*7c478bd9Sstevel@tonic-gate * the address can be looked up. The reason for this requirement is that 224*7c478bd9Sstevel@tonic-gate * in real life the client address is available (at least with IP). Let 225*7c478bd9Sstevel@tonic-gate * eval_hostname() figure out if this host is properly registered, while 226*7c478bd9Sstevel@tonic-gate * using the request.client structure as a cache for host name and 227*7c478bd9Sstevel@tonic-gate * address conversion results. 228*7c478bd9Sstevel@tonic-gate */ 229*7c478bd9Sstevel@tonic-gate if ((hp = find_inet_addr(client)) == 0) 230*7c478bd9Sstevel@tonic-gate exit(1); 231*7c478bd9Sstevel@tonic-gate memset((char *) &client_sin, 0, sizeof(client_sin)); 232*7c478bd9Sstevel@tonic-gate client_sin.sg_family = hp->h_addrtype; 233*7c478bd9Sstevel@tonic-gate request_set(&request, RQ_CLIENT_SIN, &client_sin, 0); 234*7c478bd9Sstevel@tonic-gate 235*7c478bd9Sstevel@tonic-gate for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { 236*7c478bd9Sstevel@tonic-gate memcpy((char *) SGADDRP(&client_sin), addr, hp->h_length); 237*7c478bd9Sstevel@tonic-gate 238*7c478bd9Sstevel@tonic-gate /* 239*7c478bd9Sstevel@tonic-gate * Force evaluation of client host name and address. Host name 240*7c478bd9Sstevel@tonic-gate * conflicts will be reported while eval_hostname() does its job. 241*7c478bd9Sstevel@tonic-gate */ 242*7c478bd9Sstevel@tonic-gate request_set(&request, RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0); 243*7c478bd9Sstevel@tonic-gate if (STR_EQ(eval_hostname(request.client), unknown)) 244*7c478bd9Sstevel@tonic-gate tcpd_warn("host address %s->name lookup failed", 245*7c478bd9Sstevel@tonic-gate eval_hostaddr(request.client)); 246*7c478bd9Sstevel@tonic-gate tcpdmatch(&request); 247*7c478bd9Sstevel@tonic-gate if (hp->h_addr_list[count + 1]) 248*7c478bd9Sstevel@tonic-gate printf("\n"); 249*7c478bd9Sstevel@tonic-gate } 250*7c478bd9Sstevel@tonic-gate free((char *) hp); 251*7c478bd9Sstevel@tonic-gate exit(0); 252*7c478bd9Sstevel@tonic-gate } 253*7c478bd9Sstevel@tonic-gate 254*7c478bd9Sstevel@tonic-gate /* Explain how to use this program */ 255*7c478bd9Sstevel@tonic-gate 256*7c478bd9Sstevel@tonic-gate static void usage(myname) 257*7c478bd9Sstevel@tonic-gate char *myname; 258*7c478bd9Sstevel@tonic-gate { 259*7c478bd9Sstevel@tonic-gate fprintf(stderr, "usage: %s [-d] [-i inet_conf] daemon[@host] [user@]host\n", 260*7c478bd9Sstevel@tonic-gate myname); 261*7c478bd9Sstevel@tonic-gate fprintf(stderr, " -d: use allow/deny files in current directory\n"); 262*7c478bd9Sstevel@tonic-gate fprintf(stderr, " -i: location of inetd.conf file\n"); 263*7c478bd9Sstevel@tonic-gate exit(1); 264*7c478bd9Sstevel@tonic-gate } 265*7c478bd9Sstevel@tonic-gate 266*7c478bd9Sstevel@tonic-gate /* Print interesting expansions */ 267*7c478bd9Sstevel@tonic-gate 268*7c478bd9Sstevel@tonic-gate static void expand(text, pattern, request) 269*7c478bd9Sstevel@tonic-gate char *text; 270*7c478bd9Sstevel@tonic-gate char *pattern; 271*7c478bd9Sstevel@tonic-gate struct request_info *request; 272*7c478bd9Sstevel@tonic-gate { 273*7c478bd9Sstevel@tonic-gate char buf[BUFSIZ]; 274*7c478bd9Sstevel@tonic-gate 275*7c478bd9Sstevel@tonic-gate if (STR_NE(percent_x(buf, sizeof(buf), pattern, request), unknown)) 276*7c478bd9Sstevel@tonic-gate printf("%s %s\n", text, buf); 277*7c478bd9Sstevel@tonic-gate } 278*7c478bd9Sstevel@tonic-gate 279*7c478bd9Sstevel@tonic-gate /* Try out a (server,client) pair */ 280*7c478bd9Sstevel@tonic-gate 281*7c478bd9Sstevel@tonic-gate static void tcpdmatch(request) 282*7c478bd9Sstevel@tonic-gate struct request_info *request; 283*7c478bd9Sstevel@tonic-gate { 284*7c478bd9Sstevel@tonic-gate int verdict; 285*7c478bd9Sstevel@tonic-gate 286*7c478bd9Sstevel@tonic-gate /* 287*7c478bd9Sstevel@tonic-gate * Show what we really know. Suppress uninteresting noise. 288*7c478bd9Sstevel@tonic-gate */ 289*7c478bd9Sstevel@tonic-gate expand("client: hostname", "%n", request); 290*7c478bd9Sstevel@tonic-gate expand("client: address ", "%a", request); 291*7c478bd9Sstevel@tonic-gate expand("client: username", "%u", request); 292*7c478bd9Sstevel@tonic-gate expand("server: hostname", "%N", request); 293*7c478bd9Sstevel@tonic-gate expand("server: address ", "%A", request); 294*7c478bd9Sstevel@tonic-gate expand("server: process ", "%d", request); 295*7c478bd9Sstevel@tonic-gate 296*7c478bd9Sstevel@tonic-gate /* 297*7c478bd9Sstevel@tonic-gate * Reset stuff that might be changed by options handlers. In dry-run 298*7c478bd9Sstevel@tonic-gate * mode, extension language routines that would not return should inform 299*7c478bd9Sstevel@tonic-gate * us of their plan, by clearing the dry_run flag. This is a bit clumsy 300*7c478bd9Sstevel@tonic-gate * but we must be able to verify hosts with more than one network 301*7c478bd9Sstevel@tonic-gate * address. 302*7c478bd9Sstevel@tonic-gate */ 303*7c478bd9Sstevel@tonic-gate rfc931_timeout = RFC931_TIMEOUT; 304*7c478bd9Sstevel@tonic-gate allow_severity = SEVERITY; 305*7c478bd9Sstevel@tonic-gate deny_severity = LOG_WARNING; 306*7c478bd9Sstevel@tonic-gate dry_run = 1; 307*7c478bd9Sstevel@tonic-gate 308*7c478bd9Sstevel@tonic-gate /* 309*7c478bd9Sstevel@tonic-gate * When paranoid mode is enabled, access is rejected no matter what the 310*7c478bd9Sstevel@tonic-gate * access control rules say. 311*7c478bd9Sstevel@tonic-gate */ 312*7c478bd9Sstevel@tonic-gate #ifdef PARANOID 313*7c478bd9Sstevel@tonic-gate if (STR_EQ(eval_hostname(request->client), paranoid)) { 314*7c478bd9Sstevel@tonic-gate printf("access: denied (PARANOID mode)\n\n"); 315*7c478bd9Sstevel@tonic-gate return; 316*7c478bd9Sstevel@tonic-gate } 317*7c478bd9Sstevel@tonic-gate #endif 318*7c478bd9Sstevel@tonic-gate 319*7c478bd9Sstevel@tonic-gate /* 320*7c478bd9Sstevel@tonic-gate * Report the access control verdict. 321*7c478bd9Sstevel@tonic-gate */ 322*7c478bd9Sstevel@tonic-gate verdict = hosts_access(request); 323*7c478bd9Sstevel@tonic-gate printf("access: %s\n", 324*7c478bd9Sstevel@tonic-gate dry_run == 0 ? "delegated" : 325*7c478bd9Sstevel@tonic-gate verdict ? "granted" : "denied"); 326*7c478bd9Sstevel@tonic-gate } 327