1 /* $NetBSD: tcpdmatch.c,v 1.7 2002/06/06 21:28:50 itojun Exp $ */
4 * tcpdmatch - explain what tcpd would do in a specific case
6 * usage: tcpdmatch [-d] [-i inet_conf] daemon[@host] [user@]host
8 * -d: use the access control tables in the current directory.
10 * -i: location of inetd.conf file.
12 * All errors are reported to the standard error stream, including the errors
13 * that would normally be reported via the syslog daemon.
15 * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
18 #include <sys/cdefs.h>
21 static char sccsid
[] = "@(#) tcpdmatch.c 1.5 96/02/11 17:01:36";
23 __RCSID("$NetBSD: tcpdmatch.c,v 1.7 2002/06/06 21:28:50 itojun Exp $");
27 /* System libraries. */
29 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
43 #define INADDR_NONE (-1) /* XXX should be 0xffffffff */
47 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
50 /* Application-specific. */
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 **));
61 /* The main program */
67 struct addrinfo
*res
, *res0
;
68 char *myname
= argv
[0];
73 struct request_info request
;
77 struct sockaddr_storage server_ss
;
78 struct sockaddr_storage client_ss
;
82 * Show what rule actually matched.
84 hosts_access_verbose
= 2;
89 while ((ch
= getopt(argc
, argv
, "di:")) != -1) {
92 hosts_allow_table
= "hosts.allow";
93 hosts_deny_table
= "hosts.deny";
103 if (argc
!= optind
+ 2)
107 * When confusion really strikes...
109 if (check_path(REAL_DAEMON_DIR
, &st
) < 0) {
110 tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR
);
111 } else if (!S_ISDIR(st
.st_mode
)) {
112 tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR
);
116 * Default is to specify a daemon process name. When daemon@host is
117 * specified, separate the two parts.
119 if ((server
= split_at(argv
[optind
], '@')) == 0)
121 if (argv
[optind
][0] == '/') {
122 daemon
= strrchr(argv
[optind
], '/') + 1;
123 tcpd_warn("%s: daemon name normalized to: %s", argv
[optind
], daemon
);
125 daemon
= argv
[optind
];
129 * Default is to specify a client hostname or address. When user@host is
130 * specified, separate the two parts.
132 if ((client
= split_at(argv
[optind
+ 1], '@')) != 0) {
133 user
= argv
[optind
+ 1];
135 client
= argv
[optind
+ 1];
140 * Analyze the inetd (or tlid) configuration file, so that we can warn
141 * the user about services that may not be wrapped, services that are not
142 * configured, or services that are wrapped in an incorrect manner. Allow
143 * for services that are not run from inetd, or that have tcpd access
144 * control built into them.
146 inetcf
= inet_cfg(inetcf
);
147 inet_set("portmap", WR_NOT
);
148 inet_set("rpcbind", WR_NOT
);
149 switch (inet_get(daemon
)) {
151 tcpd_warn("%s: no such process name in %s", daemon
, inetcf
);
154 tcpd_warn("%s: service possibly not wrapped", daemon
);
159 * Check accessibility of access control files.
161 (void) check_path(hosts_allow_table
, &st
);
162 (void) check_path(hosts_deny_table
, &st
);
165 * Fill in what we have figured out sofar. Use socket and DNS routines
166 * for address and name conversions. We attach stdout to the request so
167 * that banner messages will become visible.
169 request_init(&request
, RQ_DAEMON
, daemon
, RQ_USER
, user
, RQ_FILE
, 1, 0);
170 sock_methods(&request
);
173 * If a server hostname is specified, insist that the name maps to at
174 * most one address. eval_hostname() warns the user about name server
175 * problems, while using the request.server structure as a cache for host
176 * address and name conversion results.
178 if (NOT_INADDR(server
) == 0 || HOSTNAME_KNOWN(server
)) {
179 if ((res0
= find_inet_addr(server
, 0)) == NULL
)
181 memset((char *) &server_ss
, 0, sizeof(server_ss
));
182 request_set(&request
, RQ_SERVER_SIN
, &server_ss
, 0);
185 for (res
= res0
; res
; res
= res
->ai_next
) {
187 if (res
->ai_addrlen
> sizeof(server_ss
))
189 memcpy(&server_ss
, res
->ai_addr
, res
->ai_addrlen
);
192 * Force evaluation of server host name and address. Host name
193 * conflicts will be reported while eval_hostname() does its job.
195 request_set(&request
, RQ_SERVER_NAME
, "", RQ_SERVER_ADDR
, "", 0);
196 if (STR_EQ(eval_hostname(request
.server
), unknown
))
197 tcpd_warn("host address %s->name lookup failed",
198 eval_hostaddr(request
.server
));
201 fprintf(stderr
, "Error: %s has more than one address\n", server
);
202 fprintf(stderr
, "Please specify an address instead\n");
207 request_set(&request
, RQ_SERVER_NAME
, server
, 0);
211 * If a client address is specified, we simulate the effect of client
212 * hostname lookup failure.
214 res0
= find_inet_addr(client
, AI_NUMERICHOST
);
215 if (res0
&& !res0
->ai_next
) {
216 request_set(&request
, RQ_CLIENT_SIN
, res0
->ai_addr
);
225 * Perhaps they are testing special client hostname patterns that aren't
226 * really host names at all.
228 if (NOT_INADDR(client
) && HOSTNAME_KNOWN(client
) == 0) {
229 request_set(&request
, RQ_CLIENT_NAME
, client
, 0);
235 * Otherwise, assume that a client hostname is specified, and insist that
236 * the address can be looked up. The reason for this requirement is that
237 * in real life the client address is available (at least with IP). Let
238 * eval_hostname() figure out if this host is properly registered, while
239 * using the request.client structure as a cache for host name and
240 * address conversion results.
242 if ((res0
= find_inet_addr(client
, 0)) == NULL
)
244 memset((char *) &client_ss
, 0, sizeof(client_ss
));
245 request_set(&request
, RQ_CLIENT_SIN
, &client_ss
, 0);
248 for (res
= res0
; res
; res
= res
->ai_next
) {
250 if (res
->ai_addrlen
> sizeof(client_ss
))
252 memcpy(&client_ss
, res
->ai_addr
, res
->ai_addrlen
);
255 * Force evaluation of client host name and address. Host name
256 * conflicts will be reported while eval_hostname() does its job.
258 request_set(&request
, RQ_CLIENT_NAME
, "", RQ_CLIENT_ADDR
, "", 0);
259 if (STR_EQ(eval_hostname(request
.client
), unknown
))
260 tcpd_warn("host address %s->name lookup failed",
261 eval_hostaddr(request
.client
));
270 /* Explain how to use this program */
272 static void usage(myname
)
275 fprintf(stderr
, "usage: %s [-d] [-i inet_conf] daemon[@host] [user@]host\n",
277 fprintf(stderr
, " -d: use allow/deny files in current directory\n");
278 fprintf(stderr
, " -i: location of inetd.conf file\n");
282 /* Print interesting expansions */
284 static void expand(text
, pattern
, request
)
287 struct request_info
*request
;
291 if (STR_NE(percent_x(buf
, sizeof(buf
), pattern
, request
), unknown
))
292 printf("%s %s\n", text
, buf
);
295 /* Try out a (server,client) pair */
297 static void tcpdmatch(request
)
298 struct request_info
*request
;
303 * Show what we really know. Suppress uninteresting noise.
305 expand("client: hostname", "%n", request
);
306 expand("client: address ", "%a", request
);
307 expand("client: username", "%u", request
);
308 expand("server: hostname", "%N", request
);
309 expand("server: address ", "%A", request
);
310 expand("server: process ", "%d", request
);
313 * Reset stuff that might be changed by options handlers. In dry-run
314 * mode, extension language routines that would not return should inform
315 * us of their plan, by clearing the dry_run flag. This is a bit clumsy
316 * but we must be able to verify hosts with more than one network
319 rfc931_timeout
= RFC931_TIMEOUT
;
320 allow_severity
= SEVERITY
;
321 deny_severity
= LOG_WARNING
;
325 * When paranoid mode is enabled, access is rejected no matter what the
326 * access control rules say.
329 if (STR_EQ(eval_hostname(request
->client
), paranoid
)) {
330 printf("access: denied (PARANOID mode)\n\n");
336 * Report the access control verdict.
338 verdict
= hosts_access(request
);
339 printf("access: %s\n",
340 dry_run
== 0 ? "delegated" :
341 verdict
? "granted" : "denied");