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