xref: /illumos-gate/usr/src/cmd/tsol/tnctl/tnctl.c (revision 1f041b17)
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  * 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	success = B_FALSE;
175 	tsol_rhent_t	*rhentp = NULL;
176 	FILE		*fp;
177 	int alen;
178 	/* abuf holds: <numeric-ip-addr>'/'<prefix-length>'\0' */
179 	char abuf[INET6_ADDRSTRLEN+5];
180 
181 	if ((fp = fopen(file, "r")) == NULL) {
182 		(void) fprintf(stderr,
183 		    gettext("tnctl: failed to open %1$s: %2$s\n"),
184 		    file, strerror(errno));
185 		exit(1);
186 	}
187 
188 	tsol_setrhent(1);
189 	while (rhentp = tsol_fgetrhent(fp)) {
190 		/* First time through the loop, flush it all */
191 		if (!success && flush_mode)
192 			(void) tnrh(TNDB_FLUSH, NULL);
193 		success = B_TRUE;
194 
195 		if (verbose_mode)
196 			(void) printf("loading rh entry...\n");
197 
198 		if (tnrh(TNDB_LOAD, rhentp) != 0) {
199 			(void) fclose(fp);
200 			if (errno == EFAULT)
201 				perror("tnrh");
202 			else
203 				translate_inet_addr(rhentp, &alen, abuf,
204 				    sizeof (abuf));
205 				(void) fprintf(stderr,
206 				    gettext("tnctl: load of remote-host entry "
207 				    "%1$s into kernel cache failed: %2$s\n"),
208 				    abuf, strerror(errno));
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 
223 /*
224  * The argument can be either a host name, an address
225  * in tnrhdb address format, or a complete tnrhdb entry.
226  */
227 static void
228 process_rh(const char *hostname)
229 {
230 	tsol_rhstr_t rhstr;
231 	tsol_rhent_t rhent;
232 	tsol_rhent_t *rhentp;
233 	int err;
234 	int alen;
235 	char *errstr;
236 	/* abuf holds: <numeric-ip-addr>'/'<prefix-length>'\0' */
237 	char abuf[INET6_ADDRSTRLEN+5];
238 	const char *cp;
239 	char *cp1;
240 	char *cp2;
241 	void *aptr;
242 	char buf[NSS_BUFLEN_TSOL_RH];
243 	struct in6_addr ipv6addr;
244 
245 	/* was a template name provided on the command line? */
246 	if ((cp = strrchr(hostname, ':')) != NULL && cp != hostname &&
247 	    cp[-1] != '\\') {
248 		/* use common tnrhdb line conversion function */
249 		(void) str_to_rhstr(hostname, strlen(hostname), &rhstr, buf,
250 		    sizeof (buf));
251 		rhentp = rhstr_to_ent(&rhstr, &err, &errstr);
252 		if (rhentp == NULL) {
253 			print_error(0, err, errstr);
254 			exit(1);
255 		}
256 	} else {
257 		char *hostname_p;
258 		char *prefix_p;
259 		struct hostent *hp;
260 
261 		/* Check for a subnet prefix length */
262 		if ((prefix_p = strchr(hostname, '/')) != NULL) {
263 			cp1 = prefix_p + 1;
264 			errno = 0;
265 			rhent.rh_prefix = strtol(cp1, &cp2, 0);
266 			if (*cp2 != '\0' || errno != 0 || rhent.rh_prefix < 0) {
267 				(void) fprintf(stderr, gettext("tnct: invalid "
268 				    "prefix length: %s\n"), cp);
269 				exit(2);
270 			}
271 		} else {
272 			rhent.rh_prefix = -1;
273 		}
274 
275 		/* Strip any backslashes from numeric address */
276 		hostname_p = malloc(strlen(hostname)+1);
277 		if (hostname_p == NULL) {
278 			perror("tnctl");
279 			exit(2);
280 		}
281 		cp1 = hostname_p;
282 		while (*hostname != '\0' && *hostname != '/') {
283 			*cp1 = *hostname++;
284 			if (*cp1 != '\\')
285 				cp1++;
286 		}
287 		*cp1 = '\0';
288 
289 		/* Convert address or hostname to binary af_inet6 format */
290 		hp = getipnodebyname(hostname_p, AF_INET6,
291 		    AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED, &err);
292 		if (hp == NULL) {
293 			(void) fprintf(stderr, gettext("tnctl: unknown host "
294 			    "or invalid literal address: %s\n"), hostname_p);
295 			if (err == TRY_AGAIN)
296 				(void) fprintf(stderr,
297 				    gettext("\t(try again later)\n"));
298 			exit(2);
299 		}
300 		free(hostname_p);
301 		(void) memcpy(&ipv6addr, hp->h_addr, hp->h_length);
302 
303 		/* if ipv4 address, convert to af_inet format */
304 		if (IN6_IS_ADDR_V4MAPPED(&ipv6addr)) {
305 			rhent.rh_address.ta_family = AF_INET;
306 			IN6_V4MAPPED_TO_INADDR(&ipv6addr,
307 			    &rhent.rh_address.ta_addr_v4);
308 			if (rhent.rh_prefix == -1)
309 				rhent.rh_prefix = 32;
310 		} else {
311 			rhent.rh_address.ta_family = AF_INET6;
312 			rhent.rh_address.ta_addr_v6 = ipv6addr;
313 			if (rhent.rh_prefix == -1)
314 				rhent.rh_prefix = 128;
315 		}
316 		rhent.rh_template[0] = '\0';
317 		rhentp = &rhent;
318 	}
319 
320 	/* produce ascii format of address and prefix length */
321 	translate_inet_addr(rhentp, &alen, abuf, sizeof (abuf));
322 
323 	/*
324 	 * look up the entry from ldap or tnrhdb if this is a load
325 	 * request and a template name was not provided.
326 	 */
327 	if (!delete_mode &&
328 	    rhentp->rh_template[0] == '\0' &&
329 	    (rhentp = tsol_getrhbyaddr(abuf, alen,
330 	    rhent.rh_address.ta_family)) == NULL) {
331 		(void) fprintf(stderr,
332 		    gettext("tnctl: database lookup failed for %s\n"),
333 		    abuf);
334 		exit(1);
335 	}
336 
337 	if (verbose_mode)
338 		(void) printf("%s rh entry %s\n", delete_mode ? "deleting" :
339 		    "loading", abuf);
340 
341 	/* update the tnrhdb entry in the kernel */
342 	if (tnrh(delete_mode ? TNDB_DELETE : TNDB_LOAD, rhentp) != 0) {
343 		if (errno == EFAULT)
344 			perror("tnrh");
345 		else if (errno == ENOENT)
346 			(void) fprintf(stderr,
347 			    gettext("tnctl: %1$s of remote-host kernel cache "
348 			    "entry %2$s failed: no such entry\n"),
349 			    delete_mode ? gettext("delete") : gettext("load"),
350 			    abuf);
351 		else
352 			(void) fprintf(stderr,
353 			    gettext("tnctl: %1$s of remote-host kernel cache "
354 			    "entry %2$s failed: %3$s\n"),
355 			    delete_mode ? gettext("delete") : gettext("load"),
356 			    abuf, strerror(errno));
357 		exit(1);
358 	}
359 	if (rhentp != &rhent)
360 		tsol_freerhent(rhentp);
361 }
362 
363 static void
364 handle_mlps(zoneid_t zoneid, tsol_mlp_t *mlp, int flags, int cmd)
365 {
366 	tsol_mlpent_t tsme;
367 
368 	tsme.tsme_zoneid = zoneid;
369 	tsme.tsme_flags = flags;
370 	while (!TSOL_MLP_END(mlp)) {
371 		tsme.tsme_mlp = *mlp;
372 		if (tnmlp(cmd, &tsme) != 0) {
373 			/*
374 			 * Usage of ?: here is ugly, but helps with
375 			 * localization.
376 			 */
377 			(void) fprintf(stderr,
378 			    flags & TSOL_MEF_SHARED ?
379 			    gettext("tnctl: cannot set "
380 			    "shared MLP on %1$d-%2$d/%3$d: %4$s\n") :
381 			    gettext("tnctl: cannot set "
382 			    "zone-specific MLP on %1$d-%2$d/%3$d: %4$s\n"),
383 			    mlp->mlp_port, mlp->mlp_port_upper, mlp->mlp_ipp,
384 			    strerror(errno));
385 			exit(1);
386 		}
387 		mlp++;
388 	}
389 }
390 
391 /*
392  * This reads the configuration for the global zone out of tnzonecfg
393  * and sets it in the kernel.  The non-global zones are configured
394  * by zoneadmd.
395  */
396 static void
397 process_tnzone(const char *file)
398 {
399 	tsol_zcent_t *zc;
400 	tsol_mlpent_t tsme;
401 	int err;
402 	char *errstr;
403 	FILE *fp;
404 	char line[2048], *cp;
405 	int linenum, errors;
406 
407 	if ((fp = fopen(file, "r")) == NULL) {
408 		(void) fprintf(stderr,
409 		    gettext("tnctl: failed to open %s: %s\n"), file,
410 		    strerror(errno));
411 		exit(1);
412 	}
413 
414 	linenum = errors = 0;
415 	zc = NULL;
416 	while (fgets(line, sizeof (line), fp) != NULL) {
417 		if ((cp = strchr(line, '\n')) != NULL)
418 			*cp = '\0';
419 
420 		linenum++;
421 		if ((zc = tsol_sgetzcent(line, &err, &errstr)) == NULL) {
422 			if (err == LTSNET_EMPTY)
423 				continue;
424 			if (errors == 0) {
425 				int errtmp = errno;
426 
427 				(void) fprintf(stderr, gettext("tnctl: errors "
428 				    "parsing %s:\n"), file);
429 				errno = errtmp;
430 			}
431 			print_error(linenum, err, errstr);
432 			errors++;
433 			continue;
434 		}
435 
436 		if (strcasecmp(zc->zc_name, "global") == 0)
437 			break;
438 		tsol_freezcent(zc);
439 	}
440 	(void) fclose(fp);
441 
442 	if (zc == NULL) {
443 		(void) fprintf(stderr,
444 		    gettext("tnctl: cannot find global zone in %s\n"), file);
445 		exit(1);
446 	}
447 
448 	tsme.tsme_zoneid = GLOBAL_ZONEID;
449 	tsme.tsme_flags = 0;
450 	if (flush_mode)
451 		(void) tnmlp(TNDB_FLUSH, &tsme);
452 
453 	handle_mlps(GLOBAL_ZONEID, zc->zc_private_mlp, 0, TNDB_LOAD);
454 	handle_mlps(GLOBAL_ZONEID, zc->zc_shared_mlp, TSOL_MEF_SHARED,
455 	    TNDB_LOAD);
456 
457 	tsol_freezcent(zc);
458 }
459 
460 static void
461 process_tpl(const char *file)
462 {
463 	FILE		*fp;
464 	boolean_t	success = B_FALSE;
465 	tsol_tpent_t	*tpentp;
466 
467 	if ((fp = fopen(file, "r")) == NULL) {
468 		(void) fprintf(stderr,
469 		    gettext("tnctl: failed to open %s: %s\n"), file,
470 		    strerror(errno));
471 		exit(1);
472 	}
473 
474 	tsol_settpent(1);
475 	while (tpentp = tsol_fgettpent(fp)) {
476 		/* First time through the loop, flush it all */
477 		if (!success && flush_mode)
478 			(void) tnrhtp(TNDB_FLUSH, NULL);
479 
480 		success = B_TRUE;
481 
482 		if (verbose_mode)
483 			(void) printf("tnctl: loading rhtp entry ...\n");
484 
485 		if (tnrhtp(TNDB_LOAD, tpentp) != 0) {
486 			(void) fclose(fp);
487 			if (errno == EFAULT)
488 				perror("tnrhtp");
489 			else
490 				(void) fprintf(stderr, gettext("tnctl: load "
491 				    "of remote-host template %1$s into kernel "
492 				    "cache failed: %2$s\n"), tpentp->name,
493 				    strerror(errno));
494 			tsol_endtpent();
495 			exit(1);
496 		}
497 		tsol_freetpent(tpentp);
498 	}
499 	if (!success) {
500 		(void) fprintf(stderr,
501 		    gettext("tnctl: No valid tnrhtp entries found in %s\n"),
502 		    file);
503 	}
504 	(void) fclose(fp);
505 	tsol_endtpent();
506 }
507 
508 static void
509 process_tp(const char *template)
510 {
511 	tsol_tpstr_t tpstr;
512 	tsol_tpent_t tpent;
513 	tsol_tpent_t *tpentp;
514 	int err;
515 	char *errstr;
516 	char buf[NSS_BUFLEN_TSOL_TP];
517 
518 	if (strchr(template, ':') != NULL) {
519 		(void) str_to_tpstr(template, strlen(template), &tpstr, buf,
520 		    sizeof (buf));
521 		tpentp = tpstr_to_ent(&tpstr, &err, &errstr);
522 		if (tpentp == NULL) {
523 			print_error(0, err, errstr);
524 			exit(1);
525 		}
526 	} else if (delete_mode) {
527 		(void) memset(&tpent, 0, sizeof (tpent));
528 		tpentp = &tpent;
529 		(void) strlcpy(tpentp->name, template, sizeof (tpentp->name));
530 	} else if ((tpentp = tsol_gettpbyname(template)) == NULL) {
531 		(void) fprintf(stderr,
532 		    gettext("tnctl: template %s not found\n"), template);
533 		exit(1);
534 	}
535 
536 	if (verbose_mode)
537 		(void) printf("%s rhtp entry ...\n", delete_mode ? "deleting" :
538 		    "loading");
539 
540 	if (tnrhtp(delete_mode ? TNDB_DELETE : TNDB_LOAD, tpentp) != 0) {
541 		if (errno == EFAULT)
542 			perror("tnrhtp");
543 		else if (errno == ENOENT)
544 			(void) fprintf(stderr,
545 			    gettext("tnctl: %1$s of remote-host template "
546 			    "kernel cache entry %2$s failed: no such "
547 			    "entry\n"),
548 			    delete_mode ? gettext("delete") : gettext("load"),
549 			    tpentp->name);
550 		else
551 			(void) fprintf(stderr,
552 			    gettext("tnctl: %1$s of remote-host template "
553 			    "kernel cache entry %2$s failed: %3$s\n"),
554 			    delete_mode ? gettext("delete") : gettext("load"),
555 			    tpentp->name, strerror(errno));
556 		exit(1);
557 	}
558 	if (tpentp != &tpent)
559 		tsol_freetpent(tpentp);
560 }
561 
562 static void
563 process_mlp(const char *str)
564 {
565 	const char *cp;
566 	char zonename[ZONENAME_MAX];
567 	zoneid_t zoneid;
568 	tsol_zcent_t *zc;
569 	int err;
570 	char *errstr;
571 	char *sbuf;
572 
573 	if ((cp = strchr(str, ':')) == NULL) {
574 		if (!delete_mode) {
575 			(void) fprintf(stderr,
576 			    gettext("tnctl: need MLP list to insert\n"));
577 			exit(2);
578 		}
579 		(void) strlcpy(zonename, str, sizeof (zonename));
580 	} else if (cp - str >= ZONENAME_MAX) {
581 		(void) fprintf(stderr, gettext("tnctl: illegal zone name\n"));
582 		exit(2);
583 	} else {
584 		(void) memcpy(zonename, str, cp - str);
585 		zonename[cp - str] = '\0';
586 		str = cp + 1;
587 	}
588 
589 	if ((zoneid = getzoneidbyname(zonename)) == -1) {
590 		(void) fprintf(stderr, gettext("tninfo: zone '%s' unknown\n"),
591 		    zonename);
592 		exit(1);
593 	}
594 
595 	sbuf = malloc(strlen(zonename) + sizeof (":ADMIN_LOW:0:") +
596 	    strlen(str));
597 	if (sbuf == NULL) {
598 		perror("malloc");
599 		exit(1);
600 	}
601 	/* LINTED: sprintf is known not to be unbounded here */
602 	(void) sprintf(sbuf, "%s:ADMIN_LOW:0:%s", zonename, str);
603 	if ((zc = tsol_sgetzcent(sbuf, &err, &errstr)) == NULL) {
604 		(void) fprintf(stderr,
605 		    gettext("tnctl: unable to parse MLPs\n"));
606 		exit(1);
607 	}
608 	handle_mlps(zoneid, zc->zc_private_mlp, 0,
609 	    delete_mode ? TNDB_DELETE : TNDB_LOAD);
610 	handle_mlps(zoneid, zc->zc_shared_mlp, TSOL_MEF_SHARED,
611 	    delete_mode ? TNDB_DELETE : TNDB_LOAD);
612 	tsol_freezcent(zc);
613 }
614 
615 static void
616 usage(void)
617 {
618 	(void) fprintf(stderr, gettext("usage: tnctl [-dfv] "
619 	    "[-h host[/prefix][:tmpl]] [-m zone:priv:share]\n\t"
620 	    "[-t tmpl[:key=val[;key=val]]] [-[HTz] file]\n"));
621 
622 	exit(1);
623 }
624