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
main(int argc,char ** argv)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
print_error(int linenum,int err,const char * errstr)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
translate_inet_addr(tsol_rhent_t * rhentp,int * alen,char abuf[],int abuflen)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
process_rhl(const char * file)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
process_rh(const char * hostname)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
handle_mlps(zoneid_t zoneid,tsol_mlp_t * mlp,int flags,int cmd)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
process_tnzone(const char * file)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
process_tpl(const char * file)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
process_tp(const char * template)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
process_mlp(const char * str)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
usage(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