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