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