/usr.bin/rlogin/rlogin.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 722 lines · 531 code · 74 blank · 117 comment · 127 complexity · ae2018e654fcd64743098dc9ab03eaee MD5 · raw file

  1. /*
  2. * Copyright (c) 1983, 1990, 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. #ifndef lint
  41. static const char copyright[] =
  42. "@(#) Copyright (c) 1983, 1990, 1993\n\
  43. The Regents of the University of California. All rights reserved.\n";
  44. #endif /* not lint */
  45. #if 0
  46. #ifndef lint
  47. static const char sccsid[] = "@(#)rlogin.c 8.1 (Berkeley) 6/6/93";
  48. #endif /* not lint */
  49. #endif
  50. #include <sys/cdefs.h>
  51. __FBSDID("$FreeBSD$");
  52. /*
  53. * rlogin - remote login
  54. */
  55. #include <sys/param.h>
  56. #include <sys/ioctl.h>
  57. #include <sys/socket.h>
  58. #include <sys/time.h>
  59. #include <sys/resource.h>
  60. #include <sys/wait.h>
  61. #include <netinet/in.h>
  62. #include <netinet/in_systm.h>
  63. #include <netinet/ip.h>
  64. #include <netinet/tcp.h>
  65. #include <err.h>
  66. #include <errno.h>
  67. #include <fcntl.h>
  68. #include <netdb.h>
  69. #include <paths.h>
  70. #include <pwd.h>
  71. #include <setjmp.h>
  72. #include <termios.h>
  73. #include <signal.h>
  74. #include <stdio.h>
  75. #include <stdlib.h>
  76. #include <string.h>
  77. #include <unistd.h>
  78. #ifndef TIOCPKT_WINDOW
  79. #define TIOCPKT_WINDOW 0x80
  80. #endif
  81. /* concession to Sun */
  82. #ifndef SIGUSR1
  83. #define SIGUSR1 30
  84. #endif
  85. int eight, rem;
  86. struct termios deftty;
  87. int family = PF_UNSPEC;
  88. int noescape;
  89. u_char escapechar = '~';
  90. #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
  91. struct winsize winsize;
  92. void catch_child(int);
  93. void copytochild(int);
  94. void doit(long) __dead2;
  95. void done(int) __dead2;
  96. void echo(char);
  97. u_int getescape(const char *);
  98. void lostpeer(int);
  99. void mode(int);
  100. void msg(const char *);
  101. void oob(int);
  102. int reader(int);
  103. void sendwindow(void);
  104. void setsignal(int);
  105. void sigwinch(int);
  106. void stop(char);
  107. void usage(void) __dead2;
  108. void writer(void);
  109. void writeroob(int);
  110. int
  111. main(int argc, char *argv[])
  112. {
  113. struct passwd *pw;
  114. struct servent *sp;
  115. struct termios tty;
  116. long omask;
  117. int argoff, ch, dflag, Dflag, one;
  118. uid_t uid;
  119. char *host, *localname, *p, *user, term[1024];
  120. speed_t ospeed;
  121. struct sockaddr_storage ss;
  122. socklen_t sslen;
  123. size_t len, len2;
  124. int i;
  125. argoff = dflag = Dflag = 0;
  126. one = 1;
  127. host = localname = user = NULL;
  128. if ((p = strrchr(argv[0], '/')))
  129. ++p;
  130. else
  131. p = argv[0];
  132. if (strcmp(p, "rlogin"))
  133. host = p;
  134. /* handle "rlogin host flags" */
  135. if (!host && argc > 2 && argv[1][0] != '-') {
  136. host = argv[1];
  137. argoff = 1;
  138. }
  139. #define OPTIONS "468DEde:i:l:"
  140. while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
  141. switch(ch) {
  142. case '4':
  143. family = PF_INET;
  144. break;
  145. case '6':
  146. family = PF_INET6;
  147. break;
  148. case '8':
  149. eight = 1;
  150. break;
  151. case 'D':
  152. Dflag = 1;
  153. break;
  154. case 'E':
  155. noescape = 1;
  156. break;
  157. case 'd':
  158. dflag = 1;
  159. break;
  160. case 'e':
  161. noescape = 0;
  162. escapechar = getescape(optarg);
  163. break;
  164. case 'i':
  165. if (getuid() != 0)
  166. errx(1, "-i user: permission denied");
  167. localname = optarg;
  168. break;
  169. case 'l':
  170. user = optarg;
  171. break;
  172. case '?':
  173. default:
  174. usage();
  175. }
  176. optind += argoff;
  177. /* if haven't gotten a host yet, do so */
  178. if (!host && !(host = argv[optind++]))
  179. usage();
  180. if (argv[optind])
  181. usage();
  182. if (!(pw = getpwuid(uid = getuid())))
  183. errx(1, "unknown user id");
  184. if (!user)
  185. user = pw->pw_name;
  186. if (!localname)
  187. localname = pw->pw_name;
  188. sp = NULL;
  189. sp = getservbyname("login", "tcp");
  190. if (sp == NULL)
  191. errx(1, "login/tcp: unknown service");
  192. if ((p = getenv("TERM")) != NULL)
  193. (void)strlcpy(term, p, sizeof(term));
  194. len = strlen(term);
  195. if (len < (sizeof(term) - 1) && tcgetattr(0, &tty) == 0) {
  196. /* start at 2 to include the / */
  197. for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++)
  198. i /= 10;
  199. if (len + len2 < sizeof(term))
  200. (void)snprintf(term + len, len2 + 1, "/%d", ospeed);
  201. }
  202. (void)get_window_size(0, &winsize);
  203. (void)signal(SIGPIPE, lostpeer);
  204. /* will use SIGUSR1 for window size hack, so hold it off */
  205. omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
  206. /*
  207. * We set SIGURG and SIGUSR1 below so that an
  208. * incoming signal will be held pending rather than being
  209. * discarded. Note that these routines will be ready to get
  210. * a signal by the time that they are unblocked below.
  211. */
  212. (void)signal(SIGURG, copytochild);
  213. (void)signal(SIGUSR1, writeroob);
  214. rem = rcmd_af(&host, sp->s_port, localname, user, term, 0, family);
  215. if (rem < 0)
  216. exit(1);
  217. if (dflag &&
  218. setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
  219. warn("setsockopt");
  220. if (Dflag &&
  221. setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0)
  222. warn("setsockopt NODELAY (ignored)");
  223. sslen = sizeof(ss);
  224. one = IPTOS_LOWDELAY;
  225. if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 &&
  226. ss.ss_family == AF_INET) {
  227. if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one,
  228. sizeof(int)) < 0)
  229. warn("setsockopt TOS (ignored)");
  230. } else
  231. if (ss.ss_family == AF_INET)
  232. warn("setsockopt getsockname failed");
  233. (void)setuid(uid);
  234. doit(omask);
  235. /*NOTREACHED*/
  236. }
  237. int child;
  238. void
  239. doit(long omask)
  240. {
  241. (void)signal(SIGINT, SIG_IGN);
  242. setsignal(SIGHUP);
  243. setsignal(SIGQUIT);
  244. mode(1);
  245. child = fork();
  246. if (child == -1) {
  247. warn("fork");
  248. done(1);
  249. }
  250. if (child == 0) {
  251. if (reader(omask) == 0) {
  252. msg("connection closed");
  253. exit(0);
  254. }
  255. sleep(1);
  256. msg("\007connection closed");
  257. exit(1);
  258. }
  259. /*
  260. * We may still own the socket, and may have a pending SIGURG (or might
  261. * receive one soon) that we really want to send to the reader. When
  262. * one of these comes in, the trap copytochild simply copies such
  263. * signals to the child. We can now unblock SIGURG and SIGUSR1
  264. * that were set above.
  265. */
  266. (void)sigsetmask(omask);
  267. (void)signal(SIGCHLD, catch_child);
  268. writer();
  269. msg("closed connection");
  270. done(0);
  271. }
  272. /* trap a signal, unless it is being ignored. */
  273. void
  274. setsignal(int sig)
  275. {
  276. int omask = sigblock(sigmask(sig));
  277. if (signal(sig, exit) == SIG_IGN)
  278. (void)signal(sig, SIG_IGN);
  279. (void)sigsetmask(omask);
  280. }
  281. void
  282. done(int status)
  283. {
  284. int w, wstatus;
  285. mode(0);
  286. if (child > 0) {
  287. /* make sure catch_child does not snap it up */
  288. (void)signal(SIGCHLD, SIG_DFL);
  289. if (kill(child, SIGKILL) >= 0)
  290. while ((w = wait(&wstatus)) > 0 && w != child);
  291. }
  292. exit(status);
  293. }
  294. int dosigwinch;
  295. /*
  296. * This is called when the reader process gets the out-of-band (urgent)
  297. * request to turn on the window-changing protocol.
  298. */
  299. /* ARGSUSED */
  300. void
  301. writeroob(int signo __unused)
  302. {
  303. if (dosigwinch == 0) {
  304. sendwindow();
  305. (void)signal(SIGWINCH, sigwinch);
  306. }
  307. dosigwinch = 1;
  308. }
  309. /* ARGSUSED */
  310. void
  311. catch_child(int signo __unused)
  312. {
  313. pid_t pid;
  314. int status;
  315. for (;;) {
  316. pid = wait3(&status, WNOHANG|WUNTRACED, NULL);
  317. if (pid == 0)
  318. return;
  319. /* if the child (reader) dies, just quit */
  320. if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
  321. done(WTERMSIG(status) | WEXITSTATUS(status));
  322. }
  323. /* NOTREACHED */
  324. }
  325. /*
  326. * writer: write to remote: 0 -> line.
  327. * ~. terminate
  328. * ~^Z suspend rlogin process.
  329. * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
  330. */
  331. void
  332. writer(void)
  333. {
  334. int bol, local, n;
  335. char c;
  336. bol = 1; /* beginning of line */
  337. local = 0;
  338. for (;;) {
  339. n = read(STDIN_FILENO, &c, 1);
  340. if (n <= 0) {
  341. if (n < 0 && errno == EINTR)
  342. continue;
  343. break;
  344. }
  345. /*
  346. * If we're at the beginning of the line and recognize a
  347. * command character, then we echo locally. Otherwise,
  348. * characters are echo'd remotely. If the command character
  349. * is doubled, this acts as a force and local echo is
  350. * suppressed.
  351. */
  352. if (bol) {
  353. bol = 0;
  354. if (!noescape && c == escapechar) {
  355. local = 1;
  356. continue;
  357. }
  358. } else if (local) {
  359. local = 0;
  360. if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) {
  361. echo(c);
  362. break;
  363. }
  364. if (CCEQ(deftty.c_cc[VSUSP], c) ||
  365. CCEQ(deftty.c_cc[VDSUSP], c)) {
  366. bol = 1;
  367. echo(c);
  368. stop(c);
  369. continue;
  370. }
  371. if (c != escapechar)
  372. (void)write(rem, &escapechar, 1);
  373. }
  374. if (write(rem, &c, 1) == 0) {
  375. msg("line gone");
  376. break;
  377. }
  378. bol = CCEQ(deftty.c_cc[VKILL], c) ||
  379. CCEQ(deftty.c_cc[VEOF], c) ||
  380. CCEQ(deftty.c_cc[VINTR], c) ||
  381. CCEQ(deftty.c_cc[VSUSP], c) ||
  382. c == '\r' || c == '\n';
  383. }
  384. }
  385. void
  386. echo(char c)
  387. {
  388. char *p;
  389. char buf[8];
  390. p = buf;
  391. c &= 0177;
  392. *p++ = escapechar;
  393. if (c < ' ') {
  394. *p++ = '^';
  395. *p++ = c + '@';
  396. } else if (c == 0177) {
  397. *p++ = '^';
  398. *p++ = '?';
  399. } else
  400. *p++ = c;
  401. *p++ = '\r';
  402. *p++ = '\n';
  403. (void)write(STDOUT_FILENO, buf, p - buf);
  404. }
  405. void
  406. stop(char cmdc)
  407. {
  408. mode(0);
  409. (void)signal(SIGCHLD, SIG_IGN);
  410. (void)kill(CCEQ(deftty.c_cc[VSUSP], cmdc) ? 0 : getpid(), SIGTSTP);
  411. (void)signal(SIGCHLD, catch_child);
  412. mode(1);
  413. sigwinch(0); /* check for size changes */
  414. }
  415. /* ARGSUSED */
  416. void
  417. sigwinch(int signo __unused)
  418. {
  419. struct winsize ws;
  420. if (dosigwinch && get_window_size(0, &ws) == 0 &&
  421. bcmp(&ws, &winsize, sizeof(ws))) {
  422. winsize = ws;
  423. sendwindow();
  424. }
  425. }
  426. /*
  427. * Send the window size to the server via the magic escape
  428. */
  429. void
  430. sendwindow(void)
  431. {
  432. struct winsize ws;
  433. char obuf[4 + sizeof (struct winsize)];
  434. obuf[0] = 0377;
  435. obuf[1] = 0377;
  436. obuf[2] = 's';
  437. obuf[3] = 's';
  438. ws.ws_row = htons(winsize.ws_row);
  439. ws.ws_col = htons(winsize.ws_col);
  440. ws.ws_xpixel = htons(winsize.ws_xpixel);
  441. ws.ws_ypixel = htons(winsize.ws_ypixel);
  442. bcopy(&ws, obuf + 4, sizeof(ws));
  443. (void)write(rem, obuf, sizeof(obuf));
  444. }
  445. /*
  446. * reader: read from remote: line -> 1
  447. */
  448. #define READING 1
  449. #define WRITING 2
  450. jmp_buf rcvtop;
  451. int rcvcnt, rcvstate;
  452. pid_t ppid;
  453. char rcvbuf[8 * 1024];
  454. /* ARGSUSED */
  455. void
  456. oob(int signo __unused)
  457. {
  458. struct termios tty;
  459. int atmark, n, rcvd;
  460. char waste[BUFSIZ], mark;
  461. rcvd = 0;
  462. while (recv(rem, &mark, 1, MSG_OOB) < 0) {
  463. switch (errno) {
  464. case EWOULDBLOCK:
  465. /*
  466. * Urgent data not here yet. It may not be possible
  467. * to send it yet if we are blocked for output and
  468. * our input buffer is full.
  469. */
  470. if (rcvcnt < (int)sizeof(rcvbuf)) {
  471. n = read(rem, rcvbuf + rcvcnt,
  472. sizeof(rcvbuf) - rcvcnt);
  473. if (n <= 0)
  474. return;
  475. rcvd += n;
  476. } else {
  477. n = read(rem, waste, sizeof(waste));
  478. if (n <= 0)
  479. return;
  480. }
  481. continue;
  482. default:
  483. return;
  484. }
  485. }
  486. if (mark & TIOCPKT_WINDOW) {
  487. /* Let server know about window size changes */
  488. (void)kill(ppid, SIGUSR1);
  489. }
  490. if (!eight && (mark & TIOCPKT_NOSTOP)) {
  491. (void)tcgetattr(0, &tty);
  492. tty.c_iflag &= ~IXON;
  493. (void)tcsetattr(0, TCSANOW, &tty);
  494. }
  495. if (!eight && (mark & TIOCPKT_DOSTOP)) {
  496. (void)tcgetattr(0, &tty);
  497. tty.c_iflag |= (deftty.c_iflag & IXON);
  498. (void)tcsetattr(0, TCSANOW, &tty);
  499. }
  500. if (mark & TIOCPKT_FLUSHWRITE) {
  501. (void)tcflush(1, TCIOFLUSH);
  502. for (;;) {
  503. if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
  504. warn("ioctl");
  505. break;
  506. }
  507. if (atmark)
  508. break;
  509. n = read(rem, waste, sizeof (waste));
  510. if (n <= 0)
  511. break;
  512. }
  513. /*
  514. * Don't want any pending data to be output, so clear the recv
  515. * buffer. If we were hanging on a write when interrupted,
  516. * don't want it to restart. If we were reading, restart
  517. * anyway.
  518. */
  519. rcvcnt = 0;
  520. longjmp(rcvtop, 1);
  521. }
  522. /* oob does not do FLUSHREAD (alas!) */
  523. /*
  524. * If we filled the receive buffer while a read was pending, longjmp
  525. * to the top to restart appropriately. Don't abort a pending write,
  526. * however, or we won't know how much was written.
  527. */
  528. if (rcvd && rcvstate == READING)
  529. longjmp(rcvtop, 1);
  530. }
  531. /* reader: read from remote: line -> 1 */
  532. int
  533. reader(int omask)
  534. {
  535. int n, remaining;
  536. char *bufp;
  537. pid_t pid;
  538. pid = getpid();
  539. (void)signal(SIGTTOU, SIG_IGN);
  540. (void)signal(SIGURG, oob);
  541. (void)signal(SIGUSR1, oob); /* When propogating SIGURG from parent */
  542. ppid = getppid();
  543. (void)fcntl(rem, F_SETOWN, pid);
  544. (void)setjmp(rcvtop);
  545. (void)sigsetmask(omask);
  546. bufp = rcvbuf;
  547. for (;;) {
  548. while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
  549. rcvstate = WRITING;
  550. n = write(STDOUT_FILENO, bufp, remaining);
  551. if (n < 0) {
  552. if (errno != EINTR)
  553. return (-1);
  554. continue;
  555. }
  556. bufp += n;
  557. }
  558. bufp = rcvbuf;
  559. rcvcnt = 0;
  560. rcvstate = READING;
  561. rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
  562. if (rcvcnt == 0)
  563. return (0);
  564. if (rcvcnt < 0) {
  565. if (errno == EINTR)
  566. continue;
  567. warn("read");
  568. return (-1);
  569. }
  570. }
  571. }
  572. void
  573. mode(int f)
  574. {
  575. struct termios tty;
  576. switch (f) {
  577. case 0:
  578. (void)tcsetattr(0, TCSANOW, &deftty);
  579. break;
  580. case 1:
  581. (void)tcgetattr(0, &deftty);
  582. tty = deftty;
  583. /* This is loosely derived from sys/kern/tty_compat.c. */
  584. tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN);
  585. tty.c_iflag &= ~ICRNL;
  586. tty.c_oflag &= ~OPOST;
  587. tty.c_cc[VMIN] = 1;
  588. tty.c_cc[VTIME] = 0;
  589. if (eight) {
  590. tty.c_iflag &= IXOFF;
  591. tty.c_cflag &= ~(CSIZE|PARENB);
  592. tty.c_cflag |= CS8;
  593. }
  594. (void)tcsetattr(0, TCSANOW, &tty);
  595. break;
  596. default:
  597. return;
  598. }
  599. }
  600. /* ARGSUSED */
  601. void
  602. lostpeer(int signo __unused)
  603. {
  604. (void)signal(SIGPIPE, SIG_IGN);
  605. msg("\007connection closed");
  606. done(1);
  607. }
  608. /* copy SIGURGs to the child process via SIGUSR1. */
  609. /* ARGSUSED */
  610. void
  611. copytochild(int signo __unused)
  612. {
  613. (void)kill(child, SIGUSR1);
  614. }
  615. void
  616. msg(const char *str)
  617. {
  618. (void)fprintf(stderr, "rlogin: %s\r\n", str);
  619. }
  620. void
  621. usage(void)
  622. {
  623. (void)fprintf(stderr,
  624. "usage: rlogin [-46%s]%s[-e char] [-i localname] [-l username] host\n",
  625. "8DEd", " ");
  626. exit(1);
  627. }
  628. u_int
  629. getescape(const char *p)
  630. {
  631. long val;
  632. size_t len;
  633. if ((len = strlen(p)) == 1) /* use any single char, including '\' */
  634. return ((u_int)*p);
  635. /* otherwise, \nnn */
  636. if (*p == '\\' && len >= 2 && len <= 4) {
  637. val = strtol(++p, NULL, 8);
  638. for (;;) {
  639. if (!*++p)
  640. return ((u_int)val);
  641. if (*p < '0' || *p > '8')
  642. break;
  643. }
  644. }
  645. msg("illegal option value -- e");
  646. usage();
  647. /* NOTREACHED */
  648. }