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#if 0 42#ifndef lint 43static const char copyright[] = 44"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\ 45 The Regents of the University of California. All rights reserved.\n"; 46#endif /* not lint */ 47 48#ifndef lint 49static const char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93"; 50#endif /* not lint */ 51#endif 52#include <sys/cdefs.h> 53__FBSDID("$FreeBSD$"); 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 "Daln" 96 97char *env[2]; 98#define NMAX 30 99char lusername[NMAX+1], rusername[NMAX+1]; 100static char term[64] = "TERM="; 101#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */ 102int keepalive = 1; 103int check_all = 0; 104int no_delay; 105 106struct passwd *pwd; 107 108union sockunion { 109 struct sockinet { 110 u_char si_len; 111 u_char si_family; 112 u_short si_port; 113 } su_si; 114 struct sockaddr_in su_sin; 115 struct sockaddr_in6 su_sin6; 116}; 117#define su_len su_si.si_len 118#define su_family su_si.si_family 119#define su_port su_si.si_port 120 121void doit(int, union sockunion *); 122int control(int, char *, int); 123void protocol(int, int); 124void cleanup(int); 125void fatal(int, char *, int); 126int do_rlogin(union sockunion *); 127void getstr(char *, int, char *); 128void setup_term(int); 129int do_krb_login(struct sockaddr_in *); 130void usage(void); 131 132 133int 134main(int argc, char *argv[]) 135{ 136 extern int __check_rhosts_file; 137 union sockunion from; 138 socklen_t fromlen; 139 int ch, on; 140 141 openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); 142 143 opterr = 0; 144 while ((ch = getopt(argc, argv, ARGSTR)) != -1) 145 switch (ch) { 146 case 'D': 147 no_delay = 1; 148 break; 149 case 'a': 150 check_all = 1; 151 break; 152 case 'l': 153 __check_rhosts_file = 0; 154 break; 155 case 'n': 156 keepalive = 0; 157 break; 158 case '?': 159 default: 160 usage(); 161 break; 162 } 163 argc -= optind; 164 argv += optind; 165 166 fromlen = sizeof (from); 167 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 168 syslog(LOG_ERR,"Can't get peer name of remote host: %m"); 169 fatal(STDERR_FILENO, "Can't get peer name of remote host", 1); 170 } 171 on = 1; 172 if (keepalive && 173 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) 174 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 175 if (no_delay && 176 setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) 177 syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); 178 if (from.su_family == AF_INET) 179 { 180 on = IPTOS_LOWDELAY; 181 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 182 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 183 } 184 185 doit(0, &from); 186 return 0; 187} 188 189int child; 190int netf; 191char line[MAXPATHLEN]; 192int confirmed; 193 194struct winsize win = { 0, 0, 0, 0 }; 195 196 197void 198doit(int f, union sockunion *fromp) 199{ 200 int master, pid, on = 1; 201 int authenticated = 0; 202 char hostname[2 * MAXHOSTNAMELEN + 1]; 203 char nameinfo[2 * INET6_ADDRSTRLEN + 1]; 204 char c; 205 206 alarm(60); 207 read(f, &c, 1); 208 209 if (c != 0) 210 exit(1); 211 212 alarm(0); 213 214 realhostname_sa(hostname, sizeof(hostname) - 1, 215 (struct sockaddr *)fromp, fromp->su_len); 216 /* error check ? */ 217 fromp->su_port = ntohs((u_short)fromp->su_port); 218 hostname[sizeof(hostname) - 1] = '\0'; 219 220 { 221 if ((fromp->su_family != AF_INET 222#ifdef INET6 223 && fromp->su_family != AF_INET6 224#endif 225 ) || 226 fromp->su_port >= IPPORT_RESERVED || 227 fromp->su_port < IPPORT_RESERVED/2) { 228 getnameinfo((struct sockaddr *)fromp, 229 fromp->su_len, 230 nameinfo, sizeof(nameinfo), NULL, 0, 231 NI_NUMERICHOST); 232 /* error check ? */ 233 syslog(LOG_NOTICE, "Connection from %s on illegal port", 234 nameinfo); 235 fatal(f, "Permission denied", 0); 236 } 237#ifdef IP_OPTIONS 238 if (fromp->su_family == AF_INET) 239 { 240 u_char optbuf[BUFSIZ/3]; 241 socklen_t optsize = sizeof(optbuf); 242 int ipproto, i; 243 struct protoent *ip; 244 245 if ((ip = getprotobyname("ip")) != NULL) 246 ipproto = ip->p_proto; 247 else 248 ipproto = IPPROTO_IP; 249 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, 250 &optsize) == 0 && optsize != 0) { 251 for (i = 0; i < optsize; ) { 252 u_char c = optbuf[i]; 253 if (c == IPOPT_LSRR || c == IPOPT_SSRR) { 254 syslog(LOG_NOTICE, 255 "Connection refused from %s with IP option %s", 256 inet_ntoa(fromp->su_sin.sin_addr), 257 c == IPOPT_LSRR ? "LSRR" : "SSRR"); 258 exit(1); 259 } 260 if (c == IPOPT_EOL) 261 break; 262 i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; 263 } 264 } 265 } 266#endif 267 if (do_rlogin(fromp) == 0) 268 authenticated++; 269 } 270 if (confirmed == 0) { 271 write(f, "", 1); 272 confirmed = 1; /* we sent the null! */ 273 } 274 netf = f; 275 276 pid = forkpty(&master, line, NULL, &win); 277 if (pid < 0) { 278 if (errno == ENOENT) 279 fatal(f, "Out of ptys", 0); 280 else 281 fatal(f, "Forkpty", 1); 282 } 283 if (pid == 0) { 284 if (f > 2) /* f should always be 0, but... */ 285 (void) close(f); 286 setup_term(0); 287 if (*lusername=='-') { 288 syslog(LOG_ERR, "tried to pass user \"%s\" to login", 289 lusername); 290 fatal(STDERR_FILENO, "invalid user", 0); 291 } 292 if (authenticated) { 293 execl(_PATH_LOGIN, "login", "-p", 294 "-h", hostname, "-f", lusername, (char *)NULL); 295 } else 296 execl(_PATH_LOGIN, "login", "-p", 297 "-h", hostname, lusername, (char *)NULL); 298 fatal(STDERR_FILENO, _PATH_LOGIN, 1); 299 /*NOTREACHED*/ 300 } 301 ioctl(f, FIONBIO, &on); 302 ioctl(master, FIONBIO, &on); 303 ioctl(master, TIOCPKT, &on); 304 signal(SIGCHLD, cleanup); 305 protocol(f, master); 306 signal(SIGCHLD, SIG_IGN); 307 cleanup(0); 308} 309 310char magic[2] = { 0377, 0377 }; 311char oobdata[] = {TIOCPKT_WINDOW}; 312 313/* 314 * Handle a "control" request (signaled by magic being present) 315 * in the data stream. For now, we are only willing to handle 316 * window size changes. 317 */ 318int 319control(int pty, char *cp, int n) 320{ 321 struct winsize w; 322 323 if (n < 4 + (int)sizeof(w) || cp[2] != 's' || cp[3] != 's') 324 return (0); 325 oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ 326 bcopy(cp+4, (char *)&w, sizeof(w)); 327 w.ws_row = ntohs(w.ws_row); 328 w.ws_col = ntohs(w.ws_col); 329 w.ws_xpixel = ntohs(w.ws_xpixel); 330 w.ws_ypixel = ntohs(w.ws_ypixel); 331 (void)ioctl(pty, TIOCSWINSZ, &w); 332 return (4+sizeof (w)); 333} 334 335/* 336 * rlogin "protocol" machine. 337 */ 338void 339protocol(int f, int p) 340{ 341 char pibuf[1024+1], fibuf[1024], *pbp = NULL, *fbp = NULL; 342 int pcc = 0, fcc = 0; 343 int cc, nfd, n; 344 char cntl; 345 346 /* 347 * Must ignore SIGTTOU, otherwise we'll stop 348 * when we try and set slave pty's window shape 349 * (our controlling tty is the master pty). 350 */ 351 (void) signal(SIGTTOU, SIG_IGN); 352 send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ 353 if (f > p) 354 nfd = f + 1; 355 else 356 nfd = p + 1; 357 if (nfd > FD_SETSIZE) { 358 syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE"); 359 fatal(f, "internal error (select mask too small)", 0); 360 } 361 for (;;) { 362 fd_set ibits, obits, ebits, *omask; 363 364 FD_ZERO(&ebits); 365 FD_ZERO(&ibits); 366 FD_ZERO(&obits); 367 omask = (fd_set *)NULL; 368 if (fcc) { 369 FD_SET(p, &obits); 370 omask = &obits; 371 } else 372 FD_SET(f, &ibits); 373 if (pcc >= 0) { 374 if (pcc) { 375 FD_SET(f, &obits); 376 omask = &obits; 377 } else 378 FD_SET(p, &ibits); 379 } 380 FD_SET(p, &ebits); 381 if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) { 382 if (errno == EINTR) 383 continue; 384 fatal(f, "select", 1); 385 } 386 if (n == 0) { 387 /* shouldn't happen... */ 388 sleep(5); 389 continue; 390 } 391#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) 392 if (FD_ISSET(p, &ebits)) { 393 cc = read(p, &cntl, 1); 394 if (cc == 1 && pkcontrol(cntl)) { 395 cntl |= oobdata[0]; 396 send(f, &cntl, 1, MSG_OOB); 397 if (cntl & TIOCPKT_FLUSHWRITE) { 398 pcc = 0; 399 FD_CLR(p, &ibits); 400 } 401 } 402 } 403 if (FD_ISSET(f, &ibits)) { 404 fcc = read(f, fibuf, sizeof(fibuf)); 405 if (fcc < 0 && errno == EWOULDBLOCK) 406 fcc = 0; 407 else { 408 char *cp; 409 int left, n; 410 411 if (fcc <= 0) 412 break; 413 fbp = fibuf; 414 415 top: 416 for (cp = fibuf; cp < fibuf+fcc-1; cp++) 417 if (cp[0] == magic[0] && 418 cp[1] == magic[1]) { 419 left = fcc - (cp-fibuf); 420 n = control(p, cp, left); 421 if (n) { 422 left -= n; 423 if (left > 0) 424 bcopy(cp+n, cp, left); 425 fcc -= n; 426 goto top; /* n^2 */ 427 } 428 } 429 FD_SET(p, &obits); /* try write */ 430 } 431 } 432 433 if (FD_ISSET(p, &obits) && fcc > 0) { 434 cc = write(p, fbp, fcc); 435 if (cc > 0) { 436 fcc -= cc; 437 fbp += cc; 438 } 439 } 440 441 if (FD_ISSET(p, &ibits)) { 442 pcc = read(p, pibuf, sizeof (pibuf)); 443 pbp = pibuf; 444 if (pcc < 0 && errno == EWOULDBLOCK) 445 pcc = 0; 446 else if (pcc <= 0) 447 break; 448 else if (pibuf[0] == 0) { 449 pbp++, pcc--; 450 FD_SET(f, &obits); /* try write */ 451 } else { 452 if (pkcontrol(pibuf[0])) { 453 pibuf[0] |= oobdata[0]; 454 send(f, &pibuf[0], 1, MSG_OOB); 455 } 456 pcc = 0; 457 } 458 } 459 if ((FD_ISSET(f, &obits)) && pcc > 0) { 460 cc = write(f, pbp, pcc); 461 if (cc < 0 && errno == EWOULDBLOCK) { 462 /* 463 * This happens when we try write after read 464 * from p, but some old kernels balk at large 465 * writes even when select returns true. 466 */ 467 if (!FD_ISSET(p, &ibits)) 468 sleep(5); 469 continue; 470 } 471 if (cc > 0) { 472 pcc -= cc; 473 pbp += cc; 474 } 475 } 476 } 477} 478 479void 480cleanup(int signo __unused) 481{ 482 483 shutdown(netf, SHUT_RDWR); 484 exit(1); 485} 486 487void 488fatal(int f, char *msg, int syserr) 489{ 490 int len; 491 char buf[BUFSIZ], *bp = buf; 492 493 /* 494 * Prepend binary one to message if we haven't sent 495 * the magic null as confirmation. 496 */ 497 if (!confirmed) 498 *bp++ = '\01'; /* error indicator */ 499 if (syserr) 500 len = snprintf(bp, sizeof(buf), "rlogind: %s: %s.\r\n", 501 msg, strerror(errno)); 502 else 503 len = snprintf(bp, sizeof(buf), "rlogind: %s.\r\n", msg); 504 if (len < 0) 505 len = 0; 506 (void) write(f, buf, bp + len - buf); 507 exit(1); 508} 509 510int 511do_rlogin(union sockunion *dest) 512{ 513 514 getstr(rusername, sizeof(rusername), "remuser too long"); 515 getstr(lusername, sizeof(lusername), "locuser too long"); 516 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long"); 517 518 pwd = getpwnam(lusername); 519 if (pwd == NULL) 520 return (-1); 521 /* XXX why don't we syslog() failure? */ 522 523 return (iruserok_sa(dest, dest->su_len, pwd->pw_uid == 0, rusername, 524 lusername)); 525} 526 527void 528getstr(char *buf, int cnt, char *errmsg) 529{ 530 char c; 531 532 do { 533 if (read(STDIN_FILENO, &c, 1) != 1) 534 exit(1); 535 if (--cnt < 0) 536 fatal(STDOUT_FILENO, errmsg, 0); 537 *buf++ = c; 538 } while (c != 0); 539} 540 541extern char **environ; 542 543void 544setup_term(int fd) 545{ 546 char *cp = index(term+ENVSIZE, '/'); 547 char *speed; 548 struct termios tt, def; 549 550#ifndef notyet 551 tcgetattr(fd, &tt); 552 if (cp) { 553 *cp++ = '\0'; 554 speed = cp; 555 cp = index(speed, '/'); 556 if (cp) 557 *cp++ = '\0'; 558 cfsetspeed(&tt, atoi(speed)); 559 } 560 561 cfmakesane(&def); 562 tt.c_iflag = def.c_iflag; 563 tt.c_oflag = def.c_oflag; 564 tt.c_lflag = def.c_lflag; 565 tcsetattr(fd, TCSAFLUSH, &tt); 566#else 567 if (cp) { 568 *cp++ = '\0'; 569 speed = cp; 570 cp = index(speed, '/'); 571 if (cp) 572 *cp++ = '\0'; 573 tcgetattr(fd, &tt); 574 cfsetspeed(&tt, atoi(speed)); 575 tcsetattr(fd, TCSAFLUSH, &tt); 576 } 577#endif 578 579 env[0] = term; 580 env[1] = 0; 581 environ = env; 582} 583 584void 585usage(void) 586{ 587 syslog(LOG_ERR, "usage: rlogind [-" ARGSTR "]"); 588} 589