rlogind.c revision 96196
1/*- 2 * Copyright (c) 1983, 1988, 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 2002 Networks Associates Technology, Inc. 5 * All rights reserved. 6 * 7 * Portions of this software were developed for the FreeBSD Project by 8 * ThinkSec AS and NAI Labs, the Security Research Division of Network 9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 10 * ("CBOSS"), as part of the DARPA CHATS research program. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 */ 40 41#ifndef lint 42static const char copyright[] = 43"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\ 44 The Regents of the University of California. All rights reserved.\n"; 45#endif /* not lint */ 46 47#ifndef lint 48#if 0 49static const char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93"; 50#endif 51static const char rcsid[] = 52 "$FreeBSD: head/libexec/rlogind/rlogind.c 96196 2002-05-08 00:47:01Z des $"; 53#endif /* not lint */ 54 55/* 56 * remote login server: 57 * \0 58 * remuser\0 59 * locuser\0 60 * terminal_type/speed\0 61 * data 62 */ 63 64#define FD_SETSIZE 16 /* don't need many bits for select */ 65#include <sys/types.h> 66#include <sys/param.h> 67#include <sys/stat.h> 68#include <sys/ioctl.h> 69#include <signal.h> 70#include <termios.h> 71 72#include <sys/socket.h> 73#include <netinet/in.h> 74#include <netinet/in_systm.h> 75#include <netinet/ip.h> 76#include <netinet/tcp.h> 77#include <arpa/inet.h> 78#include <netdb.h> 79 80#include <errno.h> 81#include <libutil.h> 82#include <paths.h> 83#include <pwd.h> 84#include <syslog.h> 85#include <stdio.h> 86#include <stdlib.h> 87#include <string.h> 88#include <unistd.h> 89 90 91#ifndef TIOCPKT_WINDOW 92#define TIOCPKT_WINDOW 0x80 93#endif 94 95#define ARGSTR "Dalnx" 96 97/* wrapper for KAME-special getnameinfo() */ 98#ifndef NI_WITHSCOPEID 99#define NI_WITHSCOPEID 0 100#endif 101 102char *env[2]; 103#define NMAX 30 104char lusername[NMAX+1], rusername[NMAX+1]; 105static char term[64] = "TERM="; 106#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */ 107int keepalive = 1; 108int check_all = 0; 109int no_delay; 110 111struct passwd *pwd; 112 113union sockunion { 114 struct sockinet { 115 u_char si_len; 116 u_char si_family; 117 u_short si_port; 118 } su_si; 119 struct sockaddr_in su_sin; 120 struct sockaddr_in6 su_sin6; 121}; 122#define su_len su_si.si_len 123#define su_family su_si.si_family 124#define su_port su_si.si_port 125 126void doit(int, union sockunion *); 127int control(int, char *, int); 128void protocol(int, int); 129void cleanup(int); 130void fatal(int, char *, int); 131int do_rlogin(union sockunion *); 132void getstr(char *, int, char *); 133void setup_term(int); 134int do_krb_login(struct sockaddr_in *); 135void usage(void); 136 137 138int 139main(int argc, char *argv[]) 140{ 141 extern int __check_rhosts_file; 142 union sockunion from; 143 int ch, fromlen, on; 144 145 openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); 146 147 opterr = 0; 148 while ((ch = getopt(argc, argv, ARGSTR)) != -1) 149 switch (ch) { 150 case 'D': 151 no_delay = 1; 152 break; 153 case 'a': 154 check_all = 1; 155 break; 156 case 'l': 157 __check_rhosts_file = 0; 158 break; 159 case 'n': 160 keepalive = 0; 161 break; 162#ifdef CRYPT 163 case 'x': 164 doencrypt = 1; 165 break; 166#endif 167 case '?': 168 default: 169 usage(); 170 break; 171 } 172 argc -= optind; 173 argv += optind; 174 175 fromlen = sizeof (from); 176 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 177 syslog(LOG_ERR,"Can't get peer name of remote host: %m"); 178 fatal(STDERR_FILENO, "Can't get peer name of remote host", 1); 179 } 180 on = 1; 181 if (keepalive && 182 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) 183 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 184 if (no_delay && 185 setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) 186 syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); 187 if (from.su_family == AF_INET) 188 { 189 on = IPTOS_LOWDELAY; 190 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 191 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 192 } 193 194 doit(0, &from); 195 return 0; 196} 197 198int child; 199int netf; 200char line[MAXPATHLEN]; 201int confirmed; 202 203struct winsize win = { 0, 0, 0, 0 }; 204 205 206void 207doit(int f, union sockunion *fromp) 208{ 209 int master, pid, on = 1; 210 int authenticated = 0; 211 char hostname[2 * MAXHOSTNAMELEN + 1]; 212 char nameinfo[2 * INET6_ADDRSTRLEN + 1]; 213 char c; 214 215 alarm(60); 216 read(f, &c, 1); 217 218 if (c != 0) 219 exit(1); 220 221 alarm(0); 222 223 realhostname_sa(hostname, sizeof(hostname) - 1, 224 (struct sockaddr *)fromp, fromp->su_len); 225 /* error check ? */ 226 fromp->su_port = ntohs((u_short)fromp->su_port); 227 hostname[sizeof(hostname) - 1] = '\0'; 228 229 { 230 if ((fromp->su_family != AF_INET 231#ifdef INET6 232 && fromp->su_family != AF_INET6 233#endif 234 ) || 235 fromp->su_port >= IPPORT_RESERVED || 236 fromp->su_port < IPPORT_RESERVED/2) { 237 getnameinfo((struct sockaddr *)fromp, 238 fromp->su_len, 239 nameinfo, sizeof(nameinfo), NULL, 0, 240 NI_NUMERICHOST|NI_WITHSCOPEID); 241 /* error check ? */ 242 syslog(LOG_NOTICE, "Connection from %s on illegal port", 243 nameinfo); 244 fatal(f, "Permission denied", 0); 245 } 246#ifdef IP_OPTIONS 247 if (fromp->su_family == AF_INET) 248 { 249 u_char optbuf[BUFSIZ/3]; 250 int optsize = sizeof(optbuf), ipproto, i; 251 struct protoent *ip; 252 253 if ((ip = getprotobyname("ip")) != NULL) 254 ipproto = ip->p_proto; 255 else 256 ipproto = IPPROTO_IP; 257 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, 258 &optsize) == 0 && optsize != 0) { 259 for (i = 0; i < optsize; ) { 260 u_char c = optbuf[i]; 261 if (c == IPOPT_LSRR || c == IPOPT_SSRR) { 262 syslog(LOG_NOTICE, 263 "Connection refused from %s with IP option %s", 264 inet_ntoa(fromp->su_sin.sin_addr), 265 c == IPOPT_LSRR ? "LSRR" : "SSRR"); 266 exit(1); 267 } 268 if (c == IPOPT_EOL) 269 break; 270 i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; 271 } 272 } 273 } 274#endif 275 if (do_rlogin(fromp) == 0) 276 authenticated++; 277 } 278 if (confirmed == 0) { 279 write(f, "", 1); 280 confirmed = 1; /* we sent the null! */ 281 } 282#ifdef CRYPT 283 if (doencrypt) 284 (void) des_enc_write(f, 285 SECURE_MESSAGE, 286 strlen(SECURE_MESSAGE), 287 schedule, &kdata->session); 288#endif 289 netf = f; 290 291 pid = forkpty(&master, line, NULL, &win); 292 if (pid < 0) { 293 if (errno == ENOENT) 294 fatal(f, "Out of ptys", 0); 295 else 296 fatal(f, "Forkpty", 1); 297 } 298 if (pid == 0) { 299 if (f > 2) /* f should always be 0, but... */ 300 (void) close(f); 301 setup_term(0); 302 if (*lusername=='-') { 303 syslog(LOG_ERR, "tried to pass user \"%s\" to login", 304 lusername); 305 fatal(STDERR_FILENO, "invalid user", 0); 306 } 307 if (authenticated) { 308 execl(_PATH_LOGIN, "login", "-p", 309 "-h", hostname, "-f", lusername, (char *)NULL); 310 } else 311 execl(_PATH_LOGIN, "login", "-p", 312 "-h", hostname, lusername, (char *)NULL); 313 fatal(STDERR_FILENO, _PATH_LOGIN, 1); 314 /*NOTREACHED*/ 315 } 316#ifdef CRYPT 317 /* 318 * If encrypted, don't turn on NBIO or the des read/write 319 * routines will croak. 320 */ 321 322 if (!doencrypt) 323#endif 324 ioctl(f, FIONBIO, &on); 325 ioctl(master, FIONBIO, &on); 326 ioctl(master, TIOCPKT, &on); 327 signal(SIGCHLD, cleanup); 328 protocol(f, master); 329 signal(SIGCHLD, SIG_IGN); 330 cleanup(0); 331} 332 333char magic[2] = { 0377, 0377 }; 334char oobdata[] = {TIOCPKT_WINDOW}; 335 336/* 337 * Handle a "control" request (signaled by magic being present) 338 * in the data stream. For now, we are only willing to handle 339 * window size changes. 340 */ 341int 342control(int pty, char *cp, int n) 343{ 344 struct winsize w; 345 346 if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') 347 return (0); 348 oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ 349 bcopy(cp+4, (char *)&w, sizeof(w)); 350 w.ws_row = ntohs(w.ws_row); 351 w.ws_col = ntohs(w.ws_col); 352 w.ws_xpixel = ntohs(w.ws_xpixel); 353 w.ws_ypixel = ntohs(w.ws_ypixel); 354 (void)ioctl(pty, TIOCSWINSZ, &w); 355 return (4+sizeof (w)); 356} 357 358/* 359 * rlogin "protocol" machine. 360 */ 361void 362protocol(int f, int p) 363{ 364 char pibuf[1024+1], fibuf[1024], *pbp = NULL, *fbp = NULL; 365 int pcc = 0, fcc = 0; 366 int cc, nfd, n; 367 char cntl; 368 369 /* 370 * Must ignore SIGTTOU, otherwise we'll stop 371 * when we try and set slave pty's window shape 372 * (our controlling tty is the master pty). 373 */ 374 (void) signal(SIGTTOU, SIG_IGN); 375 send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ 376 if (f > p) 377 nfd = f + 1; 378 else 379 nfd = p + 1; 380 if (nfd > FD_SETSIZE) { 381 syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE"); 382 fatal(f, "internal error (select mask too small)", 0); 383 } 384 for (;;) { 385 fd_set ibits, obits, ebits, *omask; 386 387 FD_ZERO(&ebits); 388 FD_ZERO(&ibits); 389 FD_ZERO(&obits); 390 omask = (fd_set *)NULL; 391 if (fcc) { 392 FD_SET(p, &obits); 393 omask = &obits; 394 } else 395 FD_SET(f, &ibits); 396 if (pcc >= 0) { 397 if (pcc) { 398 FD_SET(f, &obits); 399 omask = &obits; 400 } else 401 FD_SET(p, &ibits); 402 } 403 FD_SET(p, &ebits); 404 if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) { 405 if (errno == EINTR) 406 continue; 407 fatal(f, "select", 1); 408 } 409 if (n == 0) { 410 /* shouldn't happen... */ 411 sleep(5); 412 continue; 413 } 414#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) 415 if (FD_ISSET(p, &ebits)) { 416 cc = read(p, &cntl, 1); 417 if (cc == 1 && pkcontrol(cntl)) { 418 cntl |= oobdata[0]; 419 send(f, &cntl, 1, MSG_OOB); 420 if (cntl & TIOCPKT_FLUSHWRITE) { 421 pcc = 0; 422 FD_CLR(p, &ibits); 423 } 424 } 425 } 426 if (FD_ISSET(f, &ibits)) { 427#ifdef CRYPT 428 if (doencrypt) 429 fcc = des_enc_read(f, fibuf, sizeof(fibuf), 430 schedule, &kdata->session); 431 else 432#endif 433 fcc = read(f, fibuf, sizeof(fibuf)); 434 if (fcc < 0 && errno == EWOULDBLOCK) 435 fcc = 0; 436 else { 437 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 FD_SET(p, &obits); /* try write */ 459 } 460 } 461 462 if (FD_ISSET(p, &obits) && fcc > 0) { 463 cc = write(p, fbp, fcc); 464 if (cc > 0) { 465 fcc -= cc; 466 fbp += cc; 467 } 468 } 469 470 if (FD_ISSET(p, &ibits)) { 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 CRYPT 480 if (!doencrypt) 481#endif 482 FD_SET(f, &obits); /* try 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 ((FD_ISSET(f, &obits)) && pcc > 0) { 492#ifdef CRYPT 493 if (doencrypt) 494 cc = des_enc_write(f, pbp, pcc, 495 schedule, &kdata->session); 496 else 497#endif 498 cc = write(f, pbp, pcc); 499 if (cc < 0 && errno == EWOULDBLOCK) { 500 /* 501 * This happens when we try write after read 502 * from p, but some old kernels balk at large 503 * writes even when select returns true. 504 */ 505 if (!FD_ISSET(p, &ibits)) 506 sleep(5); 507 continue; 508 } 509 if (cc > 0) { 510 pcc -= cc; 511 pbp += cc; 512 } 513 } 514 } 515} 516 517void 518cleanup(int signo) 519{ 520 char *p; 521 522 p = line + sizeof(_PATH_DEV) - 1; 523 if (logout(p)) 524 logwtmp(p, "", ""); 525 (void)chflags(line, 0); 526 (void)chmod(line, 0666); 527 (void)chown(line, 0, 0); 528 *p = 'p'; 529 (void)chflags(line, 0); 530 (void)chmod(line, 0666); 531 (void)chown(line, 0, 0); 532 shutdown(netf, 2); 533 exit(1); 534} 535 536void 537fatal(int f, char *msg, int syserr) 538{ 539 int len; 540 char buf[BUFSIZ], *bp = buf; 541 542 /* 543 * Prepend binary one to message if we haven't sent 544 * the magic null as confirmation. 545 */ 546 if (!confirmed) 547 *bp++ = '\01'; /* error indicator */ 548 if (syserr) 549 len = snprintf(bp, sizeof(buf), "rlogind: %s: %s.\r\n", 550 msg, strerror(errno)); 551 else 552 len = snprintf(bp, sizeof(buf), "rlogind: %s.\r\n", msg); 553 if (len < 0) 554 len = 0; 555 (void) write(f, buf, bp + len - buf); 556 exit(1); 557} 558 559int 560do_rlogin(union sockunion *dest) 561{ 562 563 getstr(rusername, sizeof(rusername), "remuser too long"); 564 getstr(lusername, sizeof(lusername), "locuser too long"); 565 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long"); 566 567 pwd = getpwnam(lusername); 568 if (pwd == NULL) 569 return (-1); 570 /* XXX why don't we syslog() failure? */ 571 572 return (iruserok_sa(dest, dest->su_len, pwd->pw_uid == 0, rusername, 573 lusername)); 574} 575 576void 577getstr(char *buf, int cnt, char *errmsg) 578{ 579 char c; 580 581 do { 582 if (read(STDIN_FILENO, &c, 1) != 1) 583 exit(1); 584 if (--cnt < 0) 585 fatal(STDOUT_FILENO, errmsg, 0); 586 *buf++ = c; 587 } while (c != 0); 588} 589 590extern char **environ; 591 592void 593setup_term(int fd) 594{ 595 char *cp = index(term+ENVSIZE, '/'); 596 char *speed; 597 struct termios tt; 598 599#ifndef notyet 600 tcgetattr(fd, &tt); 601 if (cp) { 602 *cp++ = '\0'; 603 speed = cp; 604 cp = index(speed, '/'); 605 if (cp) 606 *cp++ = '\0'; 607 cfsetspeed(&tt, atoi(speed)); 608 } 609 610 tt.c_iflag = TTYDEF_IFLAG; 611 tt.c_oflag = TTYDEF_OFLAG; 612 tt.c_lflag = TTYDEF_LFLAG; 613 tcsetattr(fd, TCSAFLUSH, &tt); 614#else 615 if (cp) { 616 *cp++ = '\0'; 617 speed = cp; 618 cp = index(speed, '/'); 619 if (cp) 620 *cp++ = '\0'; 621 tcgetattr(fd, &tt); 622 cfsetspeed(&tt, atoi(speed)); 623 tcsetattr(fd, TCSAFLUSH, &tt); 624 } 625#endif 626 627 env[0] = term; 628 env[1] = 0; 629 environ = env; 630} 631 632void 633usage(void) 634{ 635 syslog(LOG_ERR, "usage: rlogind [-" ARGSTR "]"); 636} 637