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

Defined functions

check_client_list defined in line 304; used 2 times
check_daemon defined in line 333; used 2 times
check_daemon_list defined in line 276; used 2 times
check_host defined in line 387; used 4 times
check_user defined in line 365; used 2 times
main defined in line 85; never used
parse_table defined in line 179; used 3 times
print_list defined in line 254; used 3 times
reserved_name defined in line 439; used 4 times
usage defined in line 167; used 3 times

Defined variables

allow_check defined in line 82; used 3 times
defl_verdict defined in line 80; used 5 times
hosts_access_verbose defined in line 56; used 7 times
hosts_allow_table defined in line 57; used 3 times
hosts_deny_table defined in line 58; used 3 times
inetcf defined in line 83; used 5 times
myname defined in line 81; used 2 times
sccsid defined in line 18; never used
sep defined in line 52; used 6 times

Defined macros

BUFLEN defined in line 54; used 4 times
DENY defined in line 75; used 1 times
INADDR_NONE defined in line 40; used 3 times
NO defined in line 78; never used
PERMIT defined in line 74; used 1 times
YES defined in line 77; never used
Last modified: 1995-01-30
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1656
Valid CSS Valid XHTML 1.0 Strict