1 1.13 sevan /* $NetBSD: tcpdchk.c,v 1.13 2018/01/23 21:06:26 sevan Exp $ */ 2 1.2 christos 3 1.1 cjs /* 4 1.1 cjs * tcpdchk - examine all tcpd access control rules and inetd.conf entries 5 1.1 cjs * 6 1.1 cjs * Usage: tcpdchk [-a] [-d] [-i inet_conf] [-v] 7 1.1 cjs * 8 1.1 cjs * -a: complain about implicit "allow" at end of rule. 9 1.1 cjs * 10 1.1 cjs * -d: rules in current directory. 11 1.1 cjs * 12 1.1 cjs * -i: location of inetd.conf file. 13 1.1 cjs * 14 1.1 cjs * -v: show all rules. 15 1.1 cjs * 16 1.1 cjs * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. 17 1.1 cjs */ 18 1.1 cjs 19 1.2 christos #include <sys/cdefs.h> 20 1.1 cjs #ifndef lint 21 1.2 christos #if 0 22 1.8 itojun static char sccsid[] = "@(#) tcpdchk.c 1.8 97/02/12 02:13:25"; 23 1.2 christos #else 24 1.13 sevan __RCSID("$NetBSD: tcpdchk.c,v 1.13 2018/01/23 21:06:26 sevan Exp $"); 25 1.2 christos #endif 26 1.1 cjs #endif 27 1.1 cjs 28 1.1 cjs /* System libraries. */ 29 1.1 cjs 30 1.1 cjs #include <sys/types.h> 31 1.1 cjs #include <sys/stat.h> 32 1.1 cjs #include <netinet/in.h> 33 1.1 cjs #include <arpa/inet.h> 34 1.1 cjs #include <stdio.h> 35 1.1 cjs #include <syslog.h> 36 1.1 cjs #include <setjmp.h> 37 1.1 cjs #include <errno.h> 38 1.1 cjs #include <netdb.h> 39 1.1 cjs #include <string.h> 40 1.2 christos #include <stdlib.h> 41 1.2 christos #include <unistd.h> 42 1.1 cjs 43 1.1 cjs #ifndef INADDR_NONE 44 1.1 cjs #define INADDR_NONE (-1) /* XXX should be 0xffffffff */ 45 1.1 cjs #endif 46 1.1 cjs 47 1.1 cjs #ifndef S_ISDIR 48 1.1 cjs #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 49 1.1 cjs #endif 50 1.1 cjs 51 1.1 cjs /* Application-specific. */ 52 1.1 cjs 53 1.1 cjs #include "tcpd.h" 54 1.1 cjs #include "inetcf.h" 55 1.1 cjs #include "scaffold.h" 56 1.1 cjs 57 1.5 christos #ifdef NO_NETGRENT 58 1.5 christos /* SCO has no *netgrent() support */ 59 1.5 christos #else 60 1.5 christos # ifdef NETGROUP 61 1.5 christos # include <netgroup.h> 62 1.5 christos # endif 63 1.5 christos #endif 64 1.5 christos 65 1.1 cjs /* 66 1.1 cjs * Stolen from hosts_access.c... 67 1.1 cjs */ 68 1.12 matt static const char sep[] = ", \t\n"; 69 1.1 cjs 70 1.1 cjs #define BUFLEN 2048 71 1.1 cjs 72 1.1 cjs int resident = 0; 73 1.1 cjs int hosts_access_verbose = 0; 74 1.12 matt const char *hosts_allow_table = HOSTS_ALLOW; 75 1.12 matt const char *hosts_deny_table = HOSTS_DENY; 76 1.1 cjs extern jmp_buf tcpd_buf; 77 1.1 cjs 78 1.1 cjs /* 79 1.1 cjs * Local stuff. 80 1.1 cjs */ 81 1.12 matt static void usage(void); 82 1.12 matt static void parse_table(const char *, struct request_info *); 83 1.12 matt static void print_list(char *, char *); 84 1.12 matt static void check_daemon_list(char *); 85 1.12 matt static void check_client_list(char *); 86 1.12 matt static void check_daemon(char *); 87 1.12 matt static void check_user(char *); 88 1.10 itojun #ifdef INET6 89 1.12 matt static int check_inet_addr(char *); 90 1.10 itojun #endif 91 1.12 matt static int check_host(char *); 92 1.12 matt static int reserved_name(char *); 93 1.2 christos 94 1.1 cjs #define PERMIT 1 95 1.1 cjs #define DENY 0 96 1.1 cjs 97 1.1 cjs #define YES 1 98 1.1 cjs #define NO 0 99 1.1 cjs 100 1.1 cjs static int defl_verdict; 101 1.1 cjs static char *myname; 102 1.1 cjs static int allow_check; 103 1.1 cjs static char *inetcf; 104 1.1 cjs 105 1.12 matt int 106 1.12 matt main(int argc, char **argv) 107 1.1 cjs { 108 1.1 cjs struct request_info request; 109 1.1 cjs struct stat st; 110 1.1 cjs int c; 111 1.1 cjs 112 1.1 cjs myname = argv[0]; 113 1.1 cjs 114 1.1 cjs /* 115 1.1 cjs * Parse the JCL. 116 1.1 cjs */ 117 1.4 lukem while ((c = getopt(argc, argv, "adi:v")) != -1) { 118 1.1 cjs switch (c) { 119 1.1 cjs case 'a': 120 1.1 cjs allow_check = 1; 121 1.1 cjs break; 122 1.1 cjs case 'd': 123 1.1 cjs hosts_allow_table = "hosts.allow"; 124 1.1 cjs hosts_deny_table = "hosts.deny"; 125 1.1 cjs break; 126 1.1 cjs case 'i': 127 1.1 cjs inetcf = optarg; 128 1.1 cjs break; 129 1.1 cjs case 'v': 130 1.1 cjs hosts_access_verbose++; 131 1.1 cjs break; 132 1.1 cjs default: 133 1.1 cjs usage(); 134 1.1 cjs /* NOTREACHED */ 135 1.1 cjs } 136 1.1 cjs } 137 1.1 cjs if (argc != optind) 138 1.1 cjs usage(); 139 1.1 cjs 140 1.1 cjs /* 141 1.1 cjs * When confusion really strikes... 142 1.1 cjs */ 143 1.1 cjs if (check_path(REAL_DAEMON_DIR, &st) < 0) { 144 1.1 cjs tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR); 145 1.1 cjs } else if (!S_ISDIR(st.st_mode)) { 146 1.1 cjs tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR); 147 1.1 cjs } 148 1.1 cjs 149 1.1 cjs /* 150 1.1 cjs * Process the inet configuration file (or its moral equivalent). This 151 1.1 cjs * information is used later to find references in hosts.allow/deny to 152 1.1 cjs * unwrapped services, and other possible problems. 153 1.1 cjs */ 154 1.1 cjs inetcf = inet_cfg(inetcf); 155 1.1 cjs if (hosts_access_verbose) 156 1.1 cjs printf("Using network configuration file: %s\n", inetcf); 157 1.1 cjs 158 1.1 cjs /* 159 1.1 cjs * These are not run from inetd but may have built-in access control. 160 1.1 cjs */ 161 1.1 cjs inet_set("portmap", WR_NOT); 162 1.1 cjs inet_set("rpcbind", WR_NOT); 163 1.1 cjs 164 1.1 cjs /* 165 1.1 cjs * Check accessibility of access control files. 166 1.1 cjs */ 167 1.1 cjs (void) check_path(hosts_allow_table, &st); 168 1.1 cjs (void) check_path(hosts_deny_table, &st); 169 1.1 cjs 170 1.1 cjs /* 171 1.1 cjs * Fake up an arbitrary service request. 172 1.1 cjs */ 173 1.1 cjs request_init(&request, 174 1.1 cjs RQ_DAEMON, "daemon_name", 175 1.1 cjs RQ_SERVER_NAME, "server_hostname", 176 1.1 cjs RQ_SERVER_ADDR, "server_addr", 177 1.1 cjs RQ_USER, "user_name", 178 1.1 cjs RQ_CLIENT_NAME, "client_hostname", 179 1.1 cjs RQ_CLIENT_ADDR, "client_addr", 180 1.1 cjs RQ_FILE, 1, 181 1.1 cjs 0); 182 1.1 cjs 183 1.1 cjs /* 184 1.1 cjs * Examine all access-control rules. 185 1.1 cjs */ 186 1.1 cjs defl_verdict = PERMIT; 187 1.1 cjs parse_table(hosts_allow_table, &request); 188 1.1 cjs defl_verdict = DENY; 189 1.1 cjs parse_table(hosts_deny_table, &request); 190 1.1 cjs return (0); 191 1.1 cjs } 192 1.1 cjs 193 1.1 cjs /* usage - explain */ 194 1.1 cjs 195 1.12 matt static void 196 1.12 matt usage(void) 197 1.1 cjs { 198 1.1 cjs fprintf(stderr, "usage: %s [-a] [-d] [-i inet_conf] [-v]\n", myname); 199 1.1 cjs fprintf(stderr, " -a: report rules with implicit \"ALLOW\" at end\n"); 200 1.1 cjs fprintf(stderr, " -d: use allow/deny files in current directory\n"); 201 1.1 cjs fprintf(stderr, " -i: location of inetd.conf file\n"); 202 1.1 cjs fprintf(stderr, " -v: list all rules\n"); 203 1.1 cjs exit(1); 204 1.1 cjs } 205 1.1 cjs 206 1.1 cjs /* parse_table - like table_match(), but examines _all_ entries */ 207 1.1 cjs 208 1.12 matt static void 209 1.12 matt parse_table(const char *table, struct request_info *request) 210 1.1 cjs { 211 1.1 cjs FILE *fp; 212 1.12 matt volatile int real_verdict; 213 1.1 cjs char sv_list[BUFLEN]; /* becomes list of daemons */ 214 1.1 cjs char *cl_list; /* becomes list of requests */ 215 1.1 cjs char *sh_cmd; /* becomes optional shell command */ 216 1.1 cjs int verdict; 217 1.12 matt volatile struct tcpd_context saved_context; 218 1.1 cjs 219 1.1 cjs saved_context = tcpd_context; /* stupid compilers */ 220 1.1 cjs 221 1.2 christos if ((fp = fopen(table, "r")) != NULL) { 222 1.1 cjs tcpd_context.file = table; 223 1.1 cjs tcpd_context.line = 0; 224 1.1 cjs while (xgets(sv_list, sizeof(sv_list), fp)) { 225 1.1 cjs if (sv_list[strlen(sv_list) - 1] != '\n') { 226 1.1 cjs tcpd_warn("missing newline or line too long"); 227 1.1 cjs continue; 228 1.1 cjs } 229 1.1 cjs if (sv_list[0] == '#' || sv_list[strspn(sv_list, " \t\r\n")] == 0) 230 1.1 cjs continue; 231 1.1 cjs if ((cl_list = split_at(sv_list, ':')) == 0) { 232 1.1 cjs tcpd_warn("missing \":\" separator"); 233 1.1 cjs continue; 234 1.1 cjs } 235 1.1 cjs sh_cmd = split_at(cl_list, ':'); 236 1.1 cjs 237 1.1 cjs if (hosts_access_verbose) 238 1.1 cjs printf("\n>>> Rule %s line %d:\n", 239 1.1 cjs tcpd_context.file, tcpd_context.line); 240 1.1 cjs 241 1.1 cjs if (hosts_access_verbose) 242 1.1 cjs print_list("daemons: ", sv_list); 243 1.1 cjs check_daemon_list(sv_list); 244 1.1 cjs 245 1.1 cjs if (hosts_access_verbose) 246 1.1 cjs print_list("clients: ", cl_list); 247 1.1 cjs check_client_list(cl_list); 248 1.1 cjs 249 1.1 cjs #ifdef PROCESS_OPTIONS 250 1.1 cjs real_verdict = defl_verdict; 251 1.1 cjs if (sh_cmd) { 252 1.8 itojun verdict = setjmp(tcpd_buf); 253 1.8 itojun if (verdict != 0) { 254 1.1 cjs real_verdict = (verdict == AC_PERMIT); 255 1.1 cjs } else { 256 1.1 cjs dry_run = 1; 257 1.1 cjs process_options(sh_cmd, request); 258 1.1 cjs if (dry_run == 1 && real_verdict && allow_check) 259 1.1 cjs tcpd_warn("implicit \"allow\" at end of rule"); 260 1.1 cjs } 261 1.1 cjs } else if (defl_verdict && allow_check) { 262 1.1 cjs tcpd_warn("implicit \"allow\" at end of rule"); 263 1.1 cjs } 264 1.1 cjs if (hosts_access_verbose) 265 1.1 cjs printf("access: %s\n", real_verdict ? "granted" : "denied"); 266 1.1 cjs #else 267 1.1 cjs if (sh_cmd) 268 1.1 cjs shell_cmd(percent_x(buf, sizeof(buf), sh_cmd, request)); 269 1.1 cjs if (hosts_access_verbose) 270 1.1 cjs printf("access: %s\n", defl_verdict ? "granted" : "denied"); 271 1.1 cjs #endif 272 1.1 cjs } 273 1.1 cjs (void) fclose(fp); 274 1.1 cjs } else if (errno != ENOENT) { 275 1.1 cjs tcpd_warn("cannot open %s: %m", table); 276 1.1 cjs } 277 1.1 cjs tcpd_context = saved_context; 278 1.1 cjs } 279 1.1 cjs 280 1.1 cjs /* print_list - pretty-print a list */ 281 1.1 cjs 282 1.13 sevan static void print_list(char *title, char *list) 283 1.1 cjs { 284 1.1 cjs char buf[BUFLEN]; 285 1.1 cjs char *cp; 286 1.1 cjs char *next; 287 1.1 cjs 288 1.1 cjs fputs(title, stdout); 289 1.11 itojun strlcpy(buf, list, sizeof(buf)); 290 1.1 cjs 291 1.1 cjs for (cp = strtok(buf, sep); cp != 0; cp = next) { 292 1.1 cjs fputs(cp, stdout); 293 1.1 cjs next = strtok((char *) 0, sep); 294 1.1 cjs if (next != 0) 295 1.1 cjs fputs(" ", stdout); 296 1.1 cjs } 297 1.1 cjs fputs("\n", stdout); 298 1.1 cjs } 299 1.1 cjs 300 1.1 cjs /* check_daemon_list - criticize daemon list */ 301 1.1 cjs 302 1.13 sevan static void check_daemon_list(char *list) 303 1.1 cjs { 304 1.1 cjs char buf[BUFLEN]; 305 1.1 cjs char *cp; 306 1.1 cjs char *host; 307 1.1 cjs int daemons = 0; 308 1.1 cjs 309 1.11 itojun strlcpy(buf, list, sizeof(buf)); 310 1.1 cjs 311 1.1 cjs for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) { 312 1.1 cjs if (STR_EQ(cp, "EXCEPT")) { 313 1.1 cjs daemons = 0; 314 1.1 cjs } else { 315 1.1 cjs daemons++; 316 1.1 cjs if ((host = split_at(cp + 1, '@')) != 0 && check_host(host) > 1) { 317 1.1 cjs tcpd_warn("host %s has more than one address", host); 318 1.1 cjs tcpd_warn("(consider using an address instead)"); 319 1.1 cjs } 320 1.1 cjs check_daemon(cp); 321 1.1 cjs } 322 1.1 cjs } 323 1.1 cjs if (daemons == 0) 324 1.1 cjs tcpd_warn("daemon list is empty or ends in EXCEPT"); 325 1.1 cjs } 326 1.1 cjs 327 1.1 cjs /* check_client_list - criticize client list */ 328 1.1 cjs 329 1.13 sevan static void check_client_list(char *list) 330 1.1 cjs { 331 1.1 cjs char buf[BUFLEN]; 332 1.1 cjs char *cp; 333 1.1 cjs char *host; 334 1.1 cjs int clients = 0; 335 1.9 itojun #ifdef INET6 336 1.9 itojun int l; 337 1.9 itojun #endif 338 1.1 cjs 339 1.11 itojun strlcpy(buf, list, sizeof(buf)); 340 1.1 cjs 341 1.1 cjs for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) { 342 1.9 itojun #ifdef INET6 343 1.9 itojun l = strlen(cp); 344 1.9 itojun if (cp[0] == '[' && cp[l - 1] == ']') { 345 1.9 itojun cp[l - 1] = '\0'; 346 1.9 itojun cp++; 347 1.9 itojun } 348 1.9 itojun #endif 349 1.1 cjs if (STR_EQ(cp, "EXCEPT")) { 350 1.1 cjs clients = 0; 351 1.1 cjs } else { 352 1.1 cjs clients++; 353 1.2 christos if ((host = split_at(cp + 1, '@')) != NULL) { /* user@host */ 354 1.1 cjs check_user(cp); 355 1.1 cjs check_host(host); 356 1.1 cjs } else { 357 1.1 cjs check_host(cp); 358 1.1 cjs } 359 1.1 cjs } 360 1.1 cjs } 361 1.1 cjs if (clients == 0) 362 1.1 cjs tcpd_warn("client list is empty or ends in EXCEPT"); 363 1.1 cjs } 364 1.1 cjs 365 1.1 cjs /* check_daemon - criticize daemon pattern */ 366 1.1 cjs 367 1.13 sevan static void check_daemon(char *pat) 368 1.1 cjs { 369 1.1 cjs if (pat[0] == '@') { 370 1.1 cjs tcpd_warn("%s: daemon name begins with \"@\"", pat); 371 1.1 cjs } else if (pat[0] == '.') { 372 1.1 cjs tcpd_warn("%s: daemon name begins with dot", pat); 373 1.1 cjs } else if (pat[strlen(pat) - 1] == '.') { 374 1.1 cjs tcpd_warn("%s: daemon name ends in dot", pat); 375 1.1 cjs } else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown)) { 376 1.1 cjs /* void */ ; 377 1.1 cjs } else if (STR_EQ(pat, "FAIL")) { /* obsolete */ 378 1.1 cjs tcpd_warn("FAIL is no longer recognized"); 379 1.1 cjs tcpd_warn("(use EXCEPT or DENY instead)"); 380 1.1 cjs } else if (reserved_name(pat)) { 381 1.1 cjs tcpd_warn("%s: daemon name may be reserved word", pat); 382 1.1 cjs } else { 383 1.1 cjs switch (inet_get(pat)) { 384 1.1 cjs case WR_UNKNOWN: 385 1.1 cjs tcpd_warn("%s: no such process name in %s", pat, inetcf); 386 1.1 cjs inet_set(pat, WR_YES); /* shut up next time */ 387 1.1 cjs break; 388 1.1 cjs case WR_NOT: 389 1.1 cjs tcpd_warn("%s: service possibly not wrapped", pat); 390 1.1 cjs inet_set(pat, WR_YES); 391 1.1 cjs break; 392 1.1 cjs } 393 1.1 cjs } 394 1.1 cjs } 395 1.1 cjs 396 1.1 cjs /* check_user - criticize user pattern */ 397 1.1 cjs 398 1.13 sevan static void check_user(char *pat) 399 1.1 cjs { 400 1.1 cjs if (pat[0] == '@') { /* @netgroup */ 401 1.1 cjs tcpd_warn("%s: user name begins with \"@\"", pat); 402 1.1 cjs } else if (pat[0] == '.') { 403 1.1 cjs tcpd_warn("%s: user name begins with dot", pat); 404 1.1 cjs } else if (pat[strlen(pat) - 1] == '.') { 405 1.1 cjs tcpd_warn("%s: user name ends in dot", pat); 406 1.1 cjs } else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown) 407 1.1 cjs || STR_EQ(pat, "KNOWN")) { 408 1.1 cjs /* void */ ; 409 1.1 cjs } else if (STR_EQ(pat, "FAIL")) { /* obsolete */ 410 1.1 cjs tcpd_warn("FAIL is no longer recognized"); 411 1.1 cjs tcpd_warn("(use EXCEPT or DENY instead)"); 412 1.1 cjs } else if (reserved_name(pat)) { 413 1.1 cjs tcpd_warn("%s: user name may be reserved word", pat); 414 1.1 cjs } 415 1.1 cjs } 416 1.1 cjs 417 1.10 itojun #ifdef INET6 418 1.13 sevan static int check_inet_addr(char *pat) 419 1.10 itojun { 420 1.10 itojun struct addrinfo *res; 421 1.10 itojun 422 1.10 itojun res = find_inet_addr(pat, AI_NUMERICHOST); 423 1.10 itojun if (res) { 424 1.10 itojun freeaddrinfo(res); 425 1.10 itojun return 1; 426 1.10 itojun } else 427 1.10 itojun return 0; 428 1.10 itojun } 429 1.10 itojun #endif 430 1.10 itojun 431 1.1 cjs /* check_host - criticize host pattern */ 432 1.13 sevan static int check_host(char *pat) 433 1.1 cjs { 434 1.1 cjs char *mask; 435 1.1 cjs int addr_count = 1; 436 1.1 cjs 437 1.1 cjs if (pat[0] == '@') { /* @netgroup */ 438 1.1 cjs #ifdef NO_NETGRENT 439 1.1 cjs /* SCO has no *netgrent() support */ 440 1.1 cjs #else 441 1.1 cjs #ifdef NETGROUP 442 1.5 christos const char *machinep; 443 1.5 christos const char *userp; 444 1.5 christos const char *domainp; 445 1.1 cjs 446 1.1 cjs setnetgrent(pat + 1); 447 1.1 cjs if (getnetgrent(&machinep, &userp, &domainp) == 0) 448 1.1 cjs tcpd_warn("%s: unknown or empty netgroup", pat + 1); 449 1.1 cjs endnetgrent(); 450 1.1 cjs #else 451 1.1 cjs tcpd_warn("netgroup support disabled"); 452 1.1 cjs #endif 453 1.1 cjs #endif 454 1.2 christos } else if ((mask = split_at(pat, '/')) != NULL) { /* network/netmask */ 455 1.9 itojun #ifdef INET6 456 1.10 itojun char *ep; 457 1.9 itojun #endif 458 1.9 itojun if (dot_quad_addr(pat, NULL) != INADDR_NONE 459 1.9 itojun || dot_quad_addr(mask, NULL) != INADDR_NONE) 460 1.9 itojun ; /*okay*/ 461 1.9 itojun #ifdef INET6 462 1.10 itojun else if (check_inet_addr(pat) && check_inet_addr(mask)) 463 1.9 itojun ; /*okay*/ 464 1.10 itojun else if (check_inet_addr(pat) && 465 1.10 itojun (ep = NULL, strtoul(mask, &ep, 10), ep && !*ep)) 466 1.9 itojun ; /*okay*/ 467 1.9 itojun #endif 468 1.9 itojun else 469 1.1 cjs tcpd_warn("%s/%s: bad net/mask pattern", pat, mask); 470 1.1 cjs } else if (STR_EQ(pat, "FAIL")) { /* obsolete */ 471 1.1 cjs tcpd_warn("FAIL is no longer recognized"); 472 1.1 cjs tcpd_warn("(use EXCEPT or DENY instead)"); 473 1.1 cjs } else if (reserved_name(pat)) { /* other reserved */ 474 1.1 cjs /* void */ ; 475 1.1 cjs } else if (NOT_INADDR(pat)) { /* internet name */ 476 1.1 cjs if (pat[strlen(pat) - 1] == '.') { 477 1.1 cjs tcpd_warn("%s: domain or host name ends in dot", pat); 478 1.1 cjs } else if (pat[0] != '.') { 479 1.1 cjs addr_count = check_dns(pat); 480 1.1 cjs } 481 1.1 cjs } else { /* numeric form */ 482 1.1 cjs if (STR_EQ(pat, "0.0.0.0") || STR_EQ(pat, "255.255.255.255")) { 483 1.1 cjs /* void */ ; 484 1.1 cjs } else if (pat[0] == '.') { 485 1.1 cjs tcpd_warn("%s: network number begins with dot", pat); 486 1.1 cjs } else if (pat[strlen(pat) - 1] != '.') { 487 1.1 cjs check_dns(pat); 488 1.1 cjs } 489 1.1 cjs } 490 1.1 cjs return (addr_count); 491 1.1 cjs } 492 1.1 cjs 493 1.1 cjs /* reserved_name - determine if name is reserved */ 494 1.1 cjs 495 1.13 sevan static int reserved_name(char *pat) 496 1.1 cjs { 497 1.1 cjs return (STR_EQ(pat, unknown) 498 1.1 cjs || STR_EQ(pat, "KNOWN") 499 1.1 cjs || STR_EQ(pat, paranoid) 500 1.1 cjs || STR_EQ(pat, "ALL") 501 1.1 cjs || STR_EQ(pat, "LOCAL")); 502 1.1 cjs } 503