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