|
|
1.1 root 1: /*
2: * Copyright (c) 1983, 1990 The Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms are permitted provided
6: * that: (1) source distributions retain this entire copyright notice and
7: * comment, and (2) distributions including binaries display the following
8: * acknowledgement: ``This product includes software developed by the
9: * University of California, Berkeley and its contributors'' in the
10: * documentation or other materials provided with the distribution and in
11: * all advertising materials mentioning features or use of this software.
12: * Neither the name of the University nor the names of its contributors may
13: * be used to endorse or promote products derived from this software without
14: * specific prior written permission.
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
16: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
17: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18: */
19:
20: #ifndef lint
21: char copyright[] =
22: "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\
23: All rights reserved.\n";
24: #endif /* not lint */
25:
26: #ifndef lint
27: static char sccsid[] = "@(#)rlogin.c 5.32.1.1 (Berkeley) 10/21/90";
28: #endif /* not lint */
29:
30: /*
31: * $Source: mit/rlogin/RCS/rlogin.c,v $
32: * $Header: mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall
33: * Exp Locker: kfall $
34: */
35:
36: /*
37: * rlogin - remote login
38: */
39: #include <sys/param.h>
40: #include <sys/file.h>
41: #include <sys/socket.h>
42: #include <sys/signal.h>
43: #include <sys/time.h>
44: #include <sys/resource.h>
45: #include <sys/wait.h>
46:
47: #include <netinet/in.h>
48: #include <netinet/in_systm.h>
49: #include <netinet/ip.h>
50: #include <netdb.h>
51:
52: #include <sgtty.h>
53: #include <setjmp.h>
54: #include <varargs.h>
55: #include <errno.h>
56: #include <pwd.h>
57: #include <stdio.h>
58: #include <unistd.h>
59: #include <string.h>
60:
61: #ifdef KERBEROS
62: #include <kerberosIV/des.h>
63: #include <kerberosIV/krb.h>
64:
65: CREDENTIALS cred;
66: Key_schedule schedule;
67: int use_kerberos = 1, encrypt;
68: char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
69: extern char *krb_realmofhost();
70: #endif
71:
72: #ifndef TIOCPKT_WINDOW
73: #define TIOCPKT_WINDOW 0x80
74: #endif
75:
76: /* concession to Sun */
77: #ifndef SIGUSR1
78: #define SIGUSR1 30
79: #endif
80:
81: extern int errno;
82: int eight, litout, rem;
83:
84: int noescape;
85: u_char escapechar = '~';
86:
87: char *speeds[] = {
88: "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
89: "1800", "2400", "4800", "9600", "19200", "38400"
90: };
91:
92: #ifdef sun
93: struct winsize {
94: unsigned short ws_row, ws_col;
95: unsigned short ws_xpixel, ws_ypixel;
96: };
97: #endif
98: struct winsize winsize;
99:
100: #ifndef sun
101: #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
102: #endif
103:
104: void exit();
105:
106: main(argc, argv)
107: int argc;
108: char **argv;
109: {
110: extern char *optarg;
111: extern int optind;
112: struct passwd *pw;
113: struct servent *sp;
114: struct sgttyb ttyb;
115: long omask;
116: int argoff, ch, dflag, one, uid;
117: char *host, *p, *user, term[1024];
118: void lostpeer();
119: u_char getescape();
120: char *getenv();
121:
122: argoff = dflag = 0;
123: one = 1;
124: host = user = NULL;
125:
126: if (p = rindex(argv[0], '/'))
127: ++p;
128: else
129: p = argv[0];
130:
131: if (strcmp(p, "rlogin"))
132: host = p;
133:
134: /* handle "rlogin host flags" */
135: if (!host && argc > 2 && argv[1][0] != '-') {
136: host = argv[1];
137: argoff = 1;
138: }
139:
140: #ifdef KERBEROS
141: #define OPTIONS "8EKLde:k:l:x"
142: #else
143: #define OPTIONS "8EKLde:l:"
144: #endif
145: while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
146: switch(ch) {
147: case '8':
148: eight = 1;
149: break;
150: case 'E':
151: noescape = 1;
152: break;
153: case 'K':
154: #ifdef KERBEROS
155: use_kerberos = 0;
156: #endif
157: break;
158: case 'L':
159: litout = 1;
160: break;
161: case 'd':
162: dflag = 1;
163: break;
164: case 'e':
165: escapechar = getescape(optarg);
166: break;
167: #ifdef KERBEROS
168: case 'k':
169: dest_realm = dst_realm_buf;
170: (void)strncpy(dest_realm, optarg, REALM_SZ);
171: break;
172: #endif
173: case 'l':
174: user = optarg;
175: break;
176: case '?':
177: default:
178: usage();
179: }
180: optind += argoff;
181: argc -= optind;
182: argv += optind;
183:
184: /* if haven't gotten a host yet, do so */
185: if (!host && !(host = *argv++))
186: usage();
187:
188: if (*argv)
189: usage();
190:
191: if (!(pw = getpwuid(uid = getuid()))) {
192: (void)fprintf(stderr, "rlogin: unknown user id.\n");
193: exit(1);
194: }
195: if (!user)
196: user = pw->pw_name;
197:
198: sp = NULL;
199: #ifdef KERBEROS
200: if (use_kerberos) {
201: sp = getservbyname((encrypt ? "eklogin" : "klogin"), "tcp");
202: if (sp == NULL) {
203: use_kerberos = 0;
204: warning("can't get entry for %s/tcp service",
205: encrypt ? "eklogin" : "klogin");
206: }
207: }
208: #endif
209: if (sp == NULL)
210: sp = getservbyname("login", "tcp");
211: if (sp == NULL) {
212: (void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n");
213: exit(1);
214: }
215:
216: (void)strcpy(term, (p = getenv("TERM")) ? p : "network");
217: if (ioctl(0, TIOCGETP, &ttyb) == 0) {
218: (void)strcat(term, "/");
219: (void)strcat(term, speeds[ttyb.sg_ospeed]);
220: }
221:
222: (void)get_window_size(0, &winsize);
223:
224: (void)signal(SIGPIPE, lostpeer);
225: /* will use SIGUSR1 for window size hack, so hold it off */
226: omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
227:
228: #ifdef KERBEROS
229: try_connect:
230: if (use_kerberos) {
231: rem = KSUCCESS;
232: errno = 0;
233: if (dest_realm == NULL)
234: dest_realm = krb_realmofhost(host);
235:
236: rem = krcmd(&host, sp->s_port, user, term, 0,
237: dest_realm);
238: if (rem < 0) {
239: use_kerberos = 0;
240: sp = getservbyname("login", "tcp");
241: if (sp == NULL) {
242: (void)fprintf(stderr,
243: "rlogin: unknown service login/tcp.\n");
244: exit(1);
245: }
246: if (errno == ECONNREFUSED)
247: warning("remote host doesn't support Kerberos");
248: if (errno == ENOENT)
249: warning("can't provide Kerberos auth data");
250: goto try_connect;
251: }
252: } else {
253: rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
254: }
255: #else
256: rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0);
257: #endif /* KERBEROS */
258:
259: if (rem < 0)
260: exit(1);
261:
262: if (dflag &&
263: setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
264: (void)fprintf(stderr, "rlogin: setsockopt: %s.\n",
265: strerror(errno));
266: one = IPTOS_LOWDELAY;
267: if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0)
268: perror("rlogin: setsockopt TOS (ignored)");
269:
270: (void)setuid(uid);
271: doit(omask);
272: /*NOTREACHED*/
273: }
274:
275: int child, defflags, deflflags, tabflag;
276: char deferase, defkill;
277: struct tchars deftc;
278: struct ltchars defltc;
279: struct tchars notc = { -1, -1, -1, -1, -1, -1 };
280: struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
281:
282: doit(omask)
283: long omask;
284: {
285: struct sgttyb sb;
286: void catch_child(), copytochild(), exit(), writeroob();
287:
288: (void)ioctl(0, TIOCGETP, (char *)&sb);
289: defflags = sb.sg_flags;
290: tabflag = defflags & TBDELAY;
291: defflags &= ECHO | CRMOD;
292: deferase = sb.sg_erase;
293: defkill = sb.sg_kill;
294: (void)ioctl(0, TIOCLGET, (char *)&deflflags);
295: (void)ioctl(0, TIOCGETC, (char *)&deftc);
296: notc.t_startc = deftc.t_startc;
297: notc.t_stopc = deftc.t_stopc;
298: (void)ioctl(0, TIOCGLTC, (char *)&defltc);
299: (void)signal(SIGINT, SIG_IGN);
300: setsignal(SIGHUP, exit);
301: setsignal(SIGQUIT, exit);
302: child = fork();
303: if (child == -1) {
304: (void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno));
305: done(1);
306: }
307: if (child == 0) {
308: mode(1);
309: if (reader(omask) == 0) {
310: msg("connection closed.");
311: exit(0);
312: }
313: sleep(1);
314: msg("\007connection closed.");
315: exit(1);
316: }
317:
318: /*
319: * We may still own the socket, and may have a pending SIGURG (or might
320: * receive one soon) that we really want to send to the reader. Set a
321: * trap that simply copies such signals to the child.
322: */
323: (void)signal(SIGURG, copytochild);
324: (void)signal(SIGUSR1, writeroob);
325: (void)sigsetmask(omask);
326: (void)signal(SIGCHLD, catch_child);
327: writer();
328: msg("closed connection.");
329: done(0);
330: }
331:
332: /* trap a signal, unless it is being ignored. */
333: setsignal(sig, act)
334: int sig;
335: void (*act)();
336: {
337: int omask = sigblock(sigmask(sig));
338:
339: if (signal(sig, act) == SIG_IGN)
340: (void)signal(sig, SIG_IGN);
341: (void)sigsetmask(omask);
342: }
343:
344: done(status)
345: int status;
346: {
347: int w;
348:
349: mode(0);
350: if (child > 0) {
351: /* make sure catch_child does not snap it up */
352: (void)signal(SIGCHLD, SIG_DFL);
353: if (kill(child, SIGKILL) >= 0)
354: while ((w = wait((union wait *)0)) > 0 && w != child);
355: }
356: exit(status);
357: }
358:
359: int dosigwinch;
360:
361: /*
362: * This is called when the reader process gets the out-of-band (urgent)
363: * request to turn on the window-changing protocol.
364: */
365: void
366: writeroob()
367: {
368: void sigwinch();
369:
370: if (dosigwinch == 0) {
371: sendwindow();
372: (void)signal(SIGWINCH, sigwinch);
373: }
374: dosigwinch = 1;
375: }
376:
377: void
378: catch_child()
379: {
380: union wait status;
381: int pid;
382:
383: for (;;) {
384: pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0);
385: if (pid == 0)
386: return;
387: /* if the child (reader) dies, just quit */
388: if (pid < 0 || pid == child && !WIFSTOPPED(status))
389: done((int)(status.w_termsig | status.w_retcode));
390: }
391: /* NOTREACHED */
392: }
393:
394: /*
395: * writer: write to remote: 0 -> line.
396: * ~. terminate
397: * ~^Z suspend rlogin process.
398: * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
399: */
400: writer()
401: {
402: register int bol, local, n;
403: char c;
404:
405: bol = 1; /* beginning of line */
406: local = 0;
407: for (;;) {
408: n = read(STDIN_FILENO, &c, 1);
409: if (n <= 0) {
410: if (n < 0 && errno == EINTR)
411: continue;
412: break;
413: }
414: /*
415: * If we're at the beginning of the line and recognize a
416: * command character, then we echo locally. Otherwise,
417: * characters are echo'd remotely. If the command character
418: * is doubled, this acts as a force and local echo is
419: * suppressed.
420: */
421: if (bol) {
422: bol = 0;
423: if (!noescape && c == escapechar) {
424: local = 1;
425: continue;
426: }
427: } else if (local) {
428: local = 0;
429: if (c == '.' || c == deftc.t_eofc) {
430: echo(c);
431: break;
432: }
433: if (c == defltc.t_suspc || c == defltc.t_dsuspc) {
434: bol = 1;
435: echo(c);
436: stop(c);
437: continue;
438: }
439: if (c != escapechar)
440: (void)write(rem, &escapechar, 1);
441: }
442:
443: if (write(rem, &c, 1) == 0) {
444: msg("line gone");
445: break;
446: }
447: bol = c == defkill || c == deftc.t_eofc ||
448: c == deftc.t_intrc || c == defltc.t_suspc ||
449: c == '\r' || c == '\n';
450: }
451: }
452:
453: echo(c)
454: register char c;
455: {
456: register char *p;
457: char buf[8];
458:
459: p = buf;
460: c &= 0177;
461: *p++ = escapechar;
462: if (c < ' ') {
463: *p++ = '^';
464: *p++ = c + '@';
465: } else if (c == 0177) {
466: *p++ = '^';
467: *p++ = '?';
468: } else
469: *p++ = c;
470: *p++ = '\r';
471: *p++ = '\n';
472: (void)write(STDOUT_FILENO, buf, p - buf);
473: }
474:
475: stop(cmdc)
476: char cmdc;
477: {
478: mode(0);
479: (void)signal(SIGCHLD, SIG_IGN);
480: (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
481: (void)signal(SIGCHLD, catch_child);
482: mode(1);
483: sigwinch(); /* check for size changes */
484: }
485:
486: void
487: sigwinch()
488: {
489: struct winsize ws;
490:
491: if (dosigwinch && get_window_size(0, &ws) == 0 &&
492: bcmp(&ws, &winsize, sizeof(ws))) {
493: winsize = ws;
494: sendwindow();
495: }
496: }
497:
498: /*
499: * Send the window size to the server via the magic escape
500: */
501: sendwindow()
502: {
503: struct winsize *wp;
504: char obuf[4 + sizeof (struct winsize)];
505:
506: wp = (struct winsize *)(obuf+4);
507: obuf[0] = 0377;
508: obuf[1] = 0377;
509: obuf[2] = 's';
510: obuf[3] = 's';
511: wp->ws_row = htons(winsize.ws_row);
512: wp->ws_col = htons(winsize.ws_col);
513: wp->ws_xpixel = htons(winsize.ws_xpixel);
514: wp->ws_ypixel = htons(winsize.ws_ypixel);
515:
516: (void)write(rem, obuf, sizeof(obuf));
517: }
518:
519: /*
520: * reader: read from remote: line -> 1
521: */
522: #define READING 1
523: #define WRITING 2
524:
525: jmp_buf rcvtop;
526: int ppid, rcvcnt, rcvstate;
527: char rcvbuf[8 * 1024];
528:
529: void
530: oob()
531: {
532: struct sgttyb sb;
533: int atmark, n, out, rcvd;
534: char waste[BUFSIZ], mark;
535:
536: out = O_RDWR;
537: rcvd = 0;
538: while (recv(rem, &mark, 1, MSG_OOB) < 0)
539: switch (errno) {
540: case EWOULDBLOCK:
541: /*
542: * Urgent data not here yet. It may not be possible
543: * to send it yet if we are blocked for output and
544: * our input buffer is full.
545: */
546: if (rcvcnt < sizeof(rcvbuf)) {
547: n = read(rem, rcvbuf + rcvcnt,
548: sizeof(rcvbuf) - rcvcnt);
549: if (n <= 0)
550: return;
551: rcvd += n;
552: } else {
553: n = read(rem, waste, sizeof(waste));
554: if (n <= 0)
555: return;
556: }
557: continue;
558: default:
559: return;
560: }
561: if (mark & TIOCPKT_WINDOW) {
562: /* Let server know about window size changes */
563: (void)kill(ppid, SIGUSR1);
564: }
565: if (!eight && (mark & TIOCPKT_NOSTOP)) {
566: (void)ioctl(0, TIOCGETP, (char *)&sb);
567: sb.sg_flags &= ~CBREAK;
568: sb.sg_flags |= RAW;
569: (void)ioctl(0, TIOCSETN, (char *)&sb);
570: notc.t_stopc = -1;
571: notc.t_startc = -1;
572: (void)ioctl(0, TIOCSETC, (char *)¬c);
573: }
574: if (!eight && (mark & TIOCPKT_DOSTOP)) {
575: (void)ioctl(0, TIOCGETP, (char *)&sb);
576: sb.sg_flags &= ~RAW;
577: sb.sg_flags |= CBREAK;
578: (void)ioctl(0, TIOCSETN, (char *)&sb);
579: notc.t_stopc = deftc.t_stopc;
580: notc.t_startc = deftc.t_startc;
581: (void)ioctl(0, TIOCSETC, (char *)¬c);
582: }
583: if (mark & TIOCPKT_FLUSHWRITE) {
584: (void)ioctl(1, TIOCFLUSH, (char *)&out);
585: for (;;) {
586: if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
587: (void)fprintf(stderr, "rlogin: ioctl: %s.\n",
588: strerror(errno));
589: break;
590: }
591: if (atmark)
592: break;
593: n = read(rem, waste, sizeof (waste));
594: if (n <= 0)
595: break;
596: }
597: /*
598: * Don't want any pending data to be output, so clear the recv
599: * buffer. If we were hanging on a write when interrupted,
600: * don't want it to restart. If we were reading, restart
601: * anyway.
602: */
603: rcvcnt = 0;
604: longjmp(rcvtop, 1);
605: }
606:
607: /* oob does not do FLUSHREAD (alas!) */
608:
609: /*
610: * If we filled the receive buffer while a read was pending, longjmp
611: * to the top to restart appropriately. Don't abort a pending write,
612: * however, or we won't know how much was written.
613: */
614: if (rcvd && rcvstate == READING)
615: longjmp(rcvtop, 1);
616: }
617:
618: /* reader: read from remote: line -> 1 */
619: reader(omask)
620: int omask;
621: {
622: void oob();
623:
624: #if !defined(BSD) || BSD < 43
625: int pid = -getpid();
626: #else
627: int pid = getpid();
628: #endif
629: int n, remaining;
630: char *bufp = rcvbuf;
631:
632: (void)signal(SIGTTOU, SIG_IGN);
633: (void)signal(SIGURG, oob);
634: ppid = getppid();
635: (void)fcntl(rem, F_SETOWN, pid);
636: (void)setjmp(rcvtop);
637: (void)sigsetmask(omask);
638: for (;;) {
639: while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
640: rcvstate = WRITING;
641: n = write(STDOUT_FILENO, bufp, remaining);
642: if (n < 0) {
643: if (errno != EINTR)
644: return(-1);
645: continue;
646: }
647: bufp += n;
648: }
649: bufp = rcvbuf;
650: rcvcnt = 0;
651: rcvstate = READING;
652:
653: rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
654: if (rcvcnt == 0)
655: return (0);
656: if (rcvcnt < 0) {
657: if (errno == EINTR)
658: continue;
659: (void)fprintf(stderr, "rlogin: read: %s.\n",
660: strerror(errno));
661: return(-1);
662: }
663: }
664: }
665:
666: mode(f)
667: {
668: struct ltchars *ltc;
669: struct sgttyb sb;
670: struct tchars *tc;
671: int lflags;
672:
673: (void)ioctl(0, TIOCGETP, (char *)&sb);
674: (void)ioctl(0, TIOCLGET, (char *)&lflags);
675: switch(f) {
676: case 0:
677: sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
678: sb.sg_flags |= defflags|tabflag;
679: tc = &deftc;
680: ltc = &defltc;
681: sb.sg_kill = defkill;
682: sb.sg_erase = deferase;
683: lflags = deflflags;
684: break;
685: case 1:
686: sb.sg_flags |= (eight ? RAW : CBREAK);
687: sb.sg_flags &= ~defflags;
688: /* preserve tab delays, but turn off XTABS */
689: if ((sb.sg_flags & TBDELAY) == XTABS)
690: sb.sg_flags &= ~TBDELAY;
691: tc = ¬c;
692: ltc = &noltc;
693: sb.sg_kill = sb.sg_erase = -1;
694: if (litout)
695: lflags |= LLITOUT;
696: break;
697: default:
698: return;
699: }
700: (void)ioctl(0, TIOCSLTC, (char *)ltc);
701: (void)ioctl(0, TIOCSETC, (char *)tc);
702: (void)ioctl(0, TIOCSETN, (char *)&sb);
703: (void)ioctl(0, TIOCLSET, (char *)&lflags);
704: }
705:
706: void
707: lostpeer()
708: {
709: (void)signal(SIGPIPE, SIG_IGN);
710: msg("\007connection closed.");
711: done(1);
712: }
713:
714: /* copy SIGURGs to the child process. */
715: void
716: copytochild()
717: {
718: (void)kill(child, SIGURG);
719: }
720:
721: msg(str)
722: char *str;
723: {
724: (void)fprintf(stderr, "rlogin: %s\r\n", str);
725: }
726:
727: #ifdef KERBEROS
728: /* VARARGS */
729: warning(va_alist)
730: va_dcl
731: {
732: va_list ap;
733: char *fmt;
734:
735: (void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
736: va_start(ap);
737: fmt = va_arg(ap, char *);
738: vfprintf(stderr, fmt, ap);
739: va_end(ap);
740: (void)fprintf(stderr, ".\n");
741: }
742: #endif
743:
744: usage()
745: {
746: (void)fprintf(stderr,
747: "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
748: #ifdef KERBEROS
749: "8EL", " [-k realm] ");
750: #else
751: "8EL", " ");
752: #endif
753: exit(1);
754: }
755:
756: /*
757: * The following routine provides compatibility (such as it is) between 4.2BSD
758: * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
759: */
760: #ifdef sun
761: get_window_size(fd, wp)
762: int fd;
763: struct winsize *wp;
764: {
765: struct ttysize ts;
766: int error;
767:
768: if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
769: return(error);
770: wp->ws_row = ts.ts_lines;
771: wp->ws_col = ts.ts_cols;
772: wp->ws_xpixel = 0;
773: wp->ws_ypixel = 0;
774: return(0);
775: }
776: #endif
777:
778: u_char
779: getescape(p)
780: register char *p;
781: {
782: long val;
783: int len;
784:
785: if ((len = strlen(p)) == 1) /* use any single char, including '\' */
786: return((u_char)*p);
787: /* otherwise, \nnn */
788: if (*p == '\\' && len >= 2 && len <= 4) {
789: val = strtol(++p, (char **)NULL, 8);
790: for (;;) {
791: if (!*++p)
792: return((u_char)val);
793: if (*p < '0' || *p > '8')
794: break;
795: }
796: }
797: msg("illegal option value -- e");
798: usage();
799: /* NOTREACHED */
800: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.