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.22.1.6 (Berkeley) 02/07/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 * Automatic login protocol is done here, using login -f upon success, 37 * unless OLD_LOGIN is defined (then done in login, ala 4.2/4.3BSD). 38 */ 39 40 #include <stdio.h> 41 #include <sys/param.h> 42 #include <sys/stat.h> 43 #include <sys/socket.h> 44 #include <sys/wait.h> 45 #include <sys/file.h> 46 47 #include <netinet/in.h> 48 49 #include <errno.h> 50 #include <pwd.h> 51 #include <signal.h> 52 #include <sgtty.h> 53 #include <stdio.h> 54 #include <netdb.h> 55 #include <syslog.h> 56 #include <strings.h> 57 58 #ifndef TIOCPKT_WINDOW 59 #define TIOCPKT_WINDOW 0x80 60 #endif 61 62 char *env[2]; 63 #define NMAX 30 64 char lusername[NMAX+1], rusername[NMAX+1]; 65 static char term[64] = "TERM="; 66 #define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */ 67 int keepalive = 1; 68 69 #define SUPERUSER(pwd) ((pwd)->pw_uid == 0) 70 71 extern int errno; 72 int reapchild(); 73 struct passwd *getpwnam(), *pwd; 74 char *malloc(); 75 76 main(argc, argv) 77 int argc; 78 char **argv; 79 { 80 extern int opterr, optind, _check_rhosts_file; 81 int ch; 82 int on = 1, fromlen; 83 struct sockaddr_in from; 84 85 openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); 86 87 opterr = 0; 88 while ((ch = getopt(argc, argv, "ln")) != EOF) 89 switch (ch) { 90 case 'l': 91 _check_rhosts_file = 0; 92 break; 93 case 'n': 94 keepalive = 0; 95 break; 96 case '?': 97 default: 98 syslog(LOG_ERR, "usage: rlogind [-l] [-n]"); 99 break; 100 } 101 argc -= optind; 102 argv += optind; 103 104 fromlen = sizeof (from); 105 if (getpeername(0, &from, &fromlen) < 0) { 106 syslog(LOG_ERR, "Couldn't get peer name of remote host: %m"); 107 fatalperror("Can't get peer name of host"); 108 } 109 if (keepalive && 110 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) 111 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 112 doit(0, &from); 113 } 114 115 int child; 116 int cleanup(); 117 int netf; 118 char *line; 119 extern char *inet_ntoa(); 120 121 struct winsize win = { 0, 0, 0, 0 }; 122 123 124 doit(f, fromp) 125 int f; 126 struct sockaddr_in *fromp; 127 { 128 int i, p, t, pid, on = 1; 129 #ifndef OLD_LOGIN 130 int authenticated = 0, hostok = 0; 131 char remotehost[2 * MAXHOSTNAMELEN + 1]; 132 #endif 133 register struct hostent *hp; 134 struct hostent hostent; 135 char c; 136 137 alarm(60); 138 read(f, &c, 1); 139 if (c != 0) 140 exit(1); 141 142 alarm(0); 143 fromp->sin_port = ntohs((u_short)fromp->sin_port); 144 hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr), 145 fromp->sin_family); 146 if (hp == 0) { 147 /* 148 * Only the name is used below. 149 */ 150 hp = &hostent; 151 hp->h_name = inet_ntoa(fromp->sin_addr); 152 #ifndef OLD_LOGIN 153 hostok++; 154 #endif 155 } 156 #ifndef OLD_LOGIN 157 else if (local_domain(hp->h_name)) { 158 /* 159 * If name returned by gethostbyaddr is in our domain, 160 * attempt to verify that we haven't been fooled by someone 161 * in a remote net; look up the name and check that this 162 * address corresponds to the name. 163 */ 164 strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1); 165 remotehost[sizeof(remotehost) - 1] = 0; 166 hp = gethostbyname(remotehost); 167 if (hp) 168 for (; hp->h_addr_list[0]; hp->h_addr_list++) 169 if (!bcmp(hp->h_addr_list[0], (caddr_t)&fromp->sin_addr, 170 sizeof(fromp->sin_addr))) { 171 hostok++; 172 break; 173 } 174 } else 175 hostok++; 176 #endif 177 178 if (fromp->sin_family != AF_INET || 179 fromp->sin_port >= IPPORT_RESERVED || 180 fromp->sin_port < IPPORT_RESERVED/2) { 181 syslog(LOG_NOTICE, "Connection from %s on illegal port", 182 inet_ntoa(fromp->sin_addr)); 183 fatal(f, "Permission denied"); 184 } 185 #ifdef IP_OPTIONS 186 { 187 u_char optbuf[BUFSIZ/3], *cp; 188 char lbuf[BUFSIZ], *lp; 189 int optsize = sizeof(optbuf), ipproto; 190 struct protoent *ip; 191 192 if ((ip = getprotobyname("ip")) != NULL) 193 ipproto = ip->p_proto; 194 else 195 ipproto = IPPROTO_IP; 196 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0 && 197 optsize != 0) { 198 lp = lbuf; 199 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) 200 sprintf(lp, " %2.2x", *cp); 201 syslog(LOG_NOTICE, 202 "Connection received using IP options (ignored):%s", lbuf); 203 if (setsockopt(0, ipproto, IP_OPTIONS, 204 (char *)NULL, &optsize) != 0) { 205 syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); 206 exit(1); 207 } 208 } 209 } 210 #endif 211 write(f, "", 1); 212 #ifndef OLD_LOGIN 213 if (do_rlogin(hp->h_name) == 0) { 214 if (hostok) 215 authenticated++; 216 else 217 write(f, "rlogind: Host address mismatch.\r\n", 218 sizeof("rlogind: Host address mismatch.\r\n") - 1); 219 } 220 #endif 221 222 for (c = 'p'; c <= 's'; c++) { 223 struct stat stb; 224 line = "/dev/ptyXX"; 225 line[strlen("/dev/pty")] = c; 226 line[strlen("/dev/ptyp")] = '0'; 227 if (stat(line, &stb) < 0) 228 break; 229 for (i = 0; i < 16; i++) { 230 line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i]; 231 p = open(line, O_RDWR); 232 if (p > 0) 233 goto gotpty; 234 } 235 } 236 fatal(f, "Out of ptys"); 237 /*NOTREACHED*/ 238 gotpty: 239 (void) ioctl(p, TIOCSWINSZ, &win); 240 netf = f; 241 line[strlen("/dev/")] = 't'; 242 t = open(line, O_RDWR); 243 if (t < 0) 244 fatalperror(f, line); 245 if (fchmod(t, 0)) 246 fatalperror(f, line); 247 (void)signal(SIGHUP, SIG_IGN); 248 vhangup(); 249 (void)signal(SIGHUP, SIG_DFL); 250 t = open(line, O_RDWR); 251 if (t < 0) 252 fatalperror(f, line); 253 setup_term(t); 254 #ifdef DEBUG 255 { 256 int tt = open("/dev/tty", O_RDWR); 257 if (tt > 0) { 258 (void)ioctl(tt, TIOCNOTTY, 0); 259 (void)close(tt); 260 } 261 } 262 #endif 263 pid = fork(); 264 if (pid < 0) 265 fatalperror(f, ""); 266 if (pid == 0) { 267 close(f), close(p); 268 dup2(t, 0), dup2(t, 1), dup2(t, 2); 269 close(t); 270 #ifdef OLD_LOGIN 271 execl("/bin/login", "login", "-r", hp->h_name, 0); 272 #else /* OLD_LOGIN */ 273 if (authenticated) 274 execl("/bin/login", "login", "-p", "-h", hp->h_name, 275 "-f", lusername, 0); 276 else 277 execl("/bin/login", "login", "-p", "-h", hp->h_name, 278 lusername, 0); 279 #endif /* OLD_LOGIN */ 280 fatalperror(2, "/bin/login"); 281 /*NOTREACHED*/ 282 } 283 close(t); 284 285 ioctl(f, FIONBIO, &on); 286 ioctl(p, FIONBIO, &on); 287 ioctl(p, TIOCPKT, &on); 288 signal(SIGTSTP, SIG_IGN); 289 signal(SIGCHLD, cleanup); 290 setpgrp(0, 0); 291 protocol(f, p); 292 signal(SIGCHLD, SIG_IGN); 293 cleanup(); 294 } 295 296 char magic[2] = { 0377, 0377 }; 297 char oobdata[] = {TIOCPKT_WINDOW}; 298 299 /* 300 * Handle a "control" request (signaled by magic being present) 301 * in the data stream. For now, we are only willing to handle 302 * window size changes. 303 */ 304 control(pty, cp, n) 305 int pty; 306 char *cp; 307 int n; 308 { 309 struct winsize w; 310 311 if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') 312 return (0); 313 oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ 314 bcopy(cp+4, (char *)&w, sizeof(w)); 315 w.ws_row = ntohs(w.ws_row); 316 w.ws_col = ntohs(w.ws_col); 317 w.ws_xpixel = ntohs(w.ws_xpixel); 318 w.ws_ypixel = ntohs(w.ws_ypixel); 319 (void)ioctl(pty, TIOCSWINSZ, &w); 320 return (4+sizeof (w)); 321 } 322 323 /* 324 * rlogin "protocol" machine. 325 */ 326 protocol(f, p) 327 int f, p; 328 { 329 char pibuf[1024], fibuf[1024], *pbp, *fbp; 330 register pcc = 0, fcc = 0; 331 int cc, nfd, pmask, fmask; 332 char cntl; 333 334 /* 335 * Must ignore SIGTTOU, otherwise we'll stop 336 * when we try and set slave pty's window shape 337 * (our controlling tty is the master pty). 338 */ 339 (void) signal(SIGTTOU, SIG_IGN); 340 send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ 341 if (f > p) 342 nfd = f + 1; 343 else 344 nfd = p + 1; 345 fmask = 1 << f; 346 pmask = 1 << p; 347 for (;;) { 348 int ibits, obits, ebits; 349 350 ibits = 0; 351 obits = 0; 352 if (fcc) 353 obits |= pmask; 354 else 355 ibits |= fmask; 356 if (pcc >= 0) 357 if (pcc) 358 obits |= fmask; 359 else 360 ibits |= pmask; 361 ebits = pmask; 362 if (select(nfd, &ibits, obits ? &obits : (int *)NULL, 363 &ebits, 0) < 0) { 364 if (errno == EINTR) 365 continue; 366 fatalperror(f, "select"); 367 } 368 if (ibits == 0 && obits == 0 && ebits == 0) { 369 /* shouldn't happen... */ 370 sleep(5); 371 continue; 372 } 373 #define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) 374 if (ebits & pmask) { 375 cc = read(p, &cntl, 1); 376 if (cc == 1 && pkcontrol(cntl)) { 377 cntl |= oobdata[0]; 378 send(f, &cntl, 1, MSG_OOB); 379 if (cntl & TIOCPKT_FLUSHWRITE) { 380 pcc = 0; 381 ibits &= ~pmask; 382 } 383 } 384 } 385 if (ibits & fmask) { 386 fcc = read(f, fibuf, sizeof(fibuf)); 387 if (fcc < 0 && errno == EWOULDBLOCK) 388 fcc = 0; 389 else { 390 register char *cp; 391 int left, n; 392 393 if (fcc <= 0) 394 break; 395 fbp = fibuf; 396 397 top: 398 for (cp = fibuf; cp < fibuf+fcc-1; cp++) 399 if (cp[0] == magic[0] && 400 cp[1] == magic[1]) { 401 left = fcc - (cp-fibuf); 402 n = control(p, cp, left); 403 if (n) { 404 left -= n; 405 if (left > 0) 406 bcopy(cp+n, cp, left); 407 fcc -= n; 408 goto top; /* n^2 */ 409 } 410 } 411 obits |= pmask; /* try write */ 412 } 413 } 414 415 if ((obits & pmask) && fcc > 0) { 416 cc = write(p, fbp, fcc); 417 if (cc > 0) { 418 fcc -= cc; 419 fbp += cc; 420 } 421 } 422 423 if (ibits & pmask) { 424 pcc = read(p, pibuf, sizeof (pibuf)); 425 pbp = pibuf; 426 if (pcc < 0 && errno == EWOULDBLOCK) 427 pcc = 0; 428 else if (pcc <= 0) 429 break; 430 else if (pibuf[0] == 0) { 431 pbp++, pcc--; 432 obits |= fmask; /* try a write */ 433 } else { 434 if (pkcontrol(pibuf[0])) { 435 pibuf[0] |= oobdata[0]; 436 send(f, &pibuf[0], 1, MSG_OOB); 437 } 438 pcc = 0; 439 } 440 } 441 if ((obits & fmask) && pcc > 0) { 442 cc = write(f, pbp, pcc); 443 if (cc < 0 && errno == EWOULDBLOCK) { 444 /* also shouldn't happen */ 445 sleep(5); 446 continue; 447 } 448 if (cc > 0) { 449 pcc -= cc; 450 pbp += cc; 451 } 452 } 453 } 454 } 455 456 cleanup() 457 { 458 char *p; 459 460 p = line + sizeof("/dev/") - 1; 461 if (logout(p)) 462 logwtmp(p, "", ""); 463 (void)chmod(line, 0666); 464 (void)chown(line, 0, 0); 465 *p = 'p'; 466 (void)chmod(line, 0666); 467 (void)chown(line, 0, 0); 468 shutdown(netf, 2); 469 exit(1); 470 } 471 472 fatal(f, msg) 473 int f; 474 char *msg; 475 { 476 char buf[BUFSIZ]; 477 478 buf[0] = '\01'; /* error indicator */ 479 (void) sprintf(buf + 1, "rlogind: %s.\r\n", msg); 480 (void) write(f, buf, strlen(buf)); 481 exit(1); 482 } 483 484 fatalperror(f, msg) 485 int f; 486 char *msg; 487 { 488 char buf[BUFSIZ]; 489 extern int sys_nerr; 490 extern char *sys_errlist[]; 491 492 if ((unsigned)errno < sys_nerr) 493 (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]); 494 else 495 (void) sprintf(buf, "%s: Error %d", msg, errno); 496 fatal(f, buf); 497 } 498 499 #ifndef OLD_LOGIN 500 do_rlogin(host) 501 char *host; 502 { 503 504 getstr(rusername, sizeof(rusername), "remuser too long"); 505 getstr(lusername, sizeof(lusername), "locuser too long"); 506 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long"); 507 508 if (getuid()) 509 return(-1); 510 pwd = getpwnam(lusername); 511 if (pwd == NULL) 512 return(-1); 513 return(ruserok(host, SUPERUSER(pwd), rusername, lusername)); 514 } 515 516 517 getstr(buf, cnt, errmsg) 518 char *buf; 519 int cnt; 520 char *errmsg; 521 { 522 char c; 523 524 do { 525 if (read(0, &c, 1) != 1) 526 exit(1); 527 if (--cnt < 0) 528 fatal(1, errmsg); 529 *buf++ = c; 530 } while (c != 0); 531 } 532 533 extern char **environ; 534 535 char *speeds[] = { 536 "0", "50", "75", "110", "134", "150", "200", "300", "600", 537 "1200", "1800", "2400", "4800", "9600", "19200", "38400", 538 }; 539 #define NSPEEDS (sizeof(speeds) / sizeof(speeds[0])) 540 541 setup_term(fd) 542 int fd; 543 { 544 register char *cp = index(term, '/'), **cpp; 545 struct sgttyb sgttyb; 546 char *speed; 547 548 (void)ioctl(fd, TIOCGETP, &sgttyb); 549 if (cp) { 550 *cp++ = '\0'; 551 speed = cp; 552 cp = index(speed, '/'); 553 if (cp) 554 *cp++ = '\0'; 555 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++) 556 if (strcmp(*cpp, speed) == 0) { 557 sgttyb.sg_ispeed = sgttyb.sg_ospeed = cpp - speeds; 558 break; 559 } 560 } 561 sgttyb.sg_flags = ECHO|CRMOD|ANYP|XTABS; 562 (void)ioctl(fd, TIOCSETP, &sgttyb); 563 564 env[0] = term; 565 env[1] = 0; 566 environ = env; 567 } 568 569 /* 570 * Check whether host h is in our local domain, 571 * as determined by the part of the name following 572 * the first '.' in its name and in ours. 573 * If either name is unqualified (contains no '.'), 574 * assume that the host is local, as it will be 575 * interpreted as such. 576 */ 577 local_domain(h) 578 char *h; 579 { 580 char localhost[MAXHOSTNAMELEN]; 581 char *p1, *p2 = index(h, '.'); 582 583 (void) gethostname(localhost, sizeof(localhost)); 584 p1 = index(localhost, '.'); 585 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) 586 return(1); 587 return(0); 588 } 589 #endif /* OLD_LOGIN */ 590