xref: /openbsd-src/usr.sbin/ypserv/ypserv/acl.c (revision 3a50f0a93a2072911d0ba6ababa815fb04bf9a71)
1 /*	$OpenBSD: acl.c,v 1.17 2022/12/28 21:30:19 jmc Exp $ */
2 
3 /*
4  * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <netdb.h>
38 #include "acl.h"
39 
40 #define TRUE 1
41 #define FALSE 0
42 
43 static	struct aclent *acl_root = NULL;
44 
45 static int
acl_read_line(FILE * fp,char * buf,int size)46 acl_read_line(FILE *fp, char *buf, int size)
47 {
48 	int	 len = 0;
49 	char *c, *p, l;
50 
51 	/* Read a line, and remove any comment, trim space */
52 
53 	do {
54 		while (fgets(buf, size, fp)) {
55 			c = buf;
56 			while (*c != '\0') {
57 				if (*c == '#' || *c == '\n') {
58 					*c = '\0';
59 				} else {
60 					c++;
61 				}
62 			}
63 
64 			c = p = buf; l = ' ';
65 			while (*c != '\0') {
66 				if (isspace((unsigned char)l) &&
67 				    isspace((unsigned char)*c)) {
68 					c++;
69 				} else {
70 					l = *c++; *p = l; p++;
71 				}
72 			}
73 			*p = '\0';
74 
75 			if (p != buf) {
76 				--p;
77 				if (isspace((unsigned char)*p) != 0) {
78 					*p = '\0';
79 				}
80 			}
81 
82 			len = strlen(buf);
83 			return len + 1;
84 		}
85 	} while (size > 0 && !feof(fp));
86 	return len;
87 }
88 
89 int
acl_check_host(struct in_addr * addr)90 acl_check_host(struct in_addr *addr)
91 {
92 	struct aclent *p;
93 
94 	p = acl_root;
95 	while (p != NULL) {
96 		if ((addr->s_addr & p->s_mask) == p->s_addr)
97 			return(p->allow);
98 		p = p->next;
99 	}
100 	return(TRUE);
101 }
102 
103 static void
acl_add_net(int allow,struct in_addr * addr,struct in_addr * mask)104 acl_add_net(int	allow, struct in_addr *addr, struct in_addr *mask)
105 {
106 	struct aclent *acl, *p;
107 
108 	acl = malloc(sizeof(struct aclent));
109 	acl->next	 = NULL;
110 	acl->allow	= allow;
111 	acl->s_addr = addr->s_addr;
112 	acl->s_mask = mask->s_addr;
113 
114 	if (acl_root == NULL) {
115 		acl_root = acl;
116 	} else {
117 		p = acl_root;
118 		while (p->next != NULL)
119 			p = p->next;
120 		p->next = acl;
121 	}
122 }
123 
124 static void
acl_add_host(int allow,struct in_addr * addr)125 acl_add_host(int allow, struct in_addr *addr)
126 {
127 	struct in_addr mask;
128 
129 	mask.s_addr = htonl(0xffffffff);
130 	acl_add_net(allow, addr, &mask);
131 }
132 
133 int
acl_init(char * file)134 acl_init(char *file)
135 {
136 	char data_line[1024], *p, *k;
137 	int line_no = 0, len, i, state;
138 	int allow = TRUE, error_cnt = 0;
139 	struct in_addr addr, mask, *host_addr;
140 	struct hostent *host;
141 	FILE *data_file = NULL;
142 
143 	if (file != NULL)
144 		data_file = fopen(file, "r");
145 
146 	while (data_file != NULL &&
147 	    acl_read_line(data_file, data_line, sizeof(data_line))) {
148 
149 		line_no++;
150 		len = strlen(data_line);
151 		if (len == 0)
152 			continue;
153 		p = (char *) &data_line;
154 
155 		/* State 1: Initial State */
156 
157 		state = ACLS_INIT;
158 		addr.s_addr = mask.s_addr = 0;
159 
160 		k = p;			/* save start of verb */
161 		i = 0;
162 		while (*p != '\0' &&
163 		    !isspace((*p = tolower(*p)))) {
164 			p++;
165 			i++;
166 		}
167 
168 		if (*p != '\0')
169 			*p++ = '\0';
170 
171 		if (strcmp(k, "allow") == 0) {
172 			allow = TRUE;
173 			state = ACLS_ALLOW;
174 		}
175 
176 		if (strcmp(k, "deny") == 0) {
177 			allow = FALSE;
178 			state = ACLS_DENY;
179 		}
180 
181 		if (state == ACLS_INIT)
182 			state = ACLE_UVERB;
183 
184 		/* State 2: allow row */
185 		/* State 3: deny row */
186 
187 		if (*p != '\0' &&
188 		    (state == ACLS_ALLOW || state == ACLS_DENY)) {
189 			k = p;			/* save start of verb */
190 			i = 0;
191 			while (*p != '\0' &&
192 			    !isspace((*p = tolower(*p)))) {
193 				p++;
194 				i++;
195 			}
196 
197 			if (*p != '\0')
198 				*p++ = '\0';
199 
200 			if (strcmp(k, "all") == 0)
201 				state = state + ACLD_ALL;
202 
203 			if (strcmp(k, "host") == 0)
204 				state = state + ACLD_HOST;
205 
206 			if (strcmp(k, "net") == 0)
207 				state = state + ACLD_NET;
208 
209 			if (state == ACLS_ALLOW || state == ACLS_DENY)
210 				state = ACLE_U2VERB;
211 		}
212 
213 		if (state == ACLS_ALLOW || state == ACLS_DENY)
214 			state = ACLE_UEOL;
215 
216 		/* State 4 & 5: all state, remove any comment */
217 
218 		if (*p == '\0' &&
219 		    (state == ACLS_ALLOW_ALL || state == ACLS_DENY_ALL)) {
220 			acl_add_net(allow, &addr, &mask);
221 			state = ACLE_OK;
222 		}
223 
224 		/* State 6 & 7: host line */
225 		/* State 8 & 9: net line */
226 
227 		if (*p != '\0' &&
228 		    state >= ACLS_ALLOW_HOST && state <= ACLS_DENY_NET) {
229 
230 			k = p;			/* save start of verb */
231 			i = 0;
232 			while (*p != '\0' &&
233 			    !isspace((*p = tolower(*p)))) {
234 				p++;
235 				i++;
236 			}
237 
238 			if (*p != '\0')
239 				*p++ = '\0';
240 
241 			if (state == ACLS_ALLOW_HOST || state == ACLS_DENY_HOST) {
242 				if (*k >= '0' && *k <= '9') {
243 					(void)inet_aton(k, &addr);
244 					acl_add_host(allow, &addr);
245 					state = state + ACLD_HOST_DONE;
246 				} else {
247 					host = gethostbyname(k);
248 					if (host == NULL) {
249 						state = ACLE_NOHOST;
250 					} else {
251 						if (host->h_addrtype == AF_INET) {
252 							while ((host_addr = (struct in_addr *) *host->h_addr_list++) != NULL)
253 								acl_add_host(allow, host_addr);
254 						}
255 						state = state + ACLD_HOST_DONE;
256 					}
257 				}
258 			}
259 
260 			if (state == ACLS_ALLOW_NET || state == ACLS_DENY_NET) {
261 				if (*k >= '0' && *k <= '9') {
262 					(void)inet_aton(k, &addr);
263 					state = state + ACLD_NET_DONE;
264 				} else
265 					state = ACLE_NONET;
266 			}
267 
268 		}
269 
270 		if (state >= ACLS_ALLOW_HOST && state <= ACLS_DENY_NET)
271 			state = ACLE_UEOL;
272 
273 
274 		/* State 10 & 11: allow/deny host line */
275 		if (*p == '\0' &&
276 		    (state == ACLS_ALLOW_HOST_DONE || state == ACLS_DENY_HOST_DONE))
277 			state = ACLE_OK;
278 
279 		/* State 12 & 13: allow/deny net line */
280 		if (*p == '\0' &&
281 		    (state == ACLS_ALLOW_NET_DONE || state == ACLS_DENY_NET_DONE)) {
282 			mask.s_addr = htonl(0xffffff00);
283 			if (ntohl(addr.s_addr) < 0xc0000000)
284 				mask.s_addr = htonl(0xffff0000);
285 			if (ntohl(addr.s_addr) < 0x80000000)
286 				mask.s_addr = htonl(0xff000000);
287 			acl_add_net(allow, &addr, &mask);
288 			state = ACLE_OK;
289 		}
290 
291 		if (*p != '\0' &&
292 		    (state == ACLS_ALLOW_NET_DONE || state == ACLS_DENY_NET_DONE)) {
293 
294 			k = p;			/* save start of verb */
295 			i = 0;
296 			while (*p != '\0' &&
297 			    !isspace((*p = tolower(*p)))) {
298 				p++;
299 				i++;
300 			}
301 
302 			if (*p != '\0')
303 				*p++ = '\0';
304 
305 			if (strcmp(k, "netmask") == 0)
306 				state = state + ACLD_NET_MASK;
307 
308 			if (state == ACLS_ALLOW_NET_DONE ||
309 			    state == ACLS_DENY_NET_DONE)
310 				state = ACLE_NONETMASK;
311 		}
312 
313 		/* State 14 & 15: allow/deny net netmask line */
314 		if (*p != '\0' &&
315 		    (state == ACLS_ALLOW_NET_MASK || state == ACLS_DENY_NET_MASK)) {
316 
317 			k = p;		/* save start of verb */
318 			i = 0;
319 			while (*p != '\0' &&
320 			    !isspace((*p = tolower(*p)))) {
321 				p++;
322 				i++;
323 			}
324 
325 			if (*p != '\0')
326 				*p++ = '\0';
327 
328 			if (state == ACLS_ALLOW_NET_MASK ||
329 			    state == ACLS_DENY_NET_MASK) {
330 				if (*k >= '0' && *k <= '9') {
331 					(void)inet_aton(k, &mask);
332 					state = state + ACLD_NET_EOL;
333 				} else
334 					state = ACLE_NONET;
335 			}
336 
337 		}
338 
339 		if (state == ACLS_ALLOW_NET_MASK || state == ACLS_DENY_NET_MASK)
340 			state = ACLE_UEOL;
341 
342 		/* State 16 & 17: allow/deny host line */
343 		if (*p == '\0' &&
344 		    (state == ACLS_ALLOW_NET_EOL || state == ACLS_DENY_NET_EOL)) {
345 			acl_add_net(allow, &addr, &mask);
346 			state = ACLE_OK;
347 		}
348 
349 		switch (state) {
350 		case ACLE_NONETMASK:
351 			fprintf(stderr,
352 			    "acl: expected \"netmask\" missing at line %d\n",
353 			    line_no);
354 			break;
355 		case ACLE_NONET:
356 			error_cnt++;
357 			fprintf(stderr, "acl: unknown network at line %d\n",
358 			    line_no);
359 			break;
360 		case ACLE_NOHOST:
361 			error_cnt++;
362 			fprintf(stderr, "acl: unknown host at line %d\n",
363 			    line_no);
364 			break;
365 		case ACLE_UVERB:
366 			error_cnt++;
367 			fprintf(stderr, "acl: unknown verb at line %d\n",
368 			    line_no);
369 			break;
370 		case ACLE_U2VERB:
371 			error_cnt++;
372 			fprintf(stderr,
373 			    "acl: unknown secondary verb at line %d\n",
374 			    line_no);
375 			break;
376 		case ACLE_UEOL:
377 			error_cnt++;
378 			fprintf(stderr,
379 			    "acl: unexpected end of line at line %d\n",
380 			    line_no);
381 			break;
382 		case ACLE_OK:
383 			break;
384 		default:
385 			error_cnt++;
386 			fprintf(stderr, "acl: unexpected state %d %s\n",
387 			    state, k);
388 		}
389 
390 	}
391 
392 	if (data_file != NULL) {
393 		(void)fflush(stderr);
394 		(void)fclose(data_file);
395 	}
396 
397 	/* Always add a last allow all if file don't exists or */
398 	/* the file doesn't cover all cases. */
399 	addr.s_addr = mask.s_addr = 0;
400 	allow = TRUE;
401 	acl_add_net(allow, &addr, &mask);
402 	return(error_cnt);
403 }
404 
405 int
acl_securenet(char * file)406 acl_securenet(char *file)
407 {
408 	char data_line[1024], *p, *k;
409 	int line_no = 0, len, i, allow = TRUE, state;
410 	int error_cnt = 0;
411 	struct in_addr addr, mask;
412 	FILE *data_file = NULL;
413 
414 	if (file != NULL)
415 		data_file = fopen(file, "r");
416 
417 	/* Always add a localhost allow first, to be compatible with sun */
418 	addr.s_addr = htonl(0x7f000001);
419 	mask.s_addr = htonl(0xffffffff);
420 	allow = TRUE;
421 	acl_add_net(allow, &addr, &mask);
422 
423 	while (data_file != NULL &&
424 	    acl_read_line(data_file, data_line, sizeof(data_line))) {
425 		line_no++;
426 		len = strlen(data_line);
427 		if (len == 0)
428 			continue;
429 		p = (char *) &data_line;
430 
431 		/* State 1: Initial State */
432 		state = ACLS_INIT;
433 		addr.s_addr = mask.s_addr = 0;
434 
435 		k = p;				/* save start of verb */
436 		i = 0;
437 		while (*p != '\0' &&
438 		    !isspace((*p = tolower(*p)))) {
439 			p++;
440 			i++;
441 		}
442 
443 		if (*p != '\0') {
444 			*p++ = '\0';
445 			state = ACLS_ALLOW_NET_MASK;
446 		}
447 
448 		if (state == ACLS_INIT)
449 			state = ACLE_UEOL;
450 
451 		if (state == ACLS_ALLOW_NET_MASK) {
452 			if (*k >= '0' && *k <= '9') {
453 				(void)inet_aton(k, &mask);
454 				state = ACLS_ALLOW_NET;
455 			} else
456 				state = ACLE_NONET;
457 
458 			k = p;				/* save start of verb */
459 			i = 0;
460 			while (*p != '\0' &&
461 			    !isspace((*p = tolower(*p)))) {
462 				p++;
463 				i++;
464 			}
465 
466 			if (*p != '\0')
467 				*p++ = '\0';
468 		}
469 
470 		if (state == ACLS_ALLOW_NET_MASK)
471 			state = ACLE_UEOL;
472 
473 		if (state == ACLS_ALLOW_NET) {
474 			if (*k >= '0' && *k <= '9') {
475 				(void)inet_aton(k, &addr);
476 				state = ACLS_ALLOW_NET_EOL;
477 			} else
478 				state = ACLE_NONET;
479 		}
480 
481 		if (state == ACLS_ALLOW_NET)
482 			state = ACLE_UEOL;
483 
484 		if (*p == '\0' && state == ACLS_ALLOW_NET_EOL) {
485 			acl_add_net(allow, &addr, &mask);
486 			state = ACLE_OK;
487 		}
488 
489 		switch (state) {
490 		case ACLE_NONET:
491 			error_cnt++;
492 			fprintf(stderr,
493 			    "securenet: unknown network at line %d\n",
494 			    line_no);
495 			break;
496 		case ACLE_UEOL:
497 			error_cnt++;
498 			fprintf(stderr,
499 			    "securenet: unexpected end of line at line %d\n",
500 			    line_no);
501 			break;
502 		case ACLE_OK:
503 			break;
504 		default:
505 			error_cnt++;
506 			fprintf(stderr, "securenet: unexpected state %d %s\n",
507 			    state, k);
508 		}
509 	}
510 
511 	if (data_file != NULL) {
512 		(void)fflush(stderr);
513 		(void)fclose(data_file);
514 
515 		/* Always add a last deny all if file exists */
516 		addr.s_addr = mask.s_addr = 0;
517 		allow = FALSE;
518 		acl_add_net(allow, &addr, &mask);
519 	}
520 
521 	/* Always add a last allow all if file don't exists */
522 
523 	addr.s_addr = mask.s_addr = 0;
524 	allow = TRUE;
525 	acl_add_net(allow, &addr, &mask);
526 	return(error_cnt);
527 }
528 
529 void
acl_reset(void)530 acl_reset(void)
531 {
532 	struct aclent *p;
533 
534 	while (acl_root != NULL) {
535 		p = acl_root->next;
536 		free(acl_root);
537 		acl_root = p;
538 	}
539 }
540