xref: /csrg-svn/libexec/rlogind/rlogind.c (revision 36518)
1 /*
2  * Copyright (c) 1983, 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1983, 1988 The Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)rlogind.c	5.22 (Berkeley) 01/06/89";
26 #endif /* not lint */
27 
28 /*
29  * remote login server:
30  *	\0
31  *	remuser\0
32  *	locuser\0
33  *	terminal_type/speed\0
34  *	data
35  */
36 
37 #include <stdio.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/socket.h>
41 #include <sys/wait.h>
42 #include <sys/file.h>
43 
44 #include <netinet/in.h>
45 
46 #include <errno.h>
47 #include <pwd.h>
48 #include <signal.h>
49 #include <sys/ioctl.h>
50 #include <sys/termios.h>
51 #include <stdio.h>
52 #include <netdb.h>
53 #include <syslog.h>
54 #include <strings.h>
55 
56 #ifndef TIOCPKT_WINDOW
57 #define TIOCPKT_WINDOW 0x80
58 #endif
59 
60 #ifdef	KERBEROS
61 #include <sys/param.h>
62 #include <kerberos/krb.h>
63 #define	SECURE_MESSAGE "This rlogin session is using DES encryption for all transmissions.\r\n"
64 
65 AUTH_DAT	*kdata;
66 KTEXT		ticket;
67 u_char		auth_buf[sizeof(AUTH_DAT)];
68 u_char		tick_buf[sizeof(KTEXT_ST)];
69 Key_schedule	schedule;
70 int		encrypt, retval;
71 int		do_krb_login();
72 
73 #define		OLD_RCMD		0x00
74 #define		KERB_RCMD		0x01
75 #define		KERB_RCMD_MUTUAL	0x02
76 #endif	/* KERBEROS */
77 
78 char	*env[2];
79 #define	NMAX 30
80 char	lusername[NMAX+1], rusername[NMAX+1];
81 static	char term[64] = "TERM=";
82 #define	ENVSIZE	(sizeof("TERM=")-1)	/* skip null for concatenation */
83 int	keepalive = 1;
84 
85 #define	SUPERUSER(pwd)	((pwd)->pw_uid == 0)
86 
87 extern	int errno;
88 int	reapchild();
89 struct	passwd *getpwnam(), *pwd;
90 char	*malloc();
91 
92 main(argc, argv)
93 	int argc;
94 	char **argv;
95 {
96 	extern int opterr, optind, _check_rhosts_file;
97 	int ch;
98 	int on = 1, fromlen;
99 	struct sockaddr_in from;
100 
101 	openlog("rlogind", LOG_PID | LOG_AUTH, LOG_AUTH);
102 
103 	opterr = 0;
104 	while ((ch = getopt(argc, argv, "ln")) != EOF)
105 		switch (ch) {
106 		case 'l':
107 			_check_rhosts_file = 0;
108 			break;
109 		case 'n':
110 			keepalive = 0;
111 			break;
112 		case '?':
113 		default:
114 			syslog(LOG_ERR, "usage: rlogind [-l] [-n]");
115 			break;
116 		}
117 	argc -= optind;
118 	argv += optind;
119 
120 	fromlen = sizeof (from);
121 	if (getpeername(0, &from, &fromlen) < 0) {
122 		fprintf(stderr, "%s: ", argv[0]);
123 		perror("getpeername");
124 		exit(1);
125 	}
126 	if (keepalive &&
127 	    setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
128 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
129 	doit(0, &from);
130 }
131 
132 int	child;
133 int	cleanup();
134 int	netf;
135 char	*line;
136 extern	char	*inet_ntoa();
137 
138 struct winsize win = { 0, 0, 0, 0 };
139 
140 
141 doit(f, fromp)
142 	int f;
143 	struct sockaddr_in *fromp;
144 {
145 	int i, p, t, pid, on = 1;
146 	int authenticated = 0;
147 	register struct hostent *hp;
148 	struct hostent hostent;
149 	char c;
150 
151 	alarm(60);
152 	read(f, &c, 1);
153 
154 #ifdef	KERBEROS
155 	/*
156 	 * XXX 1st char tells us which client we're talking to
157 	 */
158 	switch (c) {
159 
160 	case KERB_RCMD:
161 		break;
162 
163 	case KERB_RCMD_MUTUAL:
164 		encrypt = 1;
165 		break;
166 
167 
168 	case OLD_RCMD:
169 	default:
170 		fatal(f, "Remote host requires Kerberos authentication");
171 	}
172 #else
173 	if (c != 0)
174 		exit(1);
175 #endif
176 
177 	alarm(0);
178 	fromp->sin_port = ntohs((u_short)fromp->sin_port);
179 	hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr),
180 		fromp->sin_family);
181 	if (hp == 0) {
182 		/*
183 		 * Only the name is used below.
184 		 */
185 		hp = &hostent;
186 		hp->h_name = inet_ntoa(fromp->sin_addr);
187 	}
188 
189 #ifdef	KERBEROS
190 	retval = do_krb_login(hp->h_name, fromp, encrypt);
191 	write(f, &c, 1);
192 	if (retval == 0)
193 		authenticated++;
194 	else
195 		if (retval > 0)
196 			fatal(f, krb_err_txt[retval]);
197 #else
198 	if (fromp->sin_family != AF_INET ||
199 	    fromp->sin_port >= IPPORT_RESERVED ||
200 	    fromp->sin_port < IPPORT_RESERVED/2)
201 		fatal(f, "Permission denied");
202 	write(f, "", 1);
203 #endif
204 	if (do_rlogin(hp->h_name) == 0)
205 		authenticated++;
206 
207 	for (c = 'p'; c <= 's'; c++) {
208 		struct stat stb;
209 		line = "/dev/ptyXX";
210 		line[strlen("/dev/pty")] = c;
211 		line[strlen("/dev/ptyp")] = '0';
212 		if (stat(line, &stb) < 0)
213 			break;
214 		for (i = 0; i < 16; i++) {
215 			line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
216 			p = open(line, O_RDWR);
217 			if (p > 0)
218 				goto gotpty;
219 		}
220 	}
221 	fatal(f, "Out of ptys");
222 	/*NOTREACHED*/
223 gotpty:
224 	(void) ioctl(p, TIOCSWINSZ, &win);
225 	netf = f;
226 	line[strlen("/dev/")] = 't';
227 	t = open(line, O_RDWR);
228 	if (t < 0)
229 		fatalperror(f, line);
230 	if (fchmod(t, 0))
231 		fatalperror(f, line);
232 	(void)signal(SIGHUP, SIG_IGN);
233 	vhangup();
234 	(void)signal(SIGHUP, SIG_DFL);
235 	t = open(line, O_RDWR);
236 	if (t < 0)
237 		fatalperror(f, line);
238 	setup_term(t);
239 #ifdef DEBUG
240 	{
241 		int tt = open("/dev/tty", O_RDWR);
242 		if (tt > 0) {
243 			(void)ioctl(tt, TIOCNOTTY, 0);
244 			(void)close(tt);
245 		}
246 	}
247 #endif
248 	pid = fork();
249 	if (pid < 0)
250 		fatalperror(f, "");
251 	if (pid == 0) {
252 		if (setsid() < 0)
253 			fatalperror(f, "setsid");
254 		if (ioctl(t, TIOCSCTTY, 0) < 0)
255 			fatalperror(f, "ioctl(sctty)");
256 		close(f), close(p);
257 		dup2(t, 0), dup2(t, 1), dup2(t, 2);
258 		close(t);
259 		if (authenticated)
260 			execl("/bin/login", "login", "-p", "-f",
261 			    "-h", hp->h_name, lusername, 0);
262 		else
263 			execl("/bin/login", "login", "-p", "-h", hp->h_name,
264 			    lusername, 0);
265 		fatalperror(2, "/bin/login");
266 		/*NOTREACHED*/
267 	}
268 	close(t);
269 
270 #ifdef	KERBEROS
271 	/*
272 	 * If encrypted, don't turn on NBIO or the des read/write
273 	 * routines will croak.
274 	 */
275 
276 	if (encrypt)
277 		(void) des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE));
278 	else
279 #endif
280 		ioctl(f, FIONBIO, &on);
281 	ioctl(p, FIONBIO, &on);
282 	ioctl(p, TIOCPKT, &on);
283 	signal(SIGTSTP, SIG_IGN);
284 	signal(SIGCHLD, cleanup);
285 	setpgrp(0, 0);
286 	protocol(f, p);
287 	signal(SIGCHLD, SIG_IGN);
288 	cleanup();
289 }
290 
291 char	magic[2] = { 0377, 0377 };
292 char	oobdata[] = {TIOCPKT_WINDOW};
293 
294 /*
295  * Handle a "control" request (signaled by magic being present)
296  * in the data stream.  For now, we are only willing to handle
297  * window size changes.
298  */
299 control(pty, cp, n)
300 	int pty;
301 	char *cp;
302 	int n;
303 {
304 	struct winsize w;
305 
306 	if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
307 		return (0);
308 	oobdata[0] &= ~TIOCPKT_WINDOW;	/* we know he heard */
309 	bcopy(cp+4, (char *)&w, sizeof(w));
310 	w.ws_row = ntohs(w.ws_row);
311 	w.ws_col = ntohs(w.ws_col);
312 	w.ws_xpixel = ntohs(w.ws_xpixel);
313 	w.ws_ypixel = ntohs(w.ws_ypixel);
314 	(void)ioctl(pty, TIOCSWINSZ, &w);
315 	return (4+sizeof (w));
316 }
317 
318 /*
319  * rlogin "protocol" machine.
320  */
321 protocol(f, p)
322 	int f, p;
323 {
324 	char pibuf[1024], fibuf[1024], *pbp, *fbp;
325 	register pcc = 0, fcc = 0;
326 	int cc, nfd, pmask, fmask;
327 	char cntl;
328 
329 	/*
330 	 * Must ignore SIGTTOU, otherwise we'll stop
331 	 * when we try and set slave pty's window shape
332 	 * (our controlling tty is the master pty).
333 	 */
334 	(void) signal(SIGTTOU, SIG_IGN);
335 	send(f, oobdata, 1, MSG_OOB);	/* indicate new rlogin */
336 	if (f > p)
337 		nfd = f + 1;
338 	else
339 		nfd = p + 1;
340 	fmask = 1 << f;
341 	pmask = 1 << p;
342 	for (;;) {
343 		int ibits, obits, ebits;
344 
345 		ibits = 0;
346 		obits = 0;
347 		if (fcc)
348 			obits |= pmask;
349 		else
350 			ibits |= fmask;
351 		if (pcc >= 0)
352 			if (pcc)
353 				obits |= fmask;
354 			else
355 				ibits |= pmask;
356 		ebits = pmask;
357 		if (select(nfd, &ibits, obits ? &obits : (int *)NULL,
358 		    &ebits, 0) < 0) {
359 			if (errno == EINTR)
360 				continue;
361 			fatalperror(f, "select");
362 		}
363 		if (ibits == 0 && obits == 0 && ebits == 0) {
364 			/* shouldn't happen... */
365 			sleep(5);
366 			continue;
367 		}
368 #define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
369 		if (ebits & pmask) {
370 			cc = read(p, &cntl, 1);
371 			if (cc == 1 && pkcontrol(cntl)) {
372 				cntl |= oobdata[0];
373 				send(f, &cntl, 1, MSG_OOB);
374 				if (cntl & TIOCPKT_FLUSHWRITE) {
375 					pcc = 0;
376 					ibits &= ~pmask;
377 				}
378 			}
379 		}
380 		if (ibits & fmask) {
381 #ifdef	KERBEROS
382 			if (encrypt)
383 				fcc = des_read(f, fibuf, sizeof(fibuf));
384 			else
385 #endif
386 				fcc = read(f, fibuf, sizeof(fibuf));
387 			if (fcc < 0 && errno == EWOULDBLOCK)
388 				fcc = 0;
389 			else {
390 				register char *cp;
391 				int left, n;
392 
393 				if (fcc <= 0)
394 					break;
395 				fbp = fibuf;
396 
397 			top:
398 				for (cp = fibuf; cp < fibuf+fcc-1; cp++)
399 					if (cp[0] == magic[0] &&
400 					    cp[1] == magic[1]) {
401 						left = fcc - (cp-fibuf);
402 						n = control(p, cp, left);
403 						if (n) {
404 							left -= n;
405 							if (left > 0)
406 								bcopy(cp+n, cp, left);
407 							fcc -= n;
408 							goto top; /* n^2 */
409 						}
410 					}
411 				obits |= pmask;		/* try write */
412 			}
413 		}
414 
415 		if ((obits & pmask) && fcc > 0) {
416 			cc = write(p, fbp, fcc);
417 			if (cc > 0) {
418 				fcc -= cc;
419 				fbp += cc;
420 			}
421 		}
422 
423 		if (ibits & pmask) {
424 			pcc = read(p, pibuf, sizeof (pibuf));
425 			pbp = pibuf;
426 			if (pcc < 0 && errno == EWOULDBLOCK)
427 				pcc = 0;
428 			else if (pcc <= 0)
429 				break;
430 			else if (pibuf[0] == 0) {
431 				pbp++, pcc--;
432 #ifdef	KERBEROS
433 				if (!encrypt)
434 #endif
435 					obits |= fmask;	/* try a write */
436 			} else {
437 				if (pkcontrol(pibuf[0])) {
438 					pibuf[0] |= oobdata[0];
439 					send(f, &pibuf[0], 1, MSG_OOB);
440 				}
441 				pcc = 0;
442 			}
443 		}
444 		if ((obits & fmask) && pcc > 0) {
445 #ifdef	KERBEROS
446 			if (encrypt)
447 				cc = des_write(f, pbp, pcc);
448 			else
449 #endif
450 				cc = write(f, pbp, pcc);
451 			if (cc < 0 && errno == EWOULDBLOCK) {
452 				/* also shouldn't happen */
453 				sleep(5);
454 				continue;
455 			}
456 			if (cc > 0) {
457 				pcc -= cc;
458 				pbp += cc;
459 			}
460 		}
461 	}
462 }
463 
464 cleanup()
465 {
466 	char *p;
467 
468 	p = line + sizeof("/dev/") - 1;
469 	if (logout(p))
470 		logwtmp(p, "", "");
471 	(void)chmod(line, 0666);
472 	(void)chown(line, 0, 0);
473 	*p = 'p';
474 	(void)chmod(line, 0666);
475 	(void)chown(line, 0, 0);
476 	shutdown(netf, 2);
477 	exit(1);
478 }
479 
480 fatal(f, msg)
481 	int f;
482 	char *msg;
483 {
484 	char buf[BUFSIZ];
485 
486 	buf[0] = '\01';		/* error indicator */
487 	(void) sprintf(buf + 1, "rlogind: %s.\r\n", msg);
488 	(void) write(f, buf, strlen(buf));
489 	exit(1);
490 }
491 
492 fatalperror(f, msg)
493 	int f;
494 	char *msg;
495 {
496 	char buf[BUFSIZ];
497 	extern int sys_nerr;
498 	extern char *sys_errlist[];
499 
500 	if ((unsigned)errno < sys_nerr)
501 		(void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
502 	else
503 		(void) sprintf(buf, "%s: Error %d", msg, errno);
504 	fatal(f, buf);
505 }
506 
507 do_rlogin(host)
508 	char *host;
509 {
510 
511 	getstr(rusername, sizeof(rusername), "remuser too long");
512 	getstr(lusername, sizeof(lusername), "locuser too long");
513 	getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
514 
515 	if (getuid())
516 		return(-1);
517 	pwd = getpwnam(lusername);
518 	if (pwd == NULL)
519 		return(-1);
520 	return(ruserok(host, SUPERUSER(pwd), rusername, lusername));
521 }
522 
523 
524 getstr(buf, cnt, errmsg)
525 	char *buf;
526 	int cnt;
527 	char *errmsg;
528 {
529 	char c;
530 
531 	do {
532 		if (read(0, &c, 1) != 1)
533 			exit(1);
534 		if (--cnt < 0)
535 			fatal(1, errmsg);
536 		*buf++ = c;
537 	} while (c != 0);
538 }
539 
540 extern	char **environ;
541 
542 setup_term(fd)
543 	int fd;
544 {
545 	struct termios tt;
546 	register char *cp = index(term+ENVSIZE, '/');
547 	char *speed;
548 
549 	tcgetattr(fd, &tt);
550 	if (cp) {
551 		*cp++ = '\0';
552 		speed = cp;
553 		cp = index(speed, '/');
554 		if (cp)
555 			*cp++ = '\0';
556 		cfsetspeed(&tt, atoi(speed));
557 	}
558 	tt.c_iflag = BRKINT|ICRNL|IXON|ISTRIP|IEXTEN|IMAXBEL;
559 	tt.c_oflag = OPOST|ONLCR|OXTABS;
560 	tt.c_lflag = ISIG|ICANON|ECHO;
561 	tcsetattr(fd, TCSADFLUSH, &tt);
562 
563 	env[0] = term;
564 	env[1] = 0;
565 	environ = env;
566 }
567 
568 #ifdef	KERBEROS
569 #define	VERSION_SIZE	9
570 
571 /*
572  * Do the remote kerberos login to the named host with the
573  * given inet address
574  *
575  * Return 0 on valid authorization
576  * Return -1 on valid authentication, no authorization
577  * Return >0 for error conditions
578  */
579 do_krb_login(host, dest, encrypt)
580 	char *host;
581 	struct sockaddr_in *dest;
582 	int encrypt;
583 {
584 	int rc;
585 	char instance[INST_SZ], version[VERSION_SIZE];
586 	long authopts = 0L;	/* !mutual */
587 	struct sockaddr_in faddr;
588 
589 	if (getuid())
590 		return(KFAILURE);
591 
592 	kdata = (AUTH_DAT *) auth_buf;
593 	ticket = (KTEXT) tick_buf;
594 	strcpy(instance, "*");
595 
596 	if (encrypt) {
597 		rc = sizeof(faddr);
598 		if (getsockname(0, &faddr, &rc))
599 			return(-1);
600 		authopts = KOPT_DO_MUTUAL;
601 		rc = krb_recvauth(
602 			authopts, 0,
603 			ticket, "rcmd",
604 			instance, dest, &faddr,
605 			kdata, "", schedule, version);
606 		 des_set_key(kdata->session, schedule);
607 
608 	} else {
609 		rc = krb_recvauth(
610 			authopts, 0,
611 			ticket, "rcmd",
612 			instance, dest, (struct sockaddr_in *) 0,
613 			kdata, "", (bit_64 *) 0, version);
614 	}
615 
616 	if (rc != KSUCCESS)
617 		return(rc);
618 
619 	if ((rc = krb_kntoln(kdata, rusername)) != KSUCCESS)
620 		return(rc);
621 
622 	getstr(lusername, sizeof(lusername), "locuser");
623 	/* get the "cmd" in the rcmd protocol */
624 	getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type");
625 
626 	pwd = getpwnam(lusername);
627 	if (pwd == NULL)
628 		return(-1);
629 
630 	/* XXX need to use something other than ruserok */
631 	/* returns -1 for invalid authentication */
632 	return(ruserok(host, SUPERUSER(pwd), rusername, lusername));
633 }
634 #endif /* KERBEROS */
635