1 /* 2 * Copyright (c) 1983, 1988 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 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[] = "@(#)rlogind.c 5.26 (Berkeley) 01/25/89"; 26 #endif /* not lint */ 27 28 /* 29 * remote login server: 30 * \0 31 * remuser\0 32 * locuser\0 33 * terminal_type/speed\0 34 * data 35 */ 36 37 #include <stdio.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <sys/socket.h> 41 #include <sys/wait.h> 42 #include <sys/file.h> 43 #include <sys/param.h> 44 45 #include <netinet/in.h> 46 47 #include <errno.h> 48 #include <pwd.h> 49 #include <signal.h> 50 #include <sys/ioctl.h> 51 #include <sys/termios.h> 52 #include <stdio.h> 53 #include <netdb.h> 54 #include <syslog.h> 55 #include <strings.h> 56 57 #ifndef TIOCPKT_WINDOW 58 #define TIOCPKT_WINDOW 0x80 59 #endif 60 61 #ifdef KERBEROS 62 #include <kerberos/krb.h> 63 #define SECURE_MESSAGE "This rlogin session is using DES encryption for all transmissions.\r\n" 64 65 AUTH_DAT *kdata; 66 KTEXT ticket; 67 u_char auth_buf[sizeof(AUTH_DAT)]; 68 u_char tick_buf[sizeof(KTEXT_ST)]; 69 Key_schedule schedule; 70 int encrypt, retval, use_kerberos = 0, vacuous = 0; 71 int do_krb_login(); 72 73 #define OLD_RCMD 0x00 74 #define KERB_RCMD 0x00 75 #define KERB_RCMD_MUTUAL 0x03 76 77 #define ARGSTR "lnkv" 78 #else 79 #define ARGSTR "ln" 80 #endif /* KERBEROS */ 81 82 char *env[2]; 83 #define NMAX 30 84 char lusername[NMAX+1], rusername[NMAX+1]; 85 static char term[64] = "TERM="; 86 #define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */ 87 int keepalive = 1; 88 89 #define SUPERUSER(pwd) ((pwd)->pw_uid == 0) 90 91 extern int errno; 92 int reapchild(); 93 struct passwd *getpwnam(), *pwd; 94 char *malloc(); 95 96 main(argc, argv) 97 int argc; 98 char **argv; 99 { 100 extern int opterr, optind, _check_rhosts_file; 101 int ch; 102 int on = 1, fromlen; 103 struct sockaddr_in from; 104 105 openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); 106 107 opterr = 0; 108 while ((ch = getopt(argc, argv, ARGSTR)) != EOF) 109 switch (ch) { 110 case 'l': 111 _check_rhosts_file = 0; 112 break; 113 case 'n': 114 keepalive = 0; 115 break; 116 #ifdef KERBEROS 117 case 'k': 118 use_kerberos = 1; 119 break; 120 case 'v': 121 vacuous = 1; 122 break; 123 #endif 124 case '?': 125 default: 126 usage(); 127 break; 128 } 129 argc -= optind; 130 argv += optind; 131 132 #ifdef KERBEROS 133 if (use_kerberos && vacuous) { 134 fprintf(stderr, "%s: only one of -k and -v allowed\n", argv[0]); 135 usage(); 136 exit(1); 137 } 138 #endif 139 fromlen = sizeof (from); 140 if (getpeername(0, &from, &fromlen) < 0) { 141 syslog(LOG_ERR,"Couldn't get peer name of remote host: %m"); 142 exit(1); 143 } 144 if (keepalive && 145 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) 146 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 147 doit(0, &from); 148 } 149 150 int child; 151 int cleanup(); 152 int netf; 153 char *line; 154 extern char *inet_ntoa(); 155 156 struct winsize win = { 0, 0, 0, 0 }; 157 158 159 doit(f, fromp) 160 int f; 161 struct sockaddr_in *fromp; 162 { 163 int i, p, t, pid, on = 1; 164 int authenticated = 0, hostok = 0; 165 register struct hostent *hp; 166 char remotehost[2 * MAXHOSTNAMELEN + 1]; 167 struct hostent hostent; 168 char c; 169 170 alarm(60); 171 read(f, &c, 1); 172 173 #ifdef KERBEROS 174 /* 175 * XXX 1st char tells us which client we're talking to 176 */ 177 switch (c) { 178 179 case OLD_RCMD: /* OLD_RCMD is same as KERB_RCMD */ 180 if (vacuous) 181 fatal(f, "Remote host requires Kerberos authentication"); 182 break; 183 184 case KERB_RCMD_MUTUAL: 185 encrypt = 1; 186 break; 187 188 default: 189 fatal(f, "Remote protocol error"); 190 } 191 #else 192 if (c != 0) 193 exit(1); 194 #endif 195 196 alarm(0); 197 fromp->sin_port = ntohs((u_short)fromp->sin_port); 198 hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr), 199 fromp->sin_family); 200 if (hp == 0) { 201 /* 202 * Only the name is used below. 203 */ 204 hp = &hostent; 205 hp->h_name = inet_ntoa(fromp->sin_addr); 206 } else if (local_domain(hp->h_name)) { 207 /* 208 * If name returned by gethostbyaddr is in our domain, 209 * attempt to verify that we haven't been fooled by someone 210 * in a remote net; look up the name and check that this 211 * address corresponds to the name. 212 */ 213 strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1); 214 remotehost[sizeof(remotehost) - 1] = 0; 215 hp = gethostbyname(remotehost); 216 if (hp) 217 for (; hp->h_addr_list[0]; hp->h_addr_list++) { 218 if (!bcmp(hp->h_addr_list[0], (caddr_t)&fromp->sin_addr, 219 sizeof(fromp->sin_addr))) { 220 hostok++; 221 break; 222 } 223 } 224 } 225 226 #ifdef KERBEROS 227 if (use_kerberos) { 228 retval = do_krb_login(hp->h_name, fromp, encrypt); 229 write(f, &c, 1); 230 if (retval == 0) 231 authenticated++; 232 else if (retval > 0) 233 fatal(f, krb_err_txt[retval]); 234 } else 235 #endif 236 if (fromp->sin_family != AF_INET || 237 fromp->sin_port >= IPPORT_RESERVED || 238 fromp->sin_port < IPPORT_RESERVED/2) { 239 syslog(LOG_NOTICE, "Connection from %s on illegal port", 240 inet_ntoa(fromp->sin_addr)); 241 fatal(f, "Permission denied"); 242 } else { 243 write(f, "", 1); 244 245 if (do_rlogin(hp->h_name) == 0) { 246 if (hostok) 247 authenticated++; 248 else 249 write(f, "rlogind: Host address mismatch.\r\n", 250 sizeof("rlogind: Host address mismatch.\r\n") - 1); 251 } 252 } 253 254 for (c = 'p'; c <= 's'; c++) { 255 struct stat stb; 256 line = "/dev/ptyXX"; 257 line[strlen("/dev/pty")] = c; 258 line[strlen("/dev/ptyp")] = '0'; 259 if (stat(line, &stb) < 0) 260 break; 261 for (i = 0; i < 16; i++) { 262 line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i]; 263 p = open(line, O_RDWR); 264 if (p > 0) 265 goto gotpty; 266 } 267 } 268 fatal(f, "Out of ptys"); 269 /*NOTREACHED*/ 270 gotpty: 271 (void) ioctl(p, TIOCSWINSZ, &win); 272 netf = f; 273 line[strlen("/dev/")] = 't'; 274 t = open(line, O_RDWR); 275 if (t < 0) 276 fatalperror(f, line); 277 if (fchmod(t, 0)) 278 fatalperror(f, line); 279 (void)signal(SIGHUP, SIG_IGN); 280 vhangup(); 281 (void)signal(SIGHUP, SIG_DFL); 282 t = open(line, O_RDWR); 283 if (t < 0) 284 fatalperror(f, line); 285 setup_term(t); 286 #ifdef DEBUG 287 { 288 int tt = open("/dev/tty", O_RDWR); 289 if (tt > 0) { 290 (void)ioctl(tt, TIOCNOTTY, 0); 291 (void)close(tt); 292 } 293 } 294 #endif 295 pid = fork(); 296 if (pid < 0) 297 fatalperror(f, ""); 298 if (pid == 0) { 299 if (setsid() < 0) 300 fatalperror(f, "setsid"); 301 if (ioctl(t, TIOCSCTTY, 0) < 0) 302 fatalperror(f, "ioctl(sctty)"); 303 close(f), close(p); 304 dup2(t, 0), dup2(t, 1), dup2(t, 2); 305 close(t); 306 if (authenticated) 307 execl("/bin/login", "login", "-p", 308 "-h", hp->h_name, "-f", lusername, 0); 309 else 310 execl("/bin/login", "login", "-p", 311 "-h", hp->h_name, lusername, 0); 312 fatalperror(2, "/bin/login"); 313 /*NOTREACHED*/ 314 } 315 close(t); 316 317 #ifdef KERBEROS 318 /* 319 * If encrypted, don't turn on NBIO or the des read/write 320 * routines will croak. 321 */ 322 323 if (encrypt) 324 (void) des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE)); 325 else 326 #endif 327 ioctl(f, FIONBIO, &on); 328 ioctl(p, FIONBIO, &on); 329 ioctl(p, TIOCPKT, &on); 330 signal(SIGTSTP, SIG_IGN); 331 signal(SIGCHLD, cleanup); 332 setpgrp(0, 0); 333 protocol(f, p); 334 signal(SIGCHLD, SIG_IGN); 335 cleanup(); 336 } 337 338 char magic[2] = { 0377, 0377 }; 339 char oobdata[] = {TIOCPKT_WINDOW}; 340 341 /* 342 * Handle a "control" request (signaled by magic being present) 343 * in the data stream. For now, we are only willing to handle 344 * window size changes. 345 */ 346 control(pty, cp, n) 347 int pty; 348 char *cp; 349 int n; 350 { 351 struct winsize w; 352 353 if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') 354 return (0); 355 oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ 356 bcopy(cp+4, (char *)&w, sizeof(w)); 357 w.ws_row = ntohs(w.ws_row); 358 w.ws_col = ntohs(w.ws_col); 359 w.ws_xpixel = ntohs(w.ws_xpixel); 360 w.ws_ypixel = ntohs(w.ws_ypixel); 361 (void)ioctl(pty, TIOCSWINSZ, &w); 362 return (4+sizeof (w)); 363 } 364 365 /* 366 * rlogin "protocol" machine. 367 */ 368 protocol(f, p) 369 int f, p; 370 { 371 char pibuf[1024], fibuf[1024], *pbp, *fbp; 372 register pcc = 0, fcc = 0; 373 int cc, nfd, pmask, fmask; 374 char cntl; 375 376 /* 377 * Must ignore SIGTTOU, otherwise we'll stop 378 * when we try and set slave pty's window shape 379 * (our controlling tty is the master pty). 380 */ 381 (void) signal(SIGTTOU, SIG_IGN); 382 send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ 383 if (f > p) 384 nfd = f + 1; 385 else 386 nfd = p + 1; 387 fmask = 1 << f; 388 pmask = 1 << p; 389 for (;;) { 390 int ibits, obits, ebits; 391 392 ibits = 0; 393 obits = 0; 394 if (fcc) 395 obits |= pmask; 396 else 397 ibits |= fmask; 398 if (pcc >= 0) 399 if (pcc) 400 obits |= fmask; 401 else 402 ibits |= pmask; 403 ebits = pmask; 404 if (select(nfd, &ibits, obits ? &obits : (int *)NULL, 405 &ebits, 0) < 0) { 406 if (errno == EINTR) 407 continue; 408 fatalperror(f, "select"); 409 } 410 if (ibits == 0 && obits == 0 && ebits == 0) { 411 /* shouldn't happen... */ 412 sleep(5); 413 continue; 414 } 415 #define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) 416 if (ebits & pmask) { 417 cc = read(p, &cntl, 1); 418 if (cc == 1 && pkcontrol(cntl)) { 419 cntl |= oobdata[0]; 420 send(f, &cntl, 1, MSG_OOB); 421 if (cntl & TIOCPKT_FLUSHWRITE) { 422 pcc = 0; 423 ibits &= ~pmask; 424 } 425 } 426 } 427 if (ibits & fmask) { 428 #ifdef KERBEROS 429 if (encrypt) 430 fcc = des_read(f, fibuf, sizeof(fibuf)); 431 else 432 #endif 433 fcc = read(f, fibuf, sizeof(fibuf)); 434 if (fcc < 0 && errno == EWOULDBLOCK) 435 fcc = 0; 436 else { 437 register char *cp; 438 int left, n; 439 440 if (fcc <= 0) 441 break; 442 fbp = fibuf; 443 444 top: 445 for (cp = fibuf; cp < fibuf+fcc-1; cp++) 446 if (cp[0] == magic[0] && 447 cp[1] == magic[1]) { 448 left = fcc - (cp-fibuf); 449 n = control(p, cp, left); 450 if (n) { 451 left -= n; 452 if (left > 0) 453 bcopy(cp+n, cp, left); 454 fcc -= n; 455 goto top; /* n^2 */ 456 } 457 } 458 obits |= pmask; /* try write */ 459 } 460 } 461 462 if ((obits & pmask) && fcc > 0) { 463 cc = write(p, fbp, fcc); 464 if (cc > 0) { 465 fcc -= cc; 466 fbp += cc; 467 } 468 } 469 470 if (ibits & pmask) { 471 pcc = read(p, pibuf, sizeof (pibuf)); 472 pbp = pibuf; 473 if (pcc < 0 && errno == EWOULDBLOCK) 474 pcc = 0; 475 else if (pcc <= 0) 476 break; 477 else if (pibuf[0] == 0) { 478 pbp++, pcc--; 479 #ifdef KERBEROS 480 if (!encrypt) 481 #endif 482 obits |= fmask; /* try a write */ 483 } else { 484 if (pkcontrol(pibuf[0])) { 485 pibuf[0] |= oobdata[0]; 486 send(f, &pibuf[0], 1, MSG_OOB); 487 } 488 pcc = 0; 489 } 490 } 491 if ((obits & fmask) && pcc > 0) { 492 #ifdef KERBEROS 493 if (encrypt) 494 cc = des_write(f, pbp, pcc); 495 else 496 #endif 497 cc = write(f, pbp, pcc); 498 if (cc < 0 && errno == EWOULDBLOCK) { 499 /* also shouldn't happen */ 500 sleep(5); 501 continue; 502 } 503 if (cc > 0) { 504 pcc -= cc; 505 pbp += cc; 506 } 507 } 508 } 509 } 510 511 cleanup() 512 { 513 char *p; 514 515 p = line + sizeof("/dev/") - 1; 516 if (logout(p)) 517 logwtmp(p, "", ""); 518 (void)chmod(line, 0666); 519 (void)chown(line, 0, 0); 520 *p = 'p'; 521 (void)chmod(line, 0666); 522 (void)chown(line, 0, 0); 523 shutdown(netf, 2); 524 exit(1); 525 } 526 527 fatal(f, msg) 528 int f; 529 char *msg; 530 { 531 char buf[BUFSIZ]; 532 533 buf[0] = '\01'; /* error indicator */ 534 (void) sprintf(buf + 1, "rlogind: %s.\r\n", msg); 535 (void) write(f, buf, strlen(buf)); 536 exit(1); 537 } 538 539 fatalperror(f, msg) 540 int f; 541 char *msg; 542 { 543 char buf[BUFSIZ]; 544 extern int sys_nerr; 545 extern char *sys_errlist[]; 546 547 if ((unsigned)errno < sys_nerr) 548 (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]); 549 else 550 (void) sprintf(buf, "%s: Error %d", msg, errno); 551 fatal(f, buf); 552 } 553 554 do_rlogin(host) 555 char *host; 556 { 557 558 getstr(rusername, sizeof(rusername), "remuser too long"); 559 getstr(lusername, sizeof(lusername), "locuser too long"); 560 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long"); 561 562 if (getuid()) 563 return(-1); 564 pwd = getpwnam(lusername); 565 if (pwd == NULL) 566 return(-1); 567 return(ruserok(host, SUPERUSER(pwd), rusername, lusername)); 568 } 569 570 571 getstr(buf, cnt, errmsg) 572 char *buf; 573 int cnt; 574 char *errmsg; 575 { 576 char c; 577 578 do { 579 if (read(0, &c, 1) != 1) 580 exit(1); 581 if (--cnt < 0) 582 fatal(1, errmsg); 583 *buf++ = c; 584 } while (c != 0); 585 } 586 587 extern char **environ; 588 589 setup_term(fd) 590 int fd; 591 { 592 struct termios tt; 593 register char *cp = index(term+ENVSIZE, '/'); 594 char *speed; 595 596 tcgetattr(fd, &tt); 597 if (cp) { 598 *cp++ = '\0'; 599 speed = cp; 600 cp = index(speed, '/'); 601 if (cp) 602 *cp++ = '\0'; 603 cfsetspeed(&tt, atoi(speed)); 604 } 605 tt.c_iflag = BRKINT|ICRNL|IXON|ISTRIP|IEXTEN|IMAXBEL; 606 tt.c_oflag = OPOST|ONLCR|OXTABS; 607 tt.c_lflag = ISIG|ICANON|ECHO; 608 tcsetattr(fd, TCSADFLUSH, &tt); 609 610 env[0] = term; 611 env[1] = 0; 612 environ = env; 613 } 614 615 #ifdef KERBEROS 616 #define VERSION_SIZE 9 617 618 /* 619 * Do the remote kerberos login to the named host with the 620 * given inet address 621 * 622 * Return 0 on valid authorization 623 * Return -1 on valid authentication, no authorization 624 * Return >0 for error conditions 625 */ 626 do_krb_login(host, dest, encrypt) 627 char *host; 628 struct sockaddr_in *dest; 629 int encrypt; 630 { 631 int rc; 632 char instance[INST_SZ], version[VERSION_SIZE]; 633 long authopts = 0L; /* !mutual */ 634 struct sockaddr_in faddr; 635 636 if (getuid()) 637 return(KFAILURE); 638 639 kdata = (AUTH_DAT *) auth_buf; 640 ticket = (KTEXT) tick_buf; 641 strcpy(instance, "*"); 642 643 if (encrypt) { 644 rc = sizeof(faddr); 645 if (getsockname(0, &faddr, &rc)) 646 return(-1); 647 authopts = KOPT_DO_MUTUAL; 648 rc = krb_recvauth( 649 authopts, 0, 650 ticket, "rcmd", 651 instance, dest, &faddr, 652 kdata, "", schedule, version); 653 des_set_key(kdata->session, schedule); 654 655 } else { 656 rc = krb_recvauth( 657 authopts, 0, 658 ticket, "rcmd", 659 instance, dest, (struct sockaddr_in *) 0, 660 kdata, "", (bit_64 *) 0, version); 661 } 662 663 if (rc != KSUCCESS) 664 return(rc); 665 666 if ((rc = krb_kntoln(kdata, rusername)) != KSUCCESS) 667 return(rc); 668 669 getstr(lusername, sizeof(lusername), "locuser"); 670 /* get the "cmd" in the rcmd protocol */ 671 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type"); 672 673 pwd = getpwnam(lusername); 674 if (pwd == NULL) 675 return(-1); 676 677 /* returns nonzero for no access */ 678 /* return(ruserok(host, SUPERUSER(pwd), rusername, lusername)); */ 679 if (kuserok(kdata,lusername) != 0) 680 return(-1); 681 682 return(0); 683 684 } 685 686 #endif /* KERBEROS */ 687 688 usage() 689 { 690 #ifdef KERBEROS 691 syslog(LOG_ERR, "usage: rlogind [-k | -v] [-l] [-n]"); 692 #else 693 syslog(LOG_ERR, "usage: rlogind [-l] [-n]"); 694 #endif 695 } 696 697 /* 698 * Check whether host h is in our local domain, 699 * as determined by the part of the name following 700 * the first '.' in its name and in ours. 701 * If either name is unqualified (contains no '.'), 702 * assume that the host is local, as it will be 703 * interpreted as such. 704 */ 705 local_domain(h) 706 char *h; 707 { 708 char localhost[MAXHOSTNAMELEN]; 709 char *p1, *p2 = index(h, '.'); 710 711 (void) gethostname(localhost, sizeof(localhost)); 712 p1 = index(localhost, '.'); 713 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 714 return(1); 715 return(0); 716 } 717