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 <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 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(char *myname); 52 static void tcpdmatch(struct request_info *request); 53 54 /* The main program */ 55 56 int main(argc, argv) 57 int argc; 58 char **argv; 59 { 60 #ifdef INET6 61 struct addrinfo hints, *hp, *res; 62 #else 63 struct hostent *hp; 64 #endif 65 char *myname = argv[0]; 66 char *client; 67 char *server; 68 char *addr; 69 char *user; 70 char *daemon; 71 struct request_info request; 72 int ch; 73 char *inetcf = 0; 74 int count; 75 #ifdef INET6 76 struct sockaddr_storage server_sin; 77 struct sockaddr_storage client_sin; 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 #ifndef INET6 185 memset((char *) &server_sin, 0, sizeof(server_sin)); 186 server_sin.sin_family = AF_INET; 187 #endif 188 request_set(&request, RQ_SERVER_SIN, &server_sin, 0); 189 190 #ifdef INET6 191 for (res = hp, count = 0; res; res = res->ai_next, count++) { 192 memcpy(&server_sin, res->ai_addr, res->ai_addrlen); 193 #else 194 for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { 195 memcpy((char *) &server_sin.sin_addr, addr, 196 sizeof(server_sin.sin_addr)); 197 #endif 198 199 /* 200 * Force evaluation of server host name and address. Host name 201 * conflicts will be reported while eval_hostname() does its job. 202 */ 203 request_set(&request, RQ_SERVER_NAME, "", RQ_SERVER_ADDR, "", 0); 204 if (STR_EQ(eval_hostname(request.server), unknown)) 205 tcpd_warn("host address %s->name lookup failed", 206 eval_hostaddr(request.server)); 207 } 208 if (count > 1) { 209 fprintf(stderr, "Error: %s has more than one address\n", server); 210 fprintf(stderr, "Please specify an address instead\n"); 211 exit(1); 212 } 213 #ifdef INET6 214 freeaddrinfo(hp); 215 #else 216 free((char *) hp); 217 #endif 218 } else { 219 request_set(&request, RQ_SERVER_NAME, server, 0); 220 } 221 222 /* 223 * If a client address is specified, we simulate the effect of client 224 * hostname lookup failure. 225 */ 226 if (dot_quad_addr(client) != INADDR_NONE) { 227 request_set(&request, RQ_CLIENT_ADDR, client, 0); 228 tcpdmatch(&request); 229 exit(0); 230 } 231 #ifdef INET6 232 memset(&hints, 0, sizeof(hints)); 233 hints.ai_family = AF_INET6; 234 hints.ai_socktype = SOCK_STREAM; 235 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; 236 if (getaddrinfo(client, NULL, &hints, &res) == 0) { 237 freeaddrinfo(res); 238 request_set(&request, RQ_CLIENT_ADDR, client, 0); 239 tcpdmatch(&request); 240 exit(0); 241 } 242 #endif 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 #ifdef INET6 265 request_set(&request, RQ_CLIENT_SIN, &client_sin, 0); 266 267 for (res = hp, count = 0; res; res = res->ai_next, count++) { 268 memcpy(&client_sin, res->ai_addr, res->ai_addrlen); 269 270 /* 271 * getnameinfo() doesn't do reverse lookup against link-local 272 * address. So, we pass through host name evaluation against 273 * such addresses. 274 */ 275 if (res->ai_family != AF_INET6 || 276 !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)) { 277 /* 278 * Force evaluation of client host name and address. Host name 279 * conflicts will be reported while eval_hostname() does its job. 280 */ 281 request_set(&request, RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0); 282 if (STR_EQ(eval_hostname(request.client), unknown)) 283 tcpd_warn("host address %s->name lookup failed", 284 eval_hostaddr(request.client)); 285 } 286 tcpdmatch(&request); 287 if (res->ai_next) 288 printf("\n"); 289 } 290 freeaddrinfo(hp); 291 #else 292 memset((char *) &client_sin, 0, sizeof(client_sin)); 293 client_sin.sin_family = AF_INET; 294 request_set(&request, RQ_CLIENT_SIN, &client_sin, 0); 295 296 for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) { 297 memcpy((char *) &client_sin.sin_addr, addr, 298 sizeof(client_sin.sin_addr)); 299 300 /* 301 * Force evaluation of client host name and address. Host name 302 * conflicts will be reported while eval_hostname() does its job. 303 */ 304 request_set(&request, RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0); 305 if (STR_EQ(eval_hostname(request.client), unknown)) 306 tcpd_warn("host address %s->name lookup failed", 307 eval_hostaddr(request.client)); 308 tcpdmatch(&request); 309 if (hp->h_addr_list[count + 1]) 310 printf("\n"); 311 } 312 free((char *) hp); 313 #endif 314 exit(0); 315 } 316 317 /* Explain how to use this program */ 318 319 static void usage(myname) 320 char *myname; 321 { 322 fprintf(stderr, "usage: %s [-d] [-i inet_conf] daemon[@host] [user@]host\n", 323 myname); 324 fprintf(stderr, " -d: use allow/deny files in current directory\n"); 325 fprintf(stderr, " -i: location of inetd.conf file\n"); 326 exit(1); 327 } 328 329 /* Print interesting expansions */ 330 331 static void expand(text, pattern, request) 332 char *text; 333 char *pattern; 334 struct request_info *request; 335 { 336 char buf[BUFSIZ]; 337 338 if (STR_NE(percent_x(buf, sizeof(buf), pattern, request), unknown)) 339 printf("%s %s\n", text, buf); 340 } 341 342 /* Try out a (server,client) pair */ 343 344 static void tcpdmatch(request) 345 struct request_info *request; 346 { 347 int verdict; 348 349 /* 350 * Show what we really know. Suppress uninteresting noise. 351 */ 352 expand("client: hostname", "%n", request); 353 expand("client: address ", "%a", request); 354 expand("client: username", "%u", request); 355 expand("server: hostname", "%N", request); 356 expand("server: address ", "%A", request); 357 expand("server: process ", "%d", request); 358 359 /* 360 * Reset stuff that might be changed by options handlers. In dry-run 361 * mode, extension language routines that would not return should inform 362 * us of their plan, by clearing the dry_run flag. This is a bit clumsy 363 * but we must be able to verify hosts with more than one network 364 * address. 365 */ 366 rfc931_timeout = RFC931_TIMEOUT; 367 allow_severity = SEVERITY; 368 deny_severity = LOG_WARNING; 369 dry_run = 1; 370 371 /* 372 * When paranoid mode is enabled, access is rejected no matter what the 373 * access control rules say. 374 */ 375 #ifdef PARANOID 376 if (STR_EQ(eval_hostname(request->client), paranoid)) { 377 printf("access: denied (PARANOID mode)\n\n"); 378 return; 379 } 380 #endif 381 382 /* 383 * Report the access control verdict. 384 */ 385 verdict = hosts_access(request); 386 printf("access: %s\n", 387 dry_run == 0 ? "delegated" : 388 verdict ? "granted" : "denied"); 389 } 390