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