xref: /csrg-svn/libexec/rlogind/rlogind.c (revision 36319)
1 /*
2  * Copyright (c) 1983 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 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.18 (Berkeley) 12/08/88";
26 #endif /* not lint */
27 
28 /*
29  * remote login server:
30  *	remuser\0
31  *	locuser\0
32  *	terminal info\0
33  *	data
34  */
35 
36 #include <stdio.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/socket.h>
40 #include <sys/wait.h>
41 #include <sys/file.h>
42 
43 #include <netinet/in.h>
44 
45 #include <errno.h>
46 #include <pwd.h>
47 #include <signal.h>
48 #include <sgtty.h>
49 #include <stdio.h>
50 #include <netdb.h>
51 #include <syslog.h>
52 #include <strings.h>
53 
54 # ifndef TIOCPKT_WINDOW
55 # define TIOCPKT_WINDOW 0x80
56 # endif TIOCPKT_WINDOW
57 
58 extern	int errno;
59 int	reapchild();
60 struct	passwd *getpwnam();
61 char	*malloc();
62 
63 /*ARGSUSED*/
64 main(argc, argv)
65 	int argc;
66 	char **argv;
67 {
68 	extern int opterr, optind, _check_rhosts_file;
69 	int ch;
70 	int on = 1, fromlen;
71 	struct sockaddr_in from;
72 
73 	openlog("rlogind", LOG_PID | LOG_AUTH, LOG_AUTH);
74 
75 	opterr = 0;
76 	while ((ch = getopt(argc, argv, "l")) != EOF)
77 		switch((char)ch) {
78 		case 'l':
79 			_check_rhosts_file = 0;
80 			break;
81 		case '?':
82 		default:
83 			syslog(LOG_ERR, "usage: rlogind [-l]");
84 			break;
85 		}
86 	argc -= optind;
87 	argv += optind;
88 
89 	fromlen = sizeof (from);
90 	if (getpeername(0, &from, &fromlen) < 0) {
91 		fprintf(stderr, "%s: ", argv[0]);
92 		perror("getpeername");
93 		exit(1);
94 	}
95 	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
96 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
97 	}
98 	doit(0, &from);
99 }
100 
101 int	child;
102 int	cleanup();
103 int	netf;
104 char	*line;
105 extern	char	*inet_ntoa();
106 
107 struct winsize win = { 0, 0, 0, 0 };
108 
109 
110 doit(f, fromp)
111 	int f;
112 	struct sockaddr_in *fromp;
113 {
114 	int i, p, t, pid, on = 1;
115 	register struct hostent *hp;
116 	struct hostent hostent;
117 	char c;
118 
119 	alarm(60);
120 	read(f, &c, 1);
121 	if (c != 0)
122 		exit(1);
123 	alarm(0);
124 	fromp->sin_port = ntohs((u_short)fromp->sin_port);
125 	hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr),
126 		fromp->sin_family);
127 	if (hp == 0) {
128 		/*
129 		 * Only the name is used below.
130 		 */
131 		hp = &hostent;
132 		hp->h_name = inet_ntoa(fromp->sin_addr);
133 	}
134 	if (fromp->sin_family != AF_INET ||
135 	    fromp->sin_port >= IPPORT_RESERVED ||
136 	    fromp->sin_port < IPPORT_RESERVED/2)
137 		fatal(f, "Permission denied");
138 	write(f, "", 1);
139 	for (c = 'p'; c <= 's'; c++) {
140 		struct stat stb;
141 		line = "/dev/ptyXX";
142 		line[strlen("/dev/pty")] = c;
143 		line[strlen("/dev/ptyp")] = '0';
144 		if (stat(line, &stb) < 0)
145 			break;
146 		for (i = 0; i < 16; i++) {
147 			line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
148 			p = open(line, O_RDWR);
149 			if (p > 0)
150 				goto gotpty;
151 		}
152 	}
153 	fatal(f, "Out of ptys");
154 	/*NOTREACHED*/
155 gotpty:
156 	(void) ioctl(p, TIOCSWINSZ, &win);
157 	netf = f;
158 	line[strlen("/dev/")] = 't';
159 	t = open(line, O_RDWR);
160 	if (t < 0)
161 		fatalperror(f, line);
162 	if (fchmod(t, 0))
163 		fatalperror(f, line);
164 	(void)signal(SIGHUP, SIG_IGN);
165 	vhangup();
166 	(void)signal(SIGHUP, SIG_DFL);
167 	t = open(line, O_RDWR);
168 	if (t < 0)
169 		fatalperror(f, line);
170 	{
171 		struct sgttyb b;
172 
173 		(void)ioctl(t, TIOCGETP, &b);
174 		b.sg_flags = RAW|ANYP;
175 		(void)ioctl(t, TIOCSETP, &b);
176 	}
177 #ifdef DEBUG
178 	{
179 		int tt = open("/dev/tty", O_RDWR);
180 		if (tt > 0) {
181 			(void)ioctl(tt, TIOCNOTTY, 0);
182 			(void)close(tt);
183 		}
184 	}
185 #endif
186 	pid = fork();
187 	if (pid < 0)
188 		fatalperror(f, "");
189 	if (pid == 0) {
190 		close(f), close(p);
191 		dup2(t, 0), dup2(t, 1), dup2(t, 2);
192 		close(t);
193 		execl("/bin/login", "login", "-r", hp->h_name, 0);
194 		fatalperror(2, "/bin/login");
195 		/*NOTREACHED*/
196 	}
197 	close(t);
198 	ioctl(f, FIONBIO, &on);
199 	ioctl(p, FIONBIO, &on);
200 	ioctl(p, TIOCPKT, &on);
201 	signal(SIGTSTP, SIG_IGN);
202 	signal(SIGCHLD, cleanup);
203 	setpgrp(0, 0);
204 	protocol(f, p);
205 	signal(SIGCHLD, SIG_IGN);
206 	cleanup();
207 }
208 
209 char	magic[2] = { 0377, 0377 };
210 char	oobdata[] = {TIOCPKT_WINDOW};
211 
212 /*
213  * Handle a "control" request (signaled by magic being present)
214  * in the data stream.  For now, we are only willing to handle
215  * window size changes.
216  */
217 control(pty, cp, n)
218 	int pty;
219 	char *cp;
220 	int n;
221 {
222 	struct winsize w;
223 
224 	if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
225 		return (0);
226 	oobdata[0] &= ~TIOCPKT_WINDOW;	/* we know he heard */
227 	bcopy(cp+4, (char *)&w, sizeof(w));
228 	w.ws_row = ntohs(w.ws_row);
229 	w.ws_col = ntohs(w.ws_col);
230 	w.ws_xpixel = ntohs(w.ws_xpixel);
231 	w.ws_ypixel = ntohs(w.ws_ypixel);
232 	(void)ioctl(pty, TIOCSWINSZ, &w);
233 	return (4+sizeof (w));
234 }
235 
236 /*
237  * rlogin "protocol" machine.
238  */
239 protocol(f, p)
240 	int f, p;
241 {
242 	char pibuf[1024], fibuf[1024], *pbp, *fbp;
243 	register pcc = 0, fcc = 0;
244 	int cc;
245 	char cntl;
246 
247 	/*
248 	 * Must ignore SIGTTOU, otherwise we'll stop
249 	 * when we try and set slave pty's window shape
250 	 * (our controlling tty is the master pty).
251 	 */
252 	(void) signal(SIGTTOU, SIG_IGN);
253 	send(f, oobdata, 1, MSG_OOB);	/* indicate new rlogin */
254 	for (;;) {
255 		int ibits, obits, ebits;
256 
257 		ibits = 0;
258 		obits = 0;
259 		if (fcc)
260 			obits |= (1<<p);
261 		else
262 			ibits |= (1<<f);
263 		if (pcc >= 0)
264 			if (pcc)
265 				obits |= (1<<f);
266 			else
267 				ibits |= (1<<p);
268 		ebits = (1<<p);
269 		if (select(16, &ibits, &obits, &ebits, 0) < 0) {
270 			if (errno == EINTR)
271 				continue;
272 			fatalperror(f, "select");
273 		}
274 		if (ibits == 0 && obits == 0 && ebits == 0) {
275 			/* shouldn't happen... */
276 			sleep(5);
277 			continue;
278 		}
279 #define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
280 		if (ebits & (1<<p)) {
281 			cc = read(p, &cntl, 1);
282 			if (cc == 1 && pkcontrol(cntl)) {
283 				cntl |= oobdata[0];
284 				send(f, &cntl, 1, MSG_OOB);
285 				if (cntl & TIOCPKT_FLUSHWRITE) {
286 					pcc = 0;
287 					ibits &= ~(1<<p);
288 				}
289 			}
290 		}
291 		if (ibits & (1<<f)) {
292 			fcc = read(f, fibuf, sizeof (fibuf));
293 			if (fcc < 0 && errno == EWOULDBLOCK)
294 				fcc = 0;
295 			else {
296 				register char *cp;
297 				int left, n;
298 
299 				if (fcc <= 0)
300 					break;
301 				fbp = fibuf;
302 
303 			top:
304 				for (cp = fibuf; cp < fibuf+fcc-1; cp++)
305 					if (cp[0] == magic[0] &&
306 					    cp[1] == magic[1]) {
307 						left = fcc - (cp-fibuf);
308 						n = control(p, cp, left);
309 						if (n) {
310 							left -= n;
311 							if (left > 0)
312 								bcopy(cp+n, cp, left);
313 							fcc -= n;
314 							goto top; /* n^2 */
315 						}
316 					}
317 			}
318 		}
319 
320 		if ((obits & (1<<p)) && fcc > 0) {
321 			cc = write(p, fbp, fcc);
322 			if (cc > 0) {
323 				fcc -= cc;
324 				fbp += cc;
325 			}
326 		}
327 
328 		if (ibits & (1<<p)) {
329 			pcc = read(p, pibuf, sizeof (pibuf));
330 			pbp = pibuf;
331 			if (pcc < 0 && errno == EWOULDBLOCK)
332 				pcc = 0;
333 			else if (pcc <= 0)
334 				break;
335 			else if (pibuf[0] == 0)
336 				pbp++, pcc--;
337 			else {
338 				if (pkcontrol(pibuf[0])) {
339 					pibuf[0] |= oobdata[0];
340 					send(f, &pibuf[0], 1, MSG_OOB);
341 				}
342 				pcc = 0;
343 			}
344 		}
345 		if ((obits & (1<<f)) && pcc > 0) {
346 			cc = write(f, pbp, pcc);
347 			if (cc < 0 && errno == EWOULDBLOCK) {
348 				/* also shouldn't happen */
349 				sleep(5);
350 				continue;
351 			}
352 			if (cc > 0) {
353 				pcc -= cc;
354 				pbp += cc;
355 			}
356 		}
357 	}
358 }
359 
360 cleanup()
361 {
362 	char *p;
363 
364 	p = line + sizeof("/dev/") - 1;
365 	if (logout(p))
366 		logwtmp(p, "", "");
367 	(void)chmod(line, 0666);
368 	(void)chown(line, 0, 0);
369 	*p = 'p';
370 	(void)chmod(line, 0666);
371 	(void)chown(line, 0, 0);
372 	shutdown(netf, 2);
373 	exit(1);
374 }
375 
376 fatal(f, msg)
377 	int f;
378 	char *msg;
379 {
380 	char buf[BUFSIZ];
381 
382 	buf[0] = '\01';		/* error indicator */
383 	(void) sprintf(buf + 1, "rlogind: %s.\r\n", msg);
384 	(void) write(f, buf, strlen(buf));
385 	exit(1);
386 }
387 
388 fatalperror(f, msg)
389 	int f;
390 	char *msg;
391 {
392 	char buf[BUFSIZ];
393 	extern int sys_nerr;
394 	extern char *sys_errlist[];
395 
396 	if ((unsigned)errno < sys_nerr)
397 		(void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
398 	else
399 		(void) sprintf(buf, "%s: Error %d", msg, errno);
400 	fatal(f, buf);
401 }
402