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 }
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
process_rh(const char * hostname)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
handle_mlps(zoneid_t zoneid,tsol_mlp_t * mlp,int flags,int cmd)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
process_tnzone(const char * file)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
process_tpl(const char * file)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
process_tp(const char * template)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
process_mlp(const char * str)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
usage(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