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