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