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 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