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