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