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