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