tcpdmatch.c revision 1.6 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