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