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