xref: /illumos-gate/usr/src/cmd/tsol/tnchkdb/tnchkdb.c (revision 96b5b3ca)
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 
40 static void usage(void);
41 static void check_tnrhtp(const char *);
42 static void check_tnrhdb(const char *);
43 static void check_tnzonecfg(const char *);
44 
45 static boolean_t tnrhtp_bad;
46 static int exitval;
47 
48 struct tsol_name_list {
49 	struct tsol_name_list *next;
50 	int linenum;
51 	char name[TNTNAMSIZ];
52 };
53 
54 struct tsol_addr_list {
55 	struct tsol_addr_list *next;
56 	int linenum;
57 	int prefix_len;
58 	in6_addr_t addr;
59 };
60 
61 static struct tsol_name_list *tp_list_head;
62 static struct tsol_addr_list *rh_list_head;
63 static struct tsol_name_list *zc_list_head;
64 
65 typedef 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 
72 static mlp_info_list_t *global_mlps;
73 
74 static void
add_name(struct tsol_name_list ** head,const char * name,int linenum)75 add_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 
95 static struct tsol_name_list *
find_name(struct tsol_name_list * head,const char * name)96 find_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 
106 static void
add_addr(struct tsol_addr_list ** head,int prefix_len,in6_addr_t addr,int linenum)107 add_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 
129 static struct tsol_addr_list *
find_addr(struct tsol_addr_list * head,int prefix_len,in6_addr_t addr)130 find_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 
141 static void
add_template(const char * name,int linenum)142 add_template(const char *name, int linenum)
143 {
144 	add_name(&tp_list_head, name, linenum);
145 }
146 
147 static struct tsol_name_list *
find_template(const char * name)148 find_template(const char *name)
149 {
150 	return (find_name(tp_list_head, name));
151 }
152 
153 static void
add_host(int prefix_len,in6_addr_t addr,int linenum)154 add_host(int prefix_len, in6_addr_t addr, int linenum)
155 {
156 	add_addr(&rh_list_head, prefix_len, addr, linenum);
157 }
158 
159 static struct tsol_addr_list *
find_host(int prefix_len,in6_addr_t addr)160 find_host(int prefix_len, in6_addr_t addr)
161 {
162 	return (find_addr(rh_list_head, prefix_len, addr));
163 }
164 
165 static void
add_zone(const char * name,int linenum)166 add_zone(const char *name, int linenum)
167 {
168 	add_name(&zc_list_head, name, linenum);
169 }
170 
171 static struct tsol_name_list *
find_zone(const char * name)172 find_zone(const char *name)
173 {
174 	return (find_name(zc_list_head, name));
175 }
176 
177 int
main(int argc,char ** argv)178 main(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 
215 static void
usage(void)216 usage(void)
217 {
218 	(void) fprintf(stderr, gettext(
219 	    "usage: tnchkdb [-h path] [-t path] [-z path]\n"));
220 	exit(2);
221 }
222 
223 static void
print_error(int linenum,int err,const char * errstr)224 print_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 
230 static void
cipso_representable(const bslabel_t * lab,int linenum,const char * template,const char * name)231 cipso_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 
266 static void
267 check_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 
427 static void
428 check_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 
500 static void
501 check_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 
570 static void
571 check_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