xref: /netbsd-src/lib/libc/net/rcmd.c (revision ef0582f1e7060c966166b57043cae3c26a8512b4)
1 /*	$NetBSD: rcmd.c,v 1.11 1995/02/25 06:20:53 cgd Exp $	*/
2 
3 /*
4  * Copyright (c) 1983, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #if defined(LIBC_SCCS) && !defined(lint)
37 #if 0
38 static char sccsid[] = "@(#)rcmd.c	8.3 (Berkeley) 3/26/94";
39 #else
40 static char *rcsid = "$NetBSD: rcmd.c,v 1.11 1995/02/25 06:20:53 cgd Exp $";
41 #endif
42 #endif /* LIBC_SCCS and not lint */
43 
44 #include <sys/param.h>
45 #include <sys/socket.h>
46 #include <sys/stat.h>
47 
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 
51 #include <signal.h>
52 #include <fcntl.h>
53 #include <netdb.h>
54 #include <unistd.h>
55 #include <pwd.h>
56 #include <errno.h>
57 #include <stdio.h>
58 #include <ctype.h>
59 #include <string.h>
60 
61 int	__ivaliduser __P((FILE *, u_long, const char *, const char *));
62 static int __icheckhost __P((u_long, const char *));
63 
64 int
65 rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
66 	char **ahost;
67 	u_short rport;
68 	const char *locuser, *remuser, *cmd;
69 	int *fd2p;
70 {
71 	struct hostent *hp;
72 	struct sockaddr_in sin, from;
73 	fd_set reads;
74 	long oldmask;
75 	pid_t pid;
76 	int s, lport, timo;
77 	char c;
78 
79 	pid = getpid();
80 	hp = gethostbyname(*ahost);
81 	if (hp == NULL) {
82 		herror(*ahost);
83 		return (-1);
84 	}
85 	*ahost = hp->h_name;
86 	oldmask = sigblock(sigmask(SIGURG));
87 	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
88 		s = rresvport(&lport);
89 		if (s < 0) {
90 			if (errno == EAGAIN)
91 				(void)fprintf(stderr,
92 				    "rcmd: socket: All ports in use\n");
93 			else
94 				(void)fprintf(stderr, "rcmd: socket: %s\n",
95 				    strerror(errno));
96 			sigsetmask(oldmask);
97 			return (-1);
98 		}
99 		fcntl(s, F_SETOWN, pid);
100 		sin.sin_family = hp->h_addrtype;
101 		bcopy(hp->h_addr_list[0], &sin.sin_addr, hp->h_length);
102 		sin.sin_port = rport;
103 		if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
104 			break;
105 		(void)close(s);
106 		if (errno == EADDRINUSE) {
107 			lport--;
108 			continue;
109 		}
110 		if (errno == ECONNREFUSED && timo <= 16) {
111 			(void)sleep(timo);
112 			timo *= 2;
113 			continue;
114 		}
115 		if (hp->h_addr_list[1] != NULL) {
116 			int oerrno = errno;
117 
118 			(void)fprintf(stderr, "connect to address %s: ",
119 			    inet_ntoa(sin.sin_addr));
120 			errno = oerrno;
121 			perror(0);
122 			hp->h_addr_list++;
123 			bcopy(hp->h_addr_list[0], &sin.sin_addr, hp->h_length);
124 			(void)fprintf(stderr, "Trying %s...\n",
125 			    inet_ntoa(sin.sin_addr));
126 			continue;
127 		}
128 		(void)fprintf(stderr, "%s: %s\n", hp->h_name, strerror(errno));
129 		sigsetmask(oldmask);
130 		return (-1);
131 	}
132 	lport--;
133 	if (fd2p == 0) {
134 		write(s, "", 1);
135 		lport = 0;
136 	} else {
137 		char num[8];
138 		int s2 = rresvport(&lport), s3;
139 		int len = sizeof(from);
140 
141 		if (s2 < 0)
142 			goto bad;
143 		listen(s2, 1);
144 		(void)snprintf(num, sizeof(num), "%d", lport);
145 		if (write(s, num, strlen(num)+1) != strlen(num)+1) {
146 			(void)fprintf(stderr,
147 			    "rcmd: write (setting up stderr): %s\n",
148 			    strerror(errno));
149 			(void)close(s2);
150 			goto bad;
151 		}
152 		FD_ZERO(&reads);
153 		FD_SET(s, &reads);
154 		FD_SET(s2, &reads);
155 		errno = 0;
156 		if (select(MAX(s, s2) + 1, &reads, 0, 0, 0) < 1 ||
157 		    !FD_ISSET(s2, &reads)) {
158 			if (errno != 0)
159 				(void)fprintf(stderr,
160 				    "rcmd: select (setting up stderr): %s\n",
161 				    strerror(errno));
162 			else
163 				(void)fprintf(stderr,
164 				"select: protocol failure in circuit setup\n");
165 			(void)close(s2);
166 			goto bad;
167 		}
168 		s3 = accept(s2, (struct sockaddr *)&from, &len);
169 		(void)close(s2);
170 		if (s3 < 0) {
171 			(void)fprintf(stderr,
172 			    "rcmd: accept: %s\n", strerror(errno));
173 			lport = 0;
174 			goto bad;
175 		}
176 		*fd2p = s3;
177 		from.sin_port = ntohs((u_short)from.sin_port);
178 		if (from.sin_family != AF_INET ||
179 		    from.sin_port >= IPPORT_RESERVED ||
180 		    from.sin_port < IPPORT_RESERVED / 2) {
181 			(void)fprintf(stderr,
182 			    "socket: protocol failure in circuit setup.\n");
183 			goto bad2;
184 		}
185 	}
186 	(void)write(s, locuser, strlen(locuser)+1);
187 	(void)write(s, remuser, strlen(remuser)+1);
188 	(void)write(s, cmd, strlen(cmd)+1);
189 	if (read(s, &c, 1) != 1) {
190 		(void)fprintf(stderr,
191 		    "rcmd: %s: %s\n", *ahost, strerror(errno));
192 		goto bad2;
193 	}
194 	if (c != 0) {
195 		while (read(s, &c, 1) == 1) {
196 			(void)write(STDERR_FILENO, &c, 1);
197 			if (c == '\n')
198 				break;
199 		}
200 		goto bad2;
201 	}
202 	sigsetmask(oldmask);
203 	return (s);
204 bad2:
205 	if (lport)
206 		(void)close(*fd2p);
207 bad:
208 	(void)close(s);
209 	sigsetmask(oldmask);
210 	return (-1);
211 }
212 
213 int
214 rresvport(alport)
215 	int *alport;
216 {
217 	struct sockaddr_in sin;
218 	int s;
219 
220 	sin.sin_family = AF_INET;
221 	sin.sin_addr.s_addr = INADDR_ANY;
222 	s = socket(AF_INET, SOCK_STREAM, 0);
223 	if (s < 0)
224 		return (-1);
225 	for (;;) {
226 		sin.sin_port = htons((u_short)*alport);
227 		if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
228 			return (s);
229 		if (errno != EADDRINUSE) {
230 			(void)close(s);
231 			return (-1);
232 		}
233 		(*alport)--;
234 		if (*alport == IPPORT_RESERVED/2) {
235 			(void)close(s);
236 			errno = EAGAIN;		/* close */
237 			return (-1);
238 		}
239 	}
240 }
241 
242 int	__check_rhosts_file = 1;
243 char	*__rcmd_errstr;
244 
245 int
246 ruserok(rhost, superuser, ruser, luser)
247 	const char *rhost, *ruser, *luser;
248 	int superuser;
249 {
250 	struct hostent *hp;
251 	char **ap;
252 	int i;
253 #define MAXADDRS	35
254 	u_long addrs[MAXADDRS + 1];
255 
256 	if ((hp = gethostbyname(rhost)) == NULL)
257 		return (-1);
258 	for (i = 0, ap = hp->h_addr_list; *ap && i < MAXADDRS; ++ap, ++i)
259 		bcopy(*ap, &addrs[i], sizeof(addrs[i]));
260 	addrs[i] = 0;
261 
262 	for (i = 0; i < MAXADDRS && addrs[i]; i++)
263 		if (iruserok(addrs[i], superuser, ruser, luser) == 0)
264 			return (0);
265 	return (-1);
266 }
267 
268 /*
269  * New .rhosts strategy: We are passed an ip address. We spin through
270  * hosts.equiv and .rhosts looking for a match. When the .rhosts only
271  * has ip addresses, we don't have to trust a nameserver.  When it
272  * contains hostnames, we spin through the list of addresses the nameserver
273  * gives us and look for a match.
274  *
275  * Returns 0 if ok, -1 if not ok.
276  */
277 int
278 iruserok(raddr, superuser, ruser, luser)
279 	u_long raddr;
280 	int superuser;
281 	const char *ruser, *luser;
282 {
283 	register char *cp;
284 	struct stat sbuf;
285 	struct passwd *pwd;
286 	FILE *hostf;
287 	uid_t uid;
288 	int first;
289 	char pbuf[MAXPATHLEN];
290 
291 	first = 1;
292 	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
293 again:
294 	if (hostf) {
295 		if (__ivaliduser(hostf, raddr, luser, ruser) == 0) {
296 			(void)fclose(hostf);
297 			return (0);
298 		}
299 		(void)fclose(hostf);
300 	}
301 	if (first == 1 && (__check_rhosts_file || superuser)) {
302 		first = 0;
303 		if ((pwd = getpwnam(luser)) == NULL)
304 			return (-1);
305 		(void)strcpy(pbuf, pwd->pw_dir);
306 		(void)strcat(pbuf, "/.rhosts");
307 
308 		/*
309 		 * Change effective uid while opening .rhosts.  If root and
310 		 * reading an NFS mounted file system, can't read files that
311 		 * are protected read/write owner only.
312 		 */
313 		uid = geteuid();
314 		(void)seteuid(pwd->pw_uid);
315 		hostf = fopen(pbuf, "r");
316 		(void)seteuid(uid);
317 
318 		if (hostf == NULL)
319 			return (-1);
320 		/*
321 		 * If not a regular file, or is owned by someone other than
322 		 * user or root or if writeable by anyone but the owner, quit.
323 		 */
324 		cp = NULL;
325 		if (lstat(pbuf, &sbuf) < 0)
326 			cp = ".rhosts lstat failed";
327 		else if (!S_ISREG(sbuf.st_mode))
328 			cp = ".rhosts not regular file";
329 		else if (fstat(fileno(hostf), &sbuf) < 0)
330 			cp = ".rhosts fstat failed";
331 		else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
332 			cp = "bad .rhosts owner";
333 		else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
334 			cp = ".rhosts writeable by other than owner";
335 		/* If there were any problems, quit. */
336 		if (cp) {
337 			__rcmd_errstr = cp;
338 			(void)fclose(hostf);
339 			return (-1);
340 		}
341 		goto again;
342 	}
343 	return (-1);
344 }
345 
346 /*
347  * XXX
348  * Don't make static, used by lpd(8).
349  *
350  * Returns 0 if ok, -1 if not ok.
351  */
352 int
353 __ivaliduser(hostf, raddr, luser, ruser)
354 	FILE *hostf;
355 	u_long raddr;
356 	const char *luser, *ruser;
357 {
358 	register char *user, *p;
359 	int ch;
360 	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
361 	const char *auser, *ahost;
362 	int hostok, userok;
363 	char rhost[MAXHOSTNAMELEN];
364 	struct hostent *hp;
365 	char domain[MAXHOSTNAMELEN];
366 
367 	getdomainname(domain, sizeof(domain));
368 
369 	while (fgets(buf, sizeof(buf), hostf)) {
370 		p = buf;
371 		/* Skip lines that are too long. */
372 		if (strchr(p, '\n') == NULL) {
373 			while ((ch = getc(hostf)) != '\n' && ch != EOF);
374 			continue;
375 		}
376 		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
377 			*p = isupper(*p) ? tolower(*p) : *p;
378 			p++;
379 		}
380 		if (*p == ' ' || *p == '\t') {
381 			*p++ = '\0';
382 			while (*p == ' ' || *p == '\t')
383 				p++;
384 			user = p;
385 			while (*p != '\n' && *p != ' ' &&
386 			    *p != '\t' && *p != '\0')
387 				p++;
388 		} else
389 			user = p;
390 		*p = '\0';
391 
392 		if (p == buf)
393 			continue;
394 
395 		auser = *user ? user : luser;
396 		ahost = buf;
397 
398 		if ((hp = gethostbyaddr((char *) &raddr,
399 					sizeof(raddr), AF_INET)) == NULL) {
400 			abort();
401 			return -1;
402 		}
403 		(void) strncpy(rhost, hp->h_name, sizeof(rhost));
404 		rhost[sizeof(rhost) - 1] = '\0';
405 
406 		if (ahost[0] == '+')
407 			switch (ahost[1]) {
408 			case '\0':
409 				hostok = 1;
410 				break;
411 
412 			case '@':
413 				hostok = innetgr(&ahost[2], rhost, NULL,
414 						 domain);
415 				break;
416 
417 			default:
418 				hostok = __icheckhost(raddr, &ahost[1]);
419 				break;
420 			}
421 		else if (ahost[0] == '-')
422 			switch (ahost[1]) {
423 			case '\0':
424 				hostok = -1;
425 				break;
426 
427 			case '@':
428 				hostok = -innetgr(&ahost[2], rhost, NULL,
429 						  domain);
430 				break;
431 
432 			default:
433 				hostok = -__icheckhost(raddr, &ahost[1]);
434 				break;
435 			}
436 		else
437 			hostok = __icheckhost(raddr, ahost);
438 
439 
440 		if (auser[0] == '+')
441 			switch (auser[1]) {
442 			case '\0':
443 				userok = 1;
444 				break;
445 
446 			case '@':
447 				userok = innetgr(&auser[2], NULL, ruser,
448 						 domain);
449 				break;
450 
451 			default:
452 				userok = strcmp(ruser, &auser[1]) == 0;
453 				break;
454 			}
455 		else if (auser[0] == '-')
456 			switch (auser[1]) {
457 			case '\0':
458 				userok = -1;
459 				break;
460 
461 			case '@':
462 				userok = -innetgr(&auser[2], NULL, ruser,
463 						  domain);
464 				break;
465 
466 			default:
467 				userok = -(strcmp(ruser, &auser[1]) == 0);
468 				break;
469 			}
470 		else
471 			userok = strcmp(ruser, auser) == 0;
472 
473 		/* Check if one component did not match */
474 		if (hostok == 0 || userok == 0)
475 			continue;
476 
477 		/* Check if we got a forbidden pair */
478 		if (userok == -1 || hostok == -1)
479 			return -1;
480 
481 		/* Check if we got a valid pair */
482 		if (hostok == 1 && userok == 1)
483 			return 0;
484 	}
485 	return -1;
486 }
487 
488 /*
489  * Returns "true" if match, 0 if no match.
490  */
491 static int
492 __icheckhost(raddr, lhost)
493 	u_long raddr;
494 	const char *lhost;
495 {
496 	register struct hostent *hp;
497 	register u_long laddr;
498 	register char **pp;
499 
500 	/* Try for raw ip address first. */
501 	if (isdigit(*lhost) && (long)(laddr = inet_addr(lhost)) != -1)
502 		return (raddr == laddr);
503 
504 	/* Better be a hostname. */
505 	if ((hp = gethostbyname(lhost)) == NULL)
506 		return (0);
507 
508 	/* Spin through ip addresses. */
509 	for (pp = hp->h_addr_list; *pp; ++pp)
510 		if (!bcmp(&raddr, *pp, sizeof(u_long)))
511 			return (1);
512 
513 	/* No match. */
514 	return (0);
515 }
516