xref: /csrg-svn/libexec/rlogind/rlogind.c (revision 21174)
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.1 (Berkeley) 05/28/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 	protocol(f, p);
151 	cleanup();
152 }
153 
154 char	magic[2] = { 0377, 0377 };
155 
156 /*
157  * Handle a "control" request (signaled by magic being present)
158  * in the data stream.  For now, we are only willing to handle
159  * window size changes.
160  */
161 control(pty, cp, n)
162 	int pty;
163 	char *cp;
164 	int n;
165 {
166 	struct winsize *wp;
167 
168 	if (n < 4+sizeof (*wp) || cp[2] != 's' || cp[3] != 's')
169 		return (0);
170 	wp = (struct winsize *)(cp+4);
171 	wp->ws_row = ntohs(wp->ws_row);
172 	wp->ws_col = ntohs(wp->ws_col);
173 	wp->ws_xpixel = ntohs(wp->ws_xpixel);
174 	wp->ws_ypixel = ntohs(wp->ws_ypixel);
175 	(void)ioctl(pty, TIOCSWINSZ, wp);
176 	return (4+sizeof (*wp));
177 }
178 
179 /*
180  * rlogin "protocol" machine.
181  */
182 protocol(f, p)
183 	int f, p;
184 {
185 	char pibuf[1024], fibuf[1024], *pbp, *fbp;
186 	register pcc = 0, fcc = 0;
187 	int cc, stop = TIOCPKT_DOSTOP;
188 
189 	/*
190 	 * Must ignore SIGTTOU, otherwise we'll stop
191 	 * when we try and set slave pty's window shape
192 	 * (our pgrp is that of the master pty).
193 	 */
194 	(void) signal(SIGTTOU, SIG_IGN);
195 	for (;;) {
196 		int ibits = 0, obits = 0;
197 
198 		if (fcc)
199 			obits |= (1<<p);
200 		else
201 			ibits |= (1<<f);
202 		if (pcc >= 0)
203 			if (pcc)
204 				obits |= (1<<f);
205 			else
206 				ibits |= (1<<p);
207 		if (select(16, &ibits, &obits, 0, 0) < 0) {
208 			if (errno == EINTR)
209 				continue;
210 			fatalperror(f, "select", errno);
211 		}
212 		if (ibits == 0 && obits == 0) {
213 			/* shouldn't happen... */
214 			sleep(5);
215 			continue;
216 		}
217 		if (ibits & (1<<f)) {
218 			fcc = read(f, fibuf, sizeof (fibuf));
219 			if (fcc < 0 && errno == EWOULDBLOCK)
220 				fcc = 0;
221 			else {
222 				register char *cp;
223 				int left, n;
224 
225 				if (fcc <= 0)
226 					break;
227 				fbp = fibuf;
228 			top:
229 				for (cp = fibuf; cp < fibuf+fcc; cp++)
230 					if (cp[0] == magic[0] &&
231 					    cp[1] == magic[1]) {
232 						left = fcc - (cp-fibuf);
233 						n = control(p, cp, left);
234 						if (n) {
235 							left -= n;
236 							if (left > 0)
237 								bcopy(cp, cp+n, left);
238 							fcc -= n;
239 							goto top; /* n^2 */
240 						}
241 					}
242 			}
243 		}
244 		if (ibits & (1<<p)) {
245 			pcc = read(p, pibuf, sizeof (pibuf));
246 			pbp = pibuf;
247 			if (pcc < 0 && errno == EWOULDBLOCK)
248 				pcc = 0;
249 			else if (pcc <= 0)
250 				break;
251 			else if (pibuf[0] == 0)
252 				pbp++, pcc--;
253 			else {
254 #define	pkcontrol(c)	((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
255 				if (pkcontrol(pibuf[0])) {
256 				/* The following 3 lines do nothing. */
257 					int nstop = pibuf[0] &
258 					    (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP);
259 
260 					if (nstop)
261 						stop = nstop;
262 					pibuf[0] |= nstop;
263 					send(f, &pibuf[0], 1, MSG_OOB);
264 				}
265 				pcc = 0;
266 			}
267 		}
268 		if ((obits & (1<<f)) && pcc > 0) {
269 			cc = write(f, pbp, pcc);
270 			if (cc < 0 && errno == EWOULDBLOCK) {
271 				/* also shouldn't happen */
272 				sleep(5);
273 				continue;
274 			}
275 			if (cc > 0) {
276 				pcc -= cc;
277 				pbp += cc;
278 			}
279 		}
280 		if ((obits & (1<<p)) && fcc > 0) {
281 			cc = write(p, fbp, fcc);
282 			if (cc > 0) {
283 				fcc -= cc;
284 				fbp += cc;
285 			}
286 		}
287 	}
288 }
289 
290 cleanup()
291 {
292 
293 	rmut();
294 	vhangup();		/* XXX */
295 	shutdown(netf, 2);
296 	kill(0, SIGKILL);
297 	exit(1);
298 }
299 
300 fatal(f, msg)
301 	int f;
302 	char *msg;
303 {
304 	char buf[BUFSIZ];
305 
306 	buf[0] = '\01';		/* error indicator */
307 	(void) sprintf(buf + 1, "rlogind: %s.\r\n", msg);
308 	(void) write(f, buf, strlen(buf));
309 	exit(1);
310 }
311 
312 fatalperror(f, msg, errno)
313 	int f;
314 	char *msg;
315 	int errno;
316 {
317 	char buf[BUFSIZ];
318 	extern int sys_nerr;
319 	extern char *sys_errlist[];
320 
321 	if ((unsigned)errno < sys_nerr)
322 		(void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
323 	else
324 		(void) sprintf(buf, "%s: Error %d", msg, errno);
325 	fatal(f, buf);
326 }
327 
328 #include <utmp.h>
329 
330 struct	utmp wtmp;
331 char	wtmpf[]	= "/usr/adm/wtmp";
332 char	utmp[] = "/etc/utmp";
333 #define SCPYN(a, b)	strncpy(a, b, sizeof(a))
334 #define SCMPN(a, b)	strncmp(a, b, sizeof(a))
335 
336 rmut()
337 {
338 	register f;
339 	int found = 0;
340 
341 	f = open(utmp, O_RDWR);
342 	if (f >= 0) {
343 		while(read(f, (char *)&wtmp, sizeof(wtmp)) == sizeof(wtmp)) {
344 			if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
345 				continue;
346 			lseek(f, -(long)sizeof(wtmp), L_INCR);
347 			SCPYN(wtmp.ut_name, "");
348 			SCPYN(wtmp.ut_host, "");
349 			time(&wtmp.ut_time);
350 			write(f, (char *)&wtmp, sizeof(wtmp));
351 			found++;
352 		}
353 		close(f);
354 	}
355 	if (found) {
356 		f = open(wtmpf, O_WRONLY);
357 		if (f >= 0) {
358 			SCPYN(wtmp.ut_line, line+5);
359 			SCPYN(wtmp.ut_name, "");
360 			SCPYN(wtmp.ut_host, "");
361 			time(&wtmp.ut_time);
362 			lseek(f, (long)0, L_XTND);
363 			write(f, (char *)&wtmp, sizeof(wtmp));
364 			close(f);
365 		}
366 	}
367 	chmod(line, 0666);
368 	chown(line, 0, 0);
369 	line[strlen("/dev/")] = 'p';
370 	chmod(line, 0666);
371 	chown(line, 0, 0);
372 }
373