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. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#if 0 38#ifndef lint 39static const char copyright[] = 40"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\ 41 The Regents of the University of California. All rights reserved.\n"; 42#endif /* not lint */ 43 44#ifndef lint 45static const char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93"; 46#endif /* not lint */ 47#endif 48#include <sys/cdefs.h> 49__FBSDID("$FreeBSD$"); 50 51/* 52 * remote login server: 53 * \0 54 * remuser\0 55 * locuser\0 56 * terminal_type/speed\0 57 * data 58 */ 59 60#define FD_SETSIZE 16 /* don't need many bits for select */ 61#include <sys/types.h> 62#include <sys/param.h> 63#include <sys/stat.h> 64#include <sys/ioctl.h> 65#include <signal.h> 66#include <termios.h> 67 68#include <sys/socket.h> 69#include <netinet/in.h> 70#include <netinet/in_systm.h> 71#include <netinet/ip.h> 72#include <netinet/tcp.h> 73#include <arpa/inet.h> 74#include <netdb.h> 75 76#include <errno.h> 77#include <libutil.h> 78#include <paths.h> 79#include <poll.h> 80#include <pwd.h> 81#include <syslog.h> 82#include <stdio.h> 83#include <stdlib.h> 84#include <string.h> 85#include <unistd.h> 86#ifdef USE_BLACKLIST 87#include <blacklist.h> 88#endif 89 90#ifndef TIOCPKT_WINDOW 91#define TIOCPKT_WINDOW 0x80 92#endif 93 94#define ARGSTR "Daln" 95 96char *env[2]; 97#define NMAX 30 98char lusername[NMAX+1], rusername[NMAX+1]; 99static char term[64] = "TERM="; 100#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */ 101int keepalive = 1; 102int check_all = 0; 103int no_delay; 104 105struct passwd *pwd; 106 107union sockunion { 108 struct sockinet { 109 u_char si_len; 110 u_char si_family; 111 u_short si_port; 112 } su_si; 113 struct sockaddr_in su_sin; 114 struct sockaddr_in6 su_sin6; 115}; 116#define su_len su_si.si_len 117#define su_family su_si.si_family 118#define su_port su_si.si_port 119 120void doit(int, union sockunion *); 121int control(int, char *, int); 122void protocol(int, int); 123void cleanup(int); 124void fatal(int, char *, int); 125int do_rlogin(union sockunion *); 126void getstr(char *, int, char *); 127void setup_term(int); 128int do_krb_login(struct sockaddr_in *); 129void usage(void); 130 131 132int 133main(int argc, char *argv[]) 134{ 135 extern int __check_rhosts_file; 136 union sockunion from; 137 socklen_t fromlen; 138 int ch, on; 139 140 openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); 141 142 opterr = 0; 143 while ((ch = getopt(argc, argv, ARGSTR)) != -1) 144 switch (ch) { 145 case 'D': 146 no_delay = 1; 147 break; 148 case 'a': 149 check_all = 1; 150 break; 151 case 'l': 152 __check_rhosts_file = 0; 153 break; 154 case 'n': 155 keepalive = 0; 156 break; 157 case '?': 158 default: 159 usage(); 160 break; 161 } 162 argc -= optind; 163 argv += optind; 164 165 fromlen = sizeof (from); 166 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 167 syslog(LOG_ERR,"Can't get peer name of remote host: %m"); 168 fatal(STDERR_FILENO, "Can't get peer name of remote host", 1); 169 } 170 on = 1; 171 if (keepalive && 172 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) 173 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 174 if (no_delay && 175 setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) 176 syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); 177 if (from.su_family == AF_INET) 178 { 179 on = IPTOS_LOWDELAY; 180 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 181 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 182 } 183 184 doit(0, &from); 185 return 0; 186} 187 188int child; 189int netf; 190char line[MAXPATHLEN]; 191int confirmed; 192 193struct winsize win = { 0, 0, 0, 0 }; 194 195 196void 197doit(int f, union sockunion *fromp) 198{ 199 int master, pid, on = 1; 200 int authenticated = 0; 201 char hostname[2 * MAXHOSTNAMELEN + 1]; 202 char nameinfo[2 * INET6_ADDRSTRLEN + 1]; 203 char c; 204 205 alarm(60); 206 read(f, &c, 1); 207 208 if (c != 0) 209 exit(1); 210 211 alarm(0); 212 213 realhostname_sa(hostname, sizeof(hostname) - 1, 214 (struct sockaddr *)fromp, fromp->su_len); 215 /* error check ? */ 216 fromp->su_port = ntohs((u_short)fromp->su_port); 217 hostname[sizeof(hostname) - 1] = '\0'; 218 219 { 220 if ((fromp->su_family != AF_INET 221#ifdef INET6 222 && fromp->su_family != AF_INET6 223#endif 224 ) || 225 fromp->su_port >= IPPORT_RESERVED || 226 fromp->su_port < IPPORT_RESERVED/2) { 227 getnameinfo((struct sockaddr *)fromp, 228 fromp->su_len, 229 nameinfo, sizeof(nameinfo), NULL, 0, 230 NI_NUMERICHOST); 231 /* error check ? */ 232 syslog(LOG_NOTICE, "Connection from %s on illegal port", 233 nameinfo); 234#ifdef USE_BLACKLIST 235 blacklist(1, STDIN_FILENO, "illegal port"); 236#endif 237 fatal(f, "Permission denied", 0); 238 } 239#ifdef IP_OPTIONS 240 if (fromp->su_family == AF_INET) 241 { 242 u_char optbuf[BUFSIZ/3]; 243 socklen_t optsize = sizeof(optbuf); 244 int ipproto, i; 245 struct protoent *ip; 246 247 if ((ip = getprotobyname("ip")) != NULL) 248 ipproto = ip->p_proto; 249 else 250 ipproto = IPPROTO_IP; 251 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, 252 &optsize) == 0 && optsize != 0) { 253 for (i = 0; i < optsize; ) { 254 u_char c = optbuf[i]; 255 if (c == IPOPT_LSRR || c == IPOPT_SSRR) { 256 syslog(LOG_NOTICE, 257 "Connection refused from %s with IP option %s", 258 inet_ntoa(fromp->su_sin.sin_addr), 259 c == IPOPT_LSRR ? "LSRR" : "SSRR"); 260#ifdef USE_BLACKLIST 261 blacklist(1, STDIN_FILENO, "source routing present"); 262#endif 263 exit(1); 264 } 265 if (c == IPOPT_EOL) 266 break; 267 i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; 268 } 269 } 270 } 271#endif 272 if (do_rlogin(fromp) == 0) 273 authenticated++; 274 } 275 if (confirmed == 0) { 276 write(f, "", 1); 277 confirmed = 1; /* we sent the null! */ 278 } 279 netf = f; 280 281 pid = forkpty(&master, line, NULL, &win); 282 if (pid < 0) { 283 if (errno == ENOENT) 284 fatal(f, "Out of ptys", 0); 285 else 286 fatal(f, "Forkpty", 1); 287 } 288 if (pid == 0) { 289 if (f > 2) /* f should always be 0, but... */ 290 (void) close(f); 291 setup_term(0); 292 if (*lusername=='-') { 293 syslog(LOG_ERR, "tried to pass user \"%s\" to login", 294 lusername); 295#ifdef USE_BLACKLIST 296 blacklist(1, STDIN_FILENO, "invalid user"); 297#endif 298 fatal(STDERR_FILENO, "invalid user", 0); 299 } 300#ifdef USE_BLACKLIST 301 blacklist(0, STDIN_FILENO, "success"); 302#endif 303 if (authenticated) { 304 execl(_PATH_LOGIN, "login", "-p", 305 "-h", hostname, "-f", lusername, (char *)NULL); 306 } else 307 execl(_PATH_LOGIN, "login", "-p", 308 "-h", hostname, lusername, (char *)NULL); 309 fatal(STDERR_FILENO, _PATH_LOGIN, 1); 310 /*NOTREACHED*/ 311 } 312 ioctl(f, FIONBIO, &on); 313 ioctl(master, FIONBIO, &on); 314 ioctl(master, TIOCPKT, &on); 315 signal(SIGCHLD, cleanup); 316 protocol(f, master); 317 signal(SIGCHLD, SIG_IGN); 318 cleanup(0); 319} 320 321char magic[2] = { 0377, 0377 }; 322char oobdata[] = {TIOCPKT_WINDOW}; 323 324/* 325 * Handle a "control" request (signaled by magic being present) 326 * in the data stream. For now, we are only willing to handle 327 * window size changes. 328 */ 329int 330control(int pty, char *cp, int n) 331{ 332 struct winsize w; 333 334 if (n < 4 + (int)sizeof(w) || cp[2] != 's' || cp[3] != 's') 335 return (0); 336 oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ 337 bcopy(cp+4, (char *)&w, sizeof(w)); 338 w.ws_row = ntohs(w.ws_row); 339 w.ws_col = ntohs(w.ws_col); 340 w.ws_xpixel = ntohs(w.ws_xpixel); 341 w.ws_ypixel = ntohs(w.ws_ypixel); 342 (void)ioctl(pty, TIOCSWINSZ, &w); 343 return (4+sizeof (w)); 344} 345 346/* 347 * rlogin "protocol" machine. 348 */ 349void 350protocol(int f, int p) 351{ 352 char pibuf[1024+1], fibuf[1024], *pbp = NULL, *fbp = NULL; 353 int pcc = 0, fcc = 0; 354 int cc, nfd, n; 355 char cntl; 356 357 /* 358 * Must ignore SIGTTOU, otherwise we'll stop 359 * when we try and set slave pty's window shape 360 * (our controlling tty is the master pty). 361 */ 362 (void) signal(SIGTTOU, SIG_IGN); 363 send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ 364 if (f > p) 365 nfd = f + 1; 366 else 367 nfd = p + 1; 368 for (;;) { 369 struct pollfd set[2]; 370 371 set[0].fd = p; 372 set[0].events = POLLPRI; 373 set[1].fd = f; 374 set[1].events = 0; 375 if (fcc) 376 set[0].events |= POLLOUT; 377 else 378 set[1].events |= POLLIN; 379 if (pcc >= 0) { 380 if (pcc) 381 set[1].events |= POLLOUT; 382 else 383 set[0].events |= POLLIN; 384 } 385 if ((n = poll(set, 2, INFTIM)) < 0) { 386 if (errno == EINTR) 387 continue; 388 fatal(f, "poll", 1); 389 } 390 if (n == 0) { 391 /* shouldn't happen... */ 392 sleep(5); 393 continue; 394 } 395#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) 396 if (set[0].revents & POLLPRI) { 397 cc = read(p, &cntl, 1); 398 if (cc == 1 && pkcontrol(cntl)) { 399 cntl |= oobdata[0]; 400 send(f, &cntl, 1, MSG_OOB); 401 if (cntl & TIOCPKT_FLUSHWRITE) 402 pcc = 0; 403 } 404 } 405 if (set[1].revents & POLLIN) { 406 fcc = read(f, fibuf, sizeof(fibuf)); 407 if (fcc < 0 && errno == EWOULDBLOCK) 408 fcc = 0; 409 else { 410 char *cp; 411 int left, n; 412 413 if (fcc <= 0) 414 break; 415 fbp = fibuf; 416 417 top: 418 for (cp = fibuf; cp < fibuf+fcc-1; cp++) 419 if (cp[0] == magic[0] && 420 cp[1] == magic[1]) { 421 left = fcc - (cp-fibuf); 422 n = control(p, cp, left); 423 if (n) { 424 left -= n; 425 if (left > 0) 426 bcopy(cp+n, cp, left); 427 fcc -= n; 428 goto top; /* n^2 */ 429 } 430 } 431 } 432 } 433 434 if (set[0].revents & POLLOUT && fcc > 0) { 435 cc = write(p, fbp, fcc); 436 if (cc > 0) { 437 fcc -= cc; 438 fbp += cc; 439 } 440 } 441 442 if (set[0].revents & POLLIN) { 443 pcc = read(p, pibuf, sizeof (pibuf)); 444 pbp = pibuf; 445 if (pcc < 0 && errno == EWOULDBLOCK) 446 pcc = 0; 447 else if (pcc <= 0) 448 break; 449 else if (pibuf[0] == 0) { 450 pbp++, pcc--; 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 (set[1].revents & POLLOUT && pcc > 0) { 460 cc = write(f, pbp, pcc); 461 if (cc > 0) { 462 pcc -= cc; 463 pbp += cc; 464 } 465 } 466 } 467} 468 469void 470cleanup(int signo __unused) 471{ 472 473 shutdown(netf, SHUT_RDWR); 474 exit(1); 475} 476 477void 478fatal(int f, char *msg, int syserr) 479{ 480 int len; 481 char buf[BUFSIZ], *bp = buf; 482 483 /* 484 * Prepend binary one to message if we haven't sent 485 * the magic null as confirmation. 486 */ 487 if (!confirmed) 488 *bp++ = '\01'; /* error indicator */ 489 if (syserr) 490 len = snprintf(bp, sizeof(buf), "rlogind: %s: %s.\r\n", 491 msg, strerror(errno)); 492 else 493 len = snprintf(bp, sizeof(buf), "rlogind: %s.\r\n", msg); 494 if (len < 0) 495 len = 0; 496 (void) write(f, buf, bp + len - buf); 497 exit(1); 498} 499 500int 501do_rlogin(union sockunion *dest) 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 pwd = getpwnam(lusername); 509 if (pwd == NULL) 510 return (-1); 511 /* XXX why don't we syslog() failure? */ 512 513 return (iruserok_sa(dest, dest->su_len, pwd->pw_uid == 0, rusername, 514 lusername)); 515} 516 517void 518getstr(char *buf, int cnt, char *errmsg) 519{ 520 char c; 521 522 do { 523 if (read(STDIN_FILENO, &c, 1) != 1) 524 exit(1); 525 if (--cnt < 0) { 526#ifdef USE_BLACKLIST 527 blacklist(1, STDIN_FILENO, "buffer overflow"); 528#endif 529 fatal(STDOUT_FILENO, errmsg, 0); 530 } 531 *buf++ = c; 532 } while (c != 0); 533} 534 535extern char **environ; 536 537void 538setup_term(int fd) 539{ 540 char *cp; 541 char *speed; 542 struct termios tt, def; 543 544 cp = strchr(term + ENVSIZE, '/'); 545#ifndef notyet 546 tcgetattr(fd, &tt); 547 if (cp) { 548 *cp++ = '\0'; 549 speed = cp; 550 cp = strchr(speed, '/'); 551 if (cp) 552 *cp++ = '\0'; 553 cfsetspeed(&tt, atoi(speed)); 554 } 555 556 cfmakesane(&def); 557 tt.c_iflag = def.c_iflag; 558 tt.c_oflag = def.c_oflag; 559 tt.c_lflag = def.c_lflag; 560 tcsetattr(fd, TCSAFLUSH, &tt); 561#else 562 if (cp) { 563 *cp++ = '\0'; 564 speed = cp; 565 cp = strchr(speed, '/'); 566 if (cp) 567 *cp++ = '\0'; 568 tcgetattr(fd, &tt); 569 cfsetspeed(&tt, atoi(speed)); 570 tcsetattr(fd, TCSAFLUSH, &tt); 571 } 572#endif 573 574 env[0] = term; 575 env[1] = 0; 576 environ = env; 577} 578 579void 580usage(void) 581{ 582 syslog(LOG_ERR, "usage: rlogind [-" ARGSTR "]"); 583} 584