]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - libexec/rlogind/rlogind.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / libexec / rlogind / rlogind.c
1 /*-
2  * Copyright (c) 1983, 1988, 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 2002 Networks Associates Technology, Inc.
5  * All rights reserved.
6  *
7  * Portions of this software were developed for the FreeBSD Project by
8  * ThinkSec AS and NAI Labs, the Security Research Division of Network
9  * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10  * ("CBOSS"), as part of the DARPA CHATS research program.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *      This product includes software developed by the University of
23  *      California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40
41 #if 0
42 #ifndef lint
43 static const char copyright[] =
44 "@(#) Copyright (c) 1983, 1988, 1989, 1993\n\
45         The Regents of the University of California.  All rights reserved.\n";
46 #endif /* not lint */
47
48 #ifndef lint
49 static const char sccsid[] = "@(#)rlogind.c     8.1 (Berkeley) 6/4/93";
50 #endif /* not lint */
51 #endif
52 #include <sys/cdefs.h>
53 __FBSDID("$FreeBSD$");
54
55 /*
56  * remote login server:
57  *      \0
58  *      remuser\0
59  *      locuser\0
60  *      terminal_type/speed\0
61  *      data
62  */
63
64 #define FD_SETSIZE      16              /* don't need many bits for select */
65 #include <sys/types.h>
66 #include <sys/param.h>
67 #include <sys/stat.h>
68 #include <sys/ioctl.h>
69 #include <signal.h>
70 #include <termios.h>
71
72 #include <sys/socket.h>
73 #include <netinet/in.h>
74 #include <netinet/in_systm.h>
75 #include <netinet/ip.h>
76 #include <netinet/tcp.h>
77 #include <arpa/inet.h>
78 #include <netdb.h>
79
80 #include <errno.h>
81 #include <libutil.h>
82 #include <paths.h>
83 #include <pwd.h>
84 #include <syslog.h>
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <string.h>
88 #include <unistd.h>
89
90
91 #ifndef TIOCPKT_WINDOW
92 #define TIOCPKT_WINDOW 0x80
93 #endif
94
95 #define         ARGSTR                  "Daln"
96
97 char    *env[2];
98 #define NMAX 30
99 char    lusername[NMAX+1], rusername[NMAX+1];
100 static  char term[64] = "TERM=";
101 #define ENVSIZE (sizeof("TERM=")-1)     /* skip null for concatenation */
102 int     keepalive = 1;
103 int     check_all = 0;
104 int     no_delay;
105
106 struct  passwd *pwd;
107
108 union sockunion {
109         struct sockinet {
110                 u_char si_len;
111                 u_char si_family;
112                 u_short si_port;
113         } su_si;
114         struct sockaddr_in  su_sin;
115         struct sockaddr_in6 su_sin6;
116 };
117 #define su_len          su_si.si_len
118 #define su_family       su_si.si_family
119 #define su_port         su_si.si_port
120
121 void    doit(int, union sockunion *);
122 int     control(int, char *, int);
123 void    protocol(int, int);
124 void    cleanup(int);
125 void    fatal(int, char *, int);
126 int     do_rlogin(union sockunion *);
127 void    getstr(char *, int, char *);
128 void    setup_term(int);
129 int     do_krb_login(struct sockaddr_in *);
130 void    usage(void);
131
132
133 int
134 main(int argc, char *argv[])
135 {
136         extern int __check_rhosts_file;
137         union sockunion from;
138         socklen_t fromlen;
139         int ch, on;
140
141         openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
142
143         opterr = 0;
144         while ((ch = getopt(argc, argv, ARGSTR)) != -1)
145                 switch (ch) {
146                 case 'D':
147                         no_delay = 1;
148                         break;
149                 case 'a':
150                         check_all = 1;
151                         break;
152                 case 'l':
153                         __check_rhosts_file = 0;
154                         break;
155                 case 'n':
156                         keepalive = 0;
157                         break;
158                 case '?':
159                 default:
160                         usage();
161                         break;
162                 }
163         argc -= optind;
164         argv += optind;
165
166         fromlen = sizeof (from);
167         if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
168                 syslog(LOG_ERR,"Can't get peer name of remote host: %m");
169                 fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
170         }
171         on = 1;
172         if (keepalive &&
173             setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
174                 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
175         if (no_delay &&
176             setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
177                 syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
178         if (from.su_family == AF_INET)
179       {
180         on = IPTOS_LOWDELAY;
181         if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
182                 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
183       }
184
185         doit(0, &from);
186         return 0;
187 }
188
189 int     child;
190 int     netf;
191 char    line[MAXPATHLEN];
192 int     confirmed;
193
194 struct winsize win = { 0, 0, 0, 0 };
195
196
197 void
198 doit(int f, union sockunion *fromp)
199 {
200         int master, pid, on = 1;
201         int authenticated = 0;
202         char hostname[2 * MAXHOSTNAMELEN + 1];
203         char nameinfo[2 * INET6_ADDRSTRLEN + 1];
204         char c;
205
206         alarm(60);
207         read(f, &c, 1);
208
209         if (c != 0)
210                 exit(1);
211
212         alarm(0);
213
214         realhostname_sa(hostname, sizeof(hostname) - 1,
215                             (struct sockaddr *)fromp, fromp->su_len);
216         /* error check ? */
217         fromp->su_port = ntohs((u_short)fromp->su_port);
218         hostname[sizeof(hostname) - 1] = '\0';
219
220         {
221                 if ((fromp->su_family != AF_INET
222 #ifdef INET6
223                   && fromp->su_family != AF_INET6
224 #endif
225                      ) ||
226                     fromp->su_port >= IPPORT_RESERVED ||
227                     fromp->su_port < IPPORT_RESERVED/2) {
228                         getnameinfo((struct sockaddr *)fromp,
229                                     fromp->su_len,
230                                     nameinfo, sizeof(nameinfo), NULL, 0,
231                                     NI_NUMERICHOST);
232                         /* error check ? */
233                         syslog(LOG_NOTICE, "Connection from %s on illegal port",
234                                nameinfo);
235                         fatal(f, "Permission denied", 0);
236                 }
237 #ifdef IP_OPTIONS
238                 if (fromp->su_family == AF_INET)
239               {
240                 u_char optbuf[BUFSIZ/3];
241                 socklen_t optsize = sizeof(optbuf);
242                 int ipproto, i;
243                 struct protoent *ip;
244
245                 if ((ip = getprotobyname("ip")) != NULL)
246                         ipproto = ip->p_proto;
247                 else
248                         ipproto = IPPROTO_IP;
249                 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf,
250                     &optsize) == 0 && optsize != 0) {
251                         for (i = 0; i < optsize; ) {
252                                 u_char c = optbuf[i];
253                                 if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
254                                         syslog(LOG_NOTICE,
255                                                 "Connection refused from %s with IP option %s",
256                                                 inet_ntoa(fromp->su_sin.sin_addr),
257                                                 c == IPOPT_LSRR ? "LSRR" : "SSRR");
258                                         exit(1);
259                                 }
260                                 if (c == IPOPT_EOL)
261                                         break;
262                                 i += (c == IPOPT_NOP) ? 1 : optbuf[i+1];
263                         }
264                 }
265               }
266 #endif
267                 if (do_rlogin(fromp) == 0)
268                         authenticated++;
269         }
270         if (confirmed == 0) {
271                 write(f, "", 1);
272                 confirmed = 1;          /* we sent the null! */
273         }
274         netf = f;
275
276         pid = forkpty(&master, line, NULL, &win);
277         if (pid < 0) {
278                 if (errno == ENOENT)
279                         fatal(f, "Out of ptys", 0);
280                 else
281                         fatal(f, "Forkpty", 1);
282         }
283         if (pid == 0) {
284                 if (f > 2)      /* f should always be 0, but... */
285                         (void) close(f);
286                 setup_term(0);
287                  if (*lusername=='-') {
288                         syslog(LOG_ERR, "tried to pass user \"%s\" to login",
289                                lusername);
290                         fatal(STDERR_FILENO, "invalid user", 0);
291                 }
292                 if (authenticated) {
293                         execl(_PATH_LOGIN, "login", "-p",
294                             "-h", hostname, "-f", lusername, (char *)NULL);
295                 } else
296                         execl(_PATH_LOGIN, "login", "-p",
297                             "-h", hostname, lusername, (char *)NULL);
298                 fatal(STDERR_FILENO, _PATH_LOGIN, 1);
299                 /*NOTREACHED*/
300         }
301         ioctl(f, FIONBIO, &on);
302         ioctl(master, FIONBIO, &on);
303         ioctl(master, TIOCPKT, &on);
304         signal(SIGCHLD, cleanup);
305         protocol(f, master);
306         signal(SIGCHLD, SIG_IGN);
307         cleanup(0);
308 }
309
310 char    magic[2] = { 0377, 0377 };
311 char    oobdata[] = {TIOCPKT_WINDOW};
312
313 /*
314  * Handle a "control" request (signaled by magic being present)
315  * in the data stream.  For now, we are only willing to handle
316  * window size changes.
317  */
318 int
319 control(int pty, char *cp, int n)
320 {
321         struct winsize w;
322
323         if (n < 4 + (int)sizeof(w) || cp[2] != 's' || cp[3] != 's')
324                 return (0);
325         oobdata[0] &= ~TIOCPKT_WINDOW;  /* we know he heard */
326         bcopy(cp+4, (char *)&w, sizeof(w));
327         w.ws_row = ntohs(w.ws_row);
328         w.ws_col = ntohs(w.ws_col);
329         w.ws_xpixel = ntohs(w.ws_xpixel);
330         w.ws_ypixel = ntohs(w.ws_ypixel);
331         (void)ioctl(pty, TIOCSWINSZ, &w);
332         return (4+sizeof (w));
333 }
334
335 /*
336  * rlogin "protocol" machine.
337  */
338 void
339 protocol(int f, int p)
340 {
341         char pibuf[1024+1], fibuf[1024], *pbp = NULL, *fbp = NULL;
342         int pcc = 0, fcc = 0;
343         int cc, nfd, n;
344         char cntl;
345
346         /*
347          * Must ignore SIGTTOU, otherwise we'll stop
348          * when we try and set slave pty's window shape
349          * (our controlling tty is the master pty).
350          */
351         (void) signal(SIGTTOU, SIG_IGN);
352         send(f, oobdata, 1, MSG_OOB);   /* indicate new rlogin */
353         if (f > p)
354                 nfd = f + 1;
355         else
356                 nfd = p + 1;
357         if (nfd > FD_SETSIZE) {
358                 syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE");
359                 fatal(f, "internal error (select mask too small)", 0);
360         }
361         for (;;) {
362                 fd_set ibits, obits, ebits, *omask;
363
364                 FD_ZERO(&ebits);
365                 FD_ZERO(&ibits);
366                 FD_ZERO(&obits);
367                 omask = (fd_set *)NULL;
368                 if (fcc) {
369                         FD_SET(p, &obits);
370                         omask = &obits;
371                 } else
372                         FD_SET(f, &ibits);
373                 if (pcc >= 0) {
374                         if (pcc) {
375                                 FD_SET(f, &obits);
376                                 omask = &obits;
377                         } else
378                                 FD_SET(p, &ibits);
379                 }
380                 FD_SET(p, &ebits);
381                 if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) {
382                         if (errno == EINTR)
383                                 continue;
384                         fatal(f, "select", 1);
385                 }
386                 if (n == 0) {
387                         /* shouldn't happen... */
388                         sleep(5);
389                         continue;
390                 }
391 #define pkcontrol(c)    ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
392                 if (FD_ISSET(p, &ebits)) {
393                         cc = read(p, &cntl, 1);
394                         if (cc == 1 && pkcontrol(cntl)) {
395                                 cntl |= oobdata[0];
396                                 send(f, &cntl, 1, MSG_OOB);
397                                 if (cntl & TIOCPKT_FLUSHWRITE) {
398                                         pcc = 0;
399                                         FD_CLR(p, &ibits);
400                                 }
401                         }
402                 }
403                 if (FD_ISSET(f, &ibits)) {
404                         fcc = read(f, fibuf, sizeof(fibuf));
405                         if (fcc < 0 && errno == EWOULDBLOCK)
406                                 fcc = 0;
407                         else {
408                                 char *cp;
409                                 int left, n;
410
411                                 if (fcc <= 0)
412                                         break;
413                                 fbp = fibuf;
414
415                         top:
416                                 for (cp = fibuf; cp < fibuf+fcc-1; cp++)
417                                         if (cp[0] == magic[0] &&
418                                             cp[1] == magic[1]) {
419                                                 left = fcc - (cp-fibuf);
420                                                 n = control(p, cp, left);
421                                                 if (n) {
422                                                         left -= n;
423                                                         if (left > 0)
424                                                                 bcopy(cp+n, cp, left);
425                                                         fcc -= n;
426                                                         goto top; /* n^2 */
427                                                 }
428                                         }
429                                 FD_SET(p, &obits);              /* try write */
430                         }
431                 }
432
433                 if (FD_ISSET(p, &obits) && fcc > 0) {
434                         cc = write(p, fbp, fcc);
435                         if (cc > 0) {
436                                 fcc -= cc;
437                                 fbp += cc;
438                         }
439                 }
440
441                 if (FD_ISSET(p, &ibits)) {
442                         pcc = read(p, pibuf, sizeof (pibuf));
443                         pbp = pibuf;
444                         if (pcc < 0 && errno == EWOULDBLOCK)
445                                 pcc = 0;
446                         else if (pcc <= 0)
447                                 break;
448                         else if (pibuf[0] == 0) {
449                                 pbp++, pcc--;
450                                 FD_SET(f, &obits);      /* try write */
451                         } else {
452                                 if (pkcontrol(pibuf[0])) {
453                                         pibuf[0] |= oobdata[0];
454                                         send(f, &pibuf[0], 1, MSG_OOB);
455                                 }
456                                 pcc = 0;
457                         }
458                 }
459                 if ((FD_ISSET(f, &obits)) && pcc > 0) {
460                         cc = write(f, pbp, pcc);
461                         if (cc < 0 && errno == EWOULDBLOCK) {
462                                 /*
463                                  * This happens when we try write after read
464                                  * from p, but some old kernels balk at large
465                                  * writes even when select returns true.
466                                  */
467                                 if (!FD_ISSET(p, &ibits))
468                                         sleep(5);
469                                 continue;
470                         }
471                         if (cc > 0) {
472                                 pcc -= cc;
473                                 pbp += cc;
474                         }
475                 }
476         }
477 }
478
479 void
480 cleanup(int signo __unused)
481 {
482
483         shutdown(netf, SHUT_RDWR);
484         exit(1);
485 }
486
487 void
488 fatal(int f, char *msg, int syserr)
489 {
490         int len;
491         char buf[BUFSIZ], *bp = buf;
492
493         /*
494          * Prepend binary one to message if we haven't sent
495          * the magic null as confirmation.
496          */
497         if (!confirmed)
498                 *bp++ = '\01';          /* error indicator */
499         if (syserr)
500                 len = snprintf(bp, sizeof(buf), "rlogind: %s: %s.\r\n",
501                     msg, strerror(errno));
502         else
503                 len = snprintf(bp, sizeof(buf), "rlogind: %s.\r\n", msg);
504         if (len < 0)
505                 len = 0;
506         (void) write(f, buf, bp + len - buf);
507         exit(1);
508 }
509
510 int
511 do_rlogin(union sockunion *dest)
512 {
513
514         getstr(rusername, sizeof(rusername), "remuser too long");
515         getstr(lusername, sizeof(lusername), "locuser too long");
516         getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
517
518         pwd = getpwnam(lusername);
519         if (pwd == NULL)
520                 return (-1);
521         /* XXX why don't we syslog() failure? */
522
523         return (iruserok_sa(dest, dest->su_len, pwd->pw_uid == 0, rusername,
524                             lusername));
525 }
526
527 void
528 getstr(char *buf, int cnt, char *errmsg)
529 {
530         char c;
531
532         do {
533                 if (read(STDIN_FILENO, &c, 1) != 1)
534                         exit(1);
535                 if (--cnt < 0)
536                         fatal(STDOUT_FILENO, errmsg, 0);
537                 *buf++ = c;
538         } while (c != 0);
539 }
540
541 extern  char **environ;
542
543 void
544 setup_term(int fd)
545 {
546         char *cp = index(term+ENVSIZE, '/');
547         char *speed;
548         struct termios tt, def;
549
550 #ifndef notyet
551         tcgetattr(fd, &tt);
552         if (cp) {
553                 *cp++ = '\0';
554                 speed = cp;
555                 cp = index(speed, '/');
556                 if (cp)
557                         *cp++ = '\0';
558                 cfsetspeed(&tt, atoi(speed));
559         }
560
561         cfmakesane(&def);
562         tt.c_iflag = def.c_iflag;
563         tt.c_oflag = def.c_oflag;
564         tt.c_lflag = def.c_lflag;
565         tcsetattr(fd, TCSAFLUSH, &tt);
566 #else
567         if (cp) {
568                 *cp++ = '\0';
569                 speed = cp;
570                 cp = index(speed, '/');
571                 if (cp)
572                         *cp++ = '\0';
573                 tcgetattr(fd, &tt);
574                 cfsetspeed(&tt, atoi(speed));
575                 tcsetattr(fd, TCSAFLUSH, &tt);
576         }
577 #endif
578
579         env[0] = term;
580         env[1] = 0;
581         environ = env;
582 }
583
584 void
585 usage(void)
586 {
587         syslog(LOG_ERR, "usage: rlogind [-" ARGSTR "]");
588 }