1 /* 2 * Copyright (c) 1983, 1988, 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1983, 1988, 1089 The Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)rshd.c 5.30 (Berkeley) 05/11/90"; 26 #endif /* not lint */ 27 28 /* From: 29 * $Source: /mit/kerberos/ucb/mit/rshd/RCS/rshd.c,v $ 30 * $Header: /mit/kerberos/ucb/mit/rshd/RCS/rshd.c,v 5.2 89/07/31 19:30:04 kfall Exp $ 31 */ 32 33 34 /* 35 * remote shell server: 36 * [port]\0 37 * remuser\0 38 * locuser\0 39 * command\0 40 * data 41 */ 42 #include <sys/param.h> 43 #include <sys/ioctl.h> 44 #include <sys/socket.h> 45 #include <sys/file.h> 46 #include <sys/signal.h> 47 #include <sys/time.h> 48 49 #include <netinet/in.h> 50 51 #include <arpa/inet.h> 52 53 #include <stdio.h> 54 #include <errno.h> 55 #include <pwd.h> 56 #include <netdb.h> 57 #include <syslog.h> 58 #include "pathnames.h" 59 60 int errno; 61 int keepalive = 1; 62 int check_all = 0; 63 char *index(), *rindex(), *strncat(); 64 /*VARARGS1*/ 65 int error(); 66 int sent_null; 67 68 #ifdef KERBEROS 69 #include <kerberosIV/des.h> 70 #include <kerberosIV/krb.h> 71 #define VERSION_SIZE 9 72 #define SECURE_MESSAGE "This rsh session is using DES encryption for all transmissions.\r\n" 73 #define OPTIONS "alnkvx" 74 char authbuf[sizeof(AUTH_DAT)]; 75 char tickbuf[sizeof(KTEXT_ST)]; 76 int use_kerberos = 0, vacuous = 0; 77 int encrypt = 0; 78 Key_schedule schedule; 79 #else 80 #define OPTIONS "aln" 81 #endif 82 83 /*ARGSUSED*/ 84 main(argc, argv) 85 int argc; 86 char **argv; 87 { 88 89 extern int opterr, optind; 90 extern int _check_rhosts_file; 91 struct linger linger; 92 int ch, on = 1, fromlen; 93 struct sockaddr_in from; 94 95 openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 96 97 opterr = 0; 98 while ((ch = getopt(argc, argv, OPTIONS)) != EOF) 99 switch (ch) { 100 case 'a': 101 check_all = 1; 102 break; 103 case 'l': 104 _check_rhosts_file = 0; 105 break; 106 case 'n': 107 keepalive = 0; 108 break; 109 #ifdef KERBEROS 110 case 'k': 111 use_kerberos = 1; 112 break; 113 114 case 'v': 115 vacuous = 1; 116 break; 117 118 case 'x': 119 encrypt = 1; 120 break; 121 #endif 122 case '?': 123 default: 124 usage(); 125 exit(2); 126 } 127 128 argc -= optind; 129 argv += optind; 130 131 #ifdef KERBEROS 132 if (use_kerberos && vacuous) { 133 syslog(LOG_ERR, "only one of -k and -v allowed"); 134 exit(2); 135 } 136 if (encrypt && !use_kerberos) { 137 syslog(LOG_ERR, "-k is required for -x"); 138 exit(2); 139 } 140 #endif 141 142 fromlen = sizeof (from); 143 if (getpeername(0, &from, &fromlen) < 0) { 144 syslog(LOG_ERR, "getpeername: %m"); 145 _exit(1); 146 } 147 if (keepalive && 148 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, 149 sizeof(on)) < 0) 150 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 151 linger.l_onoff = 1; 152 linger.l_linger = 60; /* XXX */ 153 if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger, 154 sizeof (linger)) < 0) 155 syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); 156 doit(&from); 157 } 158 159 char username[20] = "USER="; 160 char homedir[64] = "HOME="; 161 char shell[64] = "SHELL="; 162 char *envinit[] = 163 {homedir, shell, _PATH_DEFPATH, username, 0}; 164 char **environ; 165 166 doit(fromp) 167 struct sockaddr_in *fromp; 168 { 169 char cmdbuf[NCARGS+1], *cp; 170 char locuser[16], remuser[16]; 171 struct passwd *pwd; 172 int s; 173 struct hostent *hp; 174 char *hostname, *errorstr = NULL, *errorhost; 175 short port; 176 int pv[2], pid, cc; 177 int nfd; 178 fd_set ready, readfrom; 179 char buf[BUFSIZ], sig; 180 int one = 1; 181 char remotehost[2 * MAXHOSTNAMELEN + 1]; 182 183 #ifdef KERBEROS 184 AUTH_DAT *kdata = (AUTH_DAT *) NULL; 185 KTEXT ticket = (KTEXT) NULL; 186 char instance[INST_SZ], version[VERSION_SIZE]; 187 struct sockaddr_in fromaddr; 188 int rc; 189 long authopts; 190 int pv1[2], pv2[2]; 191 fd_set wready, writeto; 192 193 fromaddr = *fromp; 194 #endif 195 196 (void) signal(SIGINT, SIG_DFL); 197 (void) signal(SIGQUIT, SIG_DFL); 198 (void) signal(SIGTERM, SIG_DFL); 199 #ifdef DEBUG 200 { int t = open(_PATH_TTY, 2); 201 if (t >= 0) { 202 ioctl(t, TIOCNOTTY, (char *)0); 203 (void) close(t); 204 } 205 } 206 #endif 207 fromp->sin_port = ntohs((u_short)fromp->sin_port); 208 if (fromp->sin_family != AF_INET) { 209 syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", 210 fromp->sin_family); 211 exit(1); 212 } 213 #ifdef IP_OPTIONS 214 { 215 u_char optbuf[BUFSIZ/3], *cp; 216 char lbuf[BUFSIZ], *lp; 217 int optsize = sizeof(optbuf), ipproto; 218 struct protoent *ip; 219 220 if ((ip = getprotobyname("ip")) != NULL) 221 ipproto = ip->p_proto; 222 else 223 ipproto = IPPROTO_IP; 224 if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && 225 optsize != 0) { 226 lp = lbuf; 227 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) 228 sprintf(lp, " %2.2x", *cp); 229 syslog(LOG_NOTICE, 230 "Connection received from %s using IP options (ignored):%s", 231 inet_ntoa(fromp->sin_addr), lbuf); 232 if (setsockopt(0, ipproto, IP_OPTIONS, 233 (char *)NULL, &optsize) != 0) { 234 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); 235 exit(1); 236 } 237 } 238 } 239 #endif 240 241 #ifdef KERBEROS 242 if (!use_kerberos) 243 #endif 244 if (fromp->sin_port >= IPPORT_RESERVED || 245 fromp->sin_port < IPPORT_RESERVED/2) { 246 syslog(LOG_NOTICE|LOG_AUTH, 247 "Connection from %s on illegal port", 248 inet_ntoa(fromp->sin_addr)); 249 exit(1); 250 } 251 252 (void) alarm(60); 253 port = 0; 254 for (;;) { 255 char c; 256 if ((cc = read(0, &c, 1)) != 1) { 257 if (cc < 0) 258 syslog(LOG_NOTICE, "read: %m"); 259 shutdown(0, 1+1); 260 exit(1); 261 } 262 if (c== 0) 263 break; 264 port = port * 10 + c - '0'; 265 } 266 267 (void) alarm(0); 268 if (port != 0) { 269 int lport = IPPORT_RESERVED - 1; 270 s = rresvport(&lport); 271 if (s < 0) { 272 syslog(LOG_ERR, "can't get stderr port: %m"); 273 exit(1); 274 } 275 #ifdef KERBEROS 276 if (!use_kerberos) 277 #endif 278 if (port >= IPPORT_RESERVED) { 279 syslog(LOG_ERR, "2nd port not reserved\n"); 280 exit(1); 281 } 282 fromp->sin_port = htons((u_short)port); 283 if (connect(s, fromp, sizeof (*fromp)) < 0) { 284 syslog(LOG_INFO, "connect second port: %m"); 285 exit(1); 286 } 287 } 288 289 #ifdef KERBEROS 290 if (vacuous) { 291 error("rshd: remote host requires Kerberos authentication\n"); 292 exit(1); 293 } 294 #endif 295 296 #ifdef notdef 297 /* from inetd, socket is already on 0, 1, 2 */ 298 dup2(f, 0); 299 dup2(f, 1); 300 dup2(f, 2); 301 #endif 302 hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr), 303 fromp->sin_family); 304 if (hp) { 305 /* 306 * If name returned by gethostbyaddr is in our domain, 307 * attempt to verify that we haven't been fooled by someone 308 * in a remote net; look up the name and check that this 309 * address corresponds to the name. 310 */ 311 #ifdef KERBEROS 312 if (!use_kerberos) 313 #endif 314 if (check_all || local_domain(hp->h_name)) { 315 strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1); 316 remotehost[sizeof(remotehost) - 1] = 0; 317 errorhost = remotehost; 318 hp = gethostbyname(remotehost); 319 if (hp == NULL) { 320 syslog(LOG_INFO, 321 "Couldn't look up address for %s", 322 remotehost); 323 errorstr = 324 "Couldn't look up address for your host (%s)\n"; 325 hostname = inet_ntoa(fromp->sin_addr); 326 } else for (; ; hp->h_addr_list++) { 327 if (hp->h_addr_list[0] == NULL) { 328 syslog(LOG_NOTICE, 329 "Host addr %s not listed for host %s", 330 inet_ntoa(fromp->sin_addr), 331 hp->h_name); 332 errorstr = 333 "Host address mismatch for %s\n"; 334 hostname = inet_ntoa(fromp->sin_addr); 335 break; 336 } 337 if (!bcmp(hp->h_addr_list[0], 338 (caddr_t)&fromp->sin_addr, 339 sizeof(fromp->sin_addr))) { 340 hostname = hp->h_name; 341 break; 342 } 343 } 344 } 345 } else 346 errorhost = hostname = inet_ntoa(fromp->sin_addr); 347 348 #ifdef KERBEROS 349 if (use_kerberos) { 350 kdata = (AUTH_DAT *) authbuf; 351 ticket = (KTEXT) tickbuf; 352 authopts = 0L; 353 strcpy(instance, "*"); 354 version[VERSION_SIZE - 1] = '\0'; 355 if (encrypt) { 356 struct sockaddr_in local_addr; 357 rc = sizeof(local_addr); 358 if (getsockname(0, &local_addr, &rc) < 0) { 359 syslog(LOG_ERR, "getsockname: %m"); 360 exit(1); 361 } 362 authopts = KOPT_DO_MUTUAL; 363 rc = krb_recvauth(authopts, 0, ticket, 364 "rcmd", instance, &fromaddr, 365 &local_addr, kdata, "", schedule, 366 version); 367 des_set_key(kdata->session, schedule); 368 } else { 369 rc = krb_recvauth(authopts, 0, ticket, "rcmd", 370 instance, &fromaddr, 371 (struct sockaddr_in *) 0, 372 kdata, "", (bit_64 *) 0, version); 373 } 374 if (rc != KSUCCESS) { 375 fprintf(stderr, 376 "Kerberos authentication failure: %s\r\n", 377 krb_err_txt[rc]); 378 exit(1); 379 } 380 } else 381 #endif 382 getstr(remuser, sizeof(remuser), "remuser"); 383 384 getstr(locuser, sizeof(locuser), "locuser"); 385 getstr(cmdbuf, sizeof(cmdbuf), "command"); 386 setpwent(); 387 pwd = getpwnam(locuser); 388 if (pwd == NULL) { 389 if (errorstr == NULL) 390 errorstr = "Login incorrect.\n"; 391 goto fail; 392 } 393 if (chdir(pwd->pw_dir) < 0) { 394 (void) chdir("/"); 395 #ifdef notdef 396 error("No remote directory.\n"); 397 exit(1); 398 #endif 399 } 400 401 #ifdef KERBEROS 402 if (use_kerberos) { 403 if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0') { 404 if (kuserok(kdata, locuser) != 0) { 405 syslog(LOG_NOTICE|LOG_AUTH, 406 "Kerberos rsh denied to %s.%s@%s", 407 kdata->pname, kdata->pinst, kdata->prealm); 408 error("Permission denied.\n"); 409 exit(1); 410 } 411 } 412 } else 413 #endif 414 415 if (errorstr || 416 pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && 417 ruserok(hostname, pwd->pw_uid == 0, remuser, locuser) < 0) { 418 fail: 419 if (errorstr == NULL) 420 errorstr = "Permission denied.\n"; 421 error(errorstr, errorhost); 422 exit(1); 423 } 424 425 if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { 426 error("Logins currently disabled.\n"); 427 exit(1); 428 } 429 430 (void) write(2, "\0", 1); 431 sent_null = 1; 432 433 if (port) { 434 if (pipe(pv) < 0) { 435 error("Can't make pipe.\n"); 436 exit(1); 437 } 438 #ifdef KERBEROS 439 if (encrypt) { 440 if (pipe(pv1) < 0) { 441 error("Can't make 2nd pipe.\n"); 442 exit(1); 443 } 444 if (pipe(pv2) < 0) { 445 error("Can't make 3rd pipe.\n"); 446 exit(1); 447 } 448 } 449 #endif 450 pid = fork(); 451 if (pid == -1) { 452 error("Can't fork; try again.\n"); 453 exit(1); 454 } 455 if (pid) { 456 #ifdef KERBEROS 457 if (encrypt) { 458 static char msg[] = SECURE_MESSAGE; 459 (void) close(pv1[1]); 460 (void) close(pv2[1]); 461 des_write(s, msg, sizeof(msg)); 462 463 } else 464 #endif 465 { 466 (void) close(0); (void) close(1); 467 } 468 (void) close(2); (void) close(pv[1]); 469 470 FD_ZERO(&readfrom); 471 FD_SET(s, &readfrom); 472 FD_SET(pv[0], &readfrom); 473 if (pv[0] > s) 474 nfd = pv[0]; 475 else 476 nfd = s; 477 #ifdef KERBEROS 478 if (encrypt) { 479 FD_ZERO(&writeto); 480 FD_SET(pv2[0], &writeto); 481 FD_SET(pv1[0], &readfrom); 482 483 nfd = MAX(nfd, pv2[0]); 484 nfd = MAX(nfd, pv1[0]); 485 } else 486 #endif 487 ioctl(pv[0], FIONBIO, (char *)&one); 488 489 /* should set s nbio! */ 490 nfd++; 491 do { 492 ready = readfrom; 493 #ifdef KERBEROS 494 if (encrypt) { 495 wready = writeto; 496 if (select(nfd, &ready, 497 &wready, (fd_set *) 0, 498 (struct timeval *) 0) < 0) 499 break; 500 } else 501 #endif 502 if (select(nfd, &ready, (fd_set *)0, 503 (fd_set *)0, (struct timeval *)0) < 0) 504 break; 505 if (FD_ISSET(s, &ready)) { 506 int ret; 507 #ifdef KERBEROS 508 if (encrypt) 509 ret = des_read(s, &sig, 1); 510 else 511 #endif 512 ret = read(s, &sig, 1); 513 if (ret <= 0) 514 FD_CLR(s, &readfrom); 515 else 516 killpg(pid, sig); 517 } 518 if (FD_ISSET(pv[0], &ready)) { 519 errno = 0; 520 cc = read(pv[0], buf, sizeof(buf)); 521 if (cc <= 0) { 522 shutdown(s, 1+1); 523 FD_CLR(pv[0], &readfrom); 524 } else { 525 #ifdef KERBEROS 526 if (encrypt) 527 (void) 528 des_write(s, buf, cc); 529 else 530 #endif 531 (void) 532 write(s, buf, cc); 533 } 534 } 535 #ifdef KERBEROS 536 537 if (encrypt && FD_ISSET(pv1[0], &ready)) { 538 errno = 0; 539 cc = read(pv1[0], buf, sizeof(buf)); 540 if (cc <= 0) { 541 shutdown(pv1[0], 1+1); 542 FD_CLR(pv1[0], &readfrom); 543 } else 544 (void) des_write(1, buf, cc); 545 } 546 547 if (encrypt && FD_ISSET(pv2[0], &wready)) { 548 errno = 0; 549 cc = des_read(0, buf, sizeof(buf)); 550 if (cc <= 0) { 551 shutdown(pv2[0], 1+1); 552 FD_CLR(pv2[0], &writeto); 553 } else 554 (void) write(pv2[0], buf, cc); 555 } 556 #endif 557 558 } while (FD_ISSET(s, &readfrom) || 559 #ifdef KERBEROS 560 (encrypt && FD_ISSET(pv1[0], &readfrom)) || 561 #endif 562 FD_ISSET(pv[0], &readfrom)); 563 exit(0); 564 } 565 setpgrp(0, getpid()); 566 (void) close(s); (void) close(pv[0]); 567 #ifdef KERBEROS 568 if (encrypt) { 569 close(pv1[0]); close(pv2[0]); 570 dup2(pv1[1], 1); 571 dup2(pv2[1], 0); 572 close(pv1[1]); 573 close(pv2[1]); 574 } 575 #endif 576 dup2(pv[1], 2); 577 close(pv[1]); 578 } 579 if (*pwd->pw_shell == '\0') 580 pwd->pw_shell = _PATH_BSHELL; 581 #if BSD > 43 582 if (setlogin(pwd->pw_name) < 0) 583 syslog(LOG_ERR, "setlogin() failed: %m"); 584 #endif 585 (void) setgid((gid_t)pwd->pw_gid); 586 initgroups(pwd->pw_name, pwd->pw_gid); 587 (void) setuid((uid_t)pwd->pw_uid); 588 environ = envinit; 589 strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); 590 strncat(shell, pwd->pw_shell, sizeof(shell)-7); 591 strncat(username, pwd->pw_name, sizeof(username)-6); 592 cp = rindex(pwd->pw_shell, '/'); 593 if (cp) 594 cp++; 595 else 596 cp = pwd->pw_shell; 597 endpwent(); 598 if (pwd->pw_uid == 0) { 599 #ifdef KERBEROS 600 if (use_kerberos) 601 syslog(LOG_INFO|LOG_AUTH, 602 "ROOT Kerberos shell from %s.%s@%s on %s, comm: %s\n", 603 kdata->pname, kdata->pinst, kdata->prealm, 604 hostname, cmdbuf); 605 else 606 #endif 607 syslog(LOG_INFO|LOG_AUTH, 608 "ROOT shell from %s@%s, comm: %s\n", 609 remuser, hostname, cmdbuf); 610 } 611 execl(pwd->pw_shell, cp, "-c", cmdbuf, 0); 612 perror(pwd->pw_shell); 613 exit(1); 614 } 615 616 /* 617 * Report error to client. 618 * Note: can't be used until second socket has connected 619 * to client, or older clients will hang waiting 620 * for that connection first. 621 */ 622 /*VARARGS1*/ 623 error(fmt, a1, a2, a3) 624 char *fmt; 625 int a1, a2, a3; 626 { 627 char buf[BUFSIZ], *bp = buf; 628 629 if (sent_null == 0) 630 *bp++ = 1; 631 (void) sprintf(bp, fmt, a1, a2, a3); 632 (void) write(2, buf, strlen(buf)); 633 } 634 635 getstr(buf, cnt, err) 636 char *buf; 637 int cnt; 638 char *err; 639 { 640 char c; 641 642 do { 643 if (read(0, &c, 1) != 1) 644 exit(1); 645 *buf++ = c; 646 if (--cnt == 0) { 647 error("%s too long\n", err); 648 exit(1); 649 } 650 } while (c != 0); 651 } 652 653 /* 654 * Check whether host h is in our local domain, 655 * defined as sharing the last two components of the domain part, 656 * or the entire domain part if the local domain has only one component. 657 * If either name is unqualified (contains no '.'), 658 * assume that the host is local, as it will be 659 * interpreted as such. 660 */ 661 local_domain(h) 662 char *h; 663 { 664 char localhost[MAXHOSTNAMELEN]; 665 char *p1, *p2, *topdomain(); 666 667 localhost[0] = 0; 668 (void) gethostname(localhost, sizeof(localhost)); 669 p1 = topdomain(localhost); 670 p2 = topdomain(h); 671 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 672 return(1); 673 return(0); 674 } 675 676 char * 677 topdomain(h) 678 char *h; 679 { 680 register char *p; 681 char *maybe = NULL; 682 int dots = 0; 683 684 for (p = h + strlen(h); p >= h; p--) { 685 if (*p == '.') { 686 if (++dots == 2) 687 return (p); 688 maybe = p; 689 } 690 } 691 return (maybe); 692 } 693 694 usage() 695 { 696 #ifdef KERBEROS 697 syslog(LOG_ERR, "usage: rshd [-aln]"); 698 #else 699 syslog(LOG_ERR, "usage: rshd [-alknvx]"); 700 #endif 701 } 702