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