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