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