xref: /illumos-gate/usr/src/cmd/tsol/tnctl/tnctl.c (revision 44bac77bf8165ebe38afb85dda247b928d88edf8)
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 2008 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  * tnctl.c -
31  *          Trusted Network control utility
32  */
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stddef.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <locale.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <netdb.h>
47 #include <libtsnet.h>
48 #include <zone.h>
49 #include <nss_dbdefs.h>
50 
51 static void process_rh(const char *);
52 static void process_rhl(const char *);
53 static void process_mlp(const char *);
54 static void process_tp(const char *);
55 static void process_tpl(const char *);
56 static void process_tnzone(const char *);
57 static void usage(void);
58 static void translate_inet_addr(tsol_rhent_t *, int *, char [], int);
59 
60 static boolean_t verbose_mode;
61 static boolean_t delete_mode;
62 static boolean_t flush_mode;
63 
64 int
65 main(int argc, char **argv)
66 {
67 	extern char *optarg;
68 	int chr;
69 
70 	/* Don't do anything if labeling is not active. */
71 	if (!is_system_labeled())
72 		return (0);
73 
74 	/* set the locale for only the messages system (all else is clean) */
75 	(void) setlocale(LC_ALL, "");
76 #ifndef TEXT_DOMAIN		/* Should be defined by cc -D */
77 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
78 #endif
79 
80 	(void) textdomain(TEXT_DOMAIN);
81 
82 	while ((chr = getopt(argc, argv, "dfh:H:m:t:T:vz:")) != EOF) {
83 		switch (chr) {
84 		case 'd':
85 			delete_mode = B_TRUE;
86 			break;
87 		case 'f':
88 			flush_mode = B_TRUE;
89 			break;
90 		case 'h':
91 			process_rh(optarg);
92 			break;
93 		case 'H':
94 			process_rhl(optarg);
95 			break;
96 		case 'm':
97 			process_mlp(optarg);
98 			break;
99 		case 't':
100 			process_tp(optarg);
101 			break;
102 		case 'T':
103 			process_tpl(optarg);
104 			break;
105 		case 'v':
106 			verbose_mode = B_TRUE;
107 			break;
108 		case 'z':
109 			process_tnzone(optarg);
110 			break;
111 		case '?':
112 			usage();
113 		}
114 	}
115 	return (0);
116 }
117 
118 static void
119 print_error(int linenum, int err, const char *errstr)
120 {
121 	if (linenum > 0)
122 		(void) fprintf(stderr, gettext("line %1$d: %2$s:\n"), linenum,
123 		    tsol_strerror(err, errno));
124 	else
125 		(void) fprintf(stderr, gettext("tnctl: parsing error: %s\n"),
126 		    tsol_strerror(err, errno));
127 	(void) fprintf(stderr, "%.32s\n", errstr);
128 }
129 
130 /*
131  * Produce ascii format of address and prefix length
132  */
133 static void
134 translate_inet_addr(tsol_rhent_t *rhentp, int *alen, char abuf[], int abuflen)
135 {
136 	void *aptr;
137 	tsol_rhent_t rhent;
138 	struct in6_addr ipv6addr;
139 	char tmpbuf[20];
140 
141 	(void) snprintf(tmpbuf, sizeof (tmpbuf), "/%d", rhentp->rh_prefix);
142 
143 	if (rhentp->rh_address.ta_family == AF_INET6) {
144 		aptr = &(rhentp->rh_address.ta_addr_v6);
145 		*alen = sizeof (ipv6addr);
146 		(void) inet_ntop(rhentp->rh_address.ta_family, aptr, abuf,
147 		    abuflen);
148 		if (rhentp->rh_prefix != 128) {
149 			if (strlcat(abuf, tmpbuf, abuflen) >= abuflen)
150 				(void) fprintf(stderr, gettext(
151 				    "tnctl: buffer overflow detected: %s\n"),
152 				    abuf);
153 		}
154 	} else {
155 		aptr = &(rhentp->rh_address.ta_addr_v4);
156 		*alen = sizeof (rhent.rh_address.ta_addr_v4);
157 		(void) inet_ntop(rhentp->rh_address.ta_family, aptr, abuf,
158 		    abuflen);
159 		if (rhentp->rh_prefix != 32) {
160 			if (strlcat(abuf, tmpbuf, abuflen) >= abuflen)
161 				(void) fprintf(stderr, gettext(
162 				    "tnctl: buffer overflow detected: %s\n"),
163 				    abuf);
164 		}
165 	}
166 }
167 
168 /*
169  * Load remote host entries from the designated file.
170  */
171 static void
172 process_rhl(const char *file)
173 {
174 	boolean_t	error = B_FALSE;
175 	boolean_t	success = B_FALSE;
176 	tsol_rhent_t	*rhentp = NULL;
177 	FILE		*fp;
178 	int alen;
179 	/* abuf holds: <numeric-ip-addr>'/'<prefix-length>'\0' */
180 	char abuf[INET6_ADDRSTRLEN+5];
181 
182 	if ((fp = fopen(file, "r")) == NULL) {
183 		(void) fprintf(stderr,
184 		    gettext("tnctl: failed to open %1$s: %2$s\n"),
185 		    file, strerror(errno));
186 		exit(1);
187 	}
188 
189 	tsol_setrhent(1);
190 	while (rhentp = tsol_fgetrhent(fp, &error)) {
191 		/* First time through the loop, flush it all */
192 		if (!success && flush_mode)
193 			(void) tnrh(TNDB_FLUSH, NULL);
194 		success = B_TRUE;
195 
196 		if (verbose_mode)
197 			(void) printf("loading rh entry...\n");
198 
199 		if (tnrh(TNDB_LOAD, rhentp) != 0) {
200 			(void) fclose(fp);
201 			if (errno == EFAULT)
202 				perror("tnrh");
203 			else
204 				translate_inet_addr(rhentp, &alen, abuf,
205 				    sizeof (abuf));
206 				(void) fprintf(stderr,
207 				    gettext("tnctl: load of remote-host entry "
208 				    "%1$s into kernel cache failed: %2$s\n"),
209 				    abuf, strerror(errno));
210 			tsol_endrhent();
211 			exit(1);
212 		}
213 		tsol_freerhent(rhentp);
214 	}
215 	if (!success) {
216 		(void) fprintf(stderr,
217 		    gettext("tnctl: No valid tnrhdb entries found in %s\n"),
218 		    file);
219 	}
220 	(void) fclose(fp);
221 	tsol_endrhent();
222 
223 	if (error)
224 		exit(1);
225 }
226 
227 /*
228  * The argument can be either a host name, an address
229  * in tnrhdb address format, or a complete tnrhdb entry.
230  */
231 static void
232 process_rh(const char *hostname)
233 {
234 	tsol_rhstr_t rhstr;
235 	tsol_rhent_t rhent;
236 	tsol_rhent_t *rhentp;
237 	int err;
238 	int alen;
239 	char *errstr;
240 	/* abuf holds: <numeric-ip-addr>'/'<prefix-length>'\0' */
241 	char abuf[INET6_ADDRSTRLEN+5];
242 	const char *cp;
243 	char *cp1;
244 	char *cp2;
245 	void *aptr;
246 	char buf[NSS_BUFLEN_TSOL_RH];
247 	struct in6_addr ipv6addr;
248 
249 	/* was a template name provided on the command line? */
250 	if ((cp = strrchr(hostname, ':')) != NULL && cp != hostname &&
251 	    cp[-1] != '\\') {
252 		/* use common tnrhdb line conversion function */
253 		(void) str_to_rhstr(hostname, strlen(hostname), &rhstr, buf,
254 		    sizeof (buf));
255 		rhentp = rhstr_to_ent(&rhstr, &err, &errstr);
256 		if (rhentp == NULL) {
257 			print_error(0, err, errstr);
258 			exit(1);
259 		}
260 	} else {
261 		char *hostname_p;
262 		char *prefix_p;
263 		struct hostent *hp;
264 
265 		/* Check for a subnet prefix length */
266 		if ((prefix_p = strchr(hostname, '/')) != NULL) {
267 			cp1 = prefix_p + 1;
268 			errno = 0;
269 			rhent.rh_prefix = strtol(cp1, &cp2, 0);
270 			if (*cp2 != '\0' || errno != 0 || rhent.rh_prefix < 0) {
271 				(void) fprintf(stderr, gettext("tnct: invalid "
272 				    "prefix length: %s\n"), cp);
273 				exit(2);
274 			}
275 		} else {
276 			rhent.rh_prefix = -1;
277 		}
278 
279 		/* Strip any backslashes from numeric address */
280 		hostname_p = malloc(strlen(hostname)+1);
281 		if (hostname_p == NULL) {
282 			perror("tnctl");
283 			exit(2);
284 		}
285 		cp1 = hostname_p;
286 		while (*hostname != '\0' && *hostname != '/') {
287 			*cp1 = *hostname++;
288 			if (*cp1 != '\\')
289 				cp1++;
290 		}
291 		*cp1 = '\0';
292 
293 		/* Convert address or hostname to binary af_inet6 format */
294 		hp = getipnodebyname(hostname_p, AF_INET6,
295 		    AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED, &err);
296 		if (hp == NULL) {
297 			(void) fprintf(stderr, gettext("tnctl: unknown host "
298 			    "or invalid literal address: %s\n"), hostname_p);
299 			if (err == TRY_AGAIN)
300 				(void) fprintf(stderr,
301 				    gettext("\t(try again later)\n"));
302 			exit(2);
303 		}
304 		free(hostname_p);
305 		(void) memcpy(&ipv6addr, hp->h_addr, hp->h_length);
306 
307 		/* if ipv4 address, convert to af_inet format */
308 		if (IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
309 			rhent.rh_address.ta_family = AF_INET;
310 			IN6_V4MAPPED_TO_INADDR(&ipv6addr,
311 			    &rhent.rh_address.ta_addr_v4);
312 			if (rhent.rh_prefix == -1)
313 				rhent.rh_prefix = 32;
314 		} else {
315 			rhent.rh_address.ta_family = AF_INET6;
316 			rhent.rh_address.ta_addr_v6 = ipv6addr;
317 			if (rhent.rh_prefix == -1)
318 				rhent.rh_prefix = 128;
319 		}
320 		rhent.rh_template[0] = '\0';
321 		rhentp = &rhent;
322 	}
323 
324 	/* produce ascii format of address and prefix length */
325 	translate_inet_addr(rhentp, &alen, abuf, sizeof (abuf));
326 
327 	/*
328 	 * look up the entry from ldap or tnrhdb if this is a load
329 	 * request and a template name was not provided.
330 	 */
331 	if (!delete_mode &&
332 	    rhentp->rh_template[0] == '\0' &&
333 	    (rhentp = tsol_getrhbyaddr(abuf, alen,
334 	    rhent.rh_address.ta_family)) == NULL) {
335 		(void) fprintf(stderr,
336 		    gettext("tnctl: database lookup failed for %s\n"),
337 		    abuf);
338 		exit(1);
339 	}
340 
341 	if (verbose_mode)
342 		(void) printf("%s rh entry %s\n", delete_mode ? "deleting" :
343 		    "loading", abuf);
344 
345 	/* update the tnrhdb entry in the kernel */
346 	if (tnrh(delete_mode ? TNDB_DELETE : TNDB_LOAD, rhentp) != 0) {
347 		if (errno == EFAULT)
348 			perror("tnrh");
349 		else if (errno == ENOENT)
350 			(void) fprintf(stderr,
351 			    gettext("tnctl: %1$s of remote-host kernel cache "
352 			    "entry %2$s failed: no such entry\n"),
353 			    delete_mode ? gettext("delete") : gettext("load"),
354 			    abuf);
355 		else
356 			(void) fprintf(stderr,
357 			    gettext("tnctl: %1$s of remote-host kernel cache "
358 			    "entry %2$s failed: %3$s\n"),
359 			    delete_mode ? gettext("delete") : gettext("load"),
360 			    abuf, strerror(errno));
361 		exit(1);
362 	}
363 	if (rhentp != &rhent)
364 		tsol_freerhent(rhentp);
365 }
366 
367 static void
368 handle_mlps(zoneid_t zoneid, tsol_mlp_t *mlp, int flags, int cmd)
369 {
370 	tsol_mlpent_t tsme;
371 
372 	tsme.tsme_zoneid = zoneid;
373 	tsme.tsme_flags = flags;
374 	while (!TSOL_MLP_END(mlp)) {
375 		tsme.tsme_mlp = *mlp;
376 		if (tnmlp(cmd, &tsme) != 0) {
377 			/*
378 			 * Usage of ?: here is ugly, but helps with
379 			 * localization.
380 			 */
381 			(void) fprintf(stderr,
382 			    flags & TSOL_MEF_SHARED ?
383 			    gettext("tnctl: cannot set "
384 			    "shared MLP on %1$d-%2$d/%3$d: %4$s\n") :
385 			    gettext("tnctl: cannot set "
386 			    "zone-specific MLP on %1$d-%2$d/%3$d: %4$s\n"),
387 			    mlp->mlp_port, mlp->mlp_port_upper, mlp->mlp_ipp,
388 			    strerror(errno));
389 			exit(1);
390 		}
391 		mlp++;
392 	}
393 }
394 
395 /*
396  * This reads the configuration for the global zone out of tnzonecfg
397  * and sets it in the kernel.  The non-global zones are configured
398  * by zoneadmd.
399  */
400 static void
401 process_tnzone(const char *file)
402 {
403 	tsol_zcent_t *zc;
404 	tsol_mlpent_t tsme;
405 	int err;
406 	char *errstr;
407 	FILE *fp;
408 	char line[2048], *cp;
409 	int linenum, errors;
410 
411 	if ((fp = fopen(file, "r")) == NULL) {
412 		(void) fprintf(stderr,
413 		    gettext("tnctl: failed to open %s: %s\n"), file,
414 		    strerror(errno));
415 		exit(1);
416 	}
417 
418 	linenum = errors = 0;
419 	zc = NULL;
420 	while (fgets(line, sizeof (line), fp) != NULL) {
421 		if ((cp = strchr(line, '\n')) != NULL)
422 			*cp = '\0';
423 
424 		linenum++;
425 		if ((zc = tsol_sgetzcent(line, &err, &errstr)) == NULL) {
426 			if (err == LTSNET_EMPTY)
427 				continue;
428 			if (errors == 0) {
429 				int errtmp = errno;
430 
431 				(void) fprintf(stderr, gettext("tnctl: errors "
432 				    "parsing %s:\n"), file);
433 				errno = errtmp;
434 			}
435 			print_error(linenum, err, errstr);
436 			errors++;
437 			continue;
438 		}
439 
440 		if (strcasecmp(zc->zc_name, "global") == 0)
441 			break;
442 		tsol_freezcent(zc);
443 	}
444 	(void) fclose(fp);
445 
446 	if (zc == NULL) {
447 		(void) fprintf(stderr,
448 		    gettext("tnctl: cannot find global zone in %s\n"), file);
449 		exit(1);
450 	}
451 
452 	tsme.tsme_zoneid = GLOBAL_ZONEID;
453 	tsme.tsme_flags = 0;
454 	if (flush_mode)
455 		(void) tnmlp(TNDB_FLUSH, &tsme);
456 
457 	handle_mlps(GLOBAL_ZONEID, zc->zc_private_mlp, 0, TNDB_LOAD);
458 	handle_mlps(GLOBAL_ZONEID, zc->zc_shared_mlp, TSOL_MEF_SHARED,
459 	    TNDB_LOAD);
460 
461 	tsol_freezcent(zc);
462 }
463 
464 static void
465 process_tpl(const char *file)
466 {
467 	FILE		*fp;
468 	boolean_t	error = B_FALSE;
469 	boolean_t	success = B_FALSE;
470 	tsol_tpent_t	*tpentp;
471 
472 	if ((fp = fopen(file, "r")) == NULL) {
473 		(void) fprintf(stderr,
474 		    gettext("tnctl: failed to open %s: %s\n"), file,
475 		    strerror(errno));
476 		exit(1);
477 	}
478 
479 	tsol_settpent(1);
480 	while (tpentp = tsol_fgettpent(fp, &error)) {
481 		/* First time through the loop, flush it all */
482 		if (!success && flush_mode)
483 			(void) tnrhtp(TNDB_FLUSH, NULL);
484 
485 		success = B_TRUE;
486 
487 		if (verbose_mode)
488 			(void) printf("tnctl: loading rhtp entry ...\n");
489 
490 		if (tnrhtp(TNDB_LOAD, tpentp) != 0) {
491 			(void) fclose(fp);
492 			if (errno == EFAULT)
493 				perror("tnrhtp");
494 			else
495 				(void) fprintf(stderr, gettext("tnctl: load "
496 				    "of remote-host template %1$s into kernel "
497 				    "cache failed: %2$s\n"), tpentp->name,
498 				    strerror(errno));
499 			tsol_endtpent();
500 			exit(1);
501 		}
502 		tsol_freetpent(tpentp);
503 	}
504 	if (!success) {
505 		(void) fprintf(stderr,
506 		    gettext("tnctl: No valid tnrhtp entries found in %s\n"),
507 		    file);
508 	}
509 	(void) fclose(fp);
510 	tsol_endtpent();
511 
512 	if (error)
513 		exit(1);
514 }
515 
516 static void
517 process_tp(const char *template)
518 {
519 	tsol_tpstr_t tpstr;
520 	tsol_tpent_t tpent;
521 	tsol_tpent_t *tpentp;
522 	int err;
523 	char *errstr;
524 	char buf[NSS_BUFLEN_TSOL_TP];
525 
526 	if (strchr(template, ':') != NULL) {
527 		(void) str_to_tpstr(template, strlen(template), &tpstr, buf,
528 		    sizeof (buf));
529 		tpentp = tpstr_to_ent(&tpstr, &err, &errstr);
530 		if (tpentp == NULL) {
531 			print_error(0, err, errstr);
532 			exit(1);
533 		}
534 	} else if (delete_mode) {
535 		(void) memset(&tpent, 0, sizeof (tpent));
536 		tpentp = &tpent;
537 		(void) strlcpy(tpentp->name, template, sizeof (tpentp->name));
538 	} else if ((tpentp = tsol_gettpbyname(template)) == NULL) {
539 		(void) fprintf(stderr,
540 		    gettext("tnctl: template %s not found\n"), template);
541 		exit(1);
542 	}
543 
544 	if (verbose_mode)
545 		(void) printf("%s rhtp entry ...\n", delete_mode ? "deleting" :
546 		    "loading");
547 
548 	if (tnrhtp(delete_mode ? TNDB_DELETE : TNDB_LOAD, tpentp) != 0) {
549 		if (errno == EFAULT)
550 			perror("tnrhtp");
551 		else if (errno == ENOENT)
552 			(void) fprintf(stderr,
553 			    gettext("tnctl: %1$s of remote-host template "
554 			    "kernel cache entry %2$s failed: no such "
555 			    "entry\n"),
556 			    delete_mode ? gettext("delete") : gettext("load"),
557 			    tpentp->name);
558 		else
559 			(void) fprintf(stderr,
560 			    gettext("tnctl: %1$s of remote-host template "
561 			    "kernel cache entry %2$s failed: %3$s\n"),
562 			    delete_mode ? gettext("delete") : gettext("load"),
563 			    tpentp->name, strerror(errno));
564 		exit(1);
565 	}
566 	if (tpentp != &tpent)
567 		tsol_freetpent(tpentp);
568 }
569 
570 static void
571 process_mlp(const char *str)
572 {
573 	const char *cp;
574 	char zonename[ZONENAME_MAX];
575 	zoneid_t zoneid;
576 	tsol_zcent_t *zc;
577 	int err;
578 	char *errstr;
579 	char *sbuf;
580 
581 	if ((cp = strchr(str, ':')) == NULL) {
582 		if (!delete_mode) {
583 			(void) fprintf(stderr,
584 			    gettext("tnctl: need MLP list to insert\n"));
585 			exit(2);
586 		}
587 		(void) strlcpy(zonename, str, sizeof (zonename));
588 	} else if (cp - str >= ZONENAME_MAX) {
589 		(void) fprintf(stderr, gettext("tnctl: illegal zone name\n"));
590 		exit(2);
591 	} else {
592 		(void) memcpy(zonename, str, cp - str);
593 		zonename[cp - str] = '\0';
594 		str = cp + 1;
595 	}
596 
597 	if ((zoneid = getzoneidbyname(zonename)) == -1) {
598 		(void) fprintf(stderr, gettext("tninfo: zone '%s' unknown\n"),
599 		    zonename);
600 		exit(1);
601 	}
602 
603 	sbuf = malloc(strlen(zonename) + sizeof (":ADMIN_LOW:0:") +
604 	    strlen(str));
605 	if (sbuf == NULL) {
606 		perror("malloc");
607 		exit(1);
608 	}
609 	/* LINTED: sprintf is known not to be unbounded here */
610 	(void) sprintf(sbuf, "%s:ADMIN_LOW:0:%s", zonename, str);
611 	if ((zc = tsol_sgetzcent(sbuf, &err, &errstr)) == NULL) {
612 		(void) fprintf(stderr,
613 		    gettext("tnctl: unable to parse MLPs\n"));
614 		exit(1);
615 	}
616 	handle_mlps(zoneid, zc->zc_private_mlp, 0,
617 	    delete_mode ? TNDB_DELETE : TNDB_LOAD);
618 	handle_mlps(zoneid, zc->zc_shared_mlp, TSOL_MEF_SHARED,
619 	    delete_mode ? TNDB_DELETE : TNDB_LOAD);
620 	tsol_freezcent(zc);
621 }
622 
623 static void
624 usage(void)
625 {
626 	(void) fprintf(stderr, gettext("usage: tnctl [-dfv] "
627 	    "[-h host[/prefix][:tmpl]] [-m zone:priv:share]\n\t"
628 	    "[-t tmpl[:key=val[;key=val]]] [-[HTz] file]\n"));
629 
630 	exit(1);
631 }
632