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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983-1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #define _FILE_OFFSET_BITS 64 36 37 /* 38 * remote shell server: 39 * remuser\0 40 * locuser\0 41 * command\0 42 * data 43 */ 44 #include <sys/types.h> 45 #include <sys/ioctl.h> 46 #include <sys/telioctl.h> 47 #include <sys/param.h> 48 #include <sys/socket.h> 49 #include <sys/time.h> 50 #include <sys/stat.h> 51 #include <sys/file.h> 52 #include <sys/select.h> 53 54 #include <netinet/in.h> 55 56 #include <arpa/inet.h> 57 58 #include <unistd.h> 59 #include <string.h> 60 #include <stdio.h> 61 #include <stdarg.h> 62 #include <errno.h> 63 #include <pwd.h> 64 #include <grp.h> 65 #include <signal.h> 66 #include <netdb.h> 67 #include <syslog.h> 68 #include <fcntl.h> 69 #include <ctype.h> 70 #include <locale.h> 71 72 #include <sys/resource.h> 73 #include <sys/filio.h> 74 #include <shadow.h> 75 #include <stdlib.h> 76 77 #include <security/pam_appl.h> 78 #include <deflt.h> 79 80 #include <k5-int.h> 81 #include <krb5_repository.h> 82 #include <com_err.h> 83 #include <kcmd.h> 84 85 #include <addr_match.h> 86 #include <store_forw_creds.h> 87 88 #ifndef NCARGS 89 #define NCARGS 5120 90 #endif /* !NCARGS */ 91 92 static void error(char *, ...); 93 static void doit(int, struct sockaddr_storage *, char **); 94 static void getstr(int, char *, int, char *); 95 96 static int legalenvvar(char *); 97 static void add_to_envinit(char *); 98 static int locale_envmatch(char *, char *); 99 100 /* Function decls. for functions not in any header file. (Grrrr.) */ 101 extern int audit_rshd_setup(void); 102 extern int audit_rshd_success(char *, char *, char *, char *); 103 extern int audit_rshd_fail(char *, char *, char *, char *, char *); 104 extern int audit_settid(int); 105 106 static int do_encrypt = 0; 107 static pam_handle_t *pamh; 108 109 /* 110 * This is the shell/kshell daemon. The very basic protocol for checking 111 * authentication and authorization is: 112 * 1) Check authentication. 113 * 2) Check authorization via the access-control files: 114 * ~/.k5login (using krb5_kuserok) and/or 115 * Execute command if configured authoriztion checks pass, else deny 116 * permission. 117 * 118 * The configuration is done either by command-line arguments passed by inetd, 119 * or by the name of the daemon. If command-line arguments are present, they 120 * take priority. The options are: 121 * -k allow kerberos authentication (krb5 only; krb4 support is not provided) 122 * -5 same as `-k', mainly for compatability with MIT 123 * -e allow encrypted session 124 * -c demand authenticator checksum 125 * -i ignore authenticator checksum 126 * -U Refuse connections that cannot be mapped to a name via `gethostbyname' 127 * -s <tos> Set the IP TOS option 128 * -S <keytab> Set the keytab file to use 129 * -M <realm> Set the Kerberos realm to use 130 */ 131 132 #define ARGSTR "ek5ciUD:M:S:L:?:" 133 #define RSHD_BUFSIZ (50 * 1024) 134 135 static krb5_context bsd_context; 136 static krb5_keytab keytab = NULL; 137 static krb5_ccache ccache = NULL; 138 static krb5_keyblock *sessionkey = NULL; 139 140 static int require_encrypt = 0; 141 static int resolve_hostname = 0; 142 static int krb5auth_flag = 0; /* Flag set, when KERBEROS is enabled */ 143 static enum kcmd_proto kcmd_protocol; 144 145 #ifdef DEBUG 146 static int debug_port = 0; 147 #endif /* DEBUG */ 148 149 /* 150 * There are two authentication related masks: 151 * auth_ok and auth_sent. 152 * The auth_ok mask is the or'ing of authentication 153 * systems any one of which can be used. 154 * The auth_sent mask is the or'ing of one or more authentication/authorization 155 * systems that succeeded. If the and'ing 156 * of these two masks is true, then authorization is successful. 157 */ 158 159 #define AUTH_KRB5 (0x2) 160 static int auth_ok = 0; 161 static int auth_sent = 0; 162 static int checksum_required = 0; 163 static int checksum_ignored = 0; 164 165 /* 166 * Leave room for 4 environment variables to be passed. 167 * The "-L env_var" option has been added primarily to 168 * maintain compatability with MIT. 169 */ 170 #define MAXENV 4 171 static char *save_env[MAXENV]; 172 static int num_env = 0; 173 174 static void usage(void); 175 static krb5_error_code recvauth(int, int *); 176 177 /*ARGSUSED*/ 178 int 179 main(int argc, char **argv, char **renvp) 180 { 181 struct linger linger; 182 int on = 1, fromlen; 183 struct sockaddr_storage from; 184 int fd = 0; 185 186 extern int opterr, optind; 187 extern char *optarg; 188 int ch; 189 int tos = -1; 190 krb5_error_code status; 191 192 openlog("rsh", LOG_PID | LOG_ODELAY, LOG_DAEMON); 193 (void) audit_rshd_setup(); /* BSM */ 194 fromlen = sizeof (from); 195 196 (void) setlocale(LC_ALL, ""); 197 198 /* 199 * Analyze parameters. 200 */ 201 opterr = 0; 202 while ((ch = getopt(argc, argv, ARGSTR)) != EOF) 203 switch (ch) { 204 case '5': 205 case 'k': 206 auth_ok |= AUTH_KRB5; 207 krb5auth_flag++; 208 break; 209 210 case 'c': 211 checksum_required = 1; 212 krb5auth_flag++; 213 break; 214 case 'i': 215 checksum_ignored = 1; 216 krb5auth_flag++; 217 break; 218 219 case 'e': 220 require_encrypt = 1; 221 krb5auth_flag++; 222 break; 223 #ifdef DEBUG 224 case 'D': 225 debug_port = atoi(optarg); 226 break; 227 #endif /* DEBUG */ 228 case 'U': 229 resolve_hostname = 1; 230 break; 231 232 case 'M': 233 (void) krb5_set_default_realm(bsd_context, optarg); 234 krb5auth_flag++; 235 break; 236 237 case 'S': 238 if ((status = krb5_kt_resolve(bsd_context, optarg, 239 &keytab))) { 240 com_err("rsh", status, 241 gettext("while resolving " 242 "srvtab file %s"), optarg); 243 exit(2); 244 } 245 krb5auth_flag++; 246 break; 247 248 case 's': 249 if (optarg == NULL || ((tos = atoi(optarg)) < 0) || 250 (tos > 255)) { 251 syslog(LOG_ERR, "rshd: illegal tos value: " 252 "%s\n", optarg); 253 } 254 break; 255 256 case 'L': 257 if (num_env < MAXENV) { 258 save_env[num_env] = strdup(optarg); 259 if (!save_env[num_env++]) { 260 com_err("rsh", ENOMEM, 261 gettext("in saving env")); 262 exit(2); 263 } 264 } else { 265 (void) fprintf(stderr, gettext("rshd: Only %d" 266 " -L arguments allowed\n"), 267 MAXENV); 268 exit(2); 269 } 270 break; 271 272 case '?': 273 default: 274 usage(); 275 exit(1); 276 break; 277 } 278 279 if (optind == 0) { 280 usage(); 281 exit(1); 282 } 283 argc -= optind; 284 argv += optind; 285 286 if (krb5auth_flag > 0) { 287 status = krb5_init_context(&bsd_context); 288 if (status) { 289 syslog(LOG_ERR, "Error initializing krb5: %s", 290 error_message(status)); 291 exit(1); 292 } 293 } 294 295 if (!checksum_required && !checksum_ignored) 296 checksum_ignored = 1; 297 298 if (checksum_required && checksum_ignored) { 299 syslog(LOG_CRIT, gettext("Checksums are required and ignored." 300 "These options are mutually exclusive" 301 "--check the documentation.")); 302 error("Configuration error: mutually exclusive " 303 "options specified.\n"); 304 exit(1); 305 } 306 307 #ifdef DEBUG 308 if (debug_port) { 309 int s; 310 struct sockaddr_in sin; 311 312 if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) { 313 fprintf(stderr, gettext("Error in socket: %s\n"), 314 strerror(errno)); 315 exit(2); 316 } 317 (void) memset((char *)&sin, 0, sizeof (sin)); 318 sin.sin_family = AF_INET; 319 sin.sin_port = htons(debug_port); 320 sin.sin_addr.s_addr = INADDR_ANY; 321 322 (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 323 (char *)&on, sizeof (on)); 324 325 if ((bind(s, (struct sockaddr *)&sin, sizeof (sin))) < 0) { 326 (void) fprintf(stderr, gettext("Error in bind: %s\n"), 327 strerror(errno)); 328 exit(2); 329 } 330 if ((listen(s, 5)) < 0) { 331 (void) fprintf(stderr, gettext("Error in listen: %s\n"), 332 strerror(errno)); 333 exit(2); 334 } 335 if ((fd = accept(s, (struct sockaddr *)&from, 336 &fromlen)) < 0) { 337 (void) fprintf(stderr, gettext("Error in accept: %s\n"), 338 strerror(errno)); 339 exit(2); 340 } 341 (void) close(s); 342 } 343 else 344 #endif /* DEBUG */ 345 { 346 if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, 347 (socklen_t *)&fromlen) < 0) { 348 (void) fprintf(stderr, "rshd: "); 349 perror("getpeername"); 350 _exit(1); 351 } 352 fd = STDIN_FILENO; 353 } 354 355 if (audit_settid(fd) != 0) { 356 perror("settid"); 357 exit(1); 358 } 359 360 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, 361 sizeof (on)) < 0) 362 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 363 linger.l_onoff = 1; 364 linger.l_linger = 60; /* XXX */ 365 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&linger, 366 sizeof (linger)) < 0) 367 syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); 368 369 if ((tos != -1) && (setsockopt(fd, IPPROTO_IP, IP_TOS, (char *)&tos, 370 sizeof (tos)) < 0) && 371 (errno != ENOPROTOOPT)) { 372 syslog(LOG_ERR, "setsockopt (IP_TOS %d): %m"); 373 } 374 375 doit(dup(fd), &from, renvp); 376 return (0); 377 } 378 379 /* 380 * locale environments to be passed to shells. 381 */ 382 static char *localeenv[] = { 383 "LANG", 384 "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE", 385 "LC_MONETARY", "LC_MESSAGES", "LC_ALL", NULL}; 386 387 /* 388 * The following is for the environment variable list 389 * used in the call to execle(). envinit is declared here, 390 * but populated after the call to getpwnam(). 391 */ 392 static char *homedir; /* "HOME=" */ 393 static char *shell; /* "SHELL=" */ 394 static char *username; /* "USER=" */ 395 static char *tz; /* "TZ=" */ 396 397 static char homestr[] = "HOME="; 398 static char shellstr[] = "SHELL="; 399 static char userstr[] = "USER="; 400 static char tzstr[] = "TZ="; 401 402 static char **envinit; 403 #define PAM_ENV_ELIM 16 /* allow 16 PAM environment variables */ 404 #define USERNAME_LEN 16 /* maximum number of characters in user name */ 405 406 /* 407 * See PSARC opinion 1992/025 408 */ 409 static char userpath[] = "PATH=/usr/bin:"; 410 static char rootpath[] = "PATH=/usr/sbin:/usr/bin"; 411 412 static char cmdbuf[NCARGS+1]; 413 static char hostname [MAXHOSTNAMELEN + 1]; 414 static char locuser[USERNAME_LEN + 1]; 415 static char remuser[USERNAME_LEN + 1]; 416 417 #define KRB5_RECVAUTH_V5 5 418 #define SIZEOF_INADDR sizeof (struct in_addr) 419 420 #define MAX_REPOSITORY_LEN 255 421 static char repository[MAX_REPOSITORY_LEN]; 422 423 static char *kremuser; 424 static krb5_principal client = NULL; 425 426 static char remote_addr[64]; 427 static char local_addr[64]; 428 429 #define _PATH_DEFAULT_LOGIN "/etc/default/login" 430 431 static void 432 doit(int f, struct sockaddr_storage *fromp, char **renvp) 433 { 434 char *cp; 435 436 struct passwd *pwd; 437 char *path; 438 char *tzenv; 439 struct spwd *shpwd; 440 struct stat statb; 441 char **lenvp; 442 443 krb5_error_code status; 444 int valid_checksum; 445 int cnt; 446 int sin_len; 447 struct sockaddr_in localaddr; 448 449 int s; 450 in_port_t port; 451 pid_t pid; 452 int pv[2], pw[2], px[2], cc; 453 char buf[RSHD_BUFSIZ]; 454 char sig; 455 int one = 1; 456 int v = 0; 457 int err = 0; 458 int idx = 0; 459 char **pam_env; 460 char abuf[INET6_ADDRSTRLEN]; 461 struct sockaddr_in *sin; 462 struct sockaddr_in6 *sin6; 463 int fromplen; 464 int homedir_len, shell_len, username_len, tz_len; 465 int no_name; 466 boolean_t bad_port; 467 int netf = 0; 468 469 (void) signal(SIGINT, SIG_DFL); 470 (void) signal(SIGQUIT, SIG_DFL); 471 (void) signal(SIGTERM, SIG_DFL); 472 (void) signal(SIGXCPU, SIG_DFL); 473 (void) signal(SIGXFSZ, SIG_DFL); 474 (void) sigset(SIGCHLD, SIG_IGN); 475 (void) signal(SIGPIPE, SIG_DFL); 476 (void) signal(SIGHUP, SIG_DFL); 477 478 #ifdef DEBUG 479 { 480 int t = open("/dev/tty", 2); 481 482 if (t >= 0) { 483 (void) setsid(); 484 (void) close(t); 485 } 486 } 487 #endif 488 if (fromp->ss_family == AF_INET) { 489 sin = (struct sockaddr_in *)fromp; 490 port = ntohs((ushort_t)sin->sin_port); 491 fromplen = sizeof (struct sockaddr_in); 492 } else if (fromp->ss_family == AF_INET6) { 493 sin6 = (struct sockaddr_in6 *)fromp; 494 port = ntohs((ushort_t)sin6->sin6_port); 495 fromplen = sizeof (struct sockaddr_in6); 496 } else { 497 syslog(LOG_ERR, "wrong address family\n"); 498 exit(1); 499 } 500 501 if (fromp->ss_family == AF_INET6) { 502 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 503 struct in_addr ipv4_addr; 504 505 IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &ipv4_addr); 506 (void) inet_ntop(AF_INET, &ipv4_addr, abuf, 507 sizeof (abuf)); 508 } else { 509 (void) inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, 510 sizeof (abuf)); 511 } 512 } else if (fromp->ss_family == AF_INET) { 513 (void) inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof (abuf)); 514 } 515 516 sin_len = sizeof (struct sockaddr_in); 517 if (getsockname(f, (struct sockaddr *)&localaddr, &sin_len) < 0) { 518 perror("getsockname"); 519 exit(1); 520 } 521 522 netf = f; 523 524 bad_port = (port >= IPPORT_RESERVED || 525 port < (uint_t)(IPPORT_RESERVED/2)); 526 527 /* Get the name of the client side host to use later */ 528 no_name = (getnameinfo((const struct sockaddr *) fromp, fromplen, 529 hostname, sizeof (hostname), NULL, 0, 0) != 0); 530 531 if (bad_port || no_name != 0) { 532 /* 533 * If there is no host name available then use the 534 * IP address to identify the host in the PAM call 535 * below. Do the same if a bad port was used, to 536 * prevent untrustworthy authentication. 537 */ 538 (void) strlcpy(hostname, abuf, sizeof (hostname)); 539 } 540 541 if (no_name != 0) { 542 /* 543 * If the '-U' option was given on the cmd line, 544 * we must be able to lookup the hostname 545 */ 546 if (resolve_hostname) { 547 syslog(LOG_ERR, "rshd: Couldn't resolve your " 548 "address into a host name.\r\n Please " 549 "contact your net administrator"); 550 exit(1); 551 } 552 } else { 553 /* 554 * Even if getnameinfo() succeeded, we still have to check 555 * for spoofing. 556 */ 557 check_address("rshd", fromp, sin, sin6, abuf, hostname, 558 sizeof (hostname)); 559 } 560 561 if (!krb5auth_flag && bad_port) { 562 if (no_name) 563 syslog(LOG_NOTICE, "connection from %s - " 564 "bad port\n", abuf); 565 else 566 syslog(LOG_NOTICE, "connection from %s (%s) - " 567 "bad port\n", hostname, abuf); 568 exit(1); 569 } 570 571 (void) alarm(60); 572 port = 0; 573 for (;;) { 574 char c; 575 if ((cc = read(f, &c, 1)) != 1) { 576 if (cc < 0) 577 syslog(LOG_NOTICE, "read: %m"); 578 (void) shutdown(f, 1+1); 579 exit(1); 580 } 581 if (c == 0) 582 break; 583 port = port * 10 + c - '0'; 584 } 585 (void) alarm(0); 586 if (port != 0) { 587 int lport = 0; 588 struct sockaddr_storage ctl_addr; 589 int addrlen; 590 591 (void) memset(&ctl_addr, 0, sizeof (ctl_addr)); 592 addrlen = sizeof (ctl_addr); 593 if (getsockname(f, (struct sockaddr *)&ctl_addr, 594 &addrlen) < 0) { 595 syslog(LOG_ERR, "getsockname: %m"); 596 exit(1); 597 } 598 get_port: 599 /* 600 * 0 means that rresvport_addr() will bind to a port in 601 * the anonymous priviledged port range. 602 */ 603 if (krb5auth_flag) { 604 /* 605 * Kerberos does not support IPv6 yet. 606 */ 607 lport = IPPORT_RESERVED - 1; 608 } 609 s = rresvport_addr(&lport, &ctl_addr); 610 611 if (s < 0) { 612 syslog(LOG_ERR, "can't get stderr port: %m"); 613 exit(1); 614 } 615 if (!krb5auth_flag && (port >= IPPORT_RESERVED)) { 616 syslog(LOG_ERR, "2nd port not reserved\n"); 617 exit(1); 618 } 619 if (fromp->ss_family == AF_INET) { 620 sin->sin_port = htons((ushort_t)port); 621 } else if (fromp->ss_family == AF_INET6) { 622 sin6->sin6_port = htons((ushort_t)port); 623 } 624 if (connect(s, (struct sockaddr *)fromp, fromplen) < 0) { 625 if (errno == EADDRINUSE) { 626 (void) close(s); 627 goto get_port; 628 } 629 syslog(LOG_INFO, "connect second port: %m"); 630 exit(1); 631 } 632 } 633 (void) dup2(f, 0); 634 (void) dup2(f, 1); 635 (void) dup2(f, 2); 636 637 #ifdef DEBUG 638 syslog(LOG_NOTICE, "rshd: Client hostname = %s", hostname); 639 if (debug_port) 640 syslog(LOG_NOTICE, "rshd: Debug port is %d", debug_port); 641 if (krb5auth_flag > 0) 642 syslog(LOG_NOTICE, "rshd: Kerberos mode is ON"); 643 else 644 syslog(LOG_NOTICE, "rshd: Kerberos mode is OFF"); 645 #endif /* DEBUG */ 646 647 if (krb5auth_flag > 0) { 648 if ((status = recvauth(f, &valid_checksum))) { 649 syslog(LOG_ERR, gettext("Kerberos Authentication " 650 "failed \n")); 651 error("Authentication failed: %s\n", 652 error_message(status)); 653 (void) audit_rshd_fail("Kerberos Authentication " 654 "failed", hostname, remuser, locuser, cmdbuf); 655 exit(1); 656 } 657 658 if (checksum_required && !valid_checksum && 659 kcmd_protocol == KCMD_OLD_PROTOCOL) { 660 syslog(LOG_WARNING, "Client did not supply required" 661 " checksum--connection rejected."); 662 error("Client did not supply required" 663 "checksum--connection rejected.\n"); 664 (void) audit_rshd_fail("Client did not supply required" 665 " checksum--connection rejected.", hostname, 666 remuser, locuser, cmdbuf); /* BSM */ 667 goto signout; 668 } 669 670 /* 671 * Authentication has succeeded, we now need 672 * to check authorization. 673 * 674 * krb5_kuserok returns 1 if OK. 675 */ 676 if (client && krb5_kuserok(bsd_context, client, locuser)) { 677 auth_sent |= AUTH_KRB5; 678 } else { 679 syslog(LOG_ERR, "Principal %s (%s@%s) for local user " 680 "%s failed krb5_kuserok.\n", 681 kremuser, remuser, hostname, locuser); 682 } 683 } else { 684 getstr(netf, remuser, sizeof (remuser), "remuser"); 685 getstr(netf, locuser, sizeof (locuser), "locuser"); 686 getstr(netf, cmdbuf, sizeof (cmdbuf), "command"); 687 } 688 689 #ifdef DEBUG 690 syslog(LOG_NOTICE, "rshd: locuser = %s, remuser = %s, cmdbuf = %s", 691 locuser, remuser, cmdbuf); 692 #endif /* DEBUG */ 693 694 /* 695 * Note that there is no rsh conv functions at present. 696 */ 697 if (krb5auth_flag > 0) { 698 if ((err = pam_start("krsh", locuser, NULL, &pamh)) 699 != PAM_SUCCESS) { 700 syslog(LOG_ERR, "pam_start() failed: %s\n", 701 pam_strerror(0, err)); 702 exit(1); 703 } 704 } 705 else 706 { 707 if ((err = pam_start("rsh", locuser, NULL, &pamh)) 708 != PAM_SUCCESS) { 709 syslog(LOG_ERR, "pam_start() failed: %s\n", 710 pam_strerror(0, err)); 711 exit(1); 712 } 713 } 714 if ((err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS) { 715 syslog(LOG_ERR, "pam_set_item() failed: %s\n", 716 pam_strerror(pamh, err)); 717 exit(1); 718 } 719 if ((err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS) { 720 syslog(LOG_ERR, "pam_set_item() failed: %s\n", 721 pam_strerror(pamh, err)); 722 exit(1); 723 } 724 725 pwd = getpwnam(locuser); 726 shpwd = getspnam(locuser); 727 if ((pwd == NULL) || (shpwd == NULL)) { 728 if (krb5auth_flag > 0) 729 syslog(LOG_ERR, "Principal %s (%s@%s) for local user " 730 "%s has no account.\n", kremuser, remuser, 731 hostname, locuser); 732 error("permission denied.\n"); 733 (void) audit_rshd_fail("Login incorrect", hostname, 734 remuser, locuser, cmdbuf); /* BSM */ 735 exit(1); 736 } 737 738 if (krb5auth_flag > 0) { 739 (void) snprintf(repository, sizeof (repository), 740 KRB5_REPOSITORY_NAME); 741 /* 742 * We currently only support special handling of the 743 * KRB5 PAM repository 744 */ 745 if (strlen(locuser) != 0) { 746 krb5_repository_data_t krb5_data; 747 pam_repository_t pam_rep_data; 748 749 krb5_data.principal = locuser; 750 krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED; 751 752 pam_rep_data.type = repository; 753 pam_rep_data.scope = (void *)&krb5_data; 754 pam_rep_data.scope_len = sizeof (krb5_data); 755 756 (void) pam_set_item(pamh, PAM_REPOSITORY, 757 (void *)&pam_rep_data); 758 } 759 } 760 761 if (shpwd->sp_pwdp != 0) { 762 if (*shpwd->sp_pwdp != '\0') { 763 if ((v = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { 764 error("permission denied\n"); 765 (void) audit_rshd_fail("Permission denied", 766 hostname, remuser, locuser, cmdbuf); 767 (void) pam_end(pamh, v); 768 exit(1); 769 } 770 } else { 771 int flags; 772 char *p; 773 /* 774 * maintain 2.1 and 4.* and BSD semantics with 775 * anonymous rshd unless PASSREQ is set to YES in 776 * /etc/default/login: then we deny logins with empty 777 * passwords. 778 */ 779 if (defopen(_PATH_DEFAULT_LOGIN) == 0) { 780 flags = defcntl(DC_GETFLAGS, 0); 781 TURNOFF(flags, DC_CASE); 782 (void) defcntl(DC_SETFLAGS, flags); 783 784 if ((p = defread("PASSREQ=")) != NULL && 785 strcasecmp(p, "YES") == 0) { 786 error("permission denied\n"); 787 (void) audit_rshd_fail( 788 "Permission denied", hostname, 789 remuser, locuser, cmdbuf); 790 (void) pam_end(pamh, PAM_ABORT); 791 (void) defopen(NULL); 792 syslog(LOG_AUTH|LOG_NOTICE, 793 "empty password not allowed for " 794 "%s from %s.", locuser, hostname); 795 exit(1); 796 } 797 (void) defopen(NULL); 798 } 799 /* 800 * /etc/default/login not found or PASSREQ not set 801 * to YES. Allow logins without passwords. 802 */ 803 } 804 } 805 806 if (krb5auth_flag > 0) { 807 if (require_encrypt && (!do_encrypt)) { 808 error("You must use encryption.\n"); 809 (void) audit_rshd_fail("You must use encryption.", 810 hostname, remuser, locuser, cmdbuf); /* BSM */ 811 goto signout; 812 } 813 814 if (!(auth_ok & auth_sent)) { 815 if (auth_sent) { 816 error("Another authentication mechanism " 817 "must be used to access this host.\n"); 818 (void) audit_rshd_fail("Another authentication" 819 " mechanism must be used to access" 820 " this host.\n", hostname, remuser, 821 locuser, cmdbuf); /* BSM */ 822 goto signout; 823 } else { 824 error("Permission denied.\n"); 825 (void) audit_rshd_fail("Permission denied.", 826 hostname, remuser, locuser, cmdbuf); 827 /* BSM */ 828 goto signout; 829 } 830 } 831 832 833 if (pwd->pw_uid && !access("/etc/nologin", F_OK)) { 834 error("Logins currently disabled.\n"); 835 (void) audit_rshd_fail("Logins currently disabled.", 836 hostname, remuser, locuser, cmdbuf); 837 goto signout; 838 } 839 840 /* Log access to account */ 841 if (pwd && (pwd->pw_uid == 0)) { 842 syslog(LOG_NOTICE, "Executing %s for user %s (%s@%s)" 843 " as ROOT", cmdbuf, 844 kremuser, remuser, hostname); 845 } 846 } 847 848 if ((v = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) { 849 switch (v) { 850 case PAM_NEW_AUTHTOK_REQD: 851 error("password expired\n"); 852 (void) audit_rshd_fail("Password expired", hostname, 853 remuser, locuser, cmdbuf); /* BSM */ 854 break; 855 case PAM_PERM_DENIED: 856 error("account expired\n"); 857 (void) audit_rshd_fail("Account expired", hostname, 858 remuser, locuser, cmdbuf); /* BSM */ 859 break; 860 case PAM_AUTHTOK_EXPIRED: 861 error("password expired\n"); 862 (void) audit_rshd_fail("Password expired", hostname, 863 remuser, locuser, cmdbuf); /* BSM */ 864 break; 865 default: 866 error("login incorrect\n"); 867 (void) audit_rshd_fail("Permission denied", hostname, 868 remuser, locuser, cmdbuf); /* BSM */ 869 break; 870 } 871 (void) pam_end(pamh, PAM_ABORT); 872 exit(1); 873 } 874 875 if (chdir(pwd->pw_dir) < 0) { 876 (void) chdir("/"); 877 #ifdef notdef 878 error("No remote directory.\n"); 879 880 exit(1); 881 #endif 882 } 883 884 /* 885 * XXX There is no session management currently being done 886 */ 887 888 (void) write(STDERR_FILENO, "\0", 1); 889 if (port || do_encrypt) { 890 if ((pipe(pv) < 0)) { 891 error("Can't make pipe.\n"); 892 (void) pam_end(pamh, PAM_ABORT); 893 exit(1); 894 } 895 if (do_encrypt) { 896 if (pipe(pw) < 0) { 897 error("Can't make pipe 2.\n"); 898 (void) pam_end(pamh, PAM_ABORT); 899 exit(1); 900 } 901 if (pipe(px) < 0) { 902 error("Can't make pipe 3.\n"); 903 (void) pam_end(pamh, PAM_ABORT); 904 exit(1); 905 } 906 } 907 pid = fork(); 908 if (pid == (pid_t)-1) { 909 error("Fork (to start shell) failed on server. " 910 "Please try again later.\n"); 911 (void) pam_end(pamh, PAM_ABORT); 912 exit(1); 913 } 914 if (pid) { 915 fd_set ready; 916 fd_set readfrom; 917 918 (void) close(STDIN_FILENO); 919 (void) close(STDOUT_FILENO); 920 (void) close(STDERR_FILENO); 921 (void) close(pv[1]); 922 if (do_encrypt) { 923 (void) close(pw[1]); 924 (void) close(px[0]); 925 } else { 926 (void) close(f); 927 } 928 929 (void) FD_ZERO(&readfrom); 930 931 FD_SET(pv[0], &readfrom); 932 if (do_encrypt) { 933 FD_SET(pw[0], &readfrom); 934 FD_SET(f, &readfrom); 935 } 936 if (port) 937 FD_SET(s, &readfrom); 938 939 /* read f (net), write to px[1] (child stdin) */ 940 /* read pw[0] (child stdout), write to f (net) */ 941 /* read s (alt. channel), signal child */ 942 /* read pv[0] (child stderr), write to s */ 943 if (ioctl(pv[0], FIONBIO, (char *)&one) == -1) 944 syslog(LOG_INFO, "ioctl FIONBIO: %m"); 945 if (do_encrypt && 946 ioctl(pw[0], FIONBIO, (char *)&one) == -1) 947 syslog(LOG_INFO, "ioctl FIONBIO: %m"); 948 do { 949 ready = readfrom; 950 if (select(FD_SETSIZE, &ready, NULL, 951 NULL, NULL) < 0) { 952 if (errno == EINTR) { 953 continue; 954 } else { 955 break; 956 } 957 } 958 /* 959 * Read from child stderr, write to net 960 */ 961 if (port && FD_ISSET(pv[0], &ready)) { 962 errno = 0; 963 cc = read(pv[0], buf, sizeof (buf)); 964 if (cc <= 0) { 965 (void) shutdown(s, 2); 966 FD_CLR(pv[0], &readfrom); 967 } else { 968 (void) deswrite(s, buf, cc, 1); 969 } 970 } 971 /* 972 * Read from alternate channel, signal child 973 */ 974 if (port && FD_ISSET(s, &ready)) { 975 if ((int)desread(s, &sig, 1, 1) <= 0) 976 FD_CLR(s, &readfrom); 977 else 978 (void) killpg(pid, sig); 979 } 980 /* 981 * Read from child stdout, write to net 982 */ 983 if (do_encrypt && FD_ISSET(pw[0], &ready)) { 984 errno = 0; 985 cc = read(pw[0], buf, sizeof (buf)); 986 if (cc <= 0) { 987 (void) shutdown(f, 2); 988 FD_CLR(pw[0], &readfrom); 989 } else { 990 (void) deswrite(f, buf, cc, 0); 991 } 992 } 993 /* 994 * Read from the net, write to child stdin 995 */ 996 if (do_encrypt && FD_ISSET(f, &ready)) { 997 errno = 0; 998 cc = desread(f, buf, sizeof (buf), 0); 999 if (cc <= 0) { 1000 (void) close(px[1]); 1001 FD_CLR(f, &readfrom); 1002 } else { 1003 int wcc; 1004 wcc = write(px[1], buf, cc); 1005 if (wcc == -1) { 1006 /* 1007 * pipe closed, 1008 * don't read any 1009 * more 1010 * 1011 * might check for 1012 * EPIPE 1013 */ 1014 (void) close(px[1]); 1015 FD_CLR(f, &readfrom); 1016 } else if (wcc != cc) { 1017 /* CSTYLED */ 1018 syslog(LOG_INFO, gettext("only wrote %d/%d to child"), 1019 wcc, cc); 1020 } 1021 } 1022 } 1023 } while ((port && FD_ISSET(s, &readfrom)) || 1024 (port && FD_ISSET(pv[0], &readfrom)) || 1025 (do_encrypt && FD_ISSET(f, &readfrom)) || 1026 (do_encrypt && FD_ISSET(pw[0], &readfrom))); 1027 #ifdef DEBUG 1028 syslog(LOG_INFO, "Shell process completed."); 1029 #endif /* DEBUG */ 1030 if (ccache) 1031 (void) pam_close_session(pamh, 0); 1032 (void) pam_end(pamh, PAM_SUCCESS); 1033 1034 exit(0); 1035 } /* End of Parent block */ 1036 1037 (void) setsid(); /* Should be the same as above. */ 1038 (void) close(pv[0]); 1039 (void) dup2(pv[1], 2); 1040 (void) close(pv[1]); 1041 if (port) 1042 (void) close(s); 1043 if (do_encrypt) { 1044 (void) close(f); 1045 (void) close(pw[0]); 1046 (void) close(px[1]); 1047 1048 (void) dup2(px[0], 0); 1049 (void) dup2(pw[1], 1); 1050 1051 (void) close(px[0]); 1052 (void) close(pw[1]); 1053 } 1054 } 1055 1056 if (*pwd->pw_shell == '\0') 1057 pwd->pw_shell = "/bin/sh"; 1058 if (!do_encrypt) 1059 (void) close(f); 1060 /* 1061 * write audit record before making uid switch 1062 */ 1063 (void) audit_rshd_success(hostname, remuser, locuser, cmdbuf); /* BSM */ 1064 1065 /* set the real (and effective) GID */ 1066 if (setgid(pwd->pw_gid) == -1) { 1067 error("Invalid gid.\n"); 1068 (void) pam_end(pamh, PAM_ABORT); 1069 exit(1); 1070 } 1071 1072 /* 1073 * Initialize the supplementary group access list. 1074 */ 1075 if (strlen(locuser) == 0) { 1076 error("No local user.\n"); 1077 (void) pam_end(pamh, PAM_ABORT); 1078 exit(1); 1079 } 1080 if (initgroups(locuser, pwd->pw_gid) == -1) { 1081 error("Initgroup failed.\n"); 1082 (void) pam_end(pamh, PAM_ABORT); 1083 exit(1); 1084 } 1085 1086 if ((v = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 1087 error("Insufficient credentials.\n"); 1088 (void) pam_end(pamh, v); 1089 exit(1); 1090 } 1091 1092 /* set the real (and effective) UID */ 1093 if (setuid(pwd->pw_uid) == -1) { 1094 error("Invalid uid.\n"); 1095 (void) pam_end(pamh, PAM_ABORT); 1096 exit(1); 1097 } 1098 1099 /* Change directory only after becoming the appropriate user. */ 1100 if (chdir(pwd->pw_dir) < 0) { 1101 (void) chdir("/"); 1102 if (krb5auth_flag > 0) { 1103 syslog(LOG_ERR, "Principal %s (%s@%s) for local user" 1104 " %s has no home directory.", 1105 kremuser, remuser, hostname, locuser); 1106 error("No remote directory.\n"); 1107 goto signout; 1108 } 1109 #ifdef notdef 1110 error("No remote directory.\n"); 1111 exit(1); 1112 #endif 1113 } 1114 1115 path = (pwd->pw_uid == 0) ? rootpath : userpath; 1116 1117 /* 1118 * Space for the following environment variables are dynamically 1119 * allocated because their lengths are not known before calling 1120 * getpwnam(). 1121 */ 1122 homedir_len = strlen(pwd->pw_dir) + strlen(homestr) + 1; 1123 shell_len = strlen(pwd->pw_shell) + strlen(shellstr) + 1; 1124 username_len = strlen(pwd->pw_name) + strlen(userstr) + 1; 1125 homedir = (char *)malloc(homedir_len); 1126 shell = (char *)malloc(shell_len); 1127 username = (char *)malloc(username_len); 1128 if (homedir == NULL || shell == NULL || username == NULL) { 1129 perror("malloc"); 1130 exit(1); 1131 } 1132 (void) snprintf(homedir, homedir_len, "%s%s", homestr, pwd->pw_dir); 1133 (void) snprintf(shell, shell_len, "%s%s", shellstr, pwd->pw_shell); 1134 (void) snprintf(username, username_len, "%s%s", userstr, pwd->pw_name); 1135 1136 /* Pass timezone to executed command. */ 1137 if (tzenv = getenv("TZ")) { 1138 tz_len = strlen(tzenv) + strlen(tzstr) + 1; 1139 tz = malloc(tz_len); 1140 if (tz != NULL) 1141 (void) snprintf(tz, tz_len, "%s%s", tzstr, tzenv); 1142 } 1143 1144 add_to_envinit(homedir); 1145 add_to_envinit(shell); 1146 add_to_envinit(path); 1147 add_to_envinit(username); 1148 add_to_envinit(tz); 1149 1150 if (krb5auth_flag > 0) { 1151 int length; 1152 char *buffer; 1153 1154 /* 1155 * If we have KRB5CCNAME set, then copy into the child's 1156 * environment. This can't really have a fixed position 1157 * because `tz' may or may not be set. 1158 */ 1159 if (getenv("KRB5CCNAME")) { 1160 length = (int)strlen(getenv("KRB5CCNAME")) + 1161 (int)strlen("KRB5CCNAME=") + 1; 1162 buffer = (char *)malloc(length); 1163 1164 if (buffer) { 1165 (void) snprintf(buffer, length, "KRB5CCNAME=%s", 1166 getenv("KRB5CCNAME")); 1167 add_to_envinit(buffer); 1168 } 1169 } { 1170 /* These two are covered by ADDRPAD */ 1171 length = strlen(inet_ntoa(localaddr.sin_addr)) + 1 + 1172 strlen("KRB5LOCALADDR="); 1173 (void) snprintf(local_addr, length, "KRB5LOCALADDR=%s", 1174 inet_ntoa(localaddr.sin_addr)); 1175 add_to_envinit(local_addr); 1176 1177 length = strlen(inet_ntoa(sin->sin_addr)) + 1 + 1178 strlen("KRB5REMOTEADDR="); 1179 (void) snprintf(remote_addr, length, 1180 "KRB5REMOTEADDR=%s", inet_ntoa(sin->sin_addr)); 1181 add_to_envinit(remote_addr); 1182 } 1183 1184 /* 1185 * If we do anything else, make sure there is 1186 * space in the array. 1187 */ 1188 for (cnt = 0; cnt < num_env; cnt++) { 1189 char *buf; 1190 1191 if (getenv(save_env[cnt])) { 1192 length = (int)strlen(getenv(save_env[cnt])) + 1193 (int)strlen(save_env[cnt]) + 2; 1194 1195 buf = (char *)malloc(length); 1196 if (buf) { 1197 (void) snprintf(buf, length, "%s=%s", 1198 save_env[cnt], 1199 getenv(save_env[cnt])); 1200 add_to_envinit(buf); 1201 } 1202 } 1203 } 1204 1205 } 1206 1207 /* 1208 * add PAM environment variables set by modules 1209 * -- only allowed 16 (PAM_ENV_ELIM) 1210 * -- check to see if the environment variable is legal 1211 */ 1212 if ((pam_env = pam_getenvlist(pamh)) != 0) { 1213 while (pam_env[idx] != 0) { 1214 if (idx < PAM_ENV_ELIM && 1215 legalenvvar(pam_env[idx])) { 1216 add_to_envinit(pam_env[idx]); 1217 } 1218 idx++; 1219 } 1220 } 1221 1222 (void) pam_end(pamh, PAM_SUCCESS); 1223 1224 /* 1225 * Pick up locale environment variables, if any. 1226 */ 1227 lenvp = renvp; 1228 while (*lenvp != NULL) { 1229 int index; 1230 1231 for (index = 0; localeenv[index] != NULL; index++) 1232 /* 1233 * locale_envmatch() returns 1 if 1234 * *lenvp is localenev[index] and valid. 1235 */ 1236 if (locale_envmatch(localeenv[index], *lenvp)) { 1237 add_to_envinit(*lenvp); 1238 break; 1239 } 1240 1241 lenvp++; 1242 } 1243 1244 cp = strrchr(pwd->pw_shell, '/'); 1245 if (cp != NULL) 1246 cp++; 1247 else 1248 cp = pwd->pw_shell; 1249 /* 1250 * rdist has been moved to /usr/bin, so /usr/ucb/rdist might not 1251 * be present on a system. So if it doesn't exist we fall back 1252 * and try for it in /usr/bin. We take care to match the space 1253 * after the name because the only purpose of this is to protect 1254 * the internal call from old rdist's, not humans who type 1255 * "rsh foo /usr/ucb/rdist". 1256 */ 1257 #define RDIST_PROG_NAME "/usr/ucb/rdist -Server" 1258 if (strncmp(cmdbuf, RDIST_PROG_NAME, strlen(RDIST_PROG_NAME)) == 0) { 1259 if (stat("/usr/ucb/rdist", &statb) != 0) { 1260 (void) strncpy(cmdbuf + 5, "bin", 3); 1261 } 1262 } 1263 1264 #ifdef DEBUG 1265 syslog(LOG_NOTICE, "rshd: cmdbuf = %s", cmdbuf); 1266 if (do_encrypt) 1267 syslog(LOG_NOTICE, "rshd: cmd to be exec'ed = %s", 1268 ((char *)cmdbuf + 3)); 1269 #endif /* DEBUG */ 1270 1271 if (do_encrypt && (strncmp(cmdbuf, "-x ", 3) == 0)) { 1272 (void) execle(pwd->pw_shell, cp, "-c", (char *)cmdbuf + 3, 1273 NULL, envinit); 1274 } else { 1275 (void) execle(pwd->pw_shell, cp, "-c", cmdbuf, NULL, 1276 envinit); 1277 } 1278 1279 perror(pwd->pw_shell); 1280 exit(1); 1281 1282 signout: 1283 if (ccache) 1284 (void) pam_close_session(pamh, 0); 1285 ccache = NULL; 1286 (void) pam_end(pamh, PAM_ABORT); 1287 exit(1); 1288 } 1289 1290 static void 1291 getstr(fd, buf, cnt, err) 1292 int fd; 1293 char *buf; 1294 int cnt; 1295 char *err; 1296 { 1297 char c; 1298 1299 do { 1300 if (read(fd, &c, 1) != 1) 1301 exit(1); 1302 if (cnt-- == 0) { 1303 error("%s too long\n", err); 1304 exit(1); 1305 } 1306 *buf++ = c; 1307 } while (c != 0); 1308 } 1309 1310 /*PRINTFLIKE1*/ 1311 static void 1312 error(char *fmt, ...) 1313 { 1314 va_list ap; 1315 char buf[RSHD_BUFSIZ]; 1316 1317 buf[0] = 1; 1318 va_start(ap, fmt); 1319 (void) vsnprintf(&buf[1], sizeof (buf) - 1, fmt, ap); 1320 va_end(ap); 1321 (void) write(STDERR_FILENO, buf, strlen(buf)); 1322 } 1323 1324 static char *illegal[] = { 1325 "SHELL=", 1326 "HOME=", 1327 "LOGNAME=", 1328 #ifndef NO_MAIL 1329 "MAIL=", 1330 #endif 1331 "CDPATH=", 1332 "IFS=", 1333 "PATH=", 1334 "USER=", 1335 "TZ=", 1336 0 1337 }; 1338 1339 /* 1340 * legalenvvar - can PAM modules insert this environmental variable? 1341 */ 1342 1343 static int 1344 legalenvvar(char *s) 1345 { 1346 register char **p; 1347 1348 for (p = illegal; *p; p++) 1349 if (strncmp(s, *p, strlen(*p)) == 0) 1350 return (0); 1351 1352 if (s[0] == 'L' && s[1] == 'D' && s[2] == '_') 1353 return (0); 1354 1355 return (1); 1356 } 1357 1358 /* 1359 * Add a string to the environment of the new process. 1360 */ 1361 1362 static void 1363 add_to_envinit(char *string) 1364 { 1365 /* 1366 * Reserve space for 2 * 8 = 16 environment entries initially which 1367 * should be enough to avoid reallocation of "envinit" in most cases. 1368 */ 1369 static int size = 8; 1370 static int index = 0; 1371 1372 if (string == NULL) 1373 return; 1374 1375 if ((envinit == NULL) || (index == size)) { 1376 size *= 2; 1377 envinit = realloc(envinit, (size + 1) * sizeof (char *)); 1378 if (envinit == NULL) { 1379 perror("malloc"); 1380 exit(1); 1381 } 1382 } 1383 1384 envinit[index++] = string; 1385 envinit[index] = NULL; 1386 } 1387 1388 /* 1389 * Check if lenv and penv matches or not. 1390 */ 1391 static int 1392 locale_envmatch(char *lenv, char *penv) 1393 { 1394 while ((*lenv == *penv) && (*lenv != '\0') && (*penv != '=')) { 1395 lenv++; 1396 penv++; 1397 } 1398 1399 /* 1400 * '/' is eliminated for security reason. 1401 */ 1402 return ((*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')); 1403 } 1404 1405 #ifndef KRB_SENDAUTH_VLEN 1406 #define KRB_SENDAUTH_VLEN 8 /* length for version strings */ 1407 #endif 1408 1409 /* MUST be KRB_SENDAUTH_VLEN chars */ 1410 #define KRB_SENDAUTH_VERS "AUTHV0.1" 1411 #define SIZEOF_INADDR sizeof (struct in_addr) 1412 1413 static krb5_error_code 1414 recvauth(int netf, int *valid_checksum) 1415 { 1416 krb5_auth_context auth_context = NULL; 1417 krb5_error_code status; 1418 struct sockaddr_in laddr; 1419 int len; 1420 krb5_data inbuf; 1421 krb5_authenticator *authenticator; 1422 krb5_ticket *ticket; 1423 krb5_rcache rcache; 1424 krb5_data version; 1425 krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ 1426 krb5_data desinbuf; 1427 krb5_data desoutbuf; 1428 char des_inbuf[2 * RSHD_BUFSIZ]; 1429 /* needs to be > largest read size */ 1430 char des_outbuf[2 * RSHD_BUFSIZ + 4]; 1431 /* needs to be > largest write size */ 1432 1433 *valid_checksum = 0; 1434 len = sizeof (laddr); 1435 1436 if (getsockname(netf, (struct sockaddr *)&laddr, &len)) { 1437 exit(1); 1438 } 1439 1440 if (status = krb5_auth_con_init(bsd_context, &auth_context)) 1441 return (status); 1442 1443 if (status = krb5_auth_con_genaddrs(bsd_context, auth_context, netf, 1444 KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)) 1445 return (status); 1446 1447 status = krb5_auth_con_getrcache(bsd_context, auth_context, &rcache); 1448 if (status) 1449 return (status); 1450 1451 if (!rcache) { 1452 krb5_principal server; 1453 1454 status = krb5_sname_to_principal(bsd_context, 0, 0, 1455 KRB5_NT_SRV_HST, &server); 1456 if (status) 1457 return (status); 1458 1459 status = krb5_get_server_rcache(bsd_context, 1460 krb5_princ_component(bsd_context, server, 0), 1461 &rcache); 1462 krb5_free_principal(bsd_context, server); 1463 if (status) 1464 return (status); 1465 1466 status = krb5_auth_con_setrcache(bsd_context, auth_context, 1467 rcache); 1468 if (status) 1469 return (status); 1470 } 1471 1472 status = krb5_recvauth_version(bsd_context, &auth_context, &netf, 1473 NULL, /* Specify daemon principal */ 1474 0, /* no flags */ 1475 keytab, /* normally NULL to use v5srvtab */ 1476 &ticket, /* return ticket */ 1477 &version); /* application version string */ 1478 1479 1480 if (status) { 1481 getstr(netf, locuser, sizeof (locuser), "locuser"); 1482 getstr(netf, cmdbuf, sizeof (cmdbuf), "command"); 1483 getstr(netf, remuser, sizeof (locuser), "remuser"); 1484 return (status); 1485 } 1486 getstr(netf, locuser, sizeof (locuser), "locuser"); 1487 getstr(netf, cmdbuf, sizeof (cmdbuf), "command"); 1488 1489 /* Must be V5 */ 1490 1491 kcmd_protocol = KCMD_UNKNOWN_PROTOCOL; 1492 if (version.length != 9 || version.data == NULL) { 1493 syslog(LOG_ERR, "bad application version length"); 1494 error(gettext("bad application version length\n")); 1495 exit(1); 1496 } 1497 if (strncmp(version.data, "KCMDV0.1", 9) == 0) { 1498 kcmd_protocol = KCMD_OLD_PROTOCOL; 1499 } else if (strncmp(version.data, "KCMDV0.2", 9) == 0) { 1500 kcmd_protocol = KCMD_NEW_PROTOCOL; 1501 } else { 1502 syslog(LOG_ERR, "Unrecognized KCMD protocol (%s)", 1503 (char *)version.data); 1504 error(gettext("Unrecognized KCMD protocol (%s)"), 1505 (char *)version.data); 1506 exit(1); 1507 } 1508 getstr(netf, remuser, sizeof (locuser), "remuser"); 1509 1510 if ((status = krb5_unparse_name(bsd_context, ticket->enc_part2->client, 1511 &kremuser))) 1512 return (status); 1513 1514 if ((status = krb5_copy_principal(bsd_context, 1515 ticket->enc_part2->client, &client))) 1516 return (status); 1517 1518 1519 if (checksum_required && (kcmd_protocol == KCMD_OLD_PROTOCOL)) { 1520 if ((status = krb5_auth_con_getauthenticator(bsd_context, 1521 auth_context, &authenticator))) 1522 return (status); 1523 1524 if (authenticator->checksum && checksum_required) { 1525 struct sockaddr_in adr; 1526 int adr_length = sizeof (adr); 1527 int chksumsize = strlen(cmdbuf) + strlen(locuser) + 32; 1528 krb5_data input; 1529 krb5_keyblock key; 1530 1531 char *chksumbuf = (char *)malloc(chksumsize); 1532 1533 if (chksumbuf == 0) 1534 goto error_cleanup; 1535 if (getsockname(netf, (struct sockaddr *)&adr, 1536 &adr_length) != 0) 1537 goto error_cleanup; 1538 1539 (void) snprintf(chksumbuf, chksumsize, "%u:", 1540 ntohs(adr.sin_port)); 1541 if (strlcat(chksumbuf, cmdbuf, 1542 chksumsize) >= chksumsize) { 1543 syslog(LOG_ERR, "cmd buffer too long."); 1544 free(chksumbuf); 1545 return (-1); 1546 } 1547 if (strlcat(chksumbuf, locuser, 1548 chksumsize) >= chksumsize) { 1549 syslog(LOG_ERR, "locuser too long."); 1550 free(chksumbuf); 1551 return (-1); 1552 } 1553 1554 input.data = chksumbuf; 1555 input.length = strlen(chksumbuf); 1556 key.magic = ticket->enc_part2->session->magic; 1557 key.enctype = ticket->enc_part2->session->enctype; 1558 key.contents = ticket->enc_part2->session->contents; 1559 key.length = ticket->enc_part2->session->length; 1560 1561 status = krb5_c_verify_checksum(bsd_context, 1562 &key, 0, &input, authenticator->checksum, 1563 (unsigned int *)valid_checksum); 1564 1565 if (status == 0 && *valid_checksum == 0) 1566 status = KRB5KRB_AP_ERR_BAD_INTEGRITY; 1567 error_cleanup: 1568 if (chksumbuf) 1569 krb5_xfree(chksumbuf); 1570 if (status) { 1571 krb5_free_authenticator(bsd_context, 1572 authenticator); 1573 return (status); 1574 } 1575 } 1576 krb5_free_authenticator(bsd_context, authenticator); 1577 } 1578 1579 1580 if ((strncmp(cmdbuf, "-x ", 3) == 0)) { 1581 if (krb5_privacy_allowed()) { 1582 do_encrypt = 1; 1583 } else { 1584 syslog(LOG_ERR, "rshd: Encryption not supported"); 1585 error("rshd: Encryption not supported. \n"); 1586 exit(2); 1587 } 1588 1589 status = krb5_auth_con_getremotesubkey(bsd_context, 1590 auth_context, 1591 &sessionkey); 1592 if (status) { 1593 syslog(LOG_ERR, "Error getting KRB5 session subkey"); 1594 error(gettext("Error getting KRB5 session subkey")); 1595 exit(1); 1596 } 1597 /* 1598 * The "new" protocol requires that a subkey be sent. 1599 */ 1600 if (sessionkey == NULL && kcmd_protocol == KCMD_NEW_PROTOCOL) { 1601 syslog(LOG_ERR, "No KRB5 session subkey sent"); 1602 error(gettext("No KRB5 session subkey sent")); 1603 exit(1); 1604 } 1605 /* 1606 * The "old" protocol does not permit an authenticator subkey. 1607 * The key is taken from the ticket instead (see below). 1608 */ 1609 if (sessionkey != NULL && kcmd_protocol == KCMD_OLD_PROTOCOL) { 1610 syslog(LOG_ERR, "KRB5 session subkey not permitted " 1611 "with old KCMD protocol"); 1612 error(gettext("KRB5 session subkey not permitted " 1613 "with old KCMD protocol")); 1614 exit(1); 1615 } 1616 /* 1617 * If no key at this point, use the session key from 1618 * the ticket. 1619 */ 1620 if (sessionkey == NULL) { 1621 /* 1622 * Save the session key so we can configure the crypto 1623 * module later. 1624 */ 1625 status = krb5_copy_keyblock(bsd_context, 1626 ticket->enc_part2->session, 1627 &sessionkey); 1628 if (status) { 1629 syslog(LOG_ERR, "krb5_copy_keyblock failed"); 1630 error(gettext("krb5_copy_keyblock failed")); 1631 exit(1); 1632 } 1633 } 1634 /* 1635 * If session key still cannot be found, we must 1636 * exit because encryption is required here 1637 * when encr_flag (-x) is set. 1638 */ 1639 if (sessionkey == NULL) { 1640 syslog(LOG_ERR, "Could not find an encryption key"); 1641 error(gettext("Could not find an encryption key")); 1642 exit(1); 1643 } 1644 1645 /* 1646 * Initialize parameters/buffers for desread & deswrite here. 1647 */ 1648 desinbuf.data = des_inbuf; 1649 desoutbuf.data = des_outbuf; 1650 desinbuf.length = sizeof (des_inbuf); 1651 desoutbuf.length = sizeof (des_outbuf); 1652 1653 eblock.crypto_entry = sessionkey->enctype; 1654 eblock.key = (krb5_keyblock *)sessionkey; 1655 1656 init_encrypt(do_encrypt, bsd_context, kcmd_protocol, 1657 &desinbuf, &desoutbuf, SERVER, &eblock); 1658 } 1659 1660 ticket->enc_part2->session = 0; 1661 1662 if ((status = krb5_read_message(bsd_context, (krb5_pointer) & netf, 1663 &inbuf))) { 1664 error(gettext("Error reading message: %s\n"), 1665 error_message(status)); 1666 exit(1); 1667 } 1668 1669 if (inbuf.length) { 1670 krb5_creds **creds = NULL; 1671 1672 /* Forwarding being done, read creds */ 1673 if ((status = krb5_rd_cred(bsd_context, 1674 auth_context, &inbuf, &creds, 1675 NULL))) { 1676 error("Can't get forwarded credentials: %s\n", 1677 error_message(status)); 1678 exit(1); 1679 } 1680 1681 /* Store the forwarded creds in the ccache */ 1682 if ((status = store_forw_creds(bsd_context, 1683 creds, ticket, locuser, 1684 &ccache))) { 1685 error("Can't store forwarded credentials: %s\n", 1686 error_message(status)); 1687 exit(1); 1688 } 1689 krb5_free_creds(bsd_context, *creds); 1690 } 1691 1692 krb5_free_ticket(bsd_context, ticket); 1693 return (0); 1694 } 1695 1696 static void 1697 usage(void) 1698 { 1699 (void) fprintf(stderr, gettext("%s: rshd [-k5eciU] " 1700 "[-P path] [-M realm] [-s tos] " 1701 #ifdef DEBUG 1702 "[-D port] " 1703 #endif /* DEBUG */ 1704 "[-S keytab]"), gettext("usage")); 1705 1706 syslog(LOG_ERR, "%s: rshd [-k5eciU] [-P path] [-M realm] [-s tos] " 1707 #ifdef DEBUG 1708 "[-D port] " 1709 #endif /* DEBUG */ 1710 "[-S keytab]", gettext("usage")); 1711 } 1712