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