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 2008 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 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 { int t = open("/dev/tty", 2); 480 if (t >= 0) { 481 (void) setsid(); 482 (void) close(t); 483 } 484 } 485 #endif 486 if (fromp->ss_family == AF_INET) { 487 sin = (struct sockaddr_in *)fromp; 488 port = ntohs((ushort_t)sin->sin_port); 489 fromplen = sizeof (struct sockaddr_in); 490 } else if (fromp->ss_family == AF_INET6) { 491 sin6 = (struct sockaddr_in6 *)fromp; 492 port = ntohs((ushort_t)sin6->sin6_port); 493 fromplen = sizeof (struct sockaddr_in6); 494 } else { 495 syslog(LOG_ERR, "wrong address family\n"); 496 exit(1); 497 } 498 499 if (fromp->ss_family == AF_INET6) { 500 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 501 struct in_addr ipv4_addr; 502 503 IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &ipv4_addr); 504 (void) inet_ntop(AF_INET, &ipv4_addr, abuf, 505 sizeof (abuf)); 506 } else { 507 (void) inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, 508 sizeof (abuf)); 509 } 510 } else if (fromp->ss_family == AF_INET) { 511 (void) inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof (abuf)); 512 } 513 514 sin_len = sizeof (struct sockaddr_in); 515 if (getsockname(f, (struct sockaddr *)&localaddr, &sin_len) < 0) { 516 perror("getsockname"); 517 exit(1); 518 } 519 520 netf = f; 521 522 bad_port = (port >= IPPORT_RESERVED || 523 port < (uint_t)(IPPORT_RESERVED/2)); 524 525 /* Get the name of the client side host to use later */ 526 no_name = (getnameinfo((const struct sockaddr *) fromp, fromplen, 527 hostname, sizeof (hostname), NULL, 0, 0) != 0); 528 529 if (bad_port || no_name != 0) { 530 /* 531 * If there is no host name available then use the 532 * IP address to identify the host in the PAM call 533 * below. Do the same if a bad port was used, to 534 * prevent untrustworthy authentication. 535 */ 536 (void) strlcpy(hostname, abuf, sizeof (hostname)); 537 } 538 539 if (no_name != 0) { 540 /* 541 * If the '-U' option was given on the cmd line, 542 * we must be able to lookup the hostname 543 */ 544 if (resolve_hostname) { 545 syslog(LOG_ERR, "rshd: Couldn't resolve your " 546 "address into a host name.\r\n Please " 547 "contact your net administrator"); 548 exit(1); 549 } 550 } else { 551 /* 552 * Even if getnameinfo() succeeded, we still have to check 553 * for spoofing. 554 */ 555 check_address("rshd", fromp, sin, sin6, abuf, hostname, 556 sizeof (hostname)); 557 } 558 559 if (!krb5auth_flag && bad_port) { 560 if (no_name) 561 syslog(LOG_NOTICE, "connection from %s - " 562 "bad port\n", abuf); 563 else 564 syslog(LOG_NOTICE, "connection from %s (%s) - " 565 "bad port\n", hostname, abuf); 566 exit(1); 567 } 568 569 (void) alarm(60); 570 port = 0; 571 for (;;) { 572 char c; 573 if ((cc = read(f, &c, 1)) != 1) { 574 if (cc < 0) 575 syslog(LOG_NOTICE, "read: %m"); 576 (void) shutdown(f, 1+1); 577 exit(1); 578 } 579 if (c == 0) 580 break; 581 port = port * 10 + c - '0'; 582 } 583 (void) alarm(0); 584 if (port != 0) { 585 int lport = 0; 586 struct sockaddr_storage ctl_addr; 587 int addrlen; 588 589 (void) memset(&ctl_addr, 0, sizeof (ctl_addr)); 590 addrlen = sizeof (ctl_addr); 591 if (getsockname(f, (struct sockaddr *)&ctl_addr, 592 &addrlen) < 0) { 593 syslog(LOG_ERR, "getsockname: %m"); 594 exit(1); 595 } 596 get_port: 597 /* 598 * 0 means that rresvport_addr() will bind to a port in 599 * the anonymous priviledged port range. 600 */ 601 if (krb5auth_flag) { 602 /* 603 * Kerberos does not support IPv6 yet. 604 */ 605 lport = IPPORT_RESERVED - 1; 606 } 607 s = rresvport_addr(&lport, &ctl_addr); 608 609 if (s < 0) { 610 syslog(LOG_ERR, "can't get stderr port: %m"); 611 exit(1); 612 } 613 if (!krb5auth_flag && (port >= IPPORT_RESERVED)) { 614 syslog(LOG_ERR, "2nd port not reserved\n"); 615 exit(1); 616 } 617 if (fromp->ss_family == AF_INET) { 618 sin->sin_port = htons((ushort_t)port); 619 } else if (fromp->ss_family == AF_INET6) { 620 sin6->sin6_port = htons((ushort_t)port); 621 } 622 if (connect(s, (struct sockaddr *)fromp, fromplen) < 0) { 623 if (errno == EADDRINUSE) { 624 (void) close(s); 625 goto get_port; 626 } 627 syslog(LOG_INFO, "connect second port: %m"); 628 exit(1); 629 } 630 } 631 (void) dup2(f, 0); 632 (void) dup2(f, 1); 633 (void) dup2(f, 2); 634 635 #ifdef DEBUG 636 syslog(LOG_NOTICE, "rshd: Client hostname = %s", hostname); 637 if (debug_port) 638 syslog(LOG_NOTICE, "rshd: Debug port is %d", debug_port); 639 if (krb5auth_flag > 0) 640 syslog(LOG_NOTICE, "rshd: Kerberos mode is ON"); 641 else 642 syslog(LOG_NOTICE, "rshd: Kerberos mode is OFF"); 643 #endif /* DEBUG */ 644 645 if (krb5auth_flag > 0) { 646 if ((status = recvauth(f, &valid_checksum))) { 647 syslog(LOG_ERR, gettext("Kerberos Authentication " 648 "failed \n")); 649 error("Authentication failed: %s\n", 650 error_message(status)); 651 (void) audit_rshd_fail("Kerberos Authentication " 652 "failed", hostname, remuser, locuser, cmdbuf); 653 exit(1); 654 } 655 656 if (checksum_required && !valid_checksum && 657 kcmd_protocol == KCMD_OLD_PROTOCOL) { 658 syslog(LOG_WARNING, "Client did not supply required" 659 " checksum--connection rejected."); 660 error("Client did not supply required" 661 "checksum--connection rejected.\n"); 662 (void) audit_rshd_fail("Client did not supply required" 663 " checksum--connection rejected.", hostname, 664 remuser, locuser, cmdbuf); /* BSM */ 665 goto signout; 666 } 667 668 /* 669 * Authentication has succeeded, we now need 670 * to check authorization. 671 * 672 * krb5_kuserok returns 1 if OK. 673 */ 674 if (client && krb5_kuserok(bsd_context, client, locuser)) { 675 auth_sent |= AUTH_KRB5; 676 } else { 677 syslog(LOG_ERR, "Principal %s (%s@%s) for local user " 678 "%s failed krb5_kuserok.\n", 679 kremuser, remuser, hostname, locuser); 680 } 681 } else { 682 getstr(netf, remuser, sizeof (remuser), "remuser"); 683 getstr(netf, locuser, sizeof (locuser), "locuser"); 684 getstr(netf, cmdbuf, sizeof (cmdbuf), "command"); 685 } 686 687 #ifdef DEBUG 688 syslog(LOG_NOTICE, "rshd: locuser = %s, remuser = %s, cmdbuf = %s", 689 locuser, remuser, cmdbuf); 690 #endif /* DEBUG */ 691 692 /* 693 * Note that there is no rsh conv functions at present. 694 */ 695 if (krb5auth_flag > 0) { 696 if ((err = pam_start("krsh", locuser, NULL, &pamh)) 697 != PAM_SUCCESS) { 698 syslog(LOG_ERR, "pam_start() failed: %s\n", 699 pam_strerror(0, err)); 700 exit(1); 701 } 702 } 703 else 704 { 705 if ((err = pam_start("rsh", locuser, NULL, &pamh)) 706 != PAM_SUCCESS) { 707 syslog(LOG_ERR, "pam_start() failed: %s\n", 708 pam_strerror(0, err)); 709 exit(1); 710 } 711 } 712 if ((err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS) { 713 syslog(LOG_ERR, "pam_set_item() failed: %s\n", 714 pam_strerror(pamh, err)); 715 exit(1); 716 } 717 if ((err = pam_set_item(pamh, PAM_RUSER, remuser)) != PAM_SUCCESS) { 718 syslog(LOG_ERR, "pam_set_item() failed: %s\n", 719 pam_strerror(pamh, err)); 720 exit(1); 721 } 722 723 pwd = getpwnam(locuser); 724 shpwd = getspnam(locuser); 725 if ((pwd == NULL) || (shpwd == NULL)) { 726 if (krb5auth_flag > 0) 727 syslog(LOG_ERR, "Principal %s (%s@%s) for local user " 728 "%s has no account.\n", kremuser, remuser, 729 hostname, locuser); 730 error("permission denied.\n"); 731 (void) audit_rshd_fail("Login incorrect", hostname, 732 remuser, locuser, cmdbuf); /* BSM */ 733 exit(1); 734 } 735 736 if (krb5auth_flag > 0) { 737 (void) snprintf(repository, sizeof (repository), 738 KRB5_REPOSITORY_NAME); 739 /* 740 * We currently only support special handling of the 741 * KRB5 PAM repository 742 */ 743 if (strlen(locuser) != 0) { 744 krb5_repository_data_t krb5_data; 745 pam_repository_t pam_rep_data; 746 747 krb5_data.principal = locuser; 748 krb5_data.flags = SUNW_PAM_KRB5_ALREADY_AUTHENTICATED; 749 750 pam_rep_data.type = repository; 751 pam_rep_data.scope = (void *)&krb5_data; 752 pam_rep_data.scope_len = sizeof (krb5_data); 753 754 (void) pam_set_item(pamh, PAM_REPOSITORY, 755 (void *)&pam_rep_data); 756 } 757 } 758 759 if (shpwd->sp_pwdp != 0) { 760 if (*shpwd->sp_pwdp != '\0') { 761 if ((v = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { 762 error("permission denied\n"); 763 (void) audit_rshd_fail("Permission denied", 764 hostname, remuser, locuser, cmdbuf); 765 (void) pam_end(pamh, v); 766 exit(1); 767 } 768 } else { 769 int flags; 770 char *p; 771 /* 772 * maintain 2.1 and 4.* and BSD semantics with 773 * anonymous rshd unless PASSREQ is set to YES in 774 * /etc/default/login: then we deny logins with empty 775 * passwords. 776 */ 777 if (defopen(_PATH_DEFAULT_LOGIN) == 0) { 778 flags = defcntl(DC_GETFLAGS, 0); 779 TURNOFF(flags, DC_CASE); 780 (void) defcntl(DC_SETFLAGS, flags); 781 782 if ((p = defread("PASSREQ=")) != NULL && 783 strcasecmp(p, "YES") == 0) { 784 error("permission denied\n"); 785 (void) audit_rshd_fail( 786 "Permission denied", hostname, 787 remuser, locuser, cmdbuf); 788 (void) pam_end(pamh, PAM_ABORT); 789 (void) defopen(NULL); 790 syslog(LOG_AUTH|LOG_NOTICE, 791 "empty password not allowed for " 792 "%s from %s.", locuser, hostname); 793 exit(1); 794 } 795 (void) defopen(NULL); 796 } 797 /* 798 * /etc/default/login not found or PASSREQ not set 799 * to YES. Allow logins without passwords. 800 */ 801 } 802 } 803 804 if (krb5auth_flag > 0) { 805 if (require_encrypt && (!do_encrypt)) { 806 error("You must use encryption.\n"); 807 (void) audit_rshd_fail("You must use encryption.", 808 hostname, remuser, locuser, cmdbuf); /* BSM */ 809 goto signout; 810 } 811 812 if (!(auth_ok & auth_sent)) { 813 if (auth_sent) { 814 error("Another authentication mechanism " 815 "must be used to access this host.\n"); 816 (void) audit_rshd_fail("Another authentication" 817 " mechanism must be used to access" 818 " this host.\n", hostname, remuser, 819 locuser, cmdbuf); /* BSM */ 820 goto signout; 821 } else { 822 error("Permission denied.\n"); 823 (void) audit_rshd_fail("Permission denied.", 824 hostname, remuser, locuser, cmdbuf); 825 /* BSM */ 826 goto signout; 827 } 828 } 829 830 831 if (pwd->pw_uid && !access("/etc/nologin", F_OK)) { 832 error("Logins currently disabled.\n"); 833 (void) audit_rshd_fail("Logins currently disabled.", 834 hostname, remuser, locuser, cmdbuf); 835 goto signout; 836 } 837 838 /* Log access to account */ 839 if (pwd && (pwd->pw_uid == 0)) { 840 syslog(LOG_NOTICE, "Executing %s for user %s (%s@%s)" 841 " as ROOT", cmdbuf, 842 kremuser, remuser, hostname); 843 } 844 } 845 846 if ((v = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) { 847 switch (v) { 848 case PAM_NEW_AUTHTOK_REQD: 849 error("password expired\n"); 850 (void) audit_rshd_fail("Password expired", hostname, 851 remuser, locuser, cmdbuf); /* BSM */ 852 break; 853 case PAM_PERM_DENIED: 854 error("account expired\n"); 855 (void) audit_rshd_fail("Account expired", hostname, 856 remuser, locuser, cmdbuf); /* BSM */ 857 break; 858 case PAM_AUTHTOK_EXPIRED: 859 error("password expired\n"); 860 (void) audit_rshd_fail("Password expired", hostname, 861 remuser, locuser, cmdbuf); /* BSM */ 862 break; 863 default: 864 error("login incorrect\n"); 865 (void) audit_rshd_fail("Permission denied", hostname, 866 remuser, locuser, cmdbuf); /* BSM */ 867 break; 868 } 869 (void) pam_end(pamh, PAM_ABORT); 870 exit(1); 871 } 872 873 if (chdir(pwd->pw_dir) < 0) { 874 (void) chdir("/"); 875 #ifdef notdef 876 error("No remote directory.\n"); 877 878 exit(1); 879 #endif 880 } 881 882 /* 883 * XXX There is no session management currently being done 884 */ 885 886 (void) write(STDERR_FILENO, "\0", 1); 887 if (port || do_encrypt) { 888 if ((pipe(pv) < 0)) { 889 error("Can't make pipe.\n"); 890 (void) pam_end(pamh, PAM_ABORT); 891 exit(1); 892 } 893 if (do_encrypt) { 894 if (pipe(pw) < 0) { 895 error("Can't make pipe 2.\n"); 896 (void) pam_end(pamh, PAM_ABORT); 897 exit(1); 898 } 899 if (pipe(px) < 0) { 900 error("Can't make pipe 3.\n"); 901 (void) pam_end(pamh, PAM_ABORT); 902 exit(1); 903 } 904 } 905 pid = fork(); 906 if (pid == (pid_t)-1) { 907 error("Fork (to start shell) failed on server. " 908 "Please try again later.\n"); 909 (void) pam_end(pamh, PAM_ABORT); 910 exit(1); 911 } 912 if (pid) { 913 fd_set ready; 914 fd_set readfrom; 915 916 (void) close(STDIN_FILENO); 917 (void) close(STDOUT_FILENO); 918 (void) close(STDERR_FILENO); 919 (void) close(pv[1]); 920 if (do_encrypt) { 921 (void) close(pw[1]); 922 (void) close(px[0]); 923 } else { 924 (void) close(f); 925 } 926 927 (void) FD_ZERO(&readfrom); 928 929 FD_SET(pv[0], &readfrom); 930 if (do_encrypt) { 931 FD_SET(pw[0], &readfrom); 932 FD_SET(f, &readfrom); 933 } 934 if (port) 935 FD_SET(s, &readfrom); 936 937 /* read f (net), write to px[1] (child stdin) */ 938 /* read pw[0] (child stdout), write to f (net) */ 939 /* read s (alt. channel), signal child */ 940 /* read pv[0] (child stderr), write to s */ 941 if (ioctl(pv[0], FIONBIO, (char *)&one) == -1) 942 syslog(LOG_INFO, "ioctl FIONBIO: %m"); 943 if (do_encrypt && 944 ioctl(pw[0], FIONBIO, (char *)&one) == -1) 945 syslog(LOG_INFO, "ioctl FIONBIO: %m"); 946 do { 947 ready = readfrom; 948 if (select(FD_SETSIZE, &ready, NULL, 949 NULL, NULL) < 0) { 950 if (errno == EINTR) { 951 continue; 952 } else { 953 break; 954 } 955 } 956 /* 957 * Read from child stderr, write to net 958 */ 959 if (port && FD_ISSET(pv[0], &ready)) { 960 errno = 0; 961 cc = read(pv[0], buf, sizeof (buf)); 962 if (cc <= 0) { 963 (void) shutdown(s, 2); 964 FD_CLR(pv[0], &readfrom); 965 } else { 966 (void) deswrite(s, buf, cc, 1); 967 } 968 } 969 /* 970 * Read from alternate channel, signal child 971 */ 972 if (port && FD_ISSET(s, &ready)) { 973 if ((int)desread(s, &sig, 1, 1) <= 0) 974 FD_CLR(s, &readfrom); 975 else 976 (void) killpg(pid, sig); 977 } 978 /* 979 * Read from child stdout, write to net 980 */ 981 if (do_encrypt && FD_ISSET(pw[0], &ready)) { 982 errno = 0; 983 cc = read(pw[0], buf, sizeof (buf)); 984 if (cc <= 0) { 985 (void) shutdown(f, 2); 986 FD_CLR(pw[0], &readfrom); 987 } else { 988 (void) deswrite(f, buf, cc, 0); 989 } 990 } 991 /* 992 * Read from the net, write to child stdin 993 */ 994 if (do_encrypt && FD_ISSET(f, &ready)) { 995 errno = 0; 996 cc = desread(f, buf, sizeof (buf), 0); 997 if (cc <= 0) { 998 (void) close(px[1]); 999 FD_CLR(f, &readfrom); 1000 } else { 1001 int wcc; 1002 wcc = write(px[1], buf, cc); 1003 if (wcc == -1) { 1004 /* 1005 * pipe closed, 1006 * don't read any 1007 * more 1008 * 1009 * might check for 1010 * EPIPE 1011 */ 1012 (void) close(px[1]); 1013 FD_CLR(f, &readfrom); 1014 } else if (wcc != cc) { 1015 /* CSTYLED */ 1016 syslog(LOG_INFO, gettext("only wrote %d/%d to child"), 1017 wcc, cc); 1018 } 1019 } 1020 } 1021 } while ((port && FD_ISSET(s, &readfrom)) || 1022 (port && FD_ISSET(pv[0], &readfrom)) || 1023 (do_encrypt && FD_ISSET(f, &readfrom)) || 1024 (do_encrypt && FD_ISSET(pw[0], &readfrom))); 1025 #ifdef DEBUG 1026 syslog(LOG_INFO, "Shell process completed."); 1027 #endif /* DEBUG */ 1028 if (ccache) 1029 (void) pam_close_session(pamh, 0); 1030 (void) pam_end(pamh, PAM_SUCCESS); 1031 1032 exit(0); 1033 } /* End of Parent block */ 1034 1035 (void) setsid(); /* Should be the same as above. */ 1036 (void) close(pv[0]); 1037 (void) dup2(pv[1], 2); 1038 (void) close(pv[1]); 1039 if (port) 1040 (void) close(s); 1041 if (do_encrypt) { 1042 (void) close(f); 1043 (void) close(pw[0]); 1044 (void) close(px[1]); 1045 1046 (void) dup2(px[0], 0); 1047 (void) dup2(pw[1], 1); 1048 1049 (void) close(px[0]); 1050 (void) close(pw[1]); 1051 } 1052 } 1053 1054 if (*pwd->pw_shell == '\0') 1055 pwd->pw_shell = "/bin/sh"; 1056 if (!do_encrypt) 1057 (void) close(f); 1058 /* 1059 * write audit record before making uid switch 1060 */ 1061 (void) audit_rshd_success(hostname, remuser, locuser, cmdbuf); /* BSM */ 1062 1063 /* set the real (and effective) GID */ 1064 if (setgid(pwd->pw_gid) == -1) { 1065 error("Invalid gid.\n"); 1066 (void) pam_end(pamh, PAM_ABORT); 1067 exit(1); 1068 } 1069 1070 /* 1071 * Initialize the supplementary group access list. 1072 */ 1073 if (strlen(locuser) == 0) { 1074 error("No local user.\n"); 1075 (void) pam_end(pamh, PAM_ABORT); 1076 exit(1); 1077 } 1078 if (initgroups(locuser, pwd->pw_gid) == -1) { 1079 error("Initgroup failed.\n"); 1080 (void) pam_end(pamh, PAM_ABORT); 1081 exit(1); 1082 } 1083 1084 if ((v = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { 1085 error("Insufficient credentials.\n"); 1086 (void) pam_end(pamh, v); 1087 exit(1); 1088 } 1089 1090 /* set the real (and effective) UID */ 1091 if (setuid(pwd->pw_uid) == -1) { 1092 error("Invalid uid.\n"); 1093 (void) pam_end(pamh, PAM_ABORT); 1094 exit(1); 1095 } 1096 1097 /* Change directory only after becoming the appropriate user. */ 1098 if (chdir(pwd->pw_dir) < 0) { 1099 (void) chdir("/"); 1100 if (krb5auth_flag > 0) { 1101 syslog(LOG_ERR, "Principal %s (%s@%s) for local user" 1102 " %s has no home directory.", 1103 kremuser, remuser, hostname, locuser); 1104 error("No remote directory.\n"); 1105 goto signout; 1106 } 1107 #ifdef notdef 1108 error("No remote directory.\n"); 1109 exit(1); 1110 #endif 1111 } 1112 1113 path = (pwd->pw_uid == 0) ? rootpath : userpath; 1114 1115 /* 1116 * Space for the following environment variables are dynamically 1117 * allocated because their lengths are not known before calling 1118 * getpwnam(). 1119 */ 1120 homedir_len = strlen(pwd->pw_dir) + strlen(homestr) + 1; 1121 shell_len = strlen(pwd->pw_shell) + strlen(shellstr) + 1; 1122 username_len = strlen(pwd->pw_name) + strlen(userstr) + 1; 1123 homedir = (char *)malloc(homedir_len); 1124 shell = (char *)malloc(shell_len); 1125 username = (char *)malloc(username_len); 1126 if (homedir == NULL || shell == NULL || username == NULL) { 1127 perror("malloc"); 1128 exit(1); 1129 } 1130 (void) snprintf(homedir, homedir_len, "%s%s", homestr, pwd->pw_dir); 1131 (void) snprintf(shell, shell_len, "%s%s", shellstr, pwd->pw_shell); 1132 (void) snprintf(username, username_len, "%s%s", userstr, pwd->pw_name); 1133 1134 /* Pass timezone to executed command. */ 1135 if (tzenv = getenv("TZ")) { 1136 tz_len = strlen(tzenv) + strlen(tzstr) + 1; 1137 tz = malloc(tz_len); 1138 if (tz != NULL) 1139 (void) snprintf(tz, tz_len, "%s%s", tzstr, tzenv); 1140 } 1141 1142 add_to_envinit(homedir); 1143 add_to_envinit(shell); 1144 add_to_envinit(path); 1145 add_to_envinit(username); 1146 add_to_envinit(tz); 1147 1148 if (krb5auth_flag > 0) { 1149 int length; 1150 char *buffer; 1151 1152 /* 1153 * If we have KRB5CCNAME set, then copy into the child's 1154 * environment. This can't really have a fixed position 1155 * because `tz' may or may not be set. 1156 */ 1157 if (getenv("KRB5CCNAME")) { 1158 length = (int)strlen(getenv("KRB5CCNAME")) + 1159 (int)strlen("KRB5CCNAME=") + 1; 1160 buffer = (char *)malloc(length); 1161 1162 if (buffer) { 1163 (void) snprintf(buffer, length, "KRB5CCNAME=%s", 1164 getenv("KRB5CCNAME")); 1165 add_to_envinit(buffer); 1166 } 1167 } { 1168 /* These two are covered by ADDRPAD */ 1169 length = strlen(inet_ntoa(localaddr.sin_addr)) + 1 + 1170 strlen("KRB5LOCALADDR="); 1171 (void) snprintf(local_addr, length, "KRB5LOCALADDR=%s", 1172 inet_ntoa(localaddr.sin_addr)); 1173 add_to_envinit(local_addr); 1174 1175 length = strlen(inet_ntoa(sin->sin_addr)) + 1 + 1176 strlen("KRB5REMOTEADDR="); 1177 (void) snprintf(remote_addr, length, 1178 "KRB5REMOTEADDR=%s", inet_ntoa(sin->sin_addr)); 1179 add_to_envinit(remote_addr); 1180 } 1181 1182 /* 1183 * If we do anything else, make sure there is 1184 * space in the array. 1185 */ 1186 for (cnt = 0; cnt < num_env; cnt++) { 1187 char *buf; 1188 1189 if (getenv(save_env[cnt])) { 1190 length = (int)strlen(getenv(save_env[cnt])) + 1191 (int)strlen(save_env[cnt]) + 2; 1192 1193 buf = (char *)malloc(length); 1194 if (buf) { 1195 (void) snprintf(buf, length, "%s=%s", 1196 save_env[cnt], 1197 getenv(save_env[cnt])); 1198 add_to_envinit(buf); 1199 } 1200 } 1201 } 1202 1203 } 1204 1205 /* 1206 * add PAM environment variables set by modules 1207 * -- only allowed 16 (PAM_ENV_ELIM) 1208 * -- check to see if the environment variable is legal 1209 */ 1210 if ((pam_env = pam_getenvlist(pamh)) != 0) { 1211 while (pam_env[idx] != 0) { 1212 if (idx < PAM_ENV_ELIM && 1213 legalenvvar(pam_env[idx])) { 1214 add_to_envinit(pam_env[idx]); 1215 } 1216 idx++; 1217 } 1218 } 1219 1220 (void) pam_end(pamh, PAM_SUCCESS); 1221 1222 /* 1223 * Pick up locale environment variables, if any. 1224 */ 1225 lenvp = renvp; 1226 while (*lenvp != NULL) { 1227 int index; 1228 1229 for (index = 0; localeenv[index] != NULL; index++) 1230 /* 1231 * locale_envmatch() returns 1 if 1232 * *lenvp is localenev[index] and valid. 1233 */ 1234 if (locale_envmatch(localeenv[index], *lenvp)) { 1235 add_to_envinit(*lenvp); 1236 break; 1237 } 1238 1239 lenvp++; 1240 } 1241 1242 cp = strrchr(pwd->pw_shell, '/'); 1243 if (cp != NULL) 1244 cp++; 1245 else 1246 cp = pwd->pw_shell; 1247 /* 1248 * rdist has been moved to /usr/bin, so /usr/ucb/rdist might not 1249 * be present on a system. So if it doesn't exist we fall back 1250 * and try for it in /usr/bin. We take care to match the space 1251 * after the name because the only purpose of this is to protect 1252 * the internal call from old rdist's, not humans who type 1253 * "rsh foo /usr/ucb/rdist". 1254 */ 1255 #define RDIST_PROG_NAME "/usr/ucb/rdist -Server" 1256 if (strncmp(cmdbuf, RDIST_PROG_NAME, strlen(RDIST_PROG_NAME)) == 0) { 1257 if (stat("/usr/ucb/rdist", &statb) != 0) { 1258 (void) strncpy(cmdbuf + 5, "bin", 3); 1259 } 1260 } 1261 1262 #ifdef DEBUG 1263 syslog(LOG_NOTICE, "rshd: cmdbuf = %s", cmdbuf); 1264 if (do_encrypt) 1265 syslog(LOG_NOTICE, "rshd: cmd to be exec'ed = %s", 1266 ((char *)cmdbuf + 3)); 1267 #endif /* DEBUG */ 1268 1269 if (do_encrypt && (strncmp(cmdbuf, "-x ", 3) == 0)) { 1270 (void) execle(pwd->pw_shell, cp, "-c", (char *)cmdbuf + 3, 1271 NULL, envinit); 1272 } else { 1273 (void) execle(pwd->pw_shell, cp, "-c", cmdbuf, NULL, 1274 envinit); 1275 } 1276 1277 perror(pwd->pw_shell); 1278 exit(1); 1279 1280 signout: 1281 if (ccache) 1282 (void) pam_close_session(pamh, 0); 1283 ccache = NULL; 1284 (void) pam_end(pamh, PAM_ABORT); 1285 exit(1); 1286 } 1287 1288 static void 1289 getstr(fd, buf, cnt, err) 1290 int fd; 1291 char *buf; 1292 int cnt; 1293 char *err; 1294 { 1295 char c; 1296 1297 do { 1298 if (read(fd, &c, 1) != 1) 1299 exit(1); 1300 if (cnt-- == 0) { 1301 error("%s too long\n", err); 1302 exit(1); 1303 } 1304 *buf++ = c; 1305 } while (c != 0); 1306 } 1307 1308 /*PRINTFLIKE1*/ 1309 static void 1310 error(char *fmt, ...) 1311 { 1312 va_list ap; 1313 char buf[RSHD_BUFSIZ]; 1314 1315 buf[0] = 1; 1316 va_start(ap, fmt); 1317 (void) vsnprintf(&buf[1], sizeof (buf) - 1, fmt, ap); 1318 va_end(ap); 1319 (void) write(STDERR_FILENO, buf, strlen(buf)); 1320 } 1321 1322 static char *illegal[] = { 1323 "SHELL=", 1324 "HOME=", 1325 "LOGNAME=", 1326 #ifndef NO_MAIL 1327 "MAIL=", 1328 #endif 1329 "CDPATH=", 1330 "IFS=", 1331 "PATH=", 1332 "USER=", 1333 "TZ=", 1334 0 1335 }; 1336 1337 /* 1338 * legalenvvar - can PAM modules insert this environmental variable? 1339 */ 1340 1341 static int 1342 legalenvvar(char *s) 1343 { 1344 register char **p; 1345 1346 for (p = illegal; *p; p++) 1347 if (strncmp(s, *p, strlen(*p)) == 0) 1348 return (0); 1349 1350 if (s[0] == 'L' && s[1] == 'D' && s[2] == '_') 1351 return (0); 1352 1353 return (1); 1354 } 1355 1356 /* 1357 * Add a string to the environment of the new process. 1358 */ 1359 1360 static void 1361 add_to_envinit(char *string) 1362 { 1363 /* 1364 * Reserve space for 2 * 8 = 16 environment entries initially which 1365 * should be enough to avoid reallocation of "envinit" in most cases. 1366 */ 1367 static int size = 8; 1368 static int index = 0; 1369 1370 if (string == NULL) 1371 return; 1372 1373 if ((envinit == NULL) || (index == size)) { 1374 size *= 2; 1375 envinit = realloc(envinit, (size + 1) * sizeof (char *)); 1376 if (envinit == NULL) { 1377 perror("malloc"); 1378 exit(1); 1379 } 1380 } 1381 1382 envinit[index++] = string; 1383 envinit[index] = NULL; 1384 } 1385 1386 /* 1387 * Check if lenv and penv matches or not. 1388 */ 1389 static int 1390 locale_envmatch(char *lenv, char *penv) 1391 { 1392 while ((*lenv == *penv) && (*lenv != '\0') && (*penv != '=')) { 1393 lenv++; 1394 penv++; 1395 } 1396 1397 /* 1398 * '/' is eliminated for security reason. 1399 */ 1400 return ((*lenv == '\0' && *penv == '=' && *(penv + 1) != '/')); 1401 } 1402 1403 #ifndef KRB_SENDAUTH_VLEN 1404 #define KRB_SENDAUTH_VLEN 8 /* length for version strings */ 1405 #endif 1406 1407 /* MUST be KRB_SENDAUTH_VLEN chars */ 1408 #define KRB_SENDAUTH_VERS "AUTHV0.1" 1409 #define SIZEOF_INADDR sizeof (struct in_addr) 1410 1411 static krb5_error_code 1412 recvauth(int netf, int *valid_checksum) 1413 { 1414 krb5_auth_context auth_context = NULL; 1415 krb5_error_code status; 1416 struct sockaddr_in laddr; 1417 int len; 1418 krb5_data inbuf; 1419 krb5_authenticator *authenticator; 1420 krb5_ticket *ticket; 1421 krb5_rcache rcache; 1422 krb5_data version; 1423 krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */ 1424 krb5_data desinbuf; 1425 krb5_data desoutbuf; 1426 char des_inbuf[2 * RSHD_BUFSIZ]; 1427 /* needs to be > largest read size */ 1428 char des_outbuf[2 * RSHD_BUFSIZ + 4]; 1429 /* needs to be > largest write size */ 1430 1431 *valid_checksum = 0; 1432 len = sizeof (laddr); 1433 1434 if (getsockname(netf, (struct sockaddr *)&laddr, &len)) { 1435 exit(1); 1436 } 1437 1438 if (status = krb5_auth_con_init(bsd_context, &auth_context)) 1439 return (status); 1440 1441 if (status = krb5_auth_con_genaddrs(bsd_context, auth_context, netf, 1442 KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)) 1443 return (status); 1444 1445 status = krb5_auth_con_getrcache(bsd_context, auth_context, &rcache); 1446 if (status) 1447 return (status); 1448 1449 if (!rcache) { 1450 krb5_principal server; 1451 1452 status = krb5_sname_to_principal(bsd_context, 0, 0, 1453 KRB5_NT_SRV_HST, &server); 1454 if (status) 1455 return (status); 1456 1457 status = krb5_get_server_rcache(bsd_context, 1458 krb5_princ_component(bsd_context, server, 0), 1459 &rcache); 1460 krb5_free_principal(bsd_context, server); 1461 if (status) 1462 return (status); 1463 1464 status = krb5_auth_con_setrcache(bsd_context, auth_context, 1465 rcache); 1466 if (status) 1467 return (status); 1468 } 1469 1470 status = krb5_recvauth_version(bsd_context, &auth_context, &netf, 1471 NULL, /* Specify daemon principal */ 1472 0, /* no flags */ 1473 keytab, /* normally NULL to use v5srvtab */ 1474 &ticket, /* return ticket */ 1475 &version); /* application version string */ 1476 1477 1478 if (status) { 1479 getstr(netf, locuser, sizeof (locuser), "locuser"); 1480 getstr(netf, cmdbuf, sizeof (cmdbuf), "command"); 1481 getstr(netf, remuser, sizeof (locuser), "remuser"); 1482 return (status); 1483 } 1484 getstr(netf, locuser, sizeof (locuser), "locuser"); 1485 getstr(netf, cmdbuf, sizeof (cmdbuf), "command"); 1486 1487 /* Must be V5 */ 1488 1489 kcmd_protocol = KCMD_UNKNOWN_PROTOCOL; 1490 if (version.length != 9 || version.data == NULL) { 1491 syslog(LOG_ERR, "bad application version length"); 1492 error(gettext("bad application version length\n")); 1493 exit(1); 1494 } 1495 if (strncmp(version.data, "KCMDV0.1", 9) == 0) { 1496 kcmd_protocol = KCMD_OLD_PROTOCOL; 1497 } else if (strncmp(version.data, "KCMDV0.2", 9) == 0) { 1498 kcmd_protocol = KCMD_NEW_PROTOCOL; 1499 } else { 1500 syslog(LOG_ERR, "Unrecognized KCMD protocol (%s)", 1501 (char *)version.data); 1502 error(gettext("Unrecognized KCMD protocol (%s)"), 1503 (char *)version.data); 1504 exit(1); 1505 } 1506 getstr(netf, remuser, sizeof (locuser), "remuser"); 1507 1508 if ((status = krb5_unparse_name(bsd_context, ticket->enc_part2->client, 1509 &kremuser))) 1510 return (status); 1511 1512 if ((status = krb5_copy_principal(bsd_context, 1513 ticket->enc_part2->client, &client))) 1514 return (status); 1515 1516 1517 if (checksum_required && (kcmd_protocol == KCMD_OLD_PROTOCOL)) { 1518 if ((status = krb5_auth_con_getauthenticator(bsd_context, 1519 auth_context, &authenticator))) 1520 return (status); 1521 1522 if (authenticator->checksum && checksum_required) { 1523 struct sockaddr_in adr; 1524 int adr_length = sizeof (adr); 1525 int chksumsize = strlen(cmdbuf) + strlen(locuser) + 32; 1526 krb5_data input; 1527 krb5_keyblock key; 1528 1529 char *chksumbuf = (char *)malloc(chksumsize); 1530 1531 if (chksumbuf == 0) 1532 goto error_cleanup; 1533 if (getsockname(netf, (struct sockaddr *)&adr, 1534 &adr_length) != 0) 1535 goto error_cleanup; 1536 1537 (void) snprintf(chksumbuf, chksumsize, "%u:", 1538 ntohs(adr.sin_port)); 1539 if (strlcat(chksumbuf, cmdbuf, 1540 chksumsize) >= chksumsize) { 1541 syslog(LOG_ERR, "cmd buffer too long."); 1542 free(chksumbuf); 1543 return (-1); 1544 } 1545 if (strlcat(chksumbuf, locuser, 1546 chksumsize) >= chksumsize) { 1547 syslog(LOG_ERR, "locuser too long."); 1548 free(chksumbuf); 1549 return (-1); 1550 } 1551 1552 input.data = chksumbuf; 1553 input.length = strlen(chksumbuf); 1554 key.magic = ticket->enc_part2->session->magic; 1555 key.enctype = ticket->enc_part2->session->enctype; 1556 key.contents = ticket->enc_part2->session->contents; 1557 key.length = ticket->enc_part2->session->length; 1558 1559 status = krb5_c_verify_checksum(bsd_context, 1560 &key, 0, &input, authenticator->checksum, 1561 (unsigned int *)valid_checksum); 1562 1563 if (status == 0 && *valid_checksum == 0) 1564 status = KRB5KRB_AP_ERR_BAD_INTEGRITY; 1565 error_cleanup: 1566 if (chksumbuf) 1567 krb5_xfree(chksumbuf); 1568 if (status) { 1569 krb5_free_authenticator(bsd_context, 1570 authenticator); 1571 return (status); 1572 } 1573 } 1574 krb5_free_authenticator(bsd_context, authenticator); 1575 } 1576 1577 1578 if ((strncmp(cmdbuf, "-x ", 3) == 0)) { 1579 if (krb5_privacy_allowed()) { 1580 do_encrypt = 1; 1581 } else { 1582 syslog(LOG_ERR, "rshd: Encryption not supported"); 1583 error("rshd: Encryption not supported. \n"); 1584 exit(2); 1585 } 1586 1587 status = krb5_auth_con_getremotesubkey(bsd_context, 1588 auth_context, 1589 &sessionkey); 1590 if (status) { 1591 syslog(LOG_ERR, "Error getting KRB5 session subkey"); 1592 error(gettext("Error getting KRB5 session subkey")); 1593 exit(1); 1594 } 1595 /* 1596 * The "new" protocol requires that a subkey be sent. 1597 */ 1598 if (sessionkey == NULL && kcmd_protocol == KCMD_NEW_PROTOCOL) { 1599 syslog(LOG_ERR, "No KRB5 session subkey sent"); 1600 error(gettext("No KRB5 session subkey sent")); 1601 exit(1); 1602 } 1603 /* 1604 * The "old" protocol does not permit an authenticator subkey. 1605 * The key is taken from the ticket instead (see below). 1606 */ 1607 if (sessionkey != NULL && kcmd_protocol == KCMD_OLD_PROTOCOL) { 1608 syslog(LOG_ERR, "KRB5 session subkey not permitted " 1609 "with old KCMD protocol"); 1610 error(gettext("KRB5 session subkey not permitted " 1611 "with old KCMD protocol")); 1612 exit(1); 1613 } 1614 /* 1615 * If no key at this point, use the session key from 1616 * the ticket. 1617 */ 1618 if (sessionkey == NULL) { 1619 /* 1620 * Save the session key so we can configure the crypto 1621 * module later. 1622 */ 1623 status = krb5_copy_keyblock(bsd_context, 1624 ticket->enc_part2->session, 1625 &sessionkey); 1626 if (status) { 1627 syslog(LOG_ERR, "krb5_copy_keyblock failed"); 1628 error(gettext("krb5_copy_keyblock failed")); 1629 exit(1); 1630 } 1631 } 1632 /* 1633 * If session key still cannot be found, we must 1634 * exit because encryption is required here 1635 * when encr_flag (-x) is set. 1636 */ 1637 if (sessionkey == NULL) { 1638 syslog(LOG_ERR, "Could not find an encryption key"); 1639 error(gettext("Could not find an encryption key")); 1640 exit(1); 1641 } 1642 1643 /* 1644 * Initialize parameters/buffers for desread & deswrite here. 1645 */ 1646 desinbuf.data = des_inbuf; 1647 desoutbuf.data = des_outbuf; 1648 desinbuf.length = sizeof (des_inbuf); 1649 desoutbuf.length = sizeof (des_outbuf); 1650 1651 eblock.crypto_entry = sessionkey->enctype; 1652 eblock.key = (krb5_keyblock *)sessionkey; 1653 1654 init_encrypt(do_encrypt, bsd_context, kcmd_protocol, 1655 &desinbuf, &desoutbuf, SERVER, &eblock); 1656 } 1657 1658 ticket->enc_part2->session = 0; 1659 1660 if ((status = krb5_read_message(bsd_context, (krb5_pointer) & netf, 1661 &inbuf))) { 1662 error(gettext("Error reading message: %s\n"), 1663 error_message(status)); 1664 exit(1); 1665 } 1666 1667 if (inbuf.length) { 1668 krb5_creds **creds = NULL; 1669 1670 /* Forwarding being done, read creds */ 1671 if ((status = krb5_rd_cred(bsd_context, 1672 auth_context, &inbuf, &creds, 1673 NULL))) { 1674 error("Can't get forwarded credentials: %s\n", 1675 error_message(status)); 1676 exit(1); 1677 } 1678 1679 /* Store the forwarded creds in the ccache */ 1680 if ((status = store_forw_creds(bsd_context, 1681 creds, ticket, locuser, 1682 &ccache))) { 1683 error("Can't store forwarded credentials: %s\n", 1684 error_message(status)); 1685 exit(1); 1686 } 1687 krb5_free_creds(bsd_context, *creds); 1688 } 1689 1690 krb5_free_ticket(bsd_context, ticket); 1691 return (0); 1692 } 1693 1694 static void 1695 usage(void) 1696 { 1697 (void) fprintf(stderr, gettext("%s: rshd [-k5eciU] " 1698 "[-P path] [-M realm] [-s tos] " 1699 #ifdef DEBUG 1700 "[-D port] " 1701 #endif /* DEBUG */ 1702 "[-S keytab]"), gettext("usage")); 1703 1704 syslog(LOG_ERR, "%s: rshd [-k5eciU] [-P path] [-M realm] [-s tos] " 1705 #ifdef DEBUG 1706 "[-D port] " 1707 #endif /* DEBUG */ 1708 "[-S keytab]", gettext("usage")); 1709 } 1710