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