2 * tcpdmatch - explain what tcpd would do in a specific case
4 * usage: tcpdmatch [-d] [-i inet_conf] daemon[@host] [user@]host
6 * -d: use the access control tables in the current directory.
8 * -i: location of inetd.conf file.
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.
17 static char sccsid
[] = "@(#) tcpdmatch.c 1.5 96/02/11 17:01:36";
20 /* System libraries. */
22 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
38 #define INADDR_NONE (-1) /* XXX should be 0xffffffff */
42 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
45 /* Application-specific. */
52 static void tcpdmatch();
54 /* The main program */
61 char *myname
= argv
[0];
67 struct request_info request
;
71 struct sockaddr_gen server_sin
;
72 struct sockaddr_gen client_sin
;
76 * Show what rule actually matched.
78 hosts_access_verbose
= 2;
83 while ((ch
= getopt(argc
, argv
, "di:")) != EOF
) {
86 hosts_allow_table
= "hosts.allow";
87 hosts_deny_table
= "hosts.deny";
97 if (argc
!= optind
+ 2)
101 * When confusion really strikes...
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
);
110 * Default is to specify a daemon process name. When daemon@host is
111 * specified, separate the two parts.
113 if ((server
= split_at(argv
[optind
], '@')) == 0)
115 if (argv
[optind
][0] == '/') {
116 daemon
= strrchr(argv
[optind
], '/') + 1;
117 tcpd_warn("%s: daemon name normalized to: %s", argv
[optind
], daemon
);
119 daemon
= argv
[optind
];
123 * Default is to specify a client hostname or address. When user@host is
124 * specified, separate the two parts.
126 if ((client
= split_at(argv
[optind
+ 1], '@')) != 0) {
127 user
= argv
[optind
+ 1];
129 client
= argv
[optind
+ 1];
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.
140 inetcf
= inet_cfg(inetcf
);
141 inet_set("portmap", WR_NOT
);
142 inet_set("rpcbind", WR_NOT
);
143 switch (inet_get(daemon
)) {
145 tcpd_warn("%s: no such process name in %s", daemon
, inetcf
);
148 tcpd_warn("%s: service possibly not wrapped", daemon
);
153 * Check accessibility of access control files.
155 (void) check_path(hosts_allow_table
, &st
);
156 (void) check_path(hosts_deny_table
, &st
);
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.
163 request_init(&request
, RQ_DAEMON
, daemon
, RQ_USER
, user
, RQ_FILE
, 1, 0);
164 sock_methods(&request
);
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.
172 if (NOT_INADDR(server
) == 0 || HOSTNAME_KNOWN(server
)) {
173 if ((hp
= find_inet_addr(server
)) == 0)
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);
179 for (count
= 0; (addr
= hp
->h_addr_list
[count
]) != 0; count
++) {
180 memcpy((char *) SGADDRP(&server_sin
), addr
, hp
->h_length
);
183 * Force evaluation of server host name and address. Host name
184 * conflicts will be reported while eval_hostname() does its job.
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
));
192 fprintf(stderr
, "Error: %s has more than one address\n", server
);
193 fprintf(stderr
, "Please specify an address instead\n");
198 request_set(&request
, RQ_SERVER_NAME
, server
, 0);
202 * If a client address is specified, we simulate the effect of client
203 * hostname lookup failure.
205 if (numeric_addr(client
, NULL
, NULL
, NULL
) == 0) {
206 request_set(&request
, RQ_CLIENT_ADDR
, client
, 0);
212 * Perhaps they are testing special client hostname patterns that aren't
213 * really host names at all.
215 if (NOT_INADDR(client
) && HOSTNAME_KNOWN(client
) == 0) {
216 request_set(&request
, RQ_CLIENT_NAME
, client
, 0);
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.
229 if ((hp
= find_inet_addr(client
)) == 0)
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);
235 for (count
= 0; (addr
= hp
->h_addr_list
[count
]) != 0; count
++) {
236 memcpy((char *) SGADDRP(&client_sin
), addr
, hp
->h_length
);
239 * Force evaluation of client host name and address. Host name
240 * conflicts will be reported while eval_hostname() does its job.
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
));
247 if (hp
->h_addr_list
[count
+ 1])
254 /* Explain how to use this program */
256 static void usage(myname
)
259 fprintf(stderr
, "usage: %s [-d] [-i inet_conf] daemon[@host] [user@]host\n",
261 fprintf(stderr
, " -d: use allow/deny files in current directory\n");
262 fprintf(stderr
, " -i: location of inetd.conf file\n");
266 /* Print interesting expansions */
268 static void expand(text
, pattern
, request
)
271 struct request_info
*request
;
275 if (STR_NE(percent_x(buf
, sizeof(buf
), pattern
, request
), unknown
))
276 printf("%s %s\n", text
, buf
);
279 /* Try out a (server,client) pair */
281 static void tcpdmatch(request
)
282 struct request_info
*request
;
287 * Show what we really know. Suppress uninteresting noise.
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
);
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
303 rfc931_timeout
= RFC931_TIMEOUT
;
304 allow_severity
= SEVERITY
;
305 deny_severity
= LOG_WARNING
;
309 * When paranoid mode is enabled, access is rejected no matter what the
310 * access control rules say.
313 if (STR_EQ(eval_hostname(request
->client
), paranoid
)) {
314 printf("access: denied (PARANOID mode)\n\n");
320 * Report the access control verdict.
322 verdict
= hosts_access(request
);
323 printf("access: %s\n",
324 dry_run
== 0 ? "delegated" :
325 verdict
? "granted" : "denied");