rlogind.c revision 1.9 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 #ifndef lint
35 static char copyright[] =
36 "@(#) Copyright (c) 1983, 1988, 1989, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 /* from: static char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93"; */
42 static char *rcsid = "$Id: rlogind.c,v 1.9 1996/11/08 07:47:45 lukem Exp $";
43 #endif /* not lint */
44
45 /*
46 * remote login server:
47 * \0
48 * remuser\0
49 * locuser\0
50 * terminal_type/speed\0
51 * data
52 */
53
54 #define FD_SETSIZE 16 /* don't need many bits for select */
55 #include <sys/param.h>
56 #include <sys/stat.h>
57 #include <sys/ioctl.h>
58 #include <signal.h>
59 #include <termios.h>
60
61 #include <sys/socket.h>
62 #include <netinet/in.h>
63 #include <netinet/in_systm.h>
64 #include <netinet/ip.h>
65 #include <arpa/inet.h>
66 #include <netdb.h>
67
68 #include <pwd.h>
69 #include <syslog.h>
70 #include <errno.h>
71 #include <stdio.h>
72 #include <unistd.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <util.h>
76 #include <utmp.h>
77 #include "pathnames.h"
78
79 #ifndef TIOCPKT_WINDOW
80 #define TIOCPKT_WINDOW 0x80
81 #endif
82
83 #define OPTIONS "alnL"
84
85 char *env[2];
86 #define NMAX 30
87 char lusername[NMAX+1], rusername[NMAX+1];
88 static char term[64] = "TERM=";
89 #define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */
90 int keepalive = 1;
91 int check_all = 0;
92 int log_success = 0;
93
94 struct passwd *pwd;
95
96 void doit __P((int, struct sockaddr_in *));
97 int control __P((int, char *, int));
98 void protocol __P((int, int));
99 void cleanup __P((int));
100 void fatal __P((int, char *, int));
101 int do_rlogin __P((struct sockaddr_in *, char *));
102 void getstr __P((char *, int, char *));
103 void setup_term __P((int));
104 int do_krb_login __P((struct sockaddr_in *));
105 void usage __P((void));
106 int local_domain __P((char *));
107 char *topdomain __P((char *));
108
109 int
110 main(argc, argv)
111 int argc;
112 char *argv[];
113 {
114 extern int __check_rhosts_file;
115 struct sockaddr_in from;
116 int ch, fromlen, on;
117
118 openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
119
120 opterr = 0;
121 while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
122 switch (ch) {
123 case 'a':
124 check_all = 1;
125 break;
126 case 'l':
127 __check_rhosts_file = 0;
128 break;
129 case 'n':
130 keepalive = 0;
131 break;
132 case 'L':
133 log_success = 1;
134 break;
135 case '?':
136 default:
137 usage();
138 break;
139 }
140 argc -= optind;
141 argv += optind;
142
143 fromlen = sizeof (from);
144 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
145 syslog(LOG_ERR,"Can't get peer name of remote host: %m");
146 fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
147 }
148 on = 1;
149 if (keepalive &&
150 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
151 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
152 on = IPTOS_LOWDELAY;
153 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
154 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
155 doit(0, &from);
156 /* NOTREACHED */
157 }
158
159 int child;
160 int netf;
161 char line[MAXPATHLEN];
162 int confirmed;
163
164 struct winsize win = { 0, 0, 0, 0 };
165
166
167 void
168 doit(f, fromp)
169 int f;
170 struct sockaddr_in *fromp;
171 {
172 int master, pid, on = 1;
173 int authenticated = 0;
174 register struct hostent *hp;
175 char utmphost[UT_HOSTSIZE + 1];
176 char *hostname;
177 char hostnamebuf[2 * MAXHOSTNAMELEN + 1];
178 char c;
179
180 alarm(60);
181 read(f, &c, 1);
182
183 if (c != 0)
184 exit(1);
185
186 alarm(0);
187 fromp->sin_port = ntohs((u_short)fromp->sin_port);
188 hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(struct in_addr),
189 fromp->sin_family);
190 if (hp) {
191 /*
192 * If name returned by gethostbyaddr is in our domain,
193 * attempt to verify that we haven't been fooled by someone
194 * in a remote net; look up the name and check that this
195 * address corresponds to the name.
196 */
197 hostname = hp->h_name;
198 if (check_all || local_domain(hp->h_name)) {
199 strncpy(hostnamebuf, hp->h_name,
200 sizeof(hostnamebuf) - 1);
201 hostnamebuf[sizeof(hostnamebuf) - 1] = 0;
202 hp = gethostbyname(hostnamebuf);
203 if (hp == NULL) {
204 syslog(LOG_INFO,
205 "Couldn't look up address for %s",
206 hostnamebuf);
207 hostname = inet_ntoa(fromp->sin_addr);
208 } else for (; ; hp->h_addr_list++) {
209 if (hp->h_addr_list[0] == NULL) {
210 syslog(LOG_NOTICE,
211 "Host addr %s not listed for host %s",
212 inet_ntoa(fromp->sin_addr),
213 hp->h_name);
214 hostname = inet_ntoa(fromp->sin_addr);
215 break;
216 }
217 if (!bcmp(hp->h_addr_list[0],
218 (caddr_t)&fromp->sin_addr,
219 sizeof(fromp->sin_addr))) {
220 hostname = hp->h_name;
221 break;
222 }
223 }
224 }
225 hostname = strncpy(hostnamebuf, hostname,
226 sizeof(hostnamebuf) - 1);
227 } else
228 hostname = strncpy(hostnamebuf, inet_ntoa(fromp->sin_addr),
229 sizeof(hostnamebuf) - 1);
230
231 hostnamebuf[sizeof(hostnamebuf) - 1] = '\0';
232
233 if (strlen(hostname) < sizeof(utmphost))
234 (void)strcpy(utmphost, hostname);
235 else
236 (void)strncpy(utmphost, inet_ntoa(fromp->sin_addr),
237 sizeof(utmphost));
238 utmphost[sizeof(utmphost) - 1] = '\0';
239
240 if (fromp->sin_family != AF_INET ||
241 fromp->sin_port >= IPPORT_RESERVED ||
242 fromp->sin_port < IPPORT_RESERVED/2) {
243 syslog(LOG_NOTICE, "Connection from %s on illegal port",
244 inet_ntoa(fromp->sin_addr));
245 fatal(f, "Permission denied", 0);
246 }
247 #ifdef IP_OPTIONS
248 {
249 u_char optbuf[BUFSIZ/3], *cp;
250 char lbuf[BUFSIZ], *lp;
251 int optsize = sizeof(optbuf), ipproto;
252 struct protoent *ip;
253
254 if ((ip = getprotobyname("ip")) != NULL)
255 ipproto = ip->p_proto;
256 else
257 ipproto = IPPROTO_IP;
258 if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf,
259 &optsize) == 0 && optsize != 0) {
260 lp = lbuf;
261 for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
262 sprintf(lp, " %2.2x", *cp);
263 syslog(LOG_NOTICE,
264 "Connection received using IP options (ignored):%s",
265 lbuf);
266 if (setsockopt(0, ipproto, IP_OPTIONS,
267 (char *)NULL, optsize) != 0) {
268 syslog(LOG_ERR,
269 "setsockopt IP_OPTIONS NULL: %m");
270 exit(1);
271 }
272 }
273 }
274 #endif
275 if (do_rlogin(fromp, hostname) == 0)
276 authenticated++;
277 if (confirmed == 0) {
278 write(f, "", 1);
279 confirmed = 1; /* we sent the null! */
280 }
281 netf = f;
282
283 pid = forkpty(&master, line, NULL, &win);
284 if (pid < 0) {
285 if (errno == ENOENT)
286 fatal(f, "Out of ptys", 0);
287 else
288 fatal(f, "Forkpty", 1);
289 }
290 if (pid == 0) {
291 if (f > 2) /* f should always be 0, but... */
292 (void) close(f);
293 setup_term(0);
294 if (authenticated)
295 execl(_PATH_LOGIN, "login", "-p",
296 "-h", utmphost, "-f", "--", lusername, (char *)0);
297 else
298 execl(_PATH_LOGIN, "login", "-p",
299 "-h", utmphost, "--", lusername, (char *)0);
300 fatal(STDERR_FILENO, _PATH_LOGIN, 1);
301 /*NOTREACHED*/
302 }
303 ioctl(f, FIONBIO, &on);
304 ioctl(master, FIONBIO, &on);
305 ioctl(master, TIOCPKT, &on);
306 signal(SIGCHLD, cleanup);
307 protocol(f, master);
308 signal(SIGCHLD, SIG_IGN);
309 cleanup(0);
310 }
311
312 char magic[2] = { 0377, 0377 };
313 char oobdata[] = {TIOCPKT_WINDOW};
314
315 /*
316 * Handle a "control" request (signaled by magic being present)
317 * in the data stream. For now, we are only willing to handle
318 * window size changes.
319 */
320 int
321 control(pty, cp, n)
322 int pty;
323 char *cp;
324 int n;
325 {
326 struct winsize w;
327
328 if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
329 return (0);
330 oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */
331 bcopy(cp+4, (char *)&w, sizeof(w));
332 w.ws_row = ntohs(w.ws_row);
333 w.ws_col = ntohs(w.ws_col);
334 w.ws_xpixel = ntohs(w.ws_xpixel);
335 w.ws_ypixel = ntohs(w.ws_ypixel);
336 (void)ioctl(pty, TIOCSWINSZ, &w);
337 return (4+sizeof (w));
338 }
339
340 /*
341 * rlogin "protocol" machine.
342 */
343 void
344 protocol(f, p)
345 register int f, p;
346 {
347 char pibuf[1024+1], fibuf[1024], *pbp, *fbp;
348 register pcc = 0, fcc = 0;
349 int cc, nfd, n;
350 char cntl;
351
352 /*
353 * Must ignore SIGTTOU, otherwise we'll stop
354 * when we try and set slave pty's window shape
355 * (our controlling tty is the master pty).
356 */
357 (void) signal(SIGTTOU, SIG_IGN);
358 send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */
359 if (f > p)
360 nfd = f + 1;
361 else
362 nfd = p + 1;
363 if (nfd > FD_SETSIZE) {
364 syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE");
365 fatal(f, "internal error (select mask too small)", 0);
366 }
367 for (;;) {
368 fd_set ibits, obits, ebits, *omask;
369
370 FD_ZERO(&ebits);
371 FD_ZERO(&ibits);
372 FD_ZERO(&obits);
373 omask = (fd_set *)NULL;
374 if (fcc) {
375 FD_SET(p, &obits);
376 omask = &obits;
377 } else
378 FD_SET(f, &ibits);
379 if (pcc >= 0)
380 if (pcc) {
381 FD_SET(f, &obits);
382 omask = &obits;
383 } else
384 FD_SET(p, &ibits);
385 FD_SET(p, &ebits);
386 if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) {
387 if (errno == EINTR)
388 continue;
389 fatal(f, "select", 1);
390 }
391 if (n == 0) {
392 /* shouldn't happen... */
393 sleep(5);
394 continue;
395 }
396 #define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
397 if (FD_ISSET(p, &ebits)) {
398 cc = read(p, &cntl, 1);
399 if (cc == 1 && pkcontrol(cntl)) {
400 cntl |= oobdata[0];
401 send(f, &cntl, 1, MSG_OOB);
402 if (cntl & TIOCPKT_FLUSHWRITE) {
403 pcc = 0;
404 FD_CLR(p, &ibits);
405 }
406 }
407 }
408 if (FD_ISSET(f, &ibits)) {
409 fcc = read(f, fibuf, sizeof(fibuf));
410 if (fcc < 0 && errno == EWOULDBLOCK)
411 fcc = 0;
412 else {
413 register char *cp;
414 int left, n;
415
416 if (fcc <= 0)
417 break;
418 fbp = fibuf;
419
420 top:
421 for (cp = fibuf; cp < fibuf+fcc-1; cp++)
422 if (cp[0] == magic[0] &&
423 cp[1] == magic[1]) {
424 left = fcc - (cp-fibuf);
425 n = control(p, cp, left);
426 if (n) {
427 left -= n;
428 if (left > 0)
429 bcopy(cp+n, cp,
430 left);
431 fcc -= n;
432 goto top; /* n^2 */
433 }
434 }
435 FD_SET(p, &obits); /* try write */
436 }
437 }
438
439 if (FD_ISSET(p, &obits) && fcc > 0) {
440 cc = write(p, fbp, fcc);
441 if (cc > 0) {
442 fcc -= cc;
443 fbp += cc;
444 }
445 }
446
447 if (FD_ISSET(p, &ibits)) {
448 pcc = read(p, pibuf, sizeof (pibuf));
449 pbp = pibuf;
450 if (pcc < 0 && errno == EWOULDBLOCK)
451 pcc = 0;
452 else if (pcc <= 0)
453 break;
454 else if (pibuf[0] == 0) {
455 pbp++, pcc--;
456 FD_SET(f, &obits); /* try write */
457 } else {
458 if (pkcontrol(pibuf[0])) {
459 pibuf[0] |= oobdata[0];
460 send(f, &pibuf[0], 1, MSG_OOB);
461 }
462 pcc = 0;
463 }
464 }
465 if ((FD_ISSET(f, &obits)) && pcc > 0) {
466 cc = write(f, pbp, pcc);
467 if (cc < 0 && errno == EWOULDBLOCK) {
468 /*
469 * This happens when we try write after read
470 * from p, but some old kernels balk at large
471 * writes even when select returns true.
472 */
473 if (!FD_ISSET(p, &ibits))
474 sleep(5);
475 continue;
476 }
477 if (cc > 0) {
478 pcc -= cc;
479 pbp += cc;
480 }
481 }
482 }
483 }
484
485 void
486 cleanup(signo)
487 int signo;
488 {
489 char *p;
490
491 p = line + sizeof(_PATH_DEV) - 1;
492 if (logout(p))
493 logwtmp(p, "", "");
494 (void)chmod(line, 0666);
495 (void)chown(line, 0, 0);
496 *p = 'p';
497 (void)chmod(line, 0666);
498 (void)chown(line, 0, 0);
499 shutdown(netf, 2);
500 exit(1);
501 }
502
503 void
504 fatal(f, msg, syserr)
505 int f;
506 char *msg;
507 int syserr;
508 {
509 int len;
510 char buf[BUFSIZ], *bp = buf;
511
512 /*
513 * Prepend binary one to message if we haven't sent
514 * the magic null as confirmation.
515 */
516 if (!confirmed)
517 *bp++ = '\01'; /* error indicator */
518 if (syserr)
519 len = sprintf(bp, "rlogind: %s: %s.\r\n",
520 msg, strerror(errno));
521 else
522 len = sprintf(bp, "rlogind: %s.\r\n", msg);
523 (void) write(f, buf, bp + len - buf);
524 exit(1);
525 }
526
527 int
528 do_rlogin(dest, host)
529 struct sockaddr_in *dest;
530 char *host;
531 {
532 extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c */
533 int retval;
534
535 getstr(rusername, sizeof(rusername), "remuser too long");
536 getstr(lusername, sizeof(lusername), "locuser too long");
537 getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
538
539 pwd = getpwnam(lusername);
540 if (pwd == NULL) {
541 syslog(LOG_INFO,
542 "%s@%s as %s: unknown login.", rusername, host, lusername);
543 return (-1);
544 }
545 retval = iruserok(dest->sin_addr.s_addr, pwd->pw_uid == 0, rusername,
546 lusername);
547 /* XXX put inet_ntoa(dest->sin_addr.s_addr) into all messages below */
548 if (retval == 0) {
549 if (log_success)
550 syslog(LOG_INFO, "%s@%s as %s: iruserok succeeded",
551 rusername, host, lusername);
552 } else {
553 if (__rcmd_errstr)
554 syslog(LOG_INFO, "%s@%s as %s: iruserok failed (%s)",
555 rusername, host, lusername, __rcmd_errstr);
556 else
557 syslog(LOG_INFO, "%s@%s as %s: iruserok failed",
558 rusername, host, lusername);
559 }
560 return(retval);
561 }
562
563 void
564 getstr(buf, cnt, errmsg)
565 char *buf;
566 int cnt;
567 char *errmsg;
568 {
569 char c;
570
571 do {
572 if (read(0, &c, 1) != 1)
573 exit(1);
574 if (--cnt < 0)
575 fatal(STDOUT_FILENO, errmsg, 0);
576 *buf++ = c;
577 } while (c != 0);
578 }
579
580 extern char **environ;
581
582 void
583 setup_term(fd)
584 int fd;
585 {
586 register char *cp = index(term+ENVSIZE, '/');
587 char *speed;
588 struct termios tt;
589
590 #ifndef notyet
591 tcgetattr(fd, &tt);
592 if (cp) {
593 *cp++ = '\0';
594 speed = cp;
595 cp = index(speed, '/');
596 if (cp)
597 *cp++ = '\0';
598 cfsetspeed(&tt, atoi(speed));
599 }
600
601 tt.c_iflag = TTYDEF_IFLAG;
602 tt.c_oflag = TTYDEF_OFLAG;
603 tt.c_lflag = TTYDEF_LFLAG;
604 tcsetattr(fd, TCSAFLUSH, &tt);
605 #else
606 if (cp) {
607 *cp++ = '\0';
608 speed = cp;
609 cp = index(speed, '/');
610 if (cp)
611 *cp++ = '\0';
612 tcgetattr(fd, &tt);
613 cfsetspeed(&tt, atoi(speed));
614 tcsetattr(fd, TCSAFLUSH, &tt);
615 }
616 #endif
617
618 env[0] = term;
619 env[1] = 0;
620 environ = env;
621 }
622
623
624 void
625 usage()
626 {
627 syslog(LOG_ERR, "usage: rlogind [-alnL]");
628 }
629
630 /*
631 * Check whether host h is in our local domain,
632 * defined as sharing the last two components of the domain part,
633 * or the entire domain part if the local domain has only one component.
634 * If either name is unqualified (contains no '.'),
635 * assume that the host is local, as it will be
636 * interpreted as such.
637 */
638 int
639 local_domain(h)
640 char *h;
641 {
642 char localhost[MAXHOSTNAMELEN];
643 char *p1, *p2;
644
645 localhost[0] = 0;
646 (void) gethostname(localhost, sizeof(localhost));
647 p1 = topdomain(localhost);
648 p2 = topdomain(h);
649 if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
650 return (1);
651 return (0);
652 }
653
654 char *
655 topdomain(h)
656 char *h;
657 {
658 register char *p;
659 char *maybe = NULL;
660 int dots = 0;
661
662 for (p = h + strlen(h); p >= h; p--) {
663 if (*p == '.') {
664 if (++dots == 2)
665 return (p);
666 maybe = p;
667 }
668 }
669 return (maybe);
670 }
671