xref: /csrg-svn/libexec/rlogind/rlogind.c (revision 36526)
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.1.3 (Berkeley) 01/07/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  * Automatic login protocol is done here, using login -f upon success,
37  * unless OLD_LOGIN is defined (then done in login, ala 4.2/4.3BSD).
38  */
39 
40 #include <stdio.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/socket.h>
44 #include <sys/wait.h>
45 #include <sys/file.h>
46 
47 #include <netinet/in.h>
48 
49 #include <errno.h>
50 #include <pwd.h>
51 #include <signal.h>
52 #include <sgtty.h>
53 #include <stdio.h>
54 #include <netdb.h>
55 #include <syslog.h>
56 #include <strings.h>
57 
58 #ifndef TIOCPKT_WINDOW
59 #define TIOCPKT_WINDOW 0x80
60 #endif
61 
62 char	*env[2];
63 #define	NMAX 30
64 char	lusername[NMAX+1], rusername[NMAX+1];
65 static	char term[64] = "TERM=";
66 #define	ENVSIZE	(sizeof("TERM=")-1)	/* skip null for concatenation */
67 int	keepalive = 1;
68 
69 #define	SUPERUSER(pwd)	((pwd)->pw_uid == 0)
70 
71 extern	int errno;
72 int	reapchild();
73 struct	passwd *getpwnam(), *pwd;
74 char	*malloc();
75 
76 main(argc, argv)
77 	int argc;
78 	char **argv;
79 {
80 	extern int opterr, optind, _check_rhosts_file;
81 	int ch;
82 	int on = 1, fromlen;
83 	struct sockaddr_in from;
84 
85 	openlog("rlogind", LOG_PID | LOG_AUTH, LOG_AUTH);
86 
87 	opterr = 0;
88 	while ((ch = getopt(argc, argv, "ln")) != EOF)
89 		switch (ch) {
90 		case 'l':
91 			_check_rhosts_file = 0;
92 			break;
93 		case 'n':
94 			keepalive = 0;
95 			break;
96 		case '?':
97 		default:
98 			syslog(LOG_ERR, "usage: rlogind [-l] [-n]");
99 			break;
100 		}
101 	argc -= optind;
102 	argv += optind;
103 
104 	fromlen = sizeof (from);
105 	if (getpeername(0, &from, &fromlen) < 0) {
106 		fprintf(stderr, "%s: ", argv[0]);
107 		perror("getpeername");
108 		exit(1);
109 	}
110 	if (keepalive &&
111 	    setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
112 		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
113 	doit(0, &from);
114 }
115 
116 int	child;
117 int	cleanup();
118 int	netf;
119 char	*line;
120 extern	char	*inet_ntoa();
121 
122 struct winsize win = { 0, 0, 0, 0 };
123 
124 
125 doit(f, fromp)
126 	int f;
127 	struct sockaddr_in *fromp;
128 {
129 	int i, p, t, pid, on = 1;
130 #ifndef OLD_LOGIN
131 	int authenticated = 0;
132 #endif
133 	register struct hostent *hp;
134 	struct hostent hostent;
135 	char c;
136 
137 	alarm(60);
138 	read(f, &c, 1);
139 	if (c != 0)
140 		exit(1);
141 
142 	alarm(0);
143 	fromp->sin_port = ntohs((u_short)fromp->sin_port);
144 	hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr),
145 		fromp->sin_family);
146 	if (hp == 0) {
147 		/*
148 		 * Only the name is used below.
149 		 */
150 		hp = &hostent;
151 		hp->h_name = inet_ntoa(fromp->sin_addr);
152 	}
153 
154 	if (fromp->sin_family != AF_INET ||
155 	    fromp->sin_port >= IPPORT_RESERVED ||
156 	    fromp->sin_port < IPPORT_RESERVED/2)
157 		fatal(f, "Permission denied");
158 	write(f, "", 1);
159 #ifndef OLD_LOGIN
160 	if (do_rlogin(hp->h_name) == 0)
161 		authenticated++;
162 #endif
163 
164 	for (c = 'p'; c <= 's'; c++) {
165 		struct stat stb;
166 		line = "/dev/ptyXX";
167 		line[strlen("/dev/pty")] = c;
168 		line[strlen("/dev/ptyp")] = '0';
169 		if (stat(line, &stb) < 0)
170 			break;
171 		for (i = 0; i < 16; i++) {
172 			line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
173 			p = open(line, O_RDWR);
174 			if (p > 0)
175 				goto gotpty;
176 		}
177 	}
178 	fatal(f, "Out of ptys");
179 	/*NOTREACHED*/
180 gotpty:
181 	(void) ioctl(p, TIOCSWINSZ, &win);
182 	netf = f;
183 	line[strlen("/dev/")] = 't';
184 	t = open(line, O_RDWR);
185 	if (t < 0)
186 		fatalperror(f, line);
187 	if (fchmod(t, 0))
188 		fatalperror(f, line);
189 	(void)signal(SIGHUP, SIG_IGN);
190 	vhangup();
191 	(void)signal(SIGHUP, SIG_DFL);
192 	t = open(line, O_RDWR);
193 	if (t < 0)
194 		fatalperror(f, line);
195 	setup_term(t);
196 #ifdef DEBUG
197 	{
198 		int tt = open("/dev/tty", O_RDWR);
199 		if (tt > 0) {
200 			(void)ioctl(tt, TIOCNOTTY, 0);
201 			(void)close(tt);
202 		}
203 	}
204 #endif
205 	pid = fork();
206 	if (pid < 0)
207 		fatalperror(f, "");
208 	if (pid == 0) {
209 		close(f), close(p);
210 		dup2(t, 0), dup2(t, 1), dup2(t, 2);
211 		close(t);
212 #ifdef OLD_LOGIN
213 		execl("/bin/login", "login", "-r", hp->h_name, 0);
214 #else /* OLD_LOGIN */
215 		if (authenticated)
216 			execl("/bin/login", "login", "-p", "-h", hp->h_name,
217 			    "-f", lusername, 0);
218 		else
219 			execl("/bin/login", "login", "-p", "-h", hp->h_name,
220 			    lusername, 0);
221 #endif /* OLD_LOGIN */
222 		fatalperror(2, "/bin/login");
223 		/*NOTREACHED*/
224 	}
225 	close(t);
226 
227 	ioctl(f, FIONBIO, &on);
228 	ioctl(p, FIONBIO, &on);
229 	ioctl(p, TIOCPKT, &on);
230 	signal(SIGTSTP, SIG_IGN);
231 	signal(SIGCHLD, cleanup);
232 	setpgrp(0, 0);
233 	protocol(f, p);
234 	signal(SIGCHLD, SIG_IGN);
235 	cleanup();
236 }
237 
238 char	magic[2] = { 0377, 0377 };
239 char	oobdata[] = {TIOCPKT_WINDOW};
240 
241 /*
242  * Handle a "control" request (signaled by magic being present)
243  * in the data stream.  For now, we are only willing to handle
244  * window size changes.
245  */
246 control(pty, cp, n)
247 	int pty;
248 	char *cp;
249 	int n;
250 {
251 	struct winsize w;
252 
253 	if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
254 		return (0);
255 	oobdata[0] &= ~TIOCPKT_WINDOW;	/* we know he heard */
256 	bcopy(cp+4, (char *)&w, sizeof(w));
257 	w.ws_row = ntohs(w.ws_row);
258 	w.ws_col = ntohs(w.ws_col);
259 	w.ws_xpixel = ntohs(w.ws_xpixel);
260 	w.ws_ypixel = ntohs(w.ws_ypixel);
261 	(void)ioctl(pty, TIOCSWINSZ, &w);
262 	return (4+sizeof (w));
263 }
264 
265 /*
266  * rlogin "protocol" machine.
267  */
268 protocol(f, p)
269 	int f, p;
270 {
271 	char pibuf[1024], fibuf[1024], *pbp, *fbp;
272 	register pcc = 0, fcc = 0;
273 	int cc, nfd, pmask, fmask;
274 	char cntl;
275 
276 	/*
277 	 * Must ignore SIGTTOU, otherwise we'll stop
278 	 * when we try and set slave pty's window shape
279 	 * (our controlling tty is the master pty).
280 	 */
281 	(void) signal(SIGTTOU, SIG_IGN);
282 	send(f, oobdata, 1, MSG_OOB);	/* indicate new rlogin */
283 	if (f > p)
284 		nfd = f + 1;
285 	else
286 		nfd = p + 1;
287 	fmask = 1 << f;
288 	pmask = 1 << p;
289 	for (;;) {
290 		int ibits, obits, ebits;
291 
292 		ibits = 0;
293 		obits = 0;
294 		if (fcc)
295 			obits |= pmask;
296 		else
297 			ibits |= fmask;
298 		if (pcc >= 0)
299 			if (pcc)
300 				obits |= fmask;
301 			else
302 				ibits |= pmask;
303 		ebits = pmask;
304 		if (select(nfd, &ibits, obits ? &obits : (int *)NULL,
305 		    &ebits, 0) < 0) {
306 			if (errno == EINTR)
307 				continue;
308 			fatalperror(f, "select");
309 		}
310 		if (ibits == 0 && obits == 0 && ebits == 0) {
311 			/* shouldn't happen... */
312 			sleep(5);
313 			continue;
314 		}
315 #define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
316 		if (ebits & pmask) {
317 			cc = read(p, &cntl, 1);
318 			if (cc == 1 && pkcontrol(cntl)) {
319 				cntl |= oobdata[0];
320 				send(f, &cntl, 1, MSG_OOB);
321 				if (cntl & TIOCPKT_FLUSHWRITE) {
322 					pcc = 0;
323 					ibits &= ~pmask;
324 				}
325 			}
326 		}
327 		if (ibits & fmask) {
328 			fcc = read(f, fibuf, sizeof(fibuf));
329 			if (fcc < 0 && errno == EWOULDBLOCK)
330 				fcc = 0;
331 			else {
332 				register char *cp;
333 				int left, n;
334 
335 				if (fcc <= 0)
336 					break;
337 				fbp = fibuf;
338 
339 			top:
340 				for (cp = fibuf; cp < fibuf+fcc-1; cp++)
341 					if (cp[0] == magic[0] &&
342 					    cp[1] == magic[1]) {
343 						left = fcc - (cp-fibuf);
344 						n = control(p, cp, left);
345 						if (n) {
346 							left -= n;
347 							if (left > 0)
348 								bcopy(cp+n, cp, left);
349 							fcc -= n;
350 							goto top; /* n^2 */
351 						}
352 					}
353 				obits |= pmask;		/* try write */
354 			}
355 		}
356 
357 		if ((obits & pmask) && fcc > 0) {
358 			cc = write(p, fbp, fcc);
359 			if (cc > 0) {
360 				fcc -= cc;
361 				fbp += cc;
362 			}
363 		}
364 
365 		if (ibits & pmask) {
366 			pcc = read(p, pibuf, sizeof (pibuf));
367 			pbp = pibuf;
368 			if (pcc < 0 && errno == EWOULDBLOCK)
369 				pcc = 0;
370 			else if (pcc <= 0)
371 				break;
372 			else if (pibuf[0] == 0) {
373 				pbp++, pcc--;
374 				obits |= fmask;	/* try a write */
375 			} else {
376 				if (pkcontrol(pibuf[0])) {
377 					pibuf[0] |= oobdata[0];
378 					send(f, &pibuf[0], 1, MSG_OOB);
379 				}
380 				pcc = 0;
381 			}
382 		}
383 		if ((obits & fmask) && pcc > 0) {
384 			cc = write(f, pbp, pcc);
385 			if (cc < 0 && errno == EWOULDBLOCK) {
386 				/* also shouldn't happen */
387 				sleep(5);
388 				continue;
389 			}
390 			if (cc > 0) {
391 				pcc -= cc;
392 				pbp += cc;
393 			}
394 		}
395 	}
396 }
397 
398 cleanup()
399 {
400 	char *p;
401 
402 	p = line + sizeof("/dev/") - 1;
403 	if (logout(p))
404 		logwtmp(p, "", "");
405 	(void)chmod(line, 0666);
406 	(void)chown(line, 0, 0);
407 	*p = 'p';
408 	(void)chmod(line, 0666);
409 	(void)chown(line, 0, 0);
410 	shutdown(netf, 2);
411 	exit(1);
412 }
413 
414 fatal(f, msg)
415 	int f;
416 	char *msg;
417 {
418 	char buf[BUFSIZ];
419 
420 	buf[0] = '\01';		/* error indicator */
421 	(void) sprintf(buf + 1, "rlogind: %s.\r\n", msg);
422 	(void) write(f, buf, strlen(buf));
423 	exit(1);
424 }
425 
426 fatalperror(f, msg)
427 	int f;
428 	char *msg;
429 {
430 	char buf[BUFSIZ];
431 	extern int sys_nerr;
432 	extern char *sys_errlist[];
433 
434 	if ((unsigned)errno < sys_nerr)
435 		(void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
436 	else
437 		(void) sprintf(buf, "%s: Error %d", msg, errno);
438 	fatal(f, buf);
439 }
440 
441 #ifndef OLD_LOGIN
442 do_rlogin(host)
443 	char *host;
444 {
445 
446 	getstr(rusername, sizeof(rusername), "remuser too long");
447 	getstr(lusername, sizeof(lusername), "locuser too long");
448 	getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long");
449 
450 	if (getuid())
451 		return(-1);
452 	pwd = getpwnam(lusername);
453 	if (pwd == NULL)
454 		return(-1);
455 	return(ruserok(host, SUPERUSER(pwd), rusername, lusername));
456 }
457 
458 
459 getstr(buf, cnt, errmsg)
460 	char *buf;
461 	int cnt;
462 	char *errmsg;
463 {
464 	char c;
465 
466 	do {
467 		if (read(0, &c, 1) != 1)
468 			exit(1);
469 		if (--cnt < 0)
470 			fatal(1, errmsg);
471 		*buf++ = c;
472 	} while (c != 0);
473 }
474 
475 extern	char **environ;
476 
477 char *speeds[] = {
478 	"0", "50", "75", "110", "134", "150", "200", "300", "600",
479 	"1200", "1800", "2400", "4800", "9600", "19200", "38400",
480 };
481 #define	NSPEEDS	(sizeof(speeds) / sizeof(speeds[0]))
482 
483 setup_term(fd)
484 	int fd;
485 {
486 	register char *cp = index(term, '/'), **cpp;
487 	struct sgttyb sgttyb;
488 	char *speed;
489 
490 	(void)ioctl(fd, TIOCGETP, &sgttyb);
491 	if (cp) {
492 		*cp++ = '\0';
493 		speed = cp;
494 		cp = index(speed, '/');
495 		if (cp)
496 			*cp++ = '\0';
497 		for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
498 		    if (strcmp(*cpp, speed) == 0) {
499 			sgttyb.sg_ispeed = sgttyb.sg_ospeed = cpp - speeds;
500 			break;
501 		    }
502 	}
503 	sgttyb.sg_flags = ECHO|CRMOD|ANYP|XTABS;
504 	(void)ioctl(fd, TIOCSETP, &sgttyb);
505 
506 	env[0] = term;
507 	env[1] = 0;
508 	environ = env;
509 }
510 #endif /* OLD_LOGIN */
511