OpenSolaris_b135/cmd/tsol/tnchkdb/tnchkdb.c

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

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 *  Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 *  Use is subject to license terms.
 */

/*
 * tnchkdb.c - Trusted network database checking utility
 */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>
#include <malloc.h>
#include <string.h>
#include <libtsnet.h>
#include <netinet/in.h>
#include <nss_dbdefs.h>

static void usage(void);
static void check_tnrhtp(const char *);
static void check_tnrhdb(const char *);
static void check_tnzonecfg(const char *);

static boolean_t tnrhtp_bad;
static int exitval;

struct tsol_name_list {
	struct tsol_name_list *next;
	int linenum;
	char name[TNTNAMSIZ];
};

struct tsol_addr_list {
	struct tsol_addr_list *next;
	int linenum;
	int prefix_len;
	in6_addr_t addr;
};

static struct tsol_name_list *tp_list_head;
static struct tsol_addr_list *rh_list_head;
static struct tsol_name_list *zc_list_head;

typedef struct mlp_info_list_s {
	struct mlp_info_list_s *next;
	int linenum;
	tsol_mlp_t mlp;
	char name[TNTNAMSIZ];
} mlp_info_list_t;

static mlp_info_list_t *global_mlps;

static void
add_name(struct tsol_name_list **head, const char *name, int linenum)
{
	int err;
	struct tsol_name_list *entry;

	entry = malloc(sizeof (struct tsol_name_list));
	if (entry == NULL) {
		err = errno;

		(void) fprintf(stderr,
		    gettext("tnchkdb: allocating name list: %s\n"),
		    strerror(err));
		exit(1);
	}
	(void) strlcpy(entry->name, name, sizeof (entry->name));
	entry->next = *head;
	entry->linenum = linenum;
	*head = entry;
}

static struct tsol_name_list *
find_name(struct tsol_name_list *head, const char *name)
{
	struct tsol_name_list *entry;

	for (entry = head; entry != NULL; entry = entry->next)
		if (strcmp(entry->name, name) == 0)
			break;
	return (entry);
}

static void
add_addr(struct tsol_addr_list **head, int prefix_len, in6_addr_t addr,
    int linenum)
{
	int err;
	struct tsol_addr_list *entry;

	entry = malloc(sizeof (struct tsol_addr_list));
	if (entry == NULL) {
		err = errno;

		(void) fprintf(stderr,
		    gettext("tnchkdb: allocating addr list: %s\n"),
		    strerror(err));
		exit(2);
	}
	entry->prefix_len = prefix_len;
	entry->addr = addr;
	entry->next = *head;
	entry->linenum = linenum;
	*head = entry;
}

static struct tsol_addr_list *
find_addr(struct tsol_addr_list *head, int prefix_len, in6_addr_t addr)
{
	struct tsol_addr_list *entry;

	for (entry = head; entry != NULL; entry = entry->next)
		if (entry->prefix_len == prefix_len &&
		    IN6_ARE_ADDR_EQUAL(&entry->addr, &addr))
			break;
	return (entry);
}

static void
add_template(const char *name, int linenum)
{
	add_name(&tp_list_head, name, linenum);
}

static struct tsol_name_list *
find_template(const char *name)
{
	return (find_name(tp_list_head, name));
}

static void
add_host(int prefix_len, in6_addr_t addr, int linenum)
{
	add_addr(&rh_list_head, prefix_len, addr, linenum);
}

static struct tsol_addr_list *
find_host(int prefix_len, in6_addr_t addr)
{
	return (find_addr(rh_list_head, prefix_len, addr));
}

static void
add_zone(const char *name, int linenum)
{
	add_name(&zc_list_head, name, linenum);
}

static struct tsol_name_list *
find_zone(const char *name)
{
	return (find_name(zc_list_head, name));
}

int
main(int argc, char **argv)
{
	const char *tnrhdb_file = TNRHDB_PATH;
	const char *tnrhtp_file = TNRHTP_PATH;
	const char *tnzonecfg_file = TNZONECFG_PATH;
	int chr;

	/* set the locale for only the messages system (all else is clean) */
	(void) setlocale(LC_ALL, "");
#ifndef TEXT_DOMAIN		/* Should be defined by cc -D */
#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
#endif
	(void) textdomain(TEXT_DOMAIN);

	while ((chr = getopt(argc, argv, "h:t:z:")) != EOF) {
		switch (chr) {
		case 'h':
			tnrhdb_file = optarg;
			break;
		case 't':
			tnrhtp_file = optarg;
			break;
		case 'z':
			tnzonecfg_file = optarg;
			break;
		default:
			usage();
		}
	}

	check_tnrhtp(tnrhtp_file);
	check_tnrhdb(tnrhdb_file);
	check_tnzonecfg(tnzonecfg_file);

	return (exitval);
}

static void
usage(void)
{
	(void) fprintf(stderr, gettext(
	    "usage: tnchkdb [-h path] [-t path] [-z path]\n"));
	exit(2);
}

static void
print_error(int linenum, int err, const char *errstr)
{
	(void) fprintf(stderr, gettext("line %1$d: %2$s: %.32s\n"), linenum,
	    tsol_strerror(err, errno), errstr);
}

static void
cipso_representable(const bslabel_t *lab, int linenum, const char *template,
    const char *name)
{
	const _blevel_impl_t *blab = (const _blevel_impl_t *)lab;
	int lclass;
	uint32_t c8;

	if (!bltype(lab, SUN_SL_ID)) {
		(void) fprintf(stderr, gettext("tnchkdb: "
		    "%1$s type %2$d is invalid for cipso labels: "
		    "line %3$d entry %4$s\n"), name, GETBLTYPE(lab), linenum,
		    template);
		exitval = 1;
	}
	lclass = LCLASS(blab);
	if (lclass & 0xff00) {
		(void) fprintf(stderr, gettext("tnchkdb: "
		    "%1$s classification %2$x is invalid for cipso labels: "
		    "line %3$d entry %4$s\n"), name, lclass, linenum,
		    template);
		exitval = 1;
	}
	c8 = blab->compartments.c8;
#ifdef  _BIG_ENDIAN
	if (c8 & 0x0000ffff) {
#else
	if (c8 & 0xffff0000) {
#endif
		(void) fprintf(stderr, gettext("tnchkdb: %1$s "
		    "compartments 241-256 must be zero for cipso labels: "
		    "line %2$d entry %3$s\n"), name, linenum, template);
		exitval = 1;
	}
}

static void
check_tnrhtp(const char *file)
{
	tsol_tpent_t *tpentp;
	tsol_tpstr_t tpstr;
	int err;
	char *errstr;
	FILE *fp;
	blevel_t *l1, *l2;
	char line[2048], *cp;
	int linenum = 0;
	struct tsol_name_list *tnl;
	char buf[NSS_BUFLEN_TSOL_TP];
	uint32_t initial_doi = 0;
	boolean_t multiple_doi_found = B_FALSE;
	boolean_t doi_zero_found = B_FALSE;

	(void) printf(gettext("checking %s ...\n"), file);

	if ((fp = fopen(file, "r")) == NULL) {
		err = errno;
		(void) fprintf(stderr,
		    gettext("tnchkdb: failed to open %1$s: %2$s\n"), file,
		    strerror(err));
		exitval = 2;
		tnrhtp_bad = B_TRUE;
		return;
	}

	while (fgets(line, sizeof (line), fp) != NULL) {
		linenum++;
		if (line[0] == '#')
			continue;
		if ((cp = strchr(line, '\n')) != NULL)
			*cp = '\0';
		(void) str_to_tpstr(line, strlen(line), &tpstr, buf,
		    sizeof (buf));
		tpentp = tpstr_to_ent(&tpstr, &err, &errstr);
		if (tpentp == NULL) {
			if (err == LTSNET_EMPTY)
				continue;
			print_error(linenum, err, errstr);
			exitval = 1;
			/*
			 * Flag is set *only* for parsing errors, which result
			 * in omitting the entry from tsol_name_list.
			 */
			tnrhtp_bad = B_TRUE;
			continue;
		}

		switch (tpentp->host_type) {
		case UNLABELED:
			/*
			 * check doi
			 */
			if (initial_doi == 0)
				initial_doi = tpentp->tp_cipso_doi_unl;
			if (tpentp->tp_cipso_doi_unl != initial_doi)
				multiple_doi_found = B_TRUE;
			if (tpentp->tp_cipso_doi_unl == 0)
				doi_zero_found = B_TRUE;

			cipso_representable(&tpentp->tp_def_label, linenum,
			    tpentp->name, TP_DEFLABEL);

			/*
			 * check max_sl dominates min_sl
			 */
			l1 = &tpentp->tp_gw_sl_range.lower_bound;
			l2 = &tpentp->tp_gw_sl_range.upper_bound;
			if (!bldominates(l2, l1)) {
				(void) fprintf(stderr,
				    gettext("tnchkdb: max_sl does not "
				    "dominate min_sl: line %1$d entry %2$s\n"),
				    linenum, tpentp->name);
				exitval = 1;
			}

			cipso_representable(l1, linenum, tpentp->name,
			    TP_MINLABEL);
			l1 = (blevel_t *)&tpentp->tp_gw_sl_set[0];
			l2 = (blevel_t *)&tpentp->tp_gw_sl_set[NSLS_MAX];
			for (; l1 < l2; l1++) {
				if (bisinvalid(l1))
					break;
				cipso_representable(l1, linenum, tpentp->name,
				    TP_SET);
			}
			break;

		case SUN_CIPSO:
			/*
			 * check max_sl dominates min_sl
			 */
			l1 = &tpentp->tp_sl_range_cipso.lower_bound;
			l2 = &tpentp->tp_sl_range_cipso.upper_bound;
			if (!bldominates(l2, l1)) {
				(void) fprintf(stderr,
				    gettext("tnchkdb: max_sl does not "
				    "dominate min_sl: line %1$d entry %2$s\n"),
				    linenum, tpentp->name);
				exitval = 1;
			}

			cipso_representable(l1, linenum, tpentp->name,
			    TP_MINLABEL);

			l1 = (blevel_t *)&tpentp->tp_sl_set_cipso[0];
			l2 = (blevel_t *)&tpentp->tp_sl_set_cipso[NSLS_MAX];
			for (; l1 < l2; l1++) {
				if (bisinvalid(l1))
					break;
				cipso_representable(l1, linenum, tpentp->name,
				    TP_SET);
			}

			/*
			 * check doi
			 */
			if (initial_doi == 0)
				initial_doi = tpentp->tp_cipso_doi_cipso;
			if (tpentp->tp_cipso_doi_cipso != initial_doi)
				multiple_doi_found = B_TRUE;
			if (tpentp->tp_cipso_doi_cipso == 0)
				doi_zero_found = B_TRUE;
			break;

		default:
			(void) fprintf(stderr, gettext("tnchkdb: unknown host "
			    "type %1$d: line %2$d entry %3$s\n"),
			    tpentp->host_type, linenum, tpentp->name);
			exitval = 1;
		} /* switch */

		/*
		 * check if a duplicated entry
		 */
		if ((tnl = find_template(tpentp->name)) != NULL) {
			(void) fprintf(stderr, gettext("tnchkdb: duplicated "
			    "entry: %1$s at lines %2$d and %3$d\n"),
			    tpentp->name, tnl->linenum, linenum);
			exitval = 1;
		} else {
			add_template(tpentp->name, linenum);
		}
		tsol_freetpent(tpentp);
	}
	if (multiple_doi_found == B_TRUE) {
		(void) fprintf(stderr,
		    gettext("tnchkdb: Warning: tnrhtp entries do not all "
		    "contain the same DOI value\n"));
	}
	if (doi_zero_found == B_TRUE) {
		(void) fprintf(stderr,
		    gettext("tnchkdb: Warning: DOI=0 found in some "
		    "tnrhtp entries\n"));
	}
	(void) fclose(fp);
}

static void
check_tnrhdb(const char *file)
{
	tsol_rhent_t *rhentp;
	tsol_rhstr_t rhstr;
	int err;
	char *errstr;
	FILE *fp;
	char line[2048], *cp;
	int linenum;
	in6_addr_t addr;
	struct tsol_addr_list *tal;
	char buf[NSS_BUFLEN_TSOL_RH];

	(void) printf(gettext("checking %s ...\n"), file);

	if ((fp = fopen(file, "r")) == NULL) {
		err = errno;
		(void) fprintf(stderr,
		    gettext("tnchkdb: failed to open %s: %s\n"), file,
		    strerror(err));
		exitval = 2;
		return;
	}

	/*
	 * check that all templates used in tnrhdb file are defined by tnrhtp
	 */
	linenum = 0;
	while (fgets(line, sizeof (line), fp) != NULL) {
		linenum++;
		if (line[0] == '#')
			continue;
		if ((cp = strchr(line, '\n')) != NULL)
			*cp = '\0';
		(void) str_to_rhstr(line, strlen(line), &rhstr, buf,
		    sizeof (buf));
		rhentp = rhstr_to_ent(&rhstr, &err, &errstr);
		if (rhentp == NULL) {
			if (err == LTSNET_EMPTY)
				continue;
			print_error(linenum, err, errstr);
			exitval = 1;
			continue;
		}

		if (rhentp->rh_address.ta_family == AF_INET) {
			IN6_INADDR_TO_V4MAPPED(&rhentp->rh_address.ta_addr_v4,
			    &addr);
		} else {
			addr = rhentp->rh_address.ta_addr_v6;
		}
		if ((tal = find_host(rhentp->rh_prefix, addr)) != NULL) {
			(void) fprintf(stderr,
			    gettext("tnchkdb: duplicate entry: lines %1$d and "
			    "%2$d\n"), tal->linenum, linenum);
			exitval = 1;
		} else {
			add_host(rhentp->rh_prefix, addr, linenum);
		}

		if (!tnrhtp_bad && find_template(rhentp->rh_template) == NULL) {
			(void) fprintf(stderr,
			    gettext("tnchkdb: unknown template name: %1$s at "
			    "line %2$d\n"), rhentp->rh_template, linenum);
			exitval = 1;
		}

		tsol_freerhent(rhentp);
	}
	(void) fclose(fp);
}

static void
check_mlp_conflicts(tsol_mlp_t *mlps, boolean_t isglobal, const char *name,
    int linenum)
{
	tsol_mlp_t *mlpptr, *mlp2;
	mlp_info_list_t *mil;

	for (mlpptr = mlps; !TSOL_MLP_END(mlpptr); mlpptr++) {
		if (mlpptr->mlp_port_upper == 0)
			mlpptr->mlp_port_upper = mlpptr->mlp_port;

		/* First, validate against self for duplicates */
		for (mlp2 = mlps; mlp2 < mlpptr; mlp2++) {
			if (mlp2->mlp_ipp == mlpptr->mlp_ipp &&
			    !(mlp2->mlp_port_upper < mlpptr->mlp_port ||
			    mlp2->mlp_port > mlpptr->mlp_port_upper))
				break;
		}

		if (mlp2 < mlpptr) {
			(void) fprintf(stderr, gettext("tnchkdb: self-overlap "
			    "of %1$s MLP protocol %2$d port %3$d-%4$d with "
			    "%5$d-%6$d: zone %7$s line %8$d\n"),
			    gettext(isglobal ? "global" : "zone-specific"),
			    mlpptr->mlp_ipp, mlpptr->mlp_port,
			    mlpptr->mlp_port_upper, mlp2->mlp_port,
			    mlp2->mlp_port_upper, name, linenum);
			exitval = 1;
		}

		if (isglobal) {
			/* Next, validate against list for duplicates */
			for (mil = global_mlps; mil != NULL; mil = mil->next) {
				if (strcmp(mil->name, name) == 0)
					continue;
				if (mil->mlp.mlp_ipp == mlpptr->mlp_ipp &&
				    !(mil->mlp.mlp_port_upper <
				    mlpptr->mlp_port ||
				    mil->mlp.mlp_port >
				    mlpptr->mlp_port_upper))
					break;
			}

			if (mil != NULL) {
				(void) fprintf(stderr, gettext("tnchkdb: "
				    "overlap of global MLP protocol %1$d port "
				    "%2$d-%3$d with zone %4$s %5$d-%6$d: zone "
				    "%7$s lines %8$d and %9$d\n"),
				    mlpptr->mlp_ipp, mlpptr->mlp_port,
				    mlpptr->mlp_port_upper, mil->name,
				    mil->mlp.mlp_port, mil->mlp.mlp_port_upper,
				    name, mil->linenum, linenum);
				exitval = 1;
			}

			/* Now throw into list */
			if ((mil = malloc(sizeof (*mil))) == NULL) {
				(void) fprintf(stderr, gettext("tnchkdb: "
				    "malloc error: %s\n"), strerror(errno));
				exit(2);
			}
			(void) strlcpy(mil->name, name, sizeof (mil->name));
			mil->linenum = linenum;
			mil->mlp = *mlpptr;
			mil->next = global_mlps;
			global_mlps = mil;
		}
	}
}

static void
check_tnzonecfg(const char *file)
{
	tsol_zcent_t *zc;
	int err;
	char *errstr;
	FILE *fp;
	char line[2048], *cp;
	int linenum;
	boolean_t saw_global;
	struct tsol_name_list *tnl;

	(void) printf(gettext("checking %s ...\n"), file);

	if ((fp = fopen(file, "r")) == NULL) {
		err = errno;
		(void) fprintf(stderr,
		    gettext("tnchkdb: failed to open %s: %s\n"), file,
		    strerror(err));
		exitval = 2;
		return;
	}

	saw_global = B_FALSE;
	linenum = 0;
	while (fgets(line, sizeof (line), fp) != NULL) {
		if ((cp = strchr(line, '\n')) != NULL)
			*cp = '\0';

		linenum++;
		if ((zc = tsol_sgetzcent(line, &err, &errstr)) == NULL) {
			if (err == LTSNET_EMPTY)
				continue;
			print_error(linenum, err, errstr);
			exitval = 1;
			continue;
		}

		cipso_representable(&zc->zc_label, linenum, zc->zc_name,
		    "label");

		if (strcmp(zc->zc_name, "global") == 0)
			saw_global = B_TRUE;

		if ((tnl = find_zone(zc->zc_name)) != NULL) {
			(void) fprintf(stderr,
			    gettext("tnchkdb: duplicate zones: %1$s at lines "
			    "%2$d and %3$d\n"), zc->zc_name, tnl->linenum,
			    linenum);
			exitval = 1;
		} else {
			add_zone(zc->zc_name, linenum);
		}

		if (zc->zc_private_mlp != NULL)
			check_mlp_conflicts(zc->zc_private_mlp, B_FALSE,
			    zc->zc_name, linenum);
		if (zc->zc_shared_mlp != NULL)
			check_mlp_conflicts(zc->zc_shared_mlp, B_TRUE,
			    zc->zc_name, linenum);

		tsol_freezcent(zc);
	}
	(void) fclose(fp);

	if (!saw_global) {
		(void) fprintf(stderr, gettext("tnchkdb: missing required "
		    "entry for global zone in %s\n"), file);
		exitval = 1;
	}
}