/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * tnctl.c - * Trusted Network control utility */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void process_rh(const char *); static void process_rhl(const char *); static void process_mlp(const char *); static void process_tp(const char *); static void process_tpl(const char *); static void process_tnzone(const char *); static void usage(void); static void translate_inet_addr(tsol_rhent_t *, int *, char [], int); static boolean_t verbose_mode; static boolean_t delete_mode; static boolean_t flush_mode; int main(int argc, char **argv) { extern char *optarg; int chr; /* Don't do anything if labeling is not active. */ if (!is_system_labeled()) return (0); /* set the locale for only the messages system (all else is clean) */ (void) setlocale(LC_ALL, ""); #ifndef TEXT_DOMAIN /* Should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ #endif (void) textdomain(TEXT_DOMAIN); while ((chr = getopt(argc, argv, "dfh:H:m:t:T:vz:")) != EOF) { switch (chr) { case 'd': delete_mode = B_TRUE; break; case 'f': flush_mode = B_TRUE; break; case 'h': process_rh(optarg); break; case 'H': process_rhl(optarg); break; case 'm': process_mlp(optarg); break; case 't': process_tp(optarg); break; case 'T': process_tpl(optarg); break; case 'v': verbose_mode = B_TRUE; break; case 'z': process_tnzone(optarg); break; case '?': usage(); } } return (0); } static void print_error(int linenum, int err, const char *errstr) { if (linenum > 0) (void) fprintf(stderr, gettext("line %1$d: %2$s:\n"), linenum, tsol_strerror(err, errno)); else (void) fprintf(stderr, gettext("tnctl: parsing error: %s\n"), tsol_strerror(err, errno)); (void) fprintf(stderr, "%.32s\n", errstr); } /* * Produce ascii format of address and prefix length */ static void translate_inet_addr(tsol_rhent_t *rhentp, int *alen, char abuf[], int abuflen) { void *aptr; tsol_rhent_t rhent; struct in6_addr ipv6addr; char tmpbuf[20]; (void) snprintf(tmpbuf, sizeof (tmpbuf), "/%d", rhentp->rh_prefix); if (rhentp->rh_address.ta_family == AF_INET6) { aptr = &(rhentp->rh_address.ta_addr_v6); (void) inet_ntop(rhentp->rh_address.ta_family, aptr, abuf, abuflen); if (rhentp->rh_prefix != 128) { if (strlcat(abuf, tmpbuf, abuflen) >= abuflen) (void) fprintf(stderr, gettext( "tnctl: buffer overflow detected: %s\n"), abuf); } *alen = strlen(abuf); } else { aptr = &(rhentp->rh_address.ta_addr_v4); (void) inet_ntop(rhentp->rh_address.ta_family, aptr, abuf, abuflen); if (rhentp->rh_prefix != 32) { if (strlcat(abuf, tmpbuf, abuflen) >= abuflen) (void) fprintf(stderr, gettext( "tnctl: buffer overflow detected: %s\n"), abuf); } *alen = strlen(abuf); } } /* * Load remote host entries from the designated file. */ static void process_rhl(const char *file) { boolean_t error = B_FALSE; boolean_t success = B_FALSE; tsol_rhent_t *rhentp = NULL; FILE *fp; int alen; /* abuf holds: '/''\0' */ char abuf[INET6_ADDRSTRLEN+5]; if ((fp = fopen(file, "r")) == NULL) { (void) fprintf(stderr, gettext("tnctl: failed to open %1$s: %2$s\n"), file, strerror(errno)); exit(1); } tsol_setrhent(1); while (rhentp = tsol_fgetrhent(fp, &error)) { /* First time through the loop, flush it all */ if (!success && flush_mode) (void) tnrh(TNDB_FLUSH, NULL); success = B_TRUE; if (verbose_mode) (void) printf("loading rh entry...\n"); if (tnrh(TNDB_LOAD, rhentp) != 0) { (void) fclose(fp); if (errno == EFAULT) { perror("tnrh"); } else { translate_inet_addr(rhentp, &alen, abuf, sizeof (abuf)); (void) fprintf(stderr, gettext("tnctl: load of remote-host entry " "%1$s into kernel cache failed: %2$s\n"), abuf, strerror(errno)); } tsol_endrhent(); exit(1); } tsol_freerhent(rhentp); } if (!success) { (void) fprintf(stderr, gettext("tnctl: No valid tnrhdb entries found in %s\n"), file); } (void) fclose(fp); tsol_endrhent(); if (error) exit(1); } /* * The argument can be either a host name, an address * in tnrhdb address format, or a complete tnrhdb entry. */ static void process_rh(const char *hostname) { tsol_rhstr_t rhstr; tsol_rhent_t rhent; tsol_rhent_t *rhentp; int err; int alen; char *errstr; /* abuf holds: '/''\0' */ char abuf[INET6_ADDRSTRLEN+5]; const char *cp; char *cp1; char *cp2; void *aptr; char buf[NSS_BUFLEN_TSOL_RH]; struct in6_addr ipv6addr; /* was a template name provided on the command line? */ if ((cp = strrchr(hostname, ':')) != NULL && cp != hostname && cp[-1] != '\\') { /* use common tnrhdb line conversion function */ (void) str_to_rhstr(hostname, strlen(hostname), &rhstr, buf, sizeof (buf)); rhentp = rhstr_to_ent(&rhstr, &err, &errstr); if (rhentp == NULL) { print_error(0, err, errstr); exit(1); } } else { char *hostname_p; char *prefix_p; struct hostent *hp; /* Check for a subnet prefix length */ if ((prefix_p = strchr(hostname, '/')) != NULL) { cp1 = prefix_p + 1; errno = 0; rhent.rh_prefix = strtol(cp1, &cp2, 0); if (*cp2 != '\0' || errno != 0 || rhent.rh_prefix < 0) { (void) fprintf(stderr, gettext("tnct: invalid " "prefix length: %s\n"), cp); exit(2); } } else { rhent.rh_prefix = -1; } /* Strip any backslashes from numeric address */ hostname_p = malloc(strlen(hostname)+1); if (hostname_p == NULL) { perror("tnctl"); exit(2); } cp1 = hostname_p; while (*hostname != '\0' && *hostname != '/') { *cp1 = *hostname++; if (*cp1 != '\\') cp1++; } *cp1 = '\0'; /* Convert address or hostname to binary af_inet6 format */ hp = getipnodebyname(hostname_p, AF_INET6, AI_ALL | AI_ADDRCONFIG | AI_V4MAPPED, &err); if (hp == NULL) { (void) fprintf(stderr, gettext("tnctl: unknown host " "or invalid literal address: %s\n"), hostname_p); if (err == TRY_AGAIN) (void) fprintf(stderr, gettext("\t(try again later)\n")); exit(2); } free(hostname_p); (void) memcpy(&ipv6addr, hp->h_addr, hp->h_length); /* if ipv4 address, convert to af_inet format */ if (IN6_IS_ADDR_V4MAPPED(&ipv6addr)) { rhent.rh_address.ta_family = AF_INET; IN6_V4MAPPED_TO_INADDR(&ipv6addr, &rhent.rh_address.ta_addr_v4); if (rhent.rh_prefix == -1) rhent.rh_prefix = 32; } else { rhent.rh_address.ta_family = AF_INET6; rhent.rh_address.ta_addr_v6 = ipv6addr; if (rhent.rh_prefix == -1) rhent.rh_prefix = 128; } rhent.rh_template[0] = '\0'; rhentp = &rhent; } /* produce ascii format of address and prefix length */ translate_inet_addr(rhentp, &alen, abuf, sizeof (abuf)); /* * look up the entry from ldap or tnrhdb if this is a load * request and a template name was not provided. */ if (!delete_mode && rhentp->rh_template[0] == '\0' && (rhentp = tsol_getrhbyaddr(abuf, alen+1, rhent.rh_address.ta_family)) == NULL) { (void) fprintf(stderr, gettext("tnctl: database lookup failed for %s\n"), abuf); exit(1); } if (verbose_mode) (void) printf("%s rh entry %s\n", delete_mode ? "deleting" : "loading", abuf); /* update the tnrhdb entry in the kernel */ if (tnrh(delete_mode ? TNDB_DELETE : TNDB_LOAD, rhentp) != 0) { if (errno == EFAULT) perror("tnrh"); else if (errno == ENOENT) (void) fprintf(stderr, gettext("tnctl: %1$s of remote-host kernel cache " "entry %2$s failed: no such entry\n"), delete_mode ? gettext("delete") : gettext("load"), abuf); else (void) fprintf(stderr, gettext("tnctl: %1$s of remote-host kernel cache " "entry %2$s failed: %3$s\n"), delete_mode ? gettext("delete") : gettext("load"), abuf, strerror(errno)); exit(1); } if (rhentp != &rhent) tsol_freerhent(rhentp); } static void handle_mlps(zoneid_t zoneid, tsol_mlp_t *mlp, int flags, int cmd) { tsol_mlpent_t tsme; tsme.tsme_zoneid = zoneid; tsme.tsme_flags = flags; while (!TSOL_MLP_END(mlp)) { tsme.tsme_mlp = *mlp; if (tnmlp(cmd, &tsme) != 0) { /* * Usage of ?: here is ugly, but helps with * localization. */ (void) fprintf(stderr, flags & TSOL_MEF_SHARED ? gettext("tnctl: cannot set " "shared MLP on %1$d-%2$d/%3$d: %4$s\n") : gettext("tnctl: cannot set " "zone-specific MLP on %1$d-%2$d/%3$d: %4$s\n"), mlp->mlp_port, mlp->mlp_port_upper, mlp->mlp_ipp, strerror(errno)); exit(1); } mlp++; } } /* * This reads the configuration for the global zone out of tnzonecfg * and sets it in the kernel. The non-global zones are configured * by zoneadmd. */ static void process_tnzone(const char *file) { tsol_zcent_t *zc; tsol_mlpent_t tsme; int err; char *errstr; FILE *fp; char line[2048], *cp; int linenum, errors; if ((fp = fopen(file, "r")) == NULL) { (void) fprintf(stderr, gettext("tnctl: failed to open %s: %s\n"), file, strerror(errno)); exit(1); } linenum = errors = 0; zc = NULL; while (fgets(line, sizeof (line), fp) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; linenum++; if ((zc = tsol_sgetzcent(line, &err, &errstr)) == NULL) { if (err == LTSNET_EMPTY) continue; if (errors == 0) { int errtmp = errno; (void) fprintf(stderr, gettext("tnctl: errors " "parsing %s:\n"), file); errno = errtmp; } print_error(linenum, err, errstr); errors++; continue; } if (strcasecmp(zc->zc_name, "global") == 0) break; tsol_freezcent(zc); } (void) fclose(fp); if (zc == NULL) { (void) fprintf(stderr, gettext("tnctl: cannot find global zone in %s\n"), file); exit(1); } tsme.tsme_zoneid = GLOBAL_ZONEID; tsme.tsme_flags = 0; if (flush_mode) (void) tnmlp(TNDB_FLUSH, &tsme); handle_mlps(GLOBAL_ZONEID, zc->zc_private_mlp, 0, TNDB_LOAD); handle_mlps(GLOBAL_ZONEID, zc->zc_shared_mlp, TSOL_MEF_SHARED, TNDB_LOAD); tsol_freezcent(zc); } static void process_tpl(const char *file) { FILE *fp; boolean_t error = B_FALSE; boolean_t success = B_FALSE; tsol_tpent_t *tpentp; if ((fp = fopen(file, "r")) == NULL) { (void) fprintf(stderr, gettext("tnctl: failed to open %s: %s\n"), file, strerror(errno)); exit(1); } tsol_settpent(1); while (tpentp = tsol_fgettpent(fp, &error)) { /* First time through the loop, flush it all */ if (!success && flush_mode) (void) tnrhtp(TNDB_FLUSH, NULL); success = B_TRUE; if (verbose_mode) (void) printf("tnctl: loading rhtp entry ...\n"); if (tnrhtp(TNDB_LOAD, tpentp) != 0) { (void) fclose(fp); if (errno == EFAULT) perror("tnrhtp"); else (void) fprintf(stderr, gettext("tnctl: load " "of remote-host template %1$s into kernel " "cache failed: %2$s\n"), tpentp->name, strerror(errno)); tsol_endtpent(); exit(1); } tsol_freetpent(tpentp); } if (!success) { (void) fprintf(stderr, gettext("tnctl: No valid tnrhtp entries found in %s\n"), file); } (void) fclose(fp); tsol_endtpent(); if (error) exit(1); } static void process_tp(const char *template) { tsol_tpstr_t tpstr; tsol_tpent_t tpent; tsol_tpent_t *tpentp; int err; char *errstr; char buf[NSS_BUFLEN_TSOL_TP]; if (strchr(template, ':') != NULL) { (void) str_to_tpstr(template, strlen(template), &tpstr, buf, sizeof (buf)); tpentp = tpstr_to_ent(&tpstr, &err, &errstr); if (tpentp == NULL) { print_error(0, err, errstr); exit(1); } } else if (delete_mode) { (void) memset(&tpent, 0, sizeof (tpent)); tpentp = &tpent; (void) strlcpy(tpentp->name, template, sizeof (tpentp->name)); } else if ((tpentp = tsol_gettpbyname(template)) == NULL) { (void) fprintf(stderr, gettext("tnctl: template %s not found\n"), template); exit(1); } if (verbose_mode) (void) printf("%s rhtp entry ...\n", delete_mode ? "deleting" : "loading"); if (tnrhtp(delete_mode ? TNDB_DELETE : TNDB_LOAD, tpentp) != 0) { if (errno == EFAULT) perror("tnrhtp"); else if (errno == ENOENT) (void) fprintf(stderr, gettext("tnctl: %1$s of remote-host template " "kernel cache entry %2$s failed: no such " "entry\n"), delete_mode ? gettext("delete") : gettext("load"), tpentp->name); else (void) fprintf(stderr, gettext("tnctl: %1$s of remote-host template " "kernel cache entry %2$s failed: %3$s\n"), delete_mode ? gettext("delete") : gettext("load"), tpentp->name, strerror(errno)); exit(1); } if (tpentp != &tpent) tsol_freetpent(tpentp); } static void process_mlp(const char *str) { const char *cp; char zonename[ZONENAME_MAX]; zoneid_t zoneid; tsol_zcent_t *zc; int err; char *errstr; char *sbuf; if ((cp = strchr(str, ':')) == NULL) { if (!delete_mode) { (void) fprintf(stderr, gettext("tnctl: need MLP list to insert\n")); exit(2); } (void) strlcpy(zonename, str, sizeof (zonename)); } else if (cp - str >= ZONENAME_MAX) { (void) fprintf(stderr, gettext("tnctl: illegal zone name\n")); exit(2); } else { (void) memcpy(zonename, str, cp - str); zonename[cp - str] = '\0'; str = cp + 1; } if ((zoneid = getzoneidbyname(zonename)) == -1) { (void) fprintf(stderr, gettext("tninfo: zone '%s' unknown\n"), zonename); exit(1); } sbuf = malloc(strlen(zonename) + sizeof (":ADMIN_LOW:0:") + strlen(str)); if (sbuf == NULL) { perror("malloc"); exit(1); } /* LINTED: sprintf is known not to be unbounded here */ (void) sprintf(sbuf, "%s:ADMIN_LOW:0:%s", zonename, str); if ((zc = tsol_sgetzcent(sbuf, &err, &errstr)) == NULL) { (void) fprintf(stderr, gettext("tnctl: unable to parse MLPs\n")); exit(1); } handle_mlps(zoneid, zc->zc_private_mlp, 0, delete_mode ? TNDB_DELETE : TNDB_LOAD); handle_mlps(zoneid, zc->zc_shared_mlp, TSOL_MEF_SHARED, delete_mode ? TNDB_DELETE : TNDB_LOAD); tsol_freezcent(zc); } static void usage(void) { (void) fprintf(stderr, gettext("usage: tnctl [-dfv] " "[-h host[/prefix][:tmpl]] [-m zone:priv:share]\n\t" "[-t tmpl[:key=val[;key=val]]] [-[HTz] file]\n")); exit(1); }