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