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