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