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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 /*
29 * This code implements the Starcat Virtual Console host daemon (see cvcd(1M)).
30 * It accepts one TCP connection at a time on a well-known port. Once a
31 * connection is accepted, the console redirection driver (cvcdredir(7D)) is
32 * opened, and console I/O is routed back and forth between the two file
33 * descriptors (network and redirection driver). Per-socket IPsec is used to
34 * secure the connection if it is enabled with the "-a", "-u" and or "-e"
35 * command line options.
36 */
37
38 #include <stdio.h>
39 #include <stdarg.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <ctype.h>
44
45 #include <fcntl.h>
46 #include <sys/filio.h> /* Just to get FIONBIO... */
47 #include <unistd.h>
48 #include <errno.h>
49 #include <stropts.h>
50 #include <signal.h>
51 #include <syslog.h>
52 #include <sys/utsname.h>
53 #include <sys/stat.h>
54 #include <locale.h>
55 #include <limits.h>
56
57 #include <sys/priocntl.h>
58 #include <sys/tspriocntl.h>
59 #include <sys/rtpriocntl.h>
60
61 #include <netdb.h>
62 #include <sys/socket.h>
63 #include <tiuser.h>
64
65 #include <sys/sc_cvcio.h>
66
67
68 /*
69 * Header files for per-socket IPsec
70 */
71 #include <netinet/in.h>
72 #include <net/pfkeyv2.h>
73
74 /*
75 * The IPsec socket option struct, from ipsec(7P):
76 *
77 * typedef struct ipsec_req {
78 * uint_t ipsr_ah_req; AH request
79 * uint_t ipsr_esp_req; ESP request
80 * uint_t ipsr_self_encap_req; Self-Encap request
81 * uint8_t ipsr_auth_alg; Auth algs for AH
82 * uint8_t ipsr_esp_alg; Encr algs for ESP
83 * uint8_t ipsr_esp_auth_alg; Auth algs for ESP
84 * } ipsec_req_t;
85 *
86 * The -a option sets the ipsr_auth_alg field. Allowable arguments
87 * are "none", "md5", or "sha1". The -e option sets the ipsr_esp_alg
88 * field. Allowable arguments are "none", "des", or "3des". "none"
89 * is the default for both options. The -u option sets ipsr_esp_auth_alg.
90 * Allowable arguments are the same as -a.
91 *
92 * The arguments ("md5", "des", etc.) are named so that they match
93 * kmd(1m)'s accepted arguments which are listed on the SC in
94 * /etc/opt/SUNWSMS/SMS/config/kmd_policy.cf.
95 */
96 #define AH_REQ (IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE)
97 #define ESP_REQ (IPSEC_PREF_REQUIRED | IPSEC_PREF_UNIQUE)
98 #define SELF_ENCAP_REQ 0x0
99
100 /*
101 * A type to hold the command line argument string used to select a
102 * particular authentication header (AH) or encapsulating security
103 * payload (ESP) algorithm and the ID used for that algorithm when
104 * filling the ipsec_req_t structure which is passed to
105 * setsockopt(3SOCKET).
106 */
107 typedef struct cvcd_alg {
108 char *arg_name;
109 uint8_t alg_id;
110 } cvcd_alg_t;
111
112 /*
113 * Misc. defines.
114 */
115 #define NODENAME "/etc/nodename"
116 #define NETWORK_PFD 0
117 #define REDIR_PFD 1
118 #define LISTEN_PFD 2
119 #define NUM_PFDS 3
120
121 /*
122 * Function prototypes
123 */
124 static void cvcd_set_priority(void);
125 static int cvcd_init_host_socket(int port, uint8_t ah_auth_alg,
126 uint8_t esp_encr_alg, uint8_t esp_auth_alg);
127 static void cvcd_do_network_console(void);
128 static void cvcd_err(int code, char *format, ...);
129 static void cvcd_usage(void);
130 static uint8_t cvcd_get_alg(cvcd_alg_t *algs, char *arg);
131 static boolean_t cvcd_global_policy(void);
132
133 /*
134 * Globals
135 */
136 static struct pollfd pfds[NUM_PFDS];
137 static char progname[MAXPATHLEN];
138 static int debug = 0;
139
140 /*
141 * Array of acceptable -a, -u and -e arguments.
142 */
143 static cvcd_alg_t auth_algs_array[] = {
144 { "none", SADB_AALG_NONE }, /* -a none or -u none */
145 { "md5", SADB_AALG_MD5HMAC }, /* -a md5 or -u md5 */
146 { "sha1", SADB_AALG_SHA1HMAC }, /* -a sha1 or -u sha1 */
147 { NULL, 0x0 }
148 }, esp_algs_array[] = {
149 { "none", SADB_EALG_NONE }, /* -e none */
150 { "des", SADB_EALG_DESCBC }, /* -e des */
151 { "3des", SADB_EALG_3DESCBC }, /* -e 3des */
152 { NULL, 0x0 }
153 };
154
155
156 int
main(int argc,char ** argv)157 main(int argc, char **argv)
158 {
159 int err;
160 int opt;
161 int tport = 0;
162 char *hostname;
163 struct utsname utsname;
164 int fd;
165 int i;
166 struct servent *se;
167 char prefix[256];
168 uint8_t ah_auth_alg = SADB_AALG_NONE;
169 uint8_t esp_encr_alg = SADB_EALG_NONE;
170 uint8_t esp_auth_alg = SADB_AALG_NONE;
171
172 (void) setlocale(LC_ALL, "");
173 (void) strcpy(progname, argv[0]);
174
175 #ifdef DEBUG
176 while ((opt = getopt(argc, argv, "a:e:u:dp:")) != EOF) {
177 #else
178 while ((opt = getopt(argc, argv, "a:e:u:")) != EOF) {
179 #endif
180 switch (opt) {
181 case 'a' :
182 case 'u' :
183 if (opt == 'a')
184 ah_auth_alg = cvcd_get_alg(
185 auth_algs_array, optarg);
186 else
187 esp_auth_alg = cvcd_get_alg(
188 auth_algs_array, optarg);
189 break;
190
191 case 'e' : esp_encr_alg = cvcd_get_alg(
192 esp_algs_array, optarg);
193 break;
194 #ifdef DEBUG
195 case 'd' : debug = 1;
196 break;
197
198 case 'p' : tport = atoi(optarg);
199 break;
200 #endif /* DEBUG */
201
202 default : cvcd_usage();
203 exit(1);
204 }
205 }
206
207 if (uname(&utsname) == -1) {
208 perror("HOSTNAME not defined");
209 exit(1);
210 }
211 hostname = utsname.nodename;
212
213 /*
214 * hostname may still be NULL, depends on when cvcd was started
215 * in the boot sequence. If it is NULL, try one more time
216 * to get a hostname -> look in the /etc/nodename file.
217 */
218 if (!strlen(hostname)) {
219 /*
220 * try to get the hostname from the /etc/nodename file
221 * we reuse the utsname.nodename buffer here! hostname
222 * already points to it.
223 */
224 if ((fd = open(NODENAME, O_RDONLY)) > 0) {
225 if ((i = read(fd, utsname.nodename, SYS_NMLN)) <= 0) {
226 cvcd_err(LOG_WARNING,
227 "failed to acquire hostname");
228 } else {
229 utsname.nodename[i-1] = '\0';
230 }
231 (void) close(fd);
232 }
233 }
234 /*
235 * If all attempts to get the hostname have failed, put something
236 * meaningful in the buffer.
237 */
238 if (!strlen(hostname)) {
239 (void) strcpy(utsname.nodename, "(unknown)");
240 }
241
242 /*
243 * Must be root.
244 */
245 if (debug == 0 && geteuid() != 0) {
246 fprintf(stderr, "cvcd: Must be root");
247 exit(1);
248 }
249
250 /*
251 * Daemonize...
252 */
253 if (debug == 0) {
254 closefrom(0);
255 (void) chdir("/");
256 (void) umask(0);
257 if (fork() != 0) {
258 exit(0);
259 }
260 (void) setpgrp();
261 (void) sprintf(prefix, "%s-(HOSTNAME:%s)", progname, hostname);
262 openlog(prefix, LOG_CONS | LOG_NDELAY, LOG_LOCAL0);
263 }
264
265 /*
266 * Initialize the array of pollfds used to track the listening socket,
267 * the connection to the console redirection driver, and the network
268 * connection.
269 */
270 (void) memset((void *)pfds, 0, NUM_PFDS * sizeof (struct pollfd));
271 for (i = 0; i < NUM_PFDS; i++) {
272 pfds[i].fd = -1;
273 }
274
275 /* SPR 94004 */
276 (void) sigignore(SIGTERM);
277
278 /*
279 * SPR 83644: cvc and kadb are not compatible under heavy loads.
280 * Fix: will give cvcd highest TS priority at execution time.
281 */
282 cvcd_set_priority();
283
284 /*
285 * If not already determined by a command-line flag, figure out which
286 * port we're supposed to be listening on.
287 */
288 if (tport == 0) {
289 if ((se = getservbyname(CVCD_SERVICE, "tcp")) == NULL) {
290 cvcd_err(LOG_ERR, "getservbyname(%s) not found",
291 CVCD_SERVICE);
292 exit(1);
293 }
294 tport = se->s_port;
295 }
296
297 if (debug == 1) {
298 cvcd_err(LOG_DEBUG, "tport = %d, debug = %d", tport, debug);
299 }
300
301 /*
302 * Attempt to initialize the socket we'll use to listen for incoming
303 * connections. No need to check the return value, as the call will
304 * exit if it fails.
305 */
306 pfds[LISTEN_PFD].fd = cvcd_init_host_socket(tport, ah_auth_alg,
307 esp_encr_alg, esp_auth_alg);
308
309 /*
310 * Now that we're all set up, we loop forever waiting for connections
311 * (one at a time) and then driving network console activity over them.
312 */
313 for (;;) {
314 /*
315 * Start by waiting for an incoming connection.
316 */
317 do {
318 pfds[LISTEN_PFD].events = POLLIN;
319 err = poll(&(pfds[LISTEN_PFD]), 1, -1);
320 if (err == -1) {
321 cvcd_err(LOG_ERR, "poll: %s", strerror(errno));
322 exit(1);
323 }
324 if ((err > 0) &&
325 (pfds[LISTEN_PFD].revents & POLLIN)) {
326 fd = accept(pfds[LISTEN_PFD].fd, NULL, NULL);
327 if ((fd == -1) && (errno != EWOULDBLOCK)) {
328 cvcd_err(LOG_ERR, "accept: %s",
329 strerror(errno));
330 exit(1);
331 }
332 }
333 } while (fd == -1);
334
335 /*
336 * We have a connection. Set the new socket nonblocking, and
337 * initialize the appropriate pollfd. In theory, the new socket
338 * is _already_ non-blocking because accept() is supposed to
339 * hand us a socket with the same properties as the socket we're
340 * listening on, but it won't hurt to make sure.
341 */
342 opt = 1;
343 err = ioctl(fd, FIONBIO, &opt);
344 if (err == -1) {
345 cvcd_err(LOG_ERR, "ioctl: %s", strerror(errno));
346 (void) close(fd);
347 continue;
348 }
349 pfds[NETWORK_PFD].fd = fd;
350
351 /*
352 * Since we're ready to do network console stuff, go ahead and
353 * open the Network Console redirection driver, which will
354 * switch traffic from the IOSRAM path to the network path if
355 * the network path has been selected in cvc.
356 */
357 fd = open(CVCREDIR_DEV, O_RDWR|O_NDELAY);
358 if (fd == -1) {
359 cvcd_err(LOG_ERR, "open(redir): %s", strerror(errno));
360 exit(1);
361 }
362 pfds[REDIR_PFD].fd = fd;
363
364 /*
365 * We have a network connection and we have the redirection
366 * driver open, so drive the network console until something
367 * changes.
368 */
369 cvcd_do_network_console();
370
371 /*
372 * cvcd_do_network_console doesn't return until there's a
373 * problem, so we need to close the network connection and the
374 * redirection driver and start the whole loop over again.
375 */
376 (void) close(pfds[NETWORK_PFD].fd);
377 pfds[NETWORK_PFD].fd = -1;
378 (void) close(pfds[REDIR_PFD].fd);
379 pfds[REDIR_PFD].fd = -1;
380 }
381
382 /* NOTREACHED */
383 return (1);
384 }
385
386 /*
387 * cvcd_get_alg
388 *
389 * Returns the ID of the first algorithm found in
390 * the 'algs' array with a name matching 'arg'. If
391 * there is no matching algorithm, the function does
392 * not return. The 'algs' array must be terminated
393 * by an entry containing a NULL 'arg_name' field.
394 */
395 static uint8_t
396 cvcd_get_alg(cvcd_alg_t *algs, char *arg)
397 {
398 cvcd_alg_t *alg;
399
400 for (alg = algs; alg->arg_name != NULL && arg != NULL; alg++) {
401 if (strncmp(alg->arg_name, arg, strlen(alg->arg_name) + 1)
402 == 0) {
403 return (alg->alg_id);
404 }
405 }
406
407 cvcd_usage();
408 exit(1);
409 /* NOTREACHED */
410 }
411
412 /*
413 * cvcd_set_priority
414 *
415 * DESCRIBE
416 * SPR 83644: cvc and kadb are not compatible under heavy loads.
417 * Fix: will give cvcd highest TS priority at execution time.
418 */
419 static void
420 cvcd_set_priority(void)
421 {
422 id_t pid, tsID;
423 pcparms_t pcparms;
424 tsparms_t *tsparmsp;
425 short tsmaxpri;
426 pcinfo_t info;
427
428 pid = getpid();
429 pcparms.pc_cid = PC_CLNULL;
430 tsparmsp = (tsparms_t *)pcparms.pc_clparms;
431
432 /* Get scheduler properties for this PID */
433 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparms) == -1L) {
434 cvcd_err(LOG_ERR, "Warning: can't set priority.");
435 cvcd_err(LOG_ERR, "priocntl(GETPARMS): %s", strerror(errno));
436 return;
437 }
438
439 /* Get class ID and maximum priority for TS process class */
440 (void) strcpy(info.pc_clname, "TS");
441 if (priocntl(0L, 0L, PC_GETCID, (caddr_t)&info) == -1L) {
442 cvcd_err(LOG_ERR, "Warning: can't set priority.");
443 cvcd_err(LOG_ERR, "priocntl(GETCID): %s", strerror(errno));
444 return;
445 }
446 tsmaxpri = ((struct tsinfo *)info.pc_clinfo)->ts_maxupri;
447 tsID = info.pc_cid;
448
449 /* Print priority info in debug mode */
450 if (debug) {
451 if (pcparms.pc_cid == tsID) {
452 cvcd_err(LOG_DEBUG,
453 "PID: %d, current priority: %d, Max priority: %d.",
454 pid, tsparmsp->ts_upri, tsmaxpri);
455 }
456 }
457 /* Change proc's priority to maxtspri */
458 pcparms.pc_cid = tsID;
459 tsparmsp->ts_upri = tsmaxpri;
460 tsparmsp->ts_uprilim = tsmaxpri;
461
462 if (priocntl(P_PID, pid, PC_SETPARMS, (caddr_t)&pcparms) == -1L) {
463 cvcd_err(LOG_ERR, "Warning: can't set priority.");
464 cvcd_err(LOG_ERR, "priocntl(SETPARMS): %s", strerror(errno));
465 }
466
467 /* Print new priority info in debug mode */
468 if (debug) {
469 if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparms) ==
470 -1L) {
471 cvcd_err(LOG_ERR, "priocntl(GETPARMS): %s",
472 strerror(errno));
473 } else {
474 cvcd_err(LOG_DEBUG, "PID: %d, new priority: %d.", pid,
475 tsparmsp->ts_upri);
476 }
477 }
478 }
479
480
481 /*
482 * cvcd_init_host_socket
483 *
484 * Given a TCP port number, create and initialize a socket appropriate for
485 * accepting incoming connections to that port.
486 */
487 static int
488 cvcd_init_host_socket(int port, uint8_t ah_auth_alg, uint8_t esp_encr_alg,
489 uint8_t esp_auth_alg)
490 {
491 int err;
492 int fd;
493 int optval;
494 int optlen = sizeof (optval);
495 ipsec_req_t ipsec_req; /* For per-socket IPsec */
496 struct sockaddr_in6 sin6; /* IPv6 listen socket */
497
498 /*
499 * Start by creating the socket, which needs to support IPv6.
500 */
501 fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
502 if (fd == -1) {
503 cvcd_err(LOG_ERR, "socket: %s", strerror(errno));
504 exit(1);
505 }
506
507 /*
508 * Set the SO_REUSEADDR option, and make the socket non-blocking.
509 */
510 optval = 1;
511 err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, optlen);
512 if (err == -1) {
513 cvcd_err(LOG_ERR, "setsockopt: %s", strerror(errno));
514 exit(1);
515 }
516
517 err = ioctl(fd, FIONBIO, &optval);
518 if (err == -1) {
519 cvcd_err(LOG_ERR, "ioctl: %s", strerror(errno));
520 exit(1);
521 }
522
523 /*
524 * Enable per-socket IPsec if the user specified an AH or ESP
525 * algorithm to use and global policy is not in effect.
526 */
527 if (!cvcd_global_policy() &&
528 (ah_auth_alg != SADB_AALG_NONE || esp_encr_alg != SADB_EALG_NONE ||
529 esp_auth_alg != SADB_AALG_NONE)) {
530 bzero(&ipsec_req, sizeof (ipsec_req));
531
532 /* Hardcoded values */
533 ipsec_req.ipsr_self_encap_req = SELF_ENCAP_REQ;
534 /* User defined */
535 ipsec_req.ipsr_auth_alg = ah_auth_alg;
536 ipsec_req.ipsr_esp_alg = esp_encr_alg;
537 if (ah_auth_alg != SADB_AALG_NONE)
538 ipsec_req.ipsr_ah_req = AH_REQ;
539 if (esp_encr_alg != SADB_EALG_NONE ||
540 esp_auth_alg != SADB_AALG_NONE) {
541 ipsec_req.ipsr_esp_req = ESP_REQ;
542 ipsec_req.ipsr_esp_auth_alg = esp_auth_alg;
543 }
544
545 err = setsockopt(fd, IPPROTO_IPV6, IPV6_SEC_OPT,
546 (void *)&ipsec_req, sizeof (ipsec_req));
547
548 if (err == -1) {
549 cvcd_err(LOG_ERR, "failed to enable per-socket IPsec");
550 cvcd_err(LOG_ERR, "setsockopt: %s", strerror(errno));
551 exit(1);
552 }
553 }
554
555 /*
556 * Bind the socket to our local address and port.
557 */
558 bzero(&sin6, sizeof (sin6));
559 sin6.sin6_family = AF_INET6;
560 sin6.sin6_port = htons(port);
561 sin6.sin6_addr = in6addr_any;
562 err = bind(fd, (struct sockaddr *)&sin6, sizeof (sin6));
563 if (err == -1) {
564 cvcd_err(LOG_ERR, "bind: %s", strerror(errno));
565 exit(1);
566 }
567
568 /*
569 * Indicate that we want to accept connections on this socket. Since we
570 * only allow one connection at a time anyway, specify a maximum backlog
571 * of 1.
572 */
573 err = listen(fd, 1);
574 if (err == -1) {
575 cvcd_err(LOG_ERR, "listen: %s", strerror(errno));
576 exit(1);
577 }
578
579 return (fd);
580 }
581
582
583 /*
584 * cvcd_do_network_console
585 *
586 * With established connections to the network and the redirection driver,
587 * shuttle data between the two until something goes wrong.
588 */
589 static void
590 cvcd_do_network_console(void)
591 {
592 int i;
593 int err;
594 int count;
595 short revents;
596 int input_len = 0;
597 int output_len = 0;
598 int input_off = 0;
599 int output_off = 0;
600 char input_buf[MAXPKTSZ];
601 char output_buf[MAXPKTSZ];
602
603 for (;;) {
604 /*
605 * Wait for activity on any of the open file descriptors, which
606 * includes the ability to write data if we have any to write.
607 * If poll() fails, break out of the network console processing
608 * loop.
609 */
610 pfds[LISTEN_PFD].events = POLLIN;
611 pfds[NETWORK_PFD].events = POLLIN;
612 if (output_len != 0) {
613 pfds[NETWORK_PFD].events |= POLLOUT;
614 }
615 pfds[REDIR_PFD].events = POLLIN;
616 if (input_len != 0) {
617 pfds[REDIR_PFD].events |= POLLOUT;
618 }
619 err = poll(pfds, NUM_PFDS, -1);
620 if (err == -1) {
621 cvcd_err(LOG_ERR, "poll: %s", strerror(errno));
622 break;
623 }
624
625 /*
626 * If any errors or hangups were detected, or one of our file
627 * descriptors is bad, bail out of the network console
628 * processing loop.
629 */
630 for (i = 0; i < NUM_PFDS; i++) {
631 revents = pfds[i].revents;
632 if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
633 cvcd_err(LOG_NOTICE,
634 "poll: status on %s fd:%s%s%s",
635 ((i == LISTEN_PFD) ? "listen" :
636 ((i == NETWORK_PFD) ? "network" : "redir")),
637 (revents & POLLERR) ? " error" : "",
638 (revents & POLLHUP) ? " hangup" : "",
639 (revents & POLLNVAL) ? " bad fd" : "");
640 goto fail; /* 'break' wouldn't work here */
641 }
642 }
643
644 /*
645 * Start by rejecting any connection attempts, since we only
646 * allow one network connection at a time.
647 */
648 if (pfds[LISTEN_PFD].revents & POLLIN) {
649 int fd;
650
651 fd = accept(pfds[LISTEN_PFD].fd, NULL, NULL);
652 if (fd > 0) {
653 (void) close(fd);
654 }
655 }
656
657 /*
658 * If we have data waiting to be written in one direction or the
659 * other, go ahead and try to send the data on its way. We're
660 * going to attempt the writes regardless of whether the poll
661 * indicated that the destinations are ready, because we want to
662 * find out if either descriptor has a problem (e.g. broken
663 * network link).
664 * If an "unexpected" error is detected, give up and break out
665 * of the network console processing loop.
666 */
667 if (output_len != 0) {
668 count = write(pfds[NETWORK_PFD].fd,
669 &(output_buf[output_off]), output_len);
670 if ((count == -1) && (errno != EAGAIN)) {
671 cvcd_err(LOG_ERR, "write(network): %s",
672 strerror(errno));
673 break;
674 } else if (count > 0) {
675 output_len -= count;
676 if (output_len == 0) {
677 output_off = 0;
678 } else {
679 output_off += count;
680 }
681 }
682 }
683
684 if (input_len != 0) {
685 count = write(pfds[REDIR_PFD].fd,
686 &(input_buf[input_off]), input_len);
687 if ((count == -1) && (errno != EAGAIN)) {
688 cvcd_err(LOG_ERR, "write(redir): %s",
689 strerror(errno));
690 break;
691 } else if (count > 0) {
692 input_len -= count;
693 if (input_len == 0) {
694 input_off = 0;
695 } else {
696 input_off += count;
697 }
698 }
699 }
700
701 /*
702 * Finally, take a look at each data source and, if there isn't
703 * any residual data from that source still waiting to be
704 * processed, see if more data can be read. We don't want to
705 * read more data from a source if we haven't finished
706 * processing the last data we read from it because doing so
707 * would maximize the amount of data lost if the network console
708 * failed or was closed.
709 * If an "unexpected" error is detected, give up and break out
710 * of the network console processing loop.
711 * The call to read() appears to be in the habit of returning 0
712 * when you've read all of the data from a stream that has been
713 * hung up, and poll apparently feels that that condition
714 * justifies setting POLLIN, so we're going to treat 0 as an
715 * error return from read().
716 */
717 if ((output_len == 0) && (pfds[REDIR_PFD].revents & POLLIN)) {
718 count = read(pfds[REDIR_PFD].fd, output_buf, MAXPKTSZ);
719 if (count <= 0) {
720 /*
721 * Reading 0 simply means there is no data
722 * available, since this is a terminal.
723 */
724 if ((count < 0) && (errno != EAGAIN)) {
725 cvcd_err(LOG_ERR, "read(redir): %s",
726 strerror(errno));
727 break;
728 }
729 } else {
730 output_len = count;
731 output_off = 0;
732 }
733 }
734
735 if ((input_len == 0) && (pfds[NETWORK_PFD].revents & POLLIN)) {
736 count = read(pfds[NETWORK_PFD].fd, input_buf, MAXPKTSZ);
737 if (count <= 0) {
738 /*
739 * Reading 0 here implies a hangup, since this
740 * is a non-blocking socket that poll() reported
741 * as having data available. This will
742 * typically occur when the console user drops
743 * to OBP or intentially switches to IOSRAM
744 * mode.
745 */
746 if (count == 0) {
747 cvcd_err(LOG_NOTICE,
748 "read(network): hangup detected");
749 break;
750 } else if (errno != EAGAIN) {
751 cvcd_err(LOG_ERR, "read(network): %s",
752 strerror(errno));
753 break;
754 }
755 } else {
756 input_len = count;
757 input_off = 0;
758 }
759 }
760 } /* End forever loop */
761
762 /*
763 * If we get here, something bad happened during an attempt to access
764 * either the redirection driver or the network connection. There
765 * doesn't appear to be any way to avoid the possibility of losing
766 * console input and/or input in that case, so we should at least report
767 * the loss if it happens.
768 * XXX - We could do more, but is it worth the effort? Logging the
769 * lost data would be pretty easy... actually preserving it
770 * in the console flow would be a lot harder. We're more robust
771 * than the previous generation at this point, at least, so
772 * perhaps that's enough for now?
773 */
774 fail:
775 if (input_len != 0) {
776 cvcd_err(LOG_ERR, "console input lost");
777 }
778 if (output_len != 0) {
779 cvcd_err(LOG_ERR, "console output lost");
780 }
781 }
782
783
784 static void
785 cvcd_usage()
786 {
787 #if defined(DEBUG)
788 (void) printf("%s [-d] [-p port] "
789 "[-a none|md5|sha1] [-e none|des|3des] [-u none|md5|sha1]\n",
790 progname);
791 #else
792 (void) printf("%s [-a none|md5|sha1] [-e none|des|3des] "
793 "[-u none|md5|sha1]\n", progname);
794 #endif /* DEBUG */
795 }
796
797 /*
798 * cvcd_err ()
799 *
800 * Description:
801 * Log messages via syslog daemon.
802 *
803 * Input:
804 * code - logging code
805 * format - messages to log
806 *
807 * Output:
808 * void
809 *
810 */
811 static void
812 cvcd_err(int code, char *format, ...)
813 {
814 va_list varg_ptr;
815 char buf[MAXPKTSZ];
816
817 va_start(varg_ptr, format);
818 (void) vsnprintf(buf, MAXPKTSZ, format, varg_ptr);
819 va_end(varg_ptr);
820
821 if (debug == 0) {
822 syslog(code, buf);
823 } else {
824 (void) fprintf(stderr, "%s: %s\n", progname, buf);
825 }
826 }
827
828 /*
829 * has_cvcd_token
830 *
831 * Look for "?port [cvc_hostd|442]" in input buf.
832 * Assume only a single thread calls here.
833 */
834 static boolean_t
835 has_cvcd_token(char *buf)
836 {
837 char *token;
838 char *delims = "{} \t\n";
839 boolean_t port = B_FALSE;
840
841 while ((token = strtok(buf, delims)) != NULL) {
842 buf = NULL;
843 if (port == B_TRUE) {
844 if (strcmp(token, "cvc_hostd") == 0 ||
845 strcmp(token, "442") == 0) {
846 return (B_TRUE);
847 } else {
848 return (B_FALSE);
849 }
850 }
851 if (strlen(token) == 5) {
852 token++;
853 if (strcmp(token, "port") == 0) {
854 port = B_TRUE;
855 continue;
856 }
857 }
858 }
859 return (B_FALSE);
860 }
861
862 /*
863 * cvcd_global_policy
864 *
865 * Check global policy file for cvcd entry. Just covers common cases.
866 */
867 static boolean_t
868 cvcd_global_policy()
869 {
870 FILE *fp;
871 char buf[256];
872 boolean_t rv = B_FALSE;
873
874 fp = fopen("/etc/inet/ipsecinit.conf", "r");
875 if (fp == NULL)
876 return (B_FALSE);
877 while (fgets(buf, sizeof (buf), fp) != NULL) {
878 if (buf[0] == '#')
879 continue;
880 if (has_cvcd_token(buf)) {
881 rv = B_TRUE;
882 cvcd_err(LOG_NOTICE, "cvcd using global policy");
883 break;
884 }
885 }
886 (void) fclose(fp);
887 return (rv);
888 }
889