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