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