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