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