]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - crypto/kerberosIV/appl/bsd/rlogind.c
This commit was generated by cvs2svn to compensate for changes in r56067,
[FreeBSD/FreeBSD.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 1999/11/25 05:27:38 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         time(&ut.ut_time);
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         time(&ut.ut_time);
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
495             execl(new_login, "login", "-p",
496                   "-h", hostname, "--", lusername, 0);
497         fatal(STDERR_FILENO, new_login, 1);
498         /*NOTREACHED*/
499     }
500     /*
501      * If encrypted, don't turn on NBIO or the des read/write
502      * routines will croak.
503      */
504
505     if (!doencrypt)
506         ioctl(f, FIONBIO, &on);
507     ioctl(master, FIONBIO, &on);
508     ioctl(master, TIOCPKT, &on);
509 #ifdef SIGTSTP
510     signal(SIGTSTP, SIG_IGN);
511 #endif
512     signal(SIGCHLD, cleanup);
513     setsid();
514     protocol(f, master);
515     signal(SIGCHLD, SIG_IGN);
516     cleanup(0);
517 }
518
519 const char      magic[2] = { 0377, 0377 };
520
521 /*
522  * Handle a "control" request (signaled by magic being present)
523  * in the data stream.  For now, we are only willing to handle
524  * window size changes.
525  */
526 static int
527 control(int master, char *cp, int n)
528 {
529     struct winsize w;
530     char *p;
531     u_int32_t tmp;
532
533     if (n < 4 + 4 * sizeof (u_int16_t) || cp[2] != 's' || cp[3] != 's')
534         return (0);
535 #ifdef TIOCSWINSZ
536     p = cp + 4;
537     p += krb_get_int(p, &tmp, 2, 0);
538     w.ws_row = tmp;
539     p += krb_get_int(p, &tmp, 2, 0);
540     w.ws_col = tmp;
541
542     p += krb_get_int(p, &tmp, 2, 0);
543 #ifdef HAVE_WS_XPIXEL
544     w.ws_xpixel = tmp;
545 #endif
546     p += krb_get_int(p, &tmp, 2, 0);
547 #ifdef HAVE_WS_YPIXEL
548     w.ws_ypixel = tmp;
549 #endif
550     ioctl(master, TIOCSWINSZ, &w);
551 #endif
552     return p - cp;
553 }
554
555 static
556 void
557 send_oob(int fd, char c)
558 {
559     static char last_oob = 0xFF;
560
561 #if (SunOS >= 50) || defined(__hpux)
562     /*
563      * PSoriasis and HP-UX always send TIOCPKT_DOSTOP at startup so we
564      * can avoid sending OOB data and thus not break on Linux by merging
565      * TIOCPKT_DOSTOP into the first TIOCPKT_WINDOW.
566      */
567     static int oob_kludge = 2;
568     if (oob_kludge == 2)
569         {
570             oob_kludge--;               /* First time send nothing */
571             return;
572         }
573     else if (oob_kludge == 1)
574         {
575             oob_kludge--;               /* Second time merge TIOCPKT_WINDOW */
576             c |= TIOCPKT_WINDOW;
577         }
578 #endif
579
580 #define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
581     c = pkcontrol(c);
582     /* Multiple OOB data breaks on Linux, avoid it when possible. */
583     if (c != last_oob)
584         send(fd, &c, 1, MSG_OOB);
585     last_oob = c;
586 }
587
588 /*
589  * rlogin "protocol" machine.
590  */
591 static void
592 protocol(int f, int master)
593 {
594     char pibuf[1024+1], fibuf[1024], *pbp, *fbp;
595     int pcc = 0, fcc = 0;
596     int cc, nfd, n;
597     char cntl;
598     unsigned char oob_queue = 0;
599
600 #ifdef SIGTTOU
601     /*
602      * Must ignore SIGTTOU, otherwise we'll stop
603      * when we try and set slave pty's window shape
604      * (our controlling tty is the master pty).
605      */
606     signal(SIGTTOU, SIG_IGN);
607 #endif
608
609     send_oob(f, TIOCPKT_WINDOW); /* indicate new rlogin */
610
611     if (f > master)
612         nfd = f + 1;
613     else
614         nfd = master + 1;
615     if (nfd > FD_SETSIZE) {
616         syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE");
617         fatal(f, "internal error (select mask too small)", 0);
618     }
619     for (;;) {
620         fd_set ibits, obits, ebits, *omask;
621
622         FD_ZERO(&ebits);
623         FD_ZERO(&ibits);
624         FD_ZERO(&obits);
625         omask = (fd_set *)NULL;
626         if (fcc) {
627             FD_SET(master, &obits);
628             omask = &obits;
629         } else
630             FD_SET(f, &ibits);
631         if (pcc >= 0) {
632             if (pcc) {
633                 FD_SET(f, &obits);
634                 omask = &obits;
635             } else
636                 FD_SET(master, &ibits);
637         }
638         FD_SET(master, &ebits);
639         if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) {
640             if (errno == EINTR)
641                 continue;
642             fatal(f, "select", 1);
643         }
644         if (n == 0) {
645             /* shouldn't happen... */
646             sleep(5);
647             continue;
648         }
649         if (FD_ISSET(master, &ebits)) {
650             cc = readstream(master, &cntl, 1);
651             if (cc == 1 && pkcontrol(cntl)) {
652 #if 0                           /* Kludge around */
653                 send_oob(f, cntl);
654 #endif
655                 oob_queue = cntl;
656                 if (cntl & TIOCPKT_FLUSHWRITE) {
657                     pcc = 0;
658                     FD_CLR(master, &ibits);
659                 }
660             }
661         }
662         if (FD_ISSET(f, &ibits)) {
663 #ifndef NOENCRYPTION
664             if (doencrypt)
665                 fcc = des_enc_read(f, fibuf,
666                                    sizeof(fibuf),
667                                    schedule, &kdata->session);
668             else
669 #endif
670                 fcc = read(f, fibuf, sizeof(fibuf));
671             if (fcc < 0 && errno == EWOULDBLOCK)
672                 fcc = 0;
673             else {
674                 char *cp;
675                 int left, n;
676
677                 if (fcc <= 0)
678                     break;
679                 fbp = fibuf;
680
681             top:
682                 for (cp = fibuf; cp < fibuf+fcc-1; cp++)
683                     if (cp[0] == magic[0] &&
684                         cp[1] == magic[1]) {
685                         left = fcc - (cp-fibuf);
686                         n = control(master, cp, left);
687                         if (n) {
688                             left -= n;
689                             if (left > 0)
690                                 memmove(cp, cp+n, left);
691                             fcc -= n;
692                             goto top; /* n^2 */
693                         }
694                     }
695                 FD_SET(master, &obits);         /* try write */
696             }
697         }
698
699         if (FD_ISSET(master, &obits) && fcc > 0) {
700             cc = write(master, fbp, fcc);
701             if (cc > 0) {
702                 fcc -= cc;
703                 fbp += cc;
704             }
705         }
706
707         if (FD_ISSET(master, &ibits)) {
708             pcc = readstream(master, pibuf, sizeof (pibuf));
709             pbp = pibuf;
710             if (pcc < 0 && errno == EWOULDBLOCK)
711                 pcc = 0;
712             else if (pcc <= 0)
713                 break;
714             else if (pibuf[0] == 0) {
715                 pbp++, pcc--;
716                 if (!doencrypt)
717                     FD_SET(f, &obits);  /* try write */
718             } else {
719                 if (pkcontrol(pibuf[0])) {
720                     oob_queue = pibuf[0];
721 #if 0                           /* Kludge around */
722                     send_oob(f, pibuf[0]);
723 #endif
724                 }
725                 pcc = 0;
726             }
727         }
728         if ((FD_ISSET(f, &obits)) && pcc > 0) {
729 #ifndef NOENCRYPTION
730             if (doencrypt)
731                 cc = des_enc_write(f, pbp, pcc, schedule, &kdata->session);
732             else
733 #endif
734                 cc = write(f, pbp, pcc);
735             if (cc < 0 && errno == EWOULDBLOCK) {
736                 /*
737                  * This happens when we try write after read
738                  * from p, but some old kernels balk at large
739                  * writes even when select returns true.
740                  */
741                 if (!FD_ISSET(master, &ibits))
742                     sleep(5);
743                 continue;
744             }
745             if (cc > 0) {
746                 pcc -= cc;
747                 pbp += cc;
748                 /* Only send urg data when normal data
749                  * has just been sent.
750                  * Linux has deep problems with more
751                  * than one byte of OOB data.
752                  */
753                 if (oob_queue) {
754                     send_oob (f, oob_queue);
755                     oob_queue = 0;
756                 }
757             }
758         }
759     }
760 }
761
762 static RETSIGTYPE
763 cleanup(int signo)
764 {
765     char *p = clean_ttyname (line);
766
767     if (rlogind_logout(p) == 0)
768         logwtmp(p, "", "");
769     chmod(line, 0666);
770     chown(line, 0, 0);
771     *p = 'p';
772     chmod(line, 0666);
773     chown(line, 0, 0);
774     shutdown(netf, 2);
775     signal(SIGHUP, SIG_IGN);
776 #ifdef HAVE_VHANGUP
777     vhangup();
778 #endif /* HAVE_VHANGUP */
779     exit(1);
780 }
781
782 void
783 fatal(int f, const char *msg, int syserr)
784 {
785     int len;
786     char buf[BUFSIZ], *bp = buf;
787
788     /*
789      * Prepend binary one to message if we haven't sent
790      * the magic null as confirmation.
791      */
792     if (!confirmed)
793         *bp++ = '\01';          /* error indicator */
794     if (syserr)
795         snprintf(bp, sizeof(buf) - (bp - buf),
796                  "rlogind: %s: %s.\r\n",
797                  msg, strerror(errno));
798     else
799         snprintf(bp, sizeof(buf) - (bp - buf),
800                  "rlogind: %s.\r\n", msg);
801     len = strlen(bp);
802 #ifndef NOENCRYPTION
803     if (doencrypt)
804         des_enc_write(f, buf, bp + len - buf, schedule, &kdata->session);
805     else
806 #endif
807         write(f, buf, bp + len - buf);
808     exit(1);
809 }
810
811 static void
812 xgetstr(char *buf, int cnt, char *errmsg)
813 {
814     char c;
815
816     do {
817         if (read(0, &c, 1) != 1)
818             exit(1);
819         if (--cnt < 0)
820             fatal(STDOUT_FILENO, errmsg, 0);
821         *buf++ = c;
822     } while (c != 0);
823 }
824
825 static int
826 do_rlogin(struct sockaddr_in *dest)
827 {
828     xgetstr(rusername, sizeof(rusername), "remuser too long");
829     xgetstr(lusername, sizeof(lusername), "locuser too long");
830     xgetstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
831
832     pwd = k_getpwnam(lusername);
833     if (pwd == NULL)
834         return (-1);
835     if (pwd->pw_uid == 0 && strcmp("root", lusername) != 0)
836         {
837             syslog(LOG_ALERT, "NIS attack, user %s has uid 0", lusername);
838             return (-1);
839         }
840     return (iruserok(dest->sin_addr.s_addr,
841                      (pwd->pw_uid == 0),
842                      rusername,
843                      lusername));
844 }
845
846 static void 
847 setup_term(int fd)
848 {
849     char *cp = strchr(term+ENVSIZE, '/');
850     char *speed;
851     struct termios tt;
852
853     tcgetattr(fd, &tt);
854     if (cp) {
855         int s;
856
857         *cp++ = '\0';
858         speed = cp;
859         cp = strchr(speed, '/');
860         if (cp)
861             *cp++ = '\0';
862         s = int2speed_t (atoi (speed));
863         if (s > 0) {
864             cfsetospeed (&tt, s);
865             cfsetispeed (&tt, s);
866         }
867     }
868
869     tt.c_iflag &= ~INPCK;
870     tt.c_iflag |= ICRNL|IXON;
871     tt.c_oflag |= OPOST|ONLCR;
872 #ifdef TAB3
873     tt.c_oflag |= TAB3;
874 #endif /* TAB3 */
875 #ifdef ONLRET
876     tt.c_oflag &= ~ONLRET;
877 #endif /* ONLRET */
878     tt.c_lflag |= (ECHO|ECHOE|ECHOK|ISIG|ICANON);
879     tt.c_cflag &= ~PARENB;
880     tt.c_cflag |= CS8;
881     tt.c_cc[VMIN] = 1;
882     tt.c_cc[VTIME] = 0;
883     tt.c_cc[VEOF] = CEOF;
884     tcsetattr(fd, TCSAFLUSH, &tt);
885
886     env[0] = term;
887     env[1] = 0;
888     environ = env;
889 }
890
891 #define VERSION_SIZE    9
892
893 /*
894  * Do the remote kerberos login to the named host with the
895  * given inet address
896  *
897  * Return 0 on valid authorization
898  * Return -1 on valid authentication, no authorization
899  * Return >0 for error conditions
900  */
901 static int
902 do_krb_login(struct sockaddr_in *dest)
903 {
904     int rc;
905     char instance[INST_SZ], version[VERSION_SIZE];
906     long authopts = 0L; /* !mutual */
907     struct sockaddr_in faddr;
908
909     kdata = (AUTH_DAT *) auth_buf;
910     ticket = (KTEXT) tick_buf;
911
912     k_getsockinst(0, instance, sizeof(instance));
913
914     if (doencrypt) {
915         rc = sizeof(faddr);
916         if (getsockname(0, (struct sockaddr *)&faddr, &rc))
917             return (-1);
918         authopts = KOPT_DO_MUTUAL;
919         rc = krb_recvauth(
920                           authopts, 0,
921                           ticket, "rcmd",
922                           instance, dest, &faddr,
923                           kdata, "", schedule, version);
924         des_set_key(&kdata->session, schedule);
925
926     } else
927         rc = krb_recvauth(
928                           authopts, 0,
929                           ticket, "rcmd",
930                           instance, dest, (struct sockaddr_in *) 0,
931                           kdata, "", 0, version);
932
933     if (rc != KSUCCESS)
934         return (rc);
935
936     xgetstr(lusername, sizeof(lusername), "locuser");
937     /* get the "cmd" in the rcmd protocol */
938     xgetstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type");
939
940     pwd = k_getpwnam(lusername);
941     if (pwd == NULL)
942         return (-1);
943     if (pwd->pw_uid == 0 && strcmp("root", lusername) != 0)
944         {
945             syslog(LOG_ALERT, "NIS attack, user %s has uid 0", lusername);
946             return (-1);
947         }
948
949     /* returns nonzero for no access */
950     if (kuserok(kdata, lusername) != 0)
951         return (-1);
952
953     return (0);
954
955 }
956
957 static void
958 usage(void)
959 {
960     syslog(LOG_ERR,
961            "usage: rlogind [-Dailn] [-p port] [-x] [-L login] [-k | -v]");
962     exit(1);
963 }