1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 *  Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 *  Use is subject to license terms.
25 */
26
27/*
28 * tnchkdb.c - Trusted network database checking utility
29 */
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <locale.h>
34#include <malloc.h>
35#include <string.h>
36#include <libtsnet.h>
37#include <netinet/in.h>
38#include <nss_dbdefs.h>
39
40static void usage(void);
41static void check_tnrhtp(const char *);
42static void check_tnrhdb(const char *);
43static void check_tnzonecfg(const char *);
44
45static boolean_t tnrhtp_bad;
46static int exitval;
47
48struct tsol_name_list {
49	struct tsol_name_list *next;
50	int linenum;
51	char name[TNTNAMSIZ];
52};
53
54struct tsol_addr_list {
55	struct tsol_addr_list *next;
56	int linenum;
57	int prefix_len;
58	in6_addr_t addr;
59};
60
61static struct tsol_name_list *tp_list_head;
62static struct tsol_addr_list *rh_list_head;
63static struct tsol_name_list *zc_list_head;
64
65typedef struct mlp_info_list_s {
66	struct mlp_info_list_s *next;
67	int linenum;
68	tsol_mlp_t mlp;
69	char name[TNTNAMSIZ];
70} mlp_info_list_t;
71
72static mlp_info_list_t *global_mlps;
73
74static void
75add_name(struct tsol_name_list **head, const char *name, int linenum)
76{
77	int err;
78	struct tsol_name_list *entry;
79
80	entry = malloc(sizeof (struct tsol_name_list));
81	if (entry == NULL) {
82		err = errno;
83
84		(void) fprintf(stderr,
85		    gettext("tnchkdb: allocating name list: %s\n"),
86		    strerror(err));
87		exit(1);
88	}
89	(void) strlcpy(entry->name, name, sizeof (entry->name));
90	entry->next = *head;
91	entry->linenum = linenum;
92	*head = entry;
93}
94
95static struct tsol_name_list *
96find_name(struct tsol_name_list *head, const char *name)
97{
98	struct tsol_name_list *entry;
99
100	for (entry = head; entry != NULL; entry = entry->next)
101		if (strcmp(entry->name, name) == 0)
102			break;
103	return (entry);
104}
105
106static void
107add_addr(struct tsol_addr_list **head, int prefix_len, in6_addr_t addr,
108    int linenum)
109{
110	int err;
111	struct tsol_addr_list *entry;
112
113	entry = malloc(sizeof (struct tsol_addr_list));
114	if (entry == NULL) {
115		err = errno;
116
117		(void) fprintf(stderr,
118		    gettext("tnchkdb: allocating addr list: %s\n"),
119		    strerror(err));
120		exit(2);
121	}
122	entry->prefix_len = prefix_len;
123	entry->addr = addr;
124	entry->next = *head;
125	entry->linenum = linenum;
126	*head = entry;
127}
128
129static struct tsol_addr_list *
130find_addr(struct tsol_addr_list *head, int prefix_len, in6_addr_t addr)
131{
132	struct tsol_addr_list *entry;
133
134	for (entry = head; entry != NULL; entry = entry->next)
135		if (entry->prefix_len == prefix_len &&
136		    IN6_ARE_ADDR_EQUAL(&entry->addr, &addr))
137			break;
138	return (entry);
139}
140
141static void
142add_template(const char *name, int linenum)
143{
144	add_name(&tp_list_head, name, linenum);
145}
146
147static struct tsol_name_list *
148find_template(const char *name)
149{
150	return (find_name(tp_list_head, name));
151}
152
153static void
154add_host(int prefix_len, in6_addr_t addr, int linenum)
155{
156	add_addr(&rh_list_head, prefix_len, addr, linenum);
157}
158
159static struct tsol_addr_list *
160find_host(int prefix_len, in6_addr_t addr)
161{
162	return (find_addr(rh_list_head, prefix_len, addr));
163}
164
165static void
166add_zone(const char *name, int linenum)
167{
168	add_name(&zc_list_head, name, linenum);
169}
170
171static struct tsol_name_list *
172find_zone(const char *name)
173{
174	return (find_name(zc_list_head, name));
175}
176
177int
178main(int argc, char **argv)
179{
180	const char *tnrhdb_file = TNRHDB_PATH;
181	const char *tnrhtp_file = TNRHTP_PATH;
182	const char *tnzonecfg_file = TNZONECFG_PATH;
183	int chr;
184
185	/* set the locale for only the messages system (all else is clean) */
186	(void) setlocale(LC_ALL, "");
187#ifndef TEXT_DOMAIN		/* Should be defined by cc -D */
188#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
189#endif
190	(void) textdomain(TEXT_DOMAIN);
191
192	while ((chr = getopt(argc, argv, "h:t:z:")) != EOF) {
193		switch (chr) {
194		case 'h':
195			tnrhdb_file = optarg;
196			break;
197		case 't':
198			tnrhtp_file = optarg;
199			break;
200		case 'z':
201			tnzonecfg_file = optarg;
202			break;
203		default:
204			usage();
205		}
206	}
207
208	check_tnrhtp(tnrhtp_file);
209	check_tnrhdb(tnrhdb_file);
210	check_tnzonecfg(tnzonecfg_file);
211
212	return (exitval);
213}
214
215static void
216usage(void)
217{
218	(void) fprintf(stderr, gettext(
219	    "usage: tnchkdb [-h path] [-t path] [-z path]\n"));
220	exit(2);
221}
222
223static void
224print_error(int linenum, int err, const char *errstr)
225{
226	(void) fprintf(stderr, gettext("line %1$d: %2$s: %.32s\n"), linenum,
227	    tsol_strerror(err, errno), errstr);
228}
229
230static void
231cipso_representable(const bslabel_t *lab, int linenum, const char *template,
232    const char *name)
233{
234	const _blevel_impl_t *blab = (const _blevel_impl_t *)lab;
235	int lclass;
236	uint32_t c8;
237
238	if (!bltype(lab, SUN_SL_ID)) {
239		(void) fprintf(stderr, gettext("tnchkdb: "
240		    "%1$s type %2$d is invalid for cipso labels: "
241		    "line %3$d entry %4$s\n"), name, GETBLTYPE(lab), linenum,
242		    template);
243		exitval = 1;
244	}
245	lclass = LCLASS(blab);
246	if (lclass & 0xff00) {
247		(void) fprintf(stderr, gettext("tnchkdb: "
248		    "%1$s classification %2$x is invalid for cipso labels: "
249		    "line %3$d entry %4$s\n"), name, lclass, linenum,
250		    template);
251		exitval = 1;
252	}
253	c8 = blab->compartments.c8;
254#ifdef  _BIG_ENDIAN
255	if (c8 & 0x0000ffff) {
256#else
257	if (c8 & 0xffff0000) {
258#endif
259		(void) fprintf(stderr, gettext("tnchkdb: %1$s "
260		    "compartments 241-256 must be zero for cipso labels: "
261		    "line %2$d entry %3$s\n"), name, linenum, template);
262		exitval = 1;
263	}
264}
265
266static void
267check_tnrhtp(const char *file)
268{
269	tsol_tpent_t *tpentp;
270	tsol_tpstr_t tpstr;
271	int err;
272	char *errstr;
273	FILE *fp;
274	blevel_t *l1, *l2;
275	char line[2048], *cp;
276	int linenum = 0;
277	struct tsol_name_list *tnl;
278	char buf[NSS_BUFLEN_TSOL_TP];
279	uint32_t initial_doi = 0;
280	boolean_t multiple_doi_found = B_FALSE;
281	boolean_t doi_zero_found = B_FALSE;
282
283	(void) printf(gettext("checking %s ...\n"), file);
284
285	if ((fp = fopen(file, "r")) == NULL) {
286		err = errno;
287		(void) fprintf(stderr,
288		    gettext("tnchkdb: failed to open %1$s: %2$s\n"), file,
289		    strerror(err));
290		exitval = 2;
291		tnrhtp_bad = B_TRUE;
292		return;
293	}
294
295	while (fgets(line, sizeof (line), fp) != NULL) {
296		linenum++;
297		if (line[0] == '#')
298			continue;
299		if ((cp = strchr(line, '\n')) != NULL)
300			*cp = '\0';
301		(void) str_to_tpstr(line, strlen(line), &tpstr, buf,
302		    sizeof (buf));
303		tpentp = tpstr_to_ent(&tpstr, &err, &errstr);
304		if (tpentp == NULL) {
305			if (err == LTSNET_EMPTY)
306				continue;
307			print_error(linenum, err, errstr);
308			exitval = 1;
309			/*
310			 * Flag is set *only* for parsing errors, which result
311			 * in omitting the entry from tsol_name_list.
312			 */
313			tnrhtp_bad = B_TRUE;
314			continue;
315		}
316
317		switch (tpentp->host_type) {
318		case UNLABELED:
319			/*
320			 * check doi
321			 */
322			if (initial_doi == 0)
323				initial_doi = tpentp->tp_cipso_doi_unl;
324			if (tpentp->tp_cipso_doi_unl != initial_doi)
325				multiple_doi_found = B_TRUE;
326			if (tpentp->tp_cipso_doi_unl == 0)
327				doi_zero_found = B_TRUE;
328
329			cipso_representable(&tpentp->tp_def_label, linenum,
330			    tpentp->name, TP_DEFLABEL);
331
332			/*
333			 * check max_sl dominates min_sl
334			 */
335			l1 = &tpentp->tp_gw_sl_range.lower_bound;
336			l2 = &tpentp->tp_gw_sl_range.upper_bound;
337			if (!bldominates(l2, l1)) {
338				(void) fprintf(stderr,
339				    gettext("tnchkdb: max_sl does not "
340				    "dominate min_sl: line %1$d entry %2$s\n"),
341				    linenum, tpentp->name);
342				exitval = 1;
343			}
344
345			cipso_representable(l1, linenum, tpentp->name,
346			    TP_MINLABEL);
347			l1 = (blevel_t *)&tpentp->tp_gw_sl_set[0];
348			l2 = (blevel_t *)&tpentp->tp_gw_sl_set[NSLS_MAX];
349			for (; l1 < l2; l1++) {
350				if (bisinvalid(l1))
351					break;
352				cipso_representable(l1, linenum, tpentp->name,
353				    TP_SET);
354			}
355			break;
356
357		case SUN_CIPSO:
358			/*
359			 * check max_sl dominates min_sl
360			 */
361			l1 = &tpentp->tp_sl_range_cipso.lower_bound;
362			l2 = &tpentp->tp_sl_range_cipso.upper_bound;
363			if (!bldominates(l2, l1)) {
364				(void) fprintf(stderr,
365				    gettext("tnchkdb: max_sl does not "
366				    "dominate min_sl: line %1$d entry %2$s\n"),
367				    linenum, tpentp->name);
368				exitval = 1;
369			}
370
371			cipso_representable(l1, linenum, tpentp->name,
372			    TP_MINLABEL);
373
374			l1 = (blevel_t *)&tpentp->tp_sl_set_cipso[0];
375			l2 = (blevel_t *)&tpentp->tp_sl_set_cipso[NSLS_MAX];
376			for (; l1 < l2; l1++) {
377				if (bisinvalid(l1))
378					break;
379				cipso_representable(l1, linenum, tpentp->name,
380				    TP_SET);
381			}
382
383			/*
384			 * check doi
385			 */
386			if (initial_doi == 0)
387				initial_doi = tpentp->tp_cipso_doi_cipso;
388			if (tpentp->tp_cipso_doi_cipso != initial_doi)
389				multiple_doi_found = B_TRUE;
390			if (tpentp->tp_cipso_doi_cipso == 0)
391				doi_zero_found = B_TRUE;
392			break;
393
394		default:
395			(void) fprintf(stderr, gettext("tnchkdb: unknown host "
396			    "type %1$d: line %2$d entry %3$s\n"),
397			    tpentp->host_type, linenum, tpentp->name);
398			exitval = 1;
399		} /* switch */
400
401		/*
402		 * check if a duplicated entry
403		 */
404		if ((tnl = find_template(tpentp->name)) != NULL) {
405			(void) fprintf(stderr, gettext("tnchkdb: duplicated "
406			    "entry: %1$s at lines %2$d and %3$d\n"),
407			    tpentp->name, tnl->linenum, linenum);
408			exitval = 1;
409		} else {
410			add_template(tpentp->name, linenum);
411		}
412		tsol_freetpent(tpentp);
413	}
414	if (multiple_doi_found == B_TRUE) {
415		(void) fprintf(stderr,
416		    gettext("tnchkdb: Warning: tnrhtp entries do not all "
417		    "contain the same DOI value\n"));
418	}
419	if (doi_zero_found == B_TRUE) {
420		(void) fprintf(stderr,
421		    gettext("tnchkdb: Warning: DOI=0 found in some "
422		    "tnrhtp entries\n"));
423	}
424	(void) fclose(fp);
425}
426
427static void
428check_tnrhdb(const char *file)
429{
430	tsol_rhent_t *rhentp;
431	tsol_rhstr_t rhstr;
432	int err;
433	char *errstr;
434	FILE *fp;
435	char line[2048], *cp;
436	int linenum;
437	in6_addr_t addr;
438	struct tsol_addr_list *tal;
439	char buf[NSS_BUFLEN_TSOL_RH];
440
441	(void) printf(gettext("checking %s ...\n"), file);
442
443	if ((fp = fopen(file, "r")) == NULL) {
444		err = errno;
445		(void) fprintf(stderr,
446		    gettext("tnchkdb: failed to open %s: %s\n"), file,
447		    strerror(err));
448		exitval = 2;
449		return;
450	}
451
452	/*
453	 * check that all templates used in tnrhdb file are defined by tnrhtp
454	 */
455	linenum = 0;
456	while (fgets(line, sizeof (line), fp) != NULL) {
457		linenum++;
458		if (line[0] == '#')
459			continue;
460		if ((cp = strchr(line, '\n')) != NULL)
461			*cp = '\0';
462		(void) str_to_rhstr(line, strlen(line), &rhstr, buf,
463		    sizeof (buf));
464		rhentp = rhstr_to_ent(&rhstr, &err, &errstr);
465		if (rhentp == NULL) {
466			if (err == LTSNET_EMPTY)
467				continue;
468			print_error(linenum, err, errstr);
469			exitval = 1;
470			continue;
471		}
472
473		if (rhentp->rh_address.ta_family == AF_INET) {
474			IN6_INADDR_TO_V4MAPPED(&rhentp->rh_address.ta_addr_v4,
475			    &addr);
476		} else {
477			addr = rhentp->rh_address.ta_addr_v6;
478		}
479		if ((tal = find_host(rhentp->rh_prefix, addr)) != NULL) {
480			(void) fprintf(stderr,
481			    gettext("tnchkdb: duplicate entry: lines %1$d and "
482			    "%2$d\n"), tal->linenum, linenum);
483			exitval = 1;
484		} else {
485			add_host(rhentp->rh_prefix, addr, linenum);
486		}
487
488		if (!tnrhtp_bad && find_template(rhentp->rh_template) == NULL) {
489			(void) fprintf(stderr,
490			    gettext("tnchkdb: unknown template name: %1$s at "
491			    "line %2$d\n"), rhentp->rh_template, linenum);
492			exitval = 1;
493		}
494
495		tsol_freerhent(rhentp);
496	}
497	(void) fclose(fp);
498}
499
500static void
501check_mlp_conflicts(tsol_mlp_t *mlps, boolean_t isglobal, const char *name,
502    int linenum)
503{
504	tsol_mlp_t *mlpptr, *mlp2;
505	mlp_info_list_t *mil;
506
507	for (mlpptr = mlps; !TSOL_MLP_END(mlpptr); mlpptr++) {
508		if (mlpptr->mlp_port_upper == 0)
509			mlpptr->mlp_port_upper = mlpptr->mlp_port;
510
511		/* First, validate against self for duplicates */
512		for (mlp2 = mlps; mlp2 < mlpptr; mlp2++) {
513			if (mlp2->mlp_ipp == mlpptr->mlp_ipp &&
514			    !(mlp2->mlp_port_upper < mlpptr->mlp_port ||
515			    mlp2->mlp_port > mlpptr->mlp_port_upper))
516				break;
517		}
518
519		if (mlp2 < mlpptr) {
520			(void) fprintf(stderr, gettext("tnchkdb: self-overlap "
521			    "of %1$s MLP protocol %2$d port %3$d-%4$d with "
522			    "%5$d-%6$d: zone %7$s line %8$d\n"),
523			    gettext(isglobal ? "global" : "zone-specific"),
524			    mlpptr->mlp_ipp, mlpptr->mlp_port,
525			    mlpptr->mlp_port_upper, mlp2->mlp_port,
526			    mlp2->mlp_port_upper, name, linenum);
527			exitval = 1;
528		}
529
530		if (isglobal) {
531			/* Next, validate against list for duplicates */
532			for (mil = global_mlps; mil != NULL; mil = mil->next) {
533				if (strcmp(mil->name, name) == 0)
534					continue;
535				if (mil->mlp.mlp_ipp == mlpptr->mlp_ipp &&
536				    !(mil->mlp.mlp_port_upper <
537				    mlpptr->mlp_port ||
538				    mil->mlp.mlp_port >
539				    mlpptr->mlp_port_upper))
540					break;
541			}
542
543			if (mil != NULL) {
544				(void) fprintf(stderr, gettext("tnchkdb: "
545				    "overlap of global MLP protocol %1$d port "
546				    "%2$d-%3$d with zone %4$s %5$d-%6$d: zone "
547				    "%7$s lines %8$d and %9$d\n"),
548				    mlpptr->mlp_ipp, mlpptr->mlp_port,
549				    mlpptr->mlp_port_upper, mil->name,
550				    mil->mlp.mlp_port, mil->mlp.mlp_port_upper,
551				    name, mil->linenum, linenum);
552				exitval = 1;
553			}
554
555			/* Now throw into list */
556			if ((mil = malloc(sizeof (*mil))) == NULL) {
557				(void) fprintf(stderr, gettext("tnchkdb: "
558				    "malloc error: %s\n"), strerror(errno));
559				exit(2);
560			}
561			(void) strlcpy(mil->name, name, sizeof (mil->name));
562			mil->linenum = linenum;
563			mil->mlp = *mlpptr;
564			mil->next = global_mlps;
565			global_mlps = mil;
566		}
567	}
568}
569
570static void
571check_tnzonecfg(const char *file)
572{
573	tsol_zcent_t *zc;
574	int err;
575	char *errstr;
576	FILE *fp;
577	char line[2048], *cp;
578	int linenum;
579	boolean_t saw_global;
580	struct tsol_name_list *tnl;
581
582	(void) printf(gettext("checking %s ...\n"), file);
583
584	if ((fp = fopen(file, "r")) == NULL) {
585		err = errno;
586		(void) fprintf(stderr,
587		    gettext("tnchkdb: failed to open %s: %s\n"), file,
588		    strerror(err));
589		exitval = 2;
590		return;
591	}
592
593	saw_global = B_FALSE;
594	linenum = 0;
595	while (fgets(line, sizeof (line), fp) != NULL) {
596		if ((cp = strchr(line, '\n')) != NULL)
597			*cp = '\0';
598
599		linenum++;
600		if ((zc = tsol_sgetzcent(line, &err, &errstr)) == NULL) {
601			if (err == LTSNET_EMPTY)
602				continue;
603			print_error(linenum, err, errstr);
604			exitval = 1;
605			continue;
606		}
607
608		cipso_representable(&zc->zc_label, linenum, zc->zc_name,
609		    "label");
610
611		if (strcmp(zc->zc_name, "global") == 0)
612			saw_global = B_TRUE;
613
614		if ((tnl = find_zone(zc->zc_name)) != NULL) {
615			(void) fprintf(stderr,
616			    gettext("tnchkdb: duplicate zones: %1$s at lines "
617			    "%2$d and %3$d\n"), zc->zc_name, tnl->linenum,
618			    linenum);
619			exitval = 1;
620		} else {
621			add_zone(zc->zc_name, linenum);
622		}
623
624		if (zc->zc_private_mlp != NULL)
625			check_mlp_conflicts(zc->zc_private_mlp, B_FALSE,
626			    zc->zc_name, linenum);
627		if (zc->zc_shared_mlp != NULL)
628			check_mlp_conflicts(zc->zc_shared_mlp, B_TRUE,
629			    zc->zc_name, linenum);
630
631		tsol_freezcent(zc);
632	}
633	(void) fclose(fp);
634
635	if (!saw_global) {
636		(void) fprintf(stderr, gettext("tnchkdb: missing required "
637		    "entry for global zone in %s\n"), file);
638		exitval = 1;
639	}
640}
641