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