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