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