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