Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / kerberosIV / appl / bsd / rlogind.c
1 /*-
2  * Copyright (c) 1983, 1988, 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 /*
35  * remote login server:
36  *      \0
37  *      remuser\0
38  *      locuser\0
39  *      terminal_type/speed\0
40  *      data
41  */
42
43 #include "bsd_locl.h"
44
45 RCSID("$Id: rlogind.c,v 1.109.2.2 2000/06/23 02:37:06 assar Exp $");
46
47 extern int __check_rhosts_file;
48
49 char *INSECURE_MESSAGE =
50 "\r\n*** Connection not encrypted! Communication may be eavesdropped. ***"
51 "\r\n*** Use telnet or rlogin -x instead! ***\r\n";
52
53 #ifndef NOENCRYPTION
54 char *SECURE_MESSAGE =
55 "This rlogin session is using DES encryption for all transmissions.\r\n";
56 #else
57 #define SECURE_MESSAGE INSECURE_MESSAGE
58 #endif
59
60 AUTH_DAT        *kdata;
61 KTEXT           ticket;
62 u_char          auth_buf[sizeof(AUTH_DAT)];
63 u_char          tick_buf[sizeof(KTEXT_ST)];
64 Key_schedule    schedule;
65 int             doencrypt, retval, use_kerberos, vacuous;
66
67 #define         ARGSTR                  "Daip:lnkvxL:"
68
69 char    *env[2];
70 #define NMAX 30
71 char    lusername[NMAX+1], rusername[NMAX+1];
72 static  char term[64] = "TERM=";
73 #define ENVSIZE (sizeof("TERM=")-1)     /* skip null for concatenation */
74 int     keepalive = 1;
75 int     check_all = 0;
76 int     no_delay = 0;
77
78 struct  passwd *pwd;
79
80 static const char *new_login = _PATH_LOGIN;
81
82 static void     doit (int, struct sockaddr_in *);
83 static int      control (int, char *, int);
84 static void     protocol (int, int);
85 static RETSIGTYPE cleanup (int);
86 void    fatal (int, const char *, int);
87 static int      do_rlogin (struct sockaddr_in *);
88 static void     setup_term (int);
89 static int      do_krb_login (struct sockaddr_in *);
90 static void     usage (void);
91
92 static int
93 readstream(int p, char *ibuf, int bufsize)
94 {
95 #ifndef HAVE_GETMSG
96     return read(p, ibuf, bufsize);
97 #else
98     static int flowison = -1;  /* current state of flow: -1 is unknown */
99     static struct strbuf strbufc, strbufd;
100     static unsigned char ctlbuf[BUFSIZ];
101     static int use_read = 1;
102
103     int flags = 0;
104     int ret;
105     struct termios tsp;
106
107     struct iocblk ip;
108     char vstop, vstart;
109     int ixon;
110     int newflow;
111
112     if (use_read)
113         {
114             ret = read(p, ibuf, bufsize);
115             if (ret < 0 && errno == EBADMSG)
116                 use_read = 0;
117             else
118                 return ret;
119         }
120
121     strbufc.maxlen = BUFSIZ;
122     strbufc.buf = (char *)ctlbuf;
123     strbufd.maxlen = bufsize-1;
124     strbufd.len = 0;
125     strbufd.buf = ibuf+1;
126     ibuf[0] = 0;
127
128     ret = getmsg(p, &strbufc, &strbufd, &flags);
129     if (ret < 0)  /* error of some sort -- probably EAGAIN */
130         return(-1);
131
132     if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) {
133         /* data message */
134         if (strbufd.len > 0) {                  /* real data */
135             return(strbufd.len + 1);    /* count header char */
136         } else {
137             /* nothing there */
138             errno = EAGAIN;
139             return(-1);
140         }
141     }
142
143     /*
144      * It's a control message.  Return 1, to look at the flag we set
145      */
146
147     switch (ctlbuf[0]) {
148     case M_FLUSH:
149         if (ibuf[1] & FLUSHW)
150             ibuf[0] = TIOCPKT_FLUSHWRITE;
151         return(1);
152
153     case M_IOCTL:
154         memcpy(&ip, (ibuf+1), sizeof(ip));
155
156         switch (ip.ioc_cmd) {
157 #ifdef TCSETS
158         case TCSETS:
159         case TCSETSW:
160         case TCSETSF:
161             memcpy(&tsp,
162                    (ibuf+1 + sizeof(struct iocblk)),
163                    sizeof(tsp));
164             vstop = tsp.c_cc[VSTOP];
165             vstart = tsp.c_cc[VSTART];
166             ixon = tsp.c_iflag & IXON;
167             break;
168 #endif
169         default:
170             errno = EAGAIN;
171             return(-1);
172         }
173
174         newflow =  (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0;
175         if (newflow != flowison) {  /* it's a change */
176             flowison = newflow;
177             ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP;
178             return(1);
179         }
180     }
181
182     /* nothing worth doing anything about */
183     errno = EAGAIN;
184     return(-1);
185 #endif
186 }
187
188 #ifdef HAVE_UTMPX_H
189 static int
190 rlogind_logout(const char *line)
191 {
192     struct utmpx utmpx, *utxp;
193     int ret = 1;
194
195     setutxent ();
196     memset(&utmpx, 0, sizeof(utmpx));
197     utmpx.ut_type = USER_PROCESS;
198     strncpy(utmpx.ut_line, line, sizeof(utmpx.ut_line));
199     utxp = getutxline(&utmpx);
200     if (utxp) {
201         utxp->ut_user[0] = '\0';
202         utxp->ut_type = DEAD_PROCESS;
203 #ifdef HAVE_STRUCT_UTMPX_UT_EXIT
204 #ifdef _STRUCT___EXIT_STATUS
205         utxp->ut_exit.__e_termination = 0;
206         utxp->ut_exit.__e_exit = 0;
207 #elif defined(__osf__) /* XXX */
208         utxp->ut_exit.ut_termination = 0;
209         utxp->ut_exit.ut_exit = 0;
210 #else   
211         utxp->ut_exit.e_termination = 0;
212         utxp->ut_exit.e_exit = 0;
213 #endif
214 #endif
215         gettimeofday(&utxp->ut_tv, NULL);
216         pututxline(utxp);
217 #ifdef WTMPX_FILE
218         updwtmpx(WTMPX_FILE, utxp);
219 #else
220         ret = 0;
221 #endif
222     }
223     endutxent();
224     return ret;
225 }
226 #else
227 static int
228 rlogind_logout(const char *line)
229 {
230     FILE *fp;
231     struct utmp ut;
232     int rval;
233
234     if (!(fp = fopen(_PATH_UTMP, "r+")))
235         return(0);
236     rval = 1;
237     while (fread(&ut, sizeof(struct utmp), 1, fp) == 1) {
238         if (!ut.ut_name[0] ||
239             strncmp(ut.ut_line, line, sizeof(ut.ut_line)))
240             continue;
241         memset(ut.ut_name, 0, sizeof(ut.ut_name));
242 #ifdef HAVE_STRUCT_UTMP_UT_HOST
243         memset(ut.ut_host, 0, sizeof(ut.ut_host));
244 #endif
245 #ifdef HAVE_STRUCT_UTMP_UT_TYPE
246         ut.ut_type = DEAD_PROCESS;
247 #endif
248 #ifdef HAVE_STRUCT_UTMP_UT_EXIT
249 #ifdef _STRUCT___EXIT_STATUS
250         ut.ut_exit.__e_termination = 0;
251         ut.ut_exit.__e_exit = 0;
252 #elif defined(__osf__) /* XXX */
253         ut.ut_exit.ut_termination = 0;
254         ut.ut_exit.ut_exit = 0;
255 #else   
256         ut.ut_exit.e_termination = 0;
257         ut.ut_exit.e_exit = 0;
258 #endif
259 #endif
260         ut.ut_time = time(NULL);
261         fseek(fp, (long)-sizeof(struct utmp), SEEK_CUR);
262         fwrite(&ut, sizeof(struct utmp), 1, fp);
263         fseek(fp, (long)0, SEEK_CUR);
264         rval = 0;
265     }
266     fclose(fp);
267     return(rval);
268 }
269 #endif
270
271 #ifndef HAVE_LOGWTMP
272 static void
273 logwtmp(const char *line, const char *name, const char *host)
274 {
275     struct utmp ut;
276     struct stat buf;
277     int fd;
278
279     memset (&ut, 0, sizeof(ut));
280     if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0)
281         return;
282     if (!fstat(fd, &buf)) {
283         strncpy(ut.ut_line, line, sizeof(ut.ut_line));
284         strncpy(ut.ut_name, name, sizeof(ut.ut_name));
285 #ifdef HAVE_STRUCT_UTMP_UT_ID
286         strncpy(ut.ut_id, make_id((char *)line), sizeof(ut.ut_id));
287 #endif
288 #ifdef HAVE_STRUCT_UTMP_UT_HOST
289         strncpy(ut.ut_host, host, sizeof(ut.ut_host));
290 #endif
291 #ifdef HAVE_STRUCT_UTMP_UT_PID
292         ut.ut_pid = getpid();
293 #endif
294 #ifdef HAVE_STRUCT_UTMP_UT_TYPE
295         if(name[0])
296             ut.ut_type = USER_PROCESS;
297         else
298             ut.ut_type = DEAD_PROCESS;
299 #endif
300         ut.ut_time = time(NULL);
301         if (write(fd, &ut, sizeof(struct utmp)) !=
302             sizeof(struct utmp))
303             ftruncate(fd, buf.st_size);
304     }
305     close(fd);
306 }
307 #endif
308
309 int
310 main(int argc, char **argv)
311 {
312     struct sockaddr_in from;
313     int ch, fromlen, on;
314     int interactive = 0;
315     int portnum = 0;
316
317     set_progname(argv[0]);
318
319     openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
320
321     opterr = 0;
322     while ((ch = getopt(argc, argv, ARGSTR)) != -1)
323         switch (ch) {
324         case 'D':
325             no_delay = 1;
326             break;
327         case 'a':
328             break;
329         case 'i':
330             interactive = 1;
331             break;
332         case 'p':
333             portnum = htons(atoi(optarg));
334             break;
335         case 'l':
336             __check_rhosts_file = 0;
337             break;
338         case 'n':
339             keepalive = 0;
340             break;
341         case 'k':
342             use_kerberos = 1;
343             break;
344         case 'v':
345             vacuous = 1;
346             break;
347         case 'x':
348             doencrypt = 1;
349             break;
350         case 'L':
351             new_login = optarg;
352             break;
353         case '?':
354         default:
355             usage();
356             break;
357         }
358     argc -= optind;
359     argv += optind;
360
361     if (use_kerberos && vacuous) {
362         usage();
363         fatal(STDERR_FILENO, "only one of -k and -v allowed", 0);
364     }
365     if (interactive) {
366         if(portnum == 0)
367             portnum = get_login_port (use_kerberos, doencrypt);
368         mini_inetd (portnum);
369     }
370
371     fromlen = sizeof (from);
372     if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
373         syslog(LOG_ERR,"Can't get peer name of remote host: %m");
374         fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
375     }
376     on = 1;
377 #ifdef HAVE_SETSOCKOPT
378 #ifdef SO_KEEPALIVE
379     if (keepalive &&
380         setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
381                    sizeof (on)) < 0)
382         syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
383 #endif
384 #ifdef TCP_NODELAY
385     if (no_delay &&
386         setsockopt(0, IPPROTO_TCP, TCP_NODELAY, (void *)&on,
387                    sizeof(on)) < 0)
388         syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
389 #endif
390
391 #ifdef IP_TOS
392     on = IPTOS_LOWDELAY;
393     if (setsockopt(0, IPPROTO_IP, IP_TOS, (void *)&on, sizeof(int)) < 0)
394         syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
395 #endif
396 #endif /* HAVE_SETSOCKOPT */
397     doit(0, &from);
398     return 0;
399 }
400
401 int     child;
402 int     netf;
403 char    line[MaxPathLen];
404 int     confirmed;
405
406 struct winsize win = { 0, 0, 0, 0 };
407
408
409 static void
410 doit(int f, struct sockaddr_in *fromp)
411 {
412     int master, pid, on = 1;
413     int authenticated = 0;
414     char hostname[2 * MaxHostNameLen + 1];
415     char c;
416
417     alarm(60);
418     read(f, &c, 1);
419
420     if (c != 0)
421         exit(1);
422     if (vacuous)
423         fatal(f, "Remote host requires Kerberos authentication", 0);
424
425     alarm(0);
426     inaddr2str (fromp->sin_addr, hostname, sizeof(hostname));
427
428     if (use_kerberos) {
429         retval = do_krb_login(fromp);
430         if (retval == 0)
431             authenticated++;
432         else if (retval > 0)
433             fatal(f, krb_get_err_text(retval), 0);
434         write(f, &c, 1);
435         confirmed = 1;          /* we sent the null! */
436     } else {
437         fromp->sin_port = ntohs((u_short)fromp->sin_port);
438         if (fromp->sin_family != AF_INET ||
439             fromp->sin_port >= IPPORT_RESERVED ||
440             fromp->sin_port < IPPORT_RESERVED/2) {
441             syslog(LOG_NOTICE, "Connection from %s on illegal port",
442                    inet_ntoa(fromp->sin_addr));
443             fatal(f, "Permission denied", 0);
444         }
445         ip_options_and_die (0, fromp);
446         if (do_rlogin(fromp) == 0)
447             authenticated++;
448     }
449     if (confirmed == 0) {
450         write(f, "", 1);
451         confirmed = 1;          /* we sent the null! */
452     }
453 #ifndef NOENCRYPTION
454     if (doencrypt)
455         des_enc_write(f, SECURE_MESSAGE,
456                       strlen(SECURE_MESSAGE),
457                       schedule, &kdata->session);
458     else
459 #endif
460         write(f, INSECURE_MESSAGE, strlen(INSECURE_MESSAGE));
461     netf = f;
462
463 #ifdef HAVE_FORKPTY
464     pid = forkpty(&master, line, NULL, NULL);
465 #else
466     pid = forkpty_truncate(&master, line, sizeof(line), NULL, NULL);
467 #endif
468     if (pid < 0) {
469         if (errno == ENOENT)
470             fatal(f, "Out of ptys", 0);
471         else
472             fatal(f, "Forkpty", 1);
473     }
474     if (pid == 0) {
475         if (f > 2)      /* f should always be 0, but... */
476             close(f);
477         setup_term(0);
478         if (lusername[0] == '-'){
479             syslog(LOG_ERR, "tried to pass user \"%s\" to login",
480                    lusername);
481             fatal(STDERR_FILENO, "invalid user", 0);
482         }
483         if (authenticated) {
484             if (use_kerberos && (pwd->pw_uid == 0))
485                 syslog(LOG_INFO|LOG_AUTH,
486                        "ROOT Kerberos login from %s on %s\n",
487                        krb_unparse_name_long(kdata->pname, 
488                                              kdata->pinst, 
489                                              kdata->prealm), 
490                        hostname);
491                     
492             execl(new_login, "login", "-p",
493                   "-h", hostname, "-f", "--", lusername, 0);
494         } else if (use_kerberos) {
495             fprintf(stderr, "User `%s' is not authorized to login as `%s'!\n",
496                     krb_unparse_name_long(kdata->pname, 
497                                           kdata->pinst, 
498                                           kdata->prealm),
499                     lusername);
500             exit(1);
501         } else
502             execl(new_login, "login", "-p",
503                   "-h", hostname, "--", lusername, 0);
504         fatal(STDERR_FILENO, new_login, 1);
505         /*NOTREACHED*/
506     }
507     /*
508      * If encrypted, don't turn on NBIO or the des read/write
509      * routines will croak.
510      */
511
512     if (!doencrypt)
513         ioctl(f, FIONBIO, &on);
514     ioctl(master, FIONBIO, &on);
515     ioctl(master, TIOCPKT, &on);
516 #ifdef SIGTSTP
517     signal(SIGTSTP, SIG_IGN);
518 #endif
519     signal(SIGCHLD, cleanup);
520     setsid();
521     protocol(f, master);
522     signal(SIGCHLD, SIG_IGN);
523     cleanup(0);
524 }
525
526 const char      magic[2] = { 0377, 0377 };
527
528 /*
529  * Handle a "control" request (signaled by magic being present)
530  * in the data stream.  For now, we are only willing to handle
531  * window size changes.
532  */
533 static int
534 control(int master, char *cp, int n)
535 {
536     struct winsize w;
537     char *p;
538     u_int32_t tmp;
539
540     if (n < 4 + 4 * sizeof (u_int16_t) || cp[2] != 's' || cp[3] != 's')
541         return (0);
542 #ifdef TIOCSWINSZ
543     p = cp + 4;
544     p += krb_get_int(p, &tmp, 2, 0);
545     w.ws_row = tmp;
546     p += krb_get_int(p, &tmp, 2, 0);
547     w.ws_col = tmp;
548
549     p += krb_get_int(p, &tmp, 2, 0);
550 #ifdef HAVE_WS_XPIXEL
551     w.ws_xpixel = tmp;
552 #endif
553     p += krb_get_int(p, &tmp, 2, 0);
554 #ifdef HAVE_WS_YPIXEL
555     w.ws_ypixel = tmp;
556 #endif
557     ioctl(master, TIOCSWINSZ, &w);
558 #endif
559     return p - cp;
560 }
561
562 static
563 void
564 send_oob(int fd, char c)
565 {
566     static char last_oob = 0xFF;
567
568 #if (SunOS >= 50) || defined(__hpux)
569     /*
570      * PSoriasis and HP-UX always send TIOCPKT_DOSTOP at startup so we
571      * can avoid sending OOB data and thus not break on Linux by merging
572      * TIOCPKT_DOSTOP into the first TIOCPKT_WINDOW.
573      */
574     static int oob_kludge = 2;
575     if (oob_kludge == 2)
576         {
577             oob_kludge--;               /* First time send nothing */
578             return;
579         }
580     else if (oob_kludge == 1)
581         {
582             oob_kludge--;               /* Second time merge TIOCPKT_WINDOW */
583             c |= TIOCPKT_WINDOW;
584         }
585 #endif
586
587 #define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
588     c = pkcontrol(c);
589     /* Multiple OOB data breaks on Linux, avoid it when possible. */
590     if (c != last_oob)
591         send(fd, &c, 1, MSG_OOB);
592     last_oob = c;
593 }
594
595 /*
596  * rlogin "protocol" machine.
597  */
598 static void
599 protocol(int f, int master)
600 {
601     char pibuf[1024+1], fibuf[1024], *pbp, *fbp;
602     int pcc = 0, fcc = 0;
603     int cc, nfd, n;
604     char cntl;
605     unsigned char oob_queue = 0;
606
607 #ifdef SIGTTOU
608     /*
609      * Must ignore SIGTTOU, otherwise we'll stop
610      * when we try and set slave pty's window shape
611      * (our controlling tty is the master pty).
612      */
613     signal(SIGTTOU, SIG_IGN);
614 #endif
615
616     send_oob(f, TIOCPKT_WINDOW); /* indicate new rlogin */
617
618     if (f > master)
619         nfd = f + 1;
620     else
621         nfd = master + 1;
622     if (nfd > FD_SETSIZE) {
623         syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE");
624         fatal(f, "internal error (select mask too small)", 0);
625     }
626     for (;;) {
627         fd_set ibits, obits, ebits, *omask;
628
629         FD_ZERO(&ebits);
630         FD_ZERO(&ibits);
631         FD_ZERO(&obits);
632         omask = (fd_set *)NULL;
633         if (fcc) {
634             FD_SET(master, &obits);
635             omask = &obits;
636         } else
637             FD_SET(f, &ibits);
638         if (pcc >= 0) {
639             if (pcc) {
640                 FD_SET(f, &obits);
641                 omask = &obits;
642             } else
643                 FD_SET(master, &ibits);
644         }
645         FD_SET(master, &ebits);
646         if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) {
647             if (errno == EINTR)
648                 continue;
649             fatal(f, "select", 1);
650         }
651         if (n == 0) {
652             /* shouldn't happen... */
653             sleep(5);
654             continue;
655         }
656         if (FD_ISSET(master, &ebits)) {
657             cc = readstream(master, &cntl, 1);
658             if (cc == 1 && pkcontrol(cntl)) {
659 #if 0                           /* Kludge around */
660                 send_oob(f, cntl);
661 #endif
662                 oob_queue = cntl;
663                 if (cntl & TIOCPKT_FLUSHWRITE) {
664                     pcc = 0;
665                     FD_CLR(master, &ibits);
666                 }
667             }
668         }
669         if (FD_ISSET(f, &ibits)) {
670 #ifndef NOENCRYPTION
671             if (doencrypt)
672                 fcc = des_enc_read(f, fibuf,
673                                    sizeof(fibuf),
674                                    schedule, &kdata->session);
675             else
676 #endif
677                 fcc = read(f, fibuf, sizeof(fibuf));
678             if (fcc < 0 && errno == EWOULDBLOCK)
679                 fcc = 0;
680             else {
681                 char *cp;
682                 int left, n;
683
684                 if (fcc <= 0)
685                     break;
686                 fbp = fibuf;
687
688             top:
689                 for (cp = fibuf; cp < fibuf+fcc-1; cp++)
690                     if (cp[0] == magic[0] &&
691                         cp[1] == magic[1]) {
692                         left = fcc - (cp-fibuf);
693                         n = control(master, cp, left);
694                         if (n) {
695                             left -= n;
696                             if (left > 0)
697                                 memmove(cp, cp+n, left);
698                             fcc -= n;
699                             goto top; /* n^2 */
700                         }
701                     }
702                 FD_SET(master, &obits);         /* try write */
703             }
704         }
705
706         if (FD_ISSET(master, &obits) && fcc > 0) {
707             cc = write(master, fbp, fcc);
708             if (cc > 0) {
709                 fcc -= cc;
710                 fbp += cc;
711             }
712         }
713
714         if (FD_ISSET(master, &ibits)) {
715             pcc = readstream(master, pibuf, sizeof (pibuf));
716             pbp = pibuf;
717             if (pcc < 0 && errno == EWOULDBLOCK)
718                 pcc = 0;
719             else if (pcc <= 0)
720                 break;
721             else if (pibuf[0] == 0) {
722                 pbp++, pcc--;
723                 if (!doencrypt)
724                     FD_SET(f, &obits);  /* try write */
725             } else {
726                 if (pkcontrol(pibuf[0])) {
727                     oob_queue = pibuf[0];
728 #if 0                           /* Kludge around */
729                     send_oob(f, pibuf[0]);
730 #endif
731                 }
732                 pcc = 0;
733             }
734         }
735         if ((FD_ISSET(f, &obits)) && pcc > 0) {
736 #ifndef NOENCRYPTION
737             if (doencrypt)
738                 cc = des_enc_write(f, pbp, pcc, schedule, &kdata->session);
739             else
740 #endif
741                 cc = write(f, pbp, pcc);
742             if (cc < 0 && errno == EWOULDBLOCK) {
743                 /*
744                  * This happens when we try write after read
745                  * from p, but some old kernels balk at large
746                  * writes even when select returns true.
747                  */
748                 if (!FD_ISSET(master, &ibits))
749                     sleep(5);
750                 continue;
751             }
752             if (cc > 0) {
753                 pcc -= cc;
754                 pbp += cc;
755                 /* Only send urg data when normal data
756                  * has just been sent.
757                  * Linux has deep problems with more
758                  * than one byte of OOB data.
759                  */
760                 if (oob_queue) {
761                     send_oob (f, oob_queue);
762                     oob_queue = 0;
763                 }
764             }
765         }
766     }
767 }
768
769 static RETSIGTYPE
770 cleanup(int signo)
771 {
772     char *p = clean_ttyname (line);
773
774     if (rlogind_logout(p) == 0)
775         logwtmp(p, "", "");
776     chmod(line, 0666);
777     chown(line, 0, 0);
778     *p = 'p';
779     chmod(line, 0666);
780     chown(line, 0, 0);
781     shutdown(netf, 2);
782     signal(SIGHUP, SIG_IGN);
783 #ifdef HAVE_VHANGUP
784     vhangup();
785 #endif /* HAVE_VHANGUP */
786     exit(1);
787 }
788
789 void
790 fatal(int f, const char *msg, int syserr)
791 {
792     int len;
793     char buf[BUFSIZ], *bp = buf;
794
795     /*
796      * Prepend binary one to message if we haven't sent
797      * the magic null as confirmation.
798      */
799     if (!confirmed)
800         *bp++ = '\01';          /* error indicator */
801     if (syserr)
802         snprintf(bp, sizeof(buf) - (bp - buf),
803                  "rlogind: %s: %s.\r\n",
804                  msg, strerror(errno));
805     else
806         snprintf(bp, sizeof(buf) - (bp - buf),
807                  "rlogind: %s.\r\n", msg);
808     len = strlen(bp);
809 #ifndef NOENCRYPTION
810     if (doencrypt)
811         des_enc_write(f, buf, bp + len - buf, schedule, &kdata->session);
812     else
813 #endif
814         write(f, buf, bp + len - buf);
815     exit(1);
816 }
817
818 static void
819 xgetstr(char *buf, int cnt, char *errmsg)
820 {
821     char c;
822
823     do {
824         if (read(0, &c, 1) != 1)
825             exit(1);
826         if (--cnt < 0)
827             fatal(STDOUT_FILENO, errmsg, 0);
828         *buf++ = c;
829     } while (c != 0);
830 }
831
832 static int
833 do_rlogin(struct sockaddr_in *dest)
834 {
835     xgetstr(rusername, sizeof(rusername), "remuser too long");
836     xgetstr(lusername, sizeof(lusername), "locuser too long");
837     xgetstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
838
839     pwd = k_getpwnam(lusername);
840     if (pwd == NULL)
841         return (-1);
842     if (pwd->pw_uid == 0 && strcmp("root", lusername) != 0)
843         {
844             syslog(LOG_ALERT, "NIS attack, user %s has uid 0", lusername);
845             return (-1);
846         }
847     return (iruserok(dest->sin_addr.s_addr,
848                      (pwd->pw_uid == 0),
849                      rusername,
850                      lusername));
851 }
852
853 static void 
854 setup_term(int fd)
855 {
856     char *cp = strchr(term+ENVSIZE, '/');
857     char *speed;
858     struct termios tt;
859
860     tcgetattr(fd, &tt);
861     if (cp) {
862         int s;
863
864         *cp++ = '\0';
865         speed = cp;
866         cp = strchr(speed, '/');
867         if (cp)
868             *cp++ = '\0';
869         s = int2speed_t (atoi (speed));
870         if (s > 0) {
871             cfsetospeed (&tt, s);
872             cfsetispeed (&tt, s);
873         }
874     }
875
876     tt.c_iflag &= ~INPCK;
877     tt.c_iflag |= ICRNL|IXON;
878     tt.c_oflag |= OPOST|ONLCR;
879 #ifdef TAB3
880     tt.c_oflag |= TAB3;
881 #endif /* TAB3 */
882 #ifdef ONLRET
883     tt.c_oflag &= ~ONLRET;
884 #endif /* ONLRET */
885     tt.c_lflag |= (ECHO|ECHOE|ECHOK|ISIG|ICANON);
886     tt.c_cflag &= ~PARENB;
887     tt.c_cflag |= CS8;
888     tt.c_cc[VMIN] = 1;
889     tt.c_cc[VTIME] = 0;
890     tt.c_cc[VEOF] = CEOF;
891     tcsetattr(fd, TCSAFLUSH, &tt);
892
893     env[0] = term;
894     env[1] = 0;
895     environ = env;
896 }
897
898 #define VERSION_SIZE    9
899
900 /*
901  * Do the remote kerberos login to the named host with the
902  * given inet address
903  *
904  * Return 0 on valid authorization
905  * Return -1 on valid authentication, no authorization
906  * Return >0 for error conditions
907  */
908 static int
909 do_krb_login(struct sockaddr_in *dest)
910 {
911     int rc;
912     char instance[INST_SZ], version[VERSION_SIZE];
913     long authopts = 0L; /* !mutual */
914     struct sockaddr_in faddr;
915
916     kdata = (AUTH_DAT *) auth_buf;
917     ticket = (KTEXT) tick_buf;
918
919     k_getsockinst(0, instance, sizeof(instance));
920
921     if (doencrypt) {
922         rc = sizeof(faddr);
923         if (getsockname(0, (struct sockaddr *)&faddr, &rc))
924             return (-1);
925         authopts = KOPT_DO_MUTUAL;
926         rc = krb_recvauth(
927                           authopts, 0,
928                           ticket, "rcmd",
929                           instance, dest, &faddr,
930                           kdata, "", schedule, version);
931         des_set_key(&kdata->session, schedule);
932
933     } else
934         rc = krb_recvauth(
935                           authopts, 0,
936                           ticket, "rcmd",
937                           instance, dest, (struct sockaddr_in *) 0,
938                           kdata, "", 0, version);
939
940     if (rc != KSUCCESS)
941         return (rc);
942
943     xgetstr(lusername, sizeof(lusername), "locuser");
944     /* get the "cmd" in the rcmd protocol */
945     xgetstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type");
946
947     pwd = k_getpwnam(lusername);
948     if (pwd == NULL)
949         return (-1);
950     if (pwd->pw_uid == 0 && strcmp("root", lusername) != 0)
951         {
952             syslog(LOG_ALERT, "NIS attack, user %s has uid 0", lusername);
953             return (-1);
954         }
955
956     /* returns nonzero for no access */
957     if (kuserok(kdata, lusername) != 0)
958         return (-1);
959
960     return (0);
961
962 }
963
964 static void
965 usage(void)
966 {
967     syslog(LOG_ERR,
968            "usage: rlogind [-Dailn] [-p port] [-x] [-L login] [-k | -v]");
969     exit(1);
970 }