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