OpenBSD-4.6/usr.sbin/ypserv/ypserv/acl.c

Compare this file to the similar file:
Show the results in this format:

/*	$OpenBSD: acl.c,v 1.13 2006/04/03 05:01:23 deraadt Exp $ */

/*
 * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef lint
static const char rcsid[] = "$OpenBSD: acl.c,v 1.13 2006/04/03 05:01:23 deraadt Exp $";
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <netdb.h>
#include "acl.h"

#define TRUE 1
#define FALSE 0

static	struct aclent *acl_root = NULL;

static int
acl_read_line(FILE *fp, char *buf, int size)
{
	int	 len = 0;
	char *c, *p, l;

	/* Read a line, and remove any comment, trim space */

	do {
		while (fgets(buf, size, fp)) {
			c = buf;
			while (*c != '\0') {
				if (*c == '#' || *c == '\n') {
					*c = '\0';
				} else {
					c++;
				}
			}

			c = p = buf; l = ' ';
			while (*c != '\0') {
				if (isspace(l) && isspace(*c)) {
					c++;
				} else {
					l = *c++; *p = l; p++;
				}
			}
			*p = '\0';

			if (p != buf) {
				--p;
				if (isspace(*p) != 0) {
					*p = '\0';
				}
			}

			len = strlen(buf);
			return len + 1;
		}
	} while (size > 0 && !feof(fp));
	return len;
}

int
acl_check_host(struct in_addr *addr)
{
	struct aclent *p;

	p = acl_root;
	while (p != NULL) {
		if ((addr->s_addr & p->s_mask) == p->s_addr)
			return(p->allow);
		p = p->next;
	}
	return(TRUE);
}

static void
acl_add_net(int	allow, struct in_addr *addr, struct in_addr *mask)
{
	struct aclent *acl, *p;

	acl = malloc(sizeof(struct aclent));
	acl->next	 = NULL;
	acl->allow	= allow;
	acl->s_addr = addr->s_addr;
	acl->s_mask = mask->s_addr;

	if (acl_root == NULL) {
		acl_root = acl;
	} else {
		p = acl_root;
		while (p->next != NULL)
			p = p->next;
		p->next = acl;
	}
}

static void
acl_add_host(int allow, struct in_addr *addr)
{
	struct in_addr mask;

	mask.s_addr = htonl(0xffffffff);
	acl_add_net(allow, addr, &mask);
}

int
acl_init(char *file)
{
	char data_line[1024], *p, *k;
	int line_no = 0, len, i, state;
	int allow = TRUE, error_cnt = 0;
	struct in_addr addr, mask, *host_addr;
	struct hostent *host;
	struct netent *net;
	FILE *data_file = NULL;

	if (file != NULL)
		data_file = fopen(file, "r");

	while (data_file != NULL &&
	    acl_read_line(data_file, data_line, sizeof(data_line))) {

		line_no++;
		len = strlen(data_line);
		if (len == 0)
			continue;
		p = (char *) &data_line;

		/* State 1: Initial State */

		state = ACLS_INIT;
		addr.s_addr = mask.s_addr = 0;

		k = p;			/* save start of verb */
		i = 0;
		while (*p != '\0' && !isspace(*p = (char)tolower(*p))) {
			p++;
			i++;
		}

		if (*p != '\0')
			*p++ = '\0';

		if (strcmp(k, "allow") == 0) {
			allow = TRUE;
			state = ACLS_ALLOW;
		}

		if (strcmp(k, "deny") == 0) {
			allow = FALSE;
			state = ACLS_DENY;
		}

		if (state == ACLS_INIT)
			state = ACLE_UVERB;

		/* State 2: allow row */
		/* State 3: deny row */

		if (*p != '\0' &&
		    (state == ACLS_ALLOW || state == ACLS_DENY)) {
			k = p;			/* save start of verb */
			i = 0;
			while (*p != '\0' && !isspace(*p = (char)tolower(*p))) {
				p++;
				i++;
			}

			if (*p != '\0')
				*p++ = '\0';

			if (strcmp(k, "all") == 0)
				state = state + ACLD_ALL;

			if (strcmp(k, "host") == 0)
				state = state + ACLD_HOST;

			if (strcmp(k, "net") == 0)
				state = state + ACLD_NET;

			if (state == ACLS_ALLOW || state == ACLS_DENY)
				state = ACLE_U2VERB;
		}

		if (state == ACLS_ALLOW || state == ACLS_DENY)
			state = ACLE_UEOL;

		/* State 4 & 5: all state, remove any comment */

		if (*p == '\0' &&
		    (state == ACLS_ALLOW_ALL || state == ACLS_DENY_ALL)) {
			acl_add_net(allow, &addr, &mask);
			state = ACLE_OK;
		}

		/* State 6 & 7: host line */
		/* State 8 & 9: net line */

		if (*p != '\0' &&
		    state >= ACLS_ALLOW_HOST && state <= ACLS_DENY_NET) {

			k = p;			/* save start of verb */
			i = 0;
			while (*p != '\0' && !isspace(*p = (char)tolower(*p))) {
				p++;
				i++;
			}

			if (*p != '\0')
				*p++ = '\0';

			if (state == ACLS_ALLOW_HOST || state == ACLS_DENY_HOST) {
				if (*k >= '0' && *k <= '9') {
					(void)inet_aton(k, &addr);
					acl_add_host(allow, &addr);
					state = state + ACLD_HOST_DONE;
				} else {
					host = gethostbyname(k);
					if (host == NULL) {
						state = ACLE_NOHOST;
					} else {
						if (host->h_addrtype == AF_INET) {
							while ((host_addr = (struct in_addr *) *host->h_addr_list++) != NULL)
								acl_add_host(allow, host_addr);
						}
						state = state + ACLD_HOST_DONE;
					}
				}
			}

			if (state == ACLS_ALLOW_NET || state == ACLS_DENY_NET) {
				if (*k >= '0' && *k <= '9') {
					(void)inet_aton(k, &addr);
					state = state + ACLD_NET_DONE;
				} else {
					net = getnetbyname(k);
					if (net == NULL) {
						state = ACLE_NONET;
					} else {
						addr.s_addr = ntohl(net->n_net);
						state = state + ACLD_NET_DONE;
					}
				}
			}

		}

		if (state >= ACLS_ALLOW_HOST && state <= ACLS_DENY_NET)
			state = ACLE_UEOL;


		/* State 10 & 11: allow/deny host line */
		if (*p == '\0' &&
		    (state == ACLS_ALLOW_HOST_DONE || state == ACLS_DENY_HOST_DONE))
			state = ACLE_OK;

		/* State 12 & 13: allow/deny net line */
		if (*p == '\0' &&
		    (state == ACLS_ALLOW_NET_DONE || state == ACLS_DENY_NET_DONE)) {
			mask.s_addr = htonl(0xffffff00);
			if (ntohl(addr.s_addr) < 0xc0000000)
				mask.s_addr = htonl(0xffff0000);
			if (ntohl(addr.s_addr) < 0x80000000)
				mask.s_addr = htonl(0xff000000);
			acl_add_net(allow, &addr, &mask);
			state = ACLE_OK;
		}

		if (*p != '\0' &&
		    (state == ACLS_ALLOW_NET_DONE || state == ACLS_DENY_NET_DONE)) {

			k = p;			/* save start of verb */
			i = 0;
			while (*p != '\0' && !isspace(*p = (char)tolower(*p))) {
				p++;
				i++;
			}

			if (*p != '\0')
				*p++ = '\0';

			if (strcmp(k, "netmask") == 0)
				state = state + ACLD_NET_MASK;

			if (state == ACLS_ALLOW_NET_DONE ||
			    state == ACLS_DENY_NET_DONE)
				state = ACLE_NONETMASK;
		}

		/* State 14 & 15: allow/deny net netmask line */
		if (*p != '\0' &&
		    (state == ACLS_ALLOW_NET_MASK || state == ACLS_DENY_NET_MASK)) {

			k = p;		/* save start of verb */
			i = 0;
			while (*p != '\0' && !isspace(*p = (char)tolower(*p))) {
				p++;
				i++;
			}

			if (*p != '\0')
				*p++ = '\0';

			if (state == ACLS_ALLOW_NET_MASK ||
			    state == ACLS_DENY_NET_MASK) {
				if (*k >= '0' && *k <= '9') {
					(void)inet_aton(k, &mask);
					state = state + ACLD_NET_EOL;
				} else {
					net = getnetbyname(k);
					if (net == NULL) {
						state = ACLE_NONET;
					} else {
						mask.s_addr = ntohl(net->n_net);
						state = state + ACLD_NET_EOL;
					}
				}
			}

		}

		if (state == ACLS_ALLOW_NET_MASK || state == ACLS_DENY_NET_MASK)
			state = ACLE_UEOL;

		/* State 16 & 17: allow/deny host line */
		if (*p == '\0' &&
		    (state == ACLS_ALLOW_NET_EOL || state == ACLS_DENY_NET_EOL)) {
			acl_add_net(allow, &addr, &mask);
			state = ACLE_OK;
		}

		switch (state) {
		case ACLE_NONETMASK:
			fprintf(stderr,
			    "acl: excpected \"netmask\" missing at line %d\n",
			    line_no);
			break;
		case ACLE_NONET:
			error_cnt++;
			fprintf(stderr, "acl: unknown network at line %d\n",
			    line_no);
			break;
		case ACLE_NOHOST:
			error_cnt++;
			fprintf(stderr, "acl: unknown host at line %d\n",
			    line_no);
			break;
		case ACLE_UVERB:
			error_cnt++;
			fprintf(stderr, "acl: unknown verb at line %d\n",
			    line_no);
			break;
		case ACLE_U2VERB:
			error_cnt++;
			fprintf(stderr,
			    "acl: unknown secondary verb at line %d\n",
			    line_no);
			break;
		case ACLE_UEOL:
			error_cnt++;
			fprintf(stderr,
			    "acl: unexpected end of line at line %d\n",
			    line_no);
			break;
		case ACLE_OK:
			break;
		default:
			error_cnt++;
			fprintf(stderr, "acl: unexpected state %d %s\n",
			    state, k);
		}

	}

	if (data_file != NULL) {
		(void)fflush(stderr);
		(void)fclose(data_file);
	}

	/* Always add a last allow all if file don't exists or */
	/* the file doesn't cover all cases. */
	addr.s_addr = mask.s_addr = 0;
	allow = TRUE;
	acl_add_net(allow, &addr, &mask);
	return(error_cnt);
}

int
acl_securenet(char *file)
{
	char data_line[1024], *p, *k;
	int line_no = 0, len, i, allow = TRUE, state;
	int error_cnt = 0;
	struct in_addr addr, mask;
	struct netent *net;
	FILE *data_file = NULL;

	if (file != NULL)
		data_file = fopen(file, "r");

	/* Always add a localhost allow first, to be compatible with sun */
	addr.s_addr = htonl(0x7f000001);
	mask.s_addr = htonl(0xffffffff);
	allow = TRUE;
	acl_add_net(allow, &addr, &mask);

	while (data_file != NULL &&
	    acl_read_line(data_file, data_line, sizeof(data_line))) {
		line_no++;
		len = strlen(data_line);
		if (len == 0)
			continue;
		p = (char *) &data_line;

		/* State 1: Initial State */
		state = ACLS_INIT;
		addr.s_addr = mask.s_addr = 0;

		k = p;				/* save start of verb */
		i = 0;
		while (*p != '\0' && !isspace(*p = (char)tolower(*p))) {
			p++;
			i++;
		}

		if (*p != '\0') {
			*p++ = '\0';
			state = ACLS_ALLOW_NET_MASK;
		}

		if (state == ACLS_INIT)
			state = ACLE_UEOL;

		if (state == ACLS_ALLOW_NET_MASK) {
			if (*k >= '0' && *k <= '9') {
				(void)inet_aton(k, &mask);
				state = ACLS_ALLOW_NET;
			} else {
				net = getnetbyname(k);
				if (net == NULL) {
					state = ACLE_NONET;
				} else {
					mask.s_addr = ntohl(net->n_net);
					state = ACLS_ALLOW_NET;
				}
			}

			k = p;				/* save start of verb */
			i = 0;
			while (*p != '\0' && !isspace(*p = (char)tolower(*p))) {
				p++;
				i++;
			}

			if (*p != '\0')
				*p++ = '\0';
		}

		if (state == ACLS_ALLOW_NET_MASK)
			state = ACLE_UEOL;

		if (state == ACLS_ALLOW_NET) {
			if (*k >= '0' && *k <= '9') {
				(void)inet_aton(k, &addr);
				state = ACLS_ALLOW_NET_EOL;
			} else {
				net = getnetbyname(k);
				if (net == NULL) {
					state = ACLE_NONET;
				} else {
					addr.s_addr = ntohl(net->n_net);
					state = ACLS_ALLOW_NET_EOL;
				}
			}
		}

		if (state == ACLS_ALLOW_NET)
			state = ACLE_UEOL;

		if (*p == '\0' && state == ACLS_ALLOW_NET_EOL) {
			acl_add_net(allow, &addr, &mask);
			state = ACLE_OK;
		}

		switch (state) {
		case ACLE_NONET:
			error_cnt++;
			fprintf(stderr,
			    "securenet: unknown network at line %d\n",
			    line_no);
			break;
		case ACLE_UEOL:
			error_cnt++;
			fprintf(stderr,
			    "securenet: unexpected end of line at line %d\n",
			    line_no);
			break;
		case ACLE_OK:
			break;
		default:
			error_cnt++;
			fprintf(stderr, "securenet: unexpected state %d %s\n",
			    state, k);
		}
	}

	if (data_file != NULL) {
		(void)fflush(stderr);
		(void)fclose(data_file);

		/* Always add a last deny all if file exists */
		addr.s_addr = mask.s_addr = 0;
		allow = FALSE;
		acl_add_net(allow, &addr, &mask);
	}

	/* Always add a last allow all if file don't exists */

	addr.s_addr = mask.s_addr = 0;
	allow = TRUE;
	acl_add_net(allow, &addr, &mask);
	return(error_cnt);
}

void
acl_reset(void)
{
	struct aclent *p;

	while (acl_root != NULL) {
		p = acl_root->next;
		free(acl_root);
		acl_root = p;
	}
}