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