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