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