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