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