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