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