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