xref: /freebsd/contrib/tcp_wrappers/scaffold.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
1  /*
2   * Routines for testing only. Not really industrial strength.
3   *
4   * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
5   *
6   * $FreeBSD$
7   */
8 
9 #ifndef lint
10 static char sccs_id[] = "@(#) scaffold.c 1.6 97/03/21 19:27:24";
11 #endif
12 
13 /* System libraries. */
14 
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <netdb.h>
21 #include <stdio.h>
22 #include <syslog.h>
23 #include <setjmp.h>
24 #include <string.h>
25 #include <resolv.h>
26 
27 #ifndef INADDR_NONE
28 #define	INADDR_NONE	(-1)		/* XXX should be 0xffffffff */
29 #endif
30 
31 extern char *malloc();
32 
33 /* Application-specific. */
34 
35 #include "tcpd.h"
36 #include "scaffold.h"
37 
38  /*
39   * These are referenced by the options module and by rfc931.c.
40   */
41 int     allow_severity = SEVERITY;
42 int     deny_severity = LOG_WARNING;
43 int     rfc931_timeout = RFC931_TIMEOUT;
44 
45 /* dup_hostent - create hostent in one memory block */
46 
47 static struct hostent *dup_hostent(hp)
48 struct hostent *hp;
49 {
50     struct hostent_block {
51 	struct hostent host;
52 	char   *addr_list[1];
53     };
54     struct hostent_block *hb;
55     int     count;
56     char   *data;
57     char   *addr;
58 
59     for (count = 0; hp->h_addr_list[count] != 0; count++)
60 	 /* void */ ;
61 
62     if ((hb = (struct hostent_block *) malloc(sizeof(struct hostent_block)
63 #ifdef INET6
64 			 + strlen(hp->h_name) + 1
65 #endif
66 			 + (hp->h_length + sizeof(char *)) * count)) == 0) {
67 	fprintf(stderr, "Sorry, out of memory\n");
68 	exit(1);
69     }
70     memset((char *) &hb->host, 0, sizeof(hb->host));
71     hb->host.h_length = hp->h_length;
72     hb->host.h_addr_list = hb->addr_list;
73     hb->host.h_addr_list[count] = 0;
74     data = (char *) (hb->host.h_addr_list + count + 1);
75 #ifdef INET6
76     hb->host.h_name = data + hp->h_length * count;
77     strcpy(hb->host.h_name, hp->h_name);
78     hb->host.h_addrtype = hp->h_addrtype;
79 #endif
80 
81     for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) {
82 	hb->host.h_addr_list[count] = data + hp->h_length * count;
83 	memcpy(hb->host.h_addr_list[count], addr, hp->h_length);
84     }
85     return (&hb->host);
86 }
87 
88 #if defined(INET6) && !defined(USE_GETIPNODEBY)
89 /* merge_hostent - merge hostent in one memory block */
90 
91 static struct hostent *merge_hostent(hp1, hp2)
92 struct hostent *hp1, *hp2;
93 {
94     struct hostent_block {
95 	struct hostent host;
96 	char   *addr_list[1];
97     };
98     struct hostent_block *hb;
99     int     count, count2;
100     char   *data;
101     char   *addr;
102 
103     for (count = 0; hp1->h_addr_list[count] != 0; count++)
104 	 /* void */ ;
105     for (count2 = 0; hp2->h_addr_list[count2] != 0; count2++)
106 	 /* void */ ;
107     count += count2;
108 
109     if ((hb = (struct hostent_block *) malloc(sizeof(struct hostent_block)
110 			 + strlen(hp1->h_name) + 1
111 			 + (hp1->h_length + sizeof(char *)) * count)) == 0) {
112 	fprintf(stderr, "Sorry, out of memory\n");
113 	exit(1);
114     }
115     memset((char *) &hb->host, 0, sizeof(hb->host));
116     hb->host.h_length = hp1->h_length;
117     hb->host.h_addr_list = hb->addr_list;
118     hb->host.h_addr_list[count] = 0;
119     data = (char *) (hb->host.h_addr_list + count + 1);
120     hb->host.h_name = data + hp1->h_length * count;
121     strcpy(hb->host.h_name, hp1->h_name);
122     hb->host.h_addrtype = hp1->h_addrtype;
123 
124     for (count = 0; (addr = hp1->h_addr_list[count]) != 0; count++) {
125 	hb->host.h_addr_list[count] = data + hp1->h_length * count;
126 	memcpy(hb->host.h_addr_list[count], addr, hp1->h_length);
127     }
128     for (count2 = 0; (addr = hp2->h_addr_list[count2]) != 0; count2++) {
129 	hb->host.h_addr_list[count] = data + hp1->h_length * count;
130 	memcpy(hb->host.h_addr_list[count], addr, hp1->h_length);
131 	++count;
132     }
133     return (&hb->host);
134 }
135 #endif
136 
137 static struct hostent *gethostbyname64(host)
138 char *host;
139 {
140     struct hostent *hp = NULL, *hp2 = NULL;
141 #ifdef USE_GETIPNODEBY
142     int h_error;
143 
144     if ((hp = getipnodebyname(host, AF_INET6,
145 			      AI_V4MAPPED | AI_ADDRCONFIG | AI_ALL,
146 			      &h_error)) != 0) {
147 	hp2 = dup_hostent(hp);
148 	freehostent(hp);
149 	return (hp2);
150     }
151 #else
152     struct hostent *hp1;
153     u_long res_options;
154 
155     if ((_res.options & RES_INIT) == 0) {
156 	if (res_init() < 0) {
157 	    tcpd_warn("%s: res_init() failed", host);
158 	    return (NULL);
159 	}
160     }
161     res_options = _res.options;
162 #ifdef INET6
163     _res.options |= RES_USE_INET6;
164     if ((hp1 = gethostbyname2(host, AF_INET6)) != NULL)
165 	hp1 = dup_hostent(hp1);
166 #endif
167     if ((hp2 = gethostbyname2(host, AF_INET)) != NULL)
168 	hp2 = dup_hostent(hp2);
169     _res.options = res_options;
170 #ifdef INET6
171     if (hp1 && hp2) {
172 	hp = merge_hostent(hp1, hp2);
173 	free((char *) hp1);
174 	free((char *) hp2);
175 	return (hp);
176     }
177     if (hp1)
178 	return (hp1);
179 #endif
180     if (hp2)
181 	return (hp2);
182 #endif
183     return (NULL);
184 }
185 
186 /* find_inet_addr - find all addresses for this host, result to free() */
187 
188 struct hostent *find_inet_addr(host)
189 char   *host;
190 {
191     struct in_addr addr;
192     struct hostent *hp;
193     static struct hostent h;
194     static char *addr_list[2];
195     static char hnamebuf[BUFSIZ];
196 
197     /*
198      * Host address: translate it to internal form.
199      */
200     if ((addr.s_addr = dot_quad_addr(host)) != INADDR_NONE) {
201 	h.h_addr_list = addr_list;
202 	h.h_addr_list[0] = (char *) &addr;
203 	h.h_length = sizeof(addr);
204 #ifdef INET6
205 	h.h_addrtype = AF_INET;
206 	h.h_name = hnamebuf;
207 	strcpy(h.h_name, host);
208 #endif
209 	return (dup_hostent(&h));
210     }
211 
212     /*
213      * Map host name to a series of addresses. Watch out for non-internet
214      * forms or aliases. The NOT_INADDR() is here in case gethostbyname() has
215      * been "enhanced" to accept numeric addresses. Make a copy of the
216      * address list so that later gethostbyXXX() calls will not clobber it.
217      */
218     if (NOT_INADDR(host) == 0) {
219 	tcpd_warn("%s: not an internet address", host);
220 	return (0);
221     }
222 #ifdef INET6
223     if ((hp = gethostbyname64(host)) == 0) {
224 #else
225     if ((hp = gethostbyname(host)) == 0) {
226 #endif
227 	tcpd_warn("%s: host not found", host);
228 	return (0);
229     }
230 #ifdef INET6
231     if (hp->h_addrtype != AF_INET6) {
232 	tcpd_warn("%d: not an internet host", hp->h_addrtype);
233 	free((char *) hp);
234 #else
235     if (hp->h_addrtype != AF_INET) {
236 	tcpd_warn("%d: not an internet host", hp->h_addrtype);
237 #endif
238 	return (0);
239     }
240     if (STR_NE(host, hp->h_name)) {
241 	tcpd_warn("%s: hostname alias", host);
242 	tcpd_warn("(official name: %.*s)", STRING_LENGTH, hp->h_name);
243     }
244 #ifdef INET6
245     return (hp);
246 #else
247     return (dup_hostent(hp));
248 #endif
249 }
250 
251 /* check_dns - give each address thorough workout, return address count */
252 
253 int     check_dns(host)
254 char   *host;
255 {
256     struct request_info request;
257 #ifdef INET6
258     struct sockaddr_storage sin;
259     char *ap;
260     int alen;
261 #else
262     struct sockaddr_in sin;
263 #endif
264     struct hostent *hp;
265     int     count;
266     char   *addr;
267 
268     if ((hp = find_inet_addr(host)) == 0)
269 	return (0);
270     request_init(&request, RQ_CLIENT_SIN, &sin, 0);
271     sock_methods(&request);
272     memset((char *) &sin, 0, sizeof(sin));
273 #ifdef INET6
274     sin.ss_family = hp->h_addrtype;
275     switch (hp->h_addrtype) {
276     case AF_INET:
277 	ap = (char *)&((struct sockaddr_in *)&sin)->sin_addr;
278 	alen = sizeof(struct sockaddr_in);
279 	break;
280     case AF_INET6:
281 	ap = (char *)&((struct sockaddr_in6 *)&sin)->sin6_addr;
282 	alen = sizeof(struct sockaddr_in6);
283 	break;
284     default:
285 	return (0);
286     }
287 #else
288     sin.sin_family = AF_INET;
289 #endif
290 
291     for (count = 0; (addr = hp->h_addr_list[count]) != 0; count++) {
292 #ifdef INET6
293 	memcpy(ap, addr, alen);
294 #else
295 	memcpy((char *) &sin.sin_addr, addr, sizeof(sin.sin_addr));
296 #endif
297 
298 	/*
299 	 * Force host name and address conversions. Use the request structure
300 	 * as a cache. Detect hostname lookup problems. Any name/name or
301 	 * name/address conflicts will be reported while eval_hostname() does
302 	 * its job.
303 	 */
304 	request_set(&request, RQ_CLIENT_ADDR, "", RQ_CLIENT_NAME, "", 0);
305 	if (STR_EQ(eval_hostname(request.client), unknown))
306 	    tcpd_warn("host address %s->name lookup failed",
307 		      eval_hostaddr(request.client));
308     }
309     free((char *) hp);
310     return (count);
311 }
312 
313 /* dummy function to intercept the real shell_cmd() */
314 
315 /* ARGSUSED */
316 
317 void    shell_cmd(command)
318 char   *command;
319 {
320     if (hosts_access_verbose)
321 	printf("command: %s", command);
322 }
323 
324 /* dummy function  to intercept the real clean_exit() */
325 
326 /* ARGSUSED */
327 
328 void    clean_exit(request)
329 struct request_info *request;
330 {
331     exit(0);
332 }
333 
334 /* dummy function  to intercept the real rfc931() */
335 
336 /* ARGSUSED */
337 
338 void    rfc931(request)
339 struct request_info *request;
340 {
341     strcpy(request->user, unknown);
342 }
343 
344 /* check_path - examine accessibility */
345 
346 int     check_path(path, st)
347 char   *path;
348 struct stat *st;
349 {
350     struct stat stbuf;
351     char    buf[BUFSIZ];
352 
353     if (stat(path, st) < 0)
354 	return (-1);
355 #ifdef notdef
356     if (st->st_uid != 0)
357 	tcpd_warn("%s: not owned by root", path);
358     if (st->st_mode & 020)
359 	tcpd_warn("%s: group writable", path);
360 #endif
361     if (st->st_mode & 002)
362 	tcpd_warn("%s: world writable", path);
363     if (path[0] == '/' && path[1] != 0) {
364 	strrchr(strcpy(buf, path), '/')[0] = 0;
365 	(void) check_path(buf[0] ? buf : "/", &stbuf);
366     }
367     return (0);
368 }
369