xref: /minix/lib/libc/net/rcmd.c (revision 0a6a1f1d)
1 /*	$NetBSD: rcmd.c,v 1.71 2014/11/26 23:44:21 enami 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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 #if 0
35 static char sccsid[] = "@(#)rcmd.c	8.3 (Berkeley) 3/26/94";
36 #else
37 __RCSID("$NetBSD: rcmd.c,v 1.71 2014/11/26 23:44:21 enami Exp $");
38 #endif
39 #endif /* LIBC_SCCS and not lint */
40 
41 #ifdef _LIBC
42 #include "namespace.h"
43 #endif
44 #include <sys/param.h>
45 #include <sys/socket.h>
46 #include <sys/stat.h>
47 #include <sys/poll.h>
48 #include <sys/wait.h>
49 
50 #include <netinet/in.h>
51 #if !defined(__minix)
52 #include <rpc/rpc.h>
53 #endif /* !defined(__minix) */
54 #include <arpa/inet.h>
55 #include <netgroup.h>
56 
57 #include <assert.h>
58 #include <ctype.h>
59 #include <err.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <grp.h>
63 #include <netdb.h>
64 #include <paths.h>
65 #include <pwd.h>
66 #include <signal.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <syslog.h>
71 #include <unistd.h>
72 
73 #include "pathnames.h"
74 
75 int	orcmd(char **, u_int, const char *, const char *, const char *, int *);
76 int	orcmd_af(char **, u_int, const char *, const char *, const char *,
77     int *, int);
78 int	__ivaliduser(FILE *, u_int32_t, const char *, const char *);
79 int	__ivaliduser_sa(FILE *, const struct sockaddr *, socklen_t,
80     const char *, const char *);
81 static	int rshrcmd(int, char **, u_int32_t, const char *,
82     const char *, const char *, int *, const char *);
83 static	int resrcmd(struct addrinfo *, char **, u_int32_t, const char *,
84     const char *, const char *, int *);
85 static	int __icheckhost(const struct sockaddr *, socklen_t,
86     const char *);
87 static	char *__gethostloop(const struct sockaddr *, socklen_t);
88 
89 int
rcmd(char ** ahost,int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p)90 rcmd(char **ahost, int rport, const char *locuser, const char *remuser,
91     const char *cmd, int *fd2p)
92 {
93 
94 	return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
95 }
96 
97 int
rcmd_af(char ** ahost,int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p,int af)98 rcmd_af(char **ahost, int rport, const char *locuser, const char *remuser,
99     const char *cmd, int *fd2p, int af)
100 {
101 	static char hbuf[MAXHOSTNAMELEN];
102 	char pbuf[NI_MAXSERV];
103 	struct addrinfo hints, *res;
104 	int error;
105 	struct servent *sp;
106 
107 	_DIAGASSERT(ahost != NULL);
108 	_DIAGASSERT(locuser != NULL);
109 	_DIAGASSERT(remuser != NULL);
110 	_DIAGASSERT(cmd != NULL);
111 	/* fd2p may be NULL */
112 
113 	snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
114 	memset(&hints, 0, sizeof(hints));
115 	hints.ai_family = af;
116 	hints.ai_socktype = SOCK_STREAM;
117 	hints.ai_flags = AI_CANONNAME;
118 	error = getaddrinfo(*ahost, pbuf, &hints, &res);
119 	if (error) {
120 		warnx("%s: %s", *ahost, gai_strerror(error));	/*XXX*/
121 		return -1;
122 	}
123 	if (res->ai_canonname) {
124 		/*
125 		 * Canonicalise hostname.
126 		 * XXX: Should we really do this?
127 		 */
128 		strlcpy(hbuf, res->ai_canonname, sizeof(hbuf));
129 		*ahost = hbuf;
130 	}
131 
132 	/*
133 	 * Check if rport is the same as the shell port, and that the fd2p.  If
134 	 * it is not, the program isn't expecting 'rsh' and so we can't use the
135 	 * RCMD_CMD environment.
136 	 */
137 	sp = getservbyname("shell", "tcp");
138 	if (sp != NULL && sp->s_port == rport)
139 		error = rshrcmd(af, ahost, (u_int32_t)rport,
140 		    locuser, remuser, cmd, fd2p, getenv("RCMD_CMD"));
141 	else
142 		error = resrcmd(res, ahost, (u_int32_t)rport,
143 		    locuser, remuser, cmd, fd2p);
144 	freeaddrinfo(res);
145 	return error;
146 }
147 
148 /* this is simply a wrapper around hprcmd() that handles ahost first */
149 int
orcmd(char ** ahost,u_int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p)150 orcmd(char **ahost, u_int rport, const char *locuser, const char *remuser,
151     const char *cmd, int *fd2p)
152 {
153 	return orcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
154 }
155 
156 int
orcmd_af(char ** ahost,u_int rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p,int af)157 orcmd_af(char **ahost, u_int rport, const char *locuser, const char *remuser,
158     const char *cmd, int *fd2p, int af)
159 {
160 	static char hbuf[MAXHOSTNAMELEN];
161 	char pbuf[NI_MAXSERV];
162 	struct addrinfo hints, *res;
163 	int error;
164 
165 	_DIAGASSERT(ahost != NULL);
166 	_DIAGASSERT(locuser != NULL);
167 	_DIAGASSERT(remuser != NULL);
168 	_DIAGASSERT(cmd != NULL);
169 	/* fd2p may be NULL */
170 
171 	snprintf(pbuf, sizeof(pbuf), "%u", ntohs(rport));
172 	memset(&hints, 0, sizeof(hints));
173 	hints.ai_family = af;
174 	hints.ai_socktype = SOCK_STREAM;
175 	hints.ai_flags = AI_CANONNAME;
176 	error = getaddrinfo(*ahost, pbuf, &hints, &res);
177 	if (error) {
178 		warnx("%s: %s", *ahost, gai_strerror(error));	/*XXX*/
179 		return -1;
180 	}
181 	if (res->ai_canonname) {
182 		strlcpy(hbuf, res->ai_canonname, sizeof(hbuf));
183 		*ahost = hbuf;
184 	}
185 
186 	error = resrcmd(res, ahost, rport, locuser, remuser, cmd, fd2p);
187 	freeaddrinfo(res);
188 	return error;
189 }
190 
191 /*ARGSUSED*/
192 static int
resrcmd(struct addrinfo * res,char ** ahost,u_int32_t rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p)193 resrcmd(struct addrinfo *res, char **ahost, u_int32_t rport,
194     const char *locuser, const char *remuser, const char *cmd, int *fd2p)
195 {
196 	struct addrinfo *r;
197 	struct sockaddr_storage from;
198 	struct pollfd reads[2];
199 	sigset_t nmask, omask;
200 	pid_t pid;
201 	int s, lport, timo;
202 	int pollr;
203 	char c;
204 	int refused;
205 
206 	_DIAGASSERT(res != NULL);
207 	_DIAGASSERT(ahost != NULL);
208 	_DIAGASSERT(locuser != NULL);
209 	_DIAGASSERT(remuser != NULL);
210 	_DIAGASSERT(cmd != NULL);
211 	/* fd2p may be NULL */
212 
213 	r = res;
214 	refused = 0;
215 	pid = getpid();
216 	sigemptyset(&nmask);
217 	sigaddset(&nmask, SIGURG);
218 	if (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)
219 		return -1;
220 	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
221 		s = rresvport_af(&lport, r->ai_family);
222 		if (s < 0) {
223 			if (errno == EAGAIN)
224 				warnx("rcmd: socket: All ports in use");
225 			else
226 				warn("rcmd: socket");
227 			if (r->ai_next) {
228 				r = r->ai_next;
229 				continue;
230 			} else {
231 				(void)sigprocmask(SIG_SETMASK, &omask, NULL);
232 				return -1;
233 			}
234 		}
235 		fcntl(s, F_SETOWN, pid);
236 		if (connect(s, r->ai_addr, r->ai_addrlen) >= 0)
237 			break;
238 		(void)close(s);
239 		if (errno == EADDRINUSE) {
240 			lport--;
241 			continue;
242 		} else if (errno == ECONNREFUSED)
243 			refused++;
244 		if (r->ai_next) {
245 			int oerrno = errno;
246 			char hbuf[NI_MAXHOST];
247 			const int niflags = NI_NUMERICHOST;
248 
249 			hbuf[0] = '\0';
250 			if (getnameinfo(r->ai_addr, r->ai_addrlen,
251 			    hbuf, (socklen_t)sizeof(hbuf), NULL, 0, niflags) !=
252 			    0)
253 				strlcpy(hbuf, "(invalid)", sizeof(hbuf));
254 			errno = oerrno;
255 			warn("rcmd: connect to address %s", hbuf);
256 			r = r->ai_next;
257 			hbuf[0] = '\0';
258 			if (getnameinfo(r->ai_addr, r->ai_addrlen,
259 			    hbuf, (socklen_t)sizeof(hbuf), NULL, 0, niflags) !=
260 			    0)
261 				strlcpy(hbuf, "(invalid)", sizeof(hbuf));
262 			(void)fprintf(stderr, "Trying %s...\n", hbuf);
263 			continue;
264 		}
265 		if (refused && timo <= 16) {
266 			(void)sleep((unsigned int)timo);
267 			timo *= 2;
268 			r = res;
269 			refused = 0;
270 			continue;
271 		}
272 		(void)fprintf(stderr, "%s: %s\n", res->ai_canonname,
273 		    strerror(errno));
274 		(void)sigprocmask(SIG_SETMASK, &omask, NULL);
275 		return -1;
276 	}
277 	lport--;
278 	if (fd2p == 0) {
279 		write(s, "", 1);
280 		lport = 0;
281 	} else {
282 		char num[8];
283 		int s2 = rresvport_af(&lport, r->ai_family), s3;
284 		socklen_t len = sizeof(from);
285 
286 		if (s2 < 0)
287 			goto bad;
288 		listen(s2, 1);
289 		(void)snprintf(num, sizeof(num), "%d", lport);
290 		if (write(s, num, strlen(num) + 1) !=
291 		    (ssize_t) (strlen(num) + 1)) {
292 			warn("rcmd: write (setting up stderr)");
293 			(void)close(s2);
294 			goto bad;
295 		}
296 		reads[0].fd = s;
297 		reads[0].events = POLLIN;
298 		reads[1].fd = s2;
299 		reads[1].events = POLLIN;
300 		errno = 0;
301 		pollr = poll(reads, 2, INFTIM);
302 		if (pollr < 1 || (reads[1].revents & POLLIN) == 0) {
303 			if (errno != 0)
304 				warn("poll: setting up stderr");
305 			else
306 				warnx(
307 				    "poll: protocol failure in circuit setup");
308 			(void)close(s2);
309 			goto bad;
310 		}
311 		s3 = accept(s2, (struct sockaddr *)(void *)&from, &len);
312 		(void)close(s2);
313 		if (s3 < 0) {
314 			warn("rcmd: accept");
315 			lport = 0;
316 			goto bad;
317 		}
318 		*fd2p = s3;
319 		switch (((struct sockaddr *)(void *)&from)->sa_family) {
320 		case AF_INET:
321 #ifdef INET6
322 		case AF_INET6:
323 #endif
324 			if (getnameinfo((struct sockaddr *)(void *)&from, len,
325 			    NULL, 0, num, (socklen_t)sizeof(num),
326 			    NI_NUMERICSERV) != 0 ||
327 			    (atoi(num) >= IPPORT_RESERVED ||
328 			     atoi(num) < IPPORT_RESERVED / 2)) {
329 				warnx(
330 				"rcmd: protocol failure in circuit setup.");
331 				goto bad2;
332 			}
333 			break;
334 		default:
335 			break;
336 		}
337 	}
338 
339 	(void)write(s, locuser, strlen(locuser)+1);
340 	(void)write(s, remuser, strlen(remuser)+1);
341 	(void)write(s, cmd, strlen(cmd)+1);
342 	if (read(s, &c, 1) != 1) {
343 		warn("%s", *ahost);
344 		goto bad2;
345 	}
346 	if (c != 0) {
347 		while (read(s, &c, 1) == 1) {
348 			(void)write(STDERR_FILENO, &c, 1);
349 			if (c == '\n')
350 				break;
351 		}
352 		goto bad2;
353 	}
354 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
355 	return s;
356 bad2:
357 	if (lport)
358 		(void)close(*fd2p);
359 bad:
360 	(void)close(s);
361 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
362 	return -1;
363 }
364 
365 /*
366  * based on code written by Chris Siebenmann <cks@utcc.utoronto.ca>
367  */
368 /* ARGSUSED */
369 static int
rshrcmd(int af,char ** ahost,u_int32_t rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p,const char * rshcmd)370 rshrcmd(int af, char **ahost, u_int32_t rport, const char *locuser,
371     const char *remuser, const char *cmd, int *fd2p, const char *rshcmd)
372 {
373 	pid_t pid;
374 	int sp[2], ep[2];
375 	char *p;
376 	struct passwd *pw, pwres;
377 	char pwbuf[1024];
378 
379 	_DIAGASSERT(ahost != NULL);
380 	_DIAGASSERT(locuser != NULL);
381 	_DIAGASSERT(remuser != NULL);
382 	_DIAGASSERT(cmd != NULL);
383 	/* fd2p may be NULL */
384 
385 	/* What rsh/shell to use. */
386 	if (rshcmd == NULL)
387 		rshcmd = _PATH_BIN_RCMD;
388 
389 	/* locuser must exist on this host. */
390 	if (getpwnam_r(locuser, &pwres, pwbuf, sizeof(pwbuf), &pw) != 0 ||
391 	    pw == NULL) {
392 		warnx("%s: unknown user: %s", __func__, locuser);
393 		return -1;
394 	}
395 
396 	/* get a socketpair we'll use for stdin and stdout. */
397 	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sp) < 0) {
398 		warn("%s: socketpair", __func__);
399 		return -1;
400 	}
401 	/* we will use this for the fd2 pointer */
402 	if (fd2p) {
403 		if (socketpair(AF_LOCAL, SOCK_STREAM, 0, ep) < 0) {
404 			warn("%s: socketpair", __func__);
405 			return -1;
406 		}
407 		*fd2p = ep[0];
408 	}
409 
410 	pid = fork();
411 	if (pid < 0) {
412 		warn("%s: fork", __func__);
413 		return -1;
414 	}
415 	if (pid == 0) {
416 		/*
417 		 * child
418 		 * - we use sp[1] to be stdin/stdout, and close sp[0]
419 		 * - with fd2p, we use ep[1] for stderr, and close ep[0]
420 		 */
421 		(void)close(sp[0]);
422 		if (dup2(sp[1], 0) < 0 || dup2(0, 1) < 0) {
423 			warn("%s: dup2", __func__);
424 			_exit(1);
425 		}
426 		(void)close(sp[1]);
427 		if (fd2p) {
428 			if (dup2(ep[1], 2) < 0) {
429 				warn("%s: dup2", __func__);
430 				_exit(1);
431 			}
432 			(void)close(ep[0]);
433 			(void)close(ep[1]);
434 		} else if (dup2(0, 2) < 0) {
435 			warn("%s: dup2", __func__);
436 			_exit(1);
437 		}
438 		/* fork again to lose parent. */
439 		pid = fork();
440 		if (pid < 0) {
441 			warn("%s: second fork", __func__);
442 			_exit(1);
443 		}
444 		if (pid > 0)
445 			_exit(0);
446 
447 		/* Orphan.  Become local user for rshprog. */
448 		if (setuid(pw->pw_uid)) {
449 			warn("%s: setuid(%lu)", __func__, (u_long)pw->pw_uid);
450 			_exit(1);
451 		}
452 
453 		/*
454 		 * If we are rcmd'ing to "localhost" as the same user as we
455 		 * are, then avoid running remote shell for efficiency.
456 		 */
457 		if (strcmp(*ahost, "localhost") == 0 &&
458 		    strcmp(locuser, remuser) == 0) {
459 			if (pw->pw_shell[0] == '\0')
460 				rshcmd = _PATH_BSHELL;
461 			else
462 				rshcmd = pw->pw_shell;
463 			p = strrchr(rshcmd, '/');
464 			execlp(rshcmd, p ? p + 1 : rshcmd, "-c", cmd, NULL);
465 		} else {
466 			const char *program;
467 			program = strrchr(rshcmd, '/');
468 			program = program ? program + 1 : rshcmd;
469 			if (fd2p)
470 				/* ask rcmd to relay signal information */
471 				setenv("RCMD_RELAY_SIGNAL", "YES", 1);
472 			switch (af) {
473 			case AF_INET:
474 				execlp(rshcmd, program, "-4", "-l", remuser,
475 				    *ahost, cmd, NULL);
476 				break;
477 
478 			case AF_INET6:
479 				execlp(rshcmd, program, "-6", "-l", remuser,
480 				    *ahost, cmd, NULL);
481 				break;
482 
483 			default:
484 				/* typically AF_UNSPEC, plus whatever */
485 				execlp(rshcmd, program,       "-l", remuser,
486 				    *ahost, cmd, NULL);
487 				break;
488 			}
489 		}
490 		warn("%s: exec %s", __func__, rshcmd);
491 		_exit(1);
492 	}
493 	/* Parent */
494 	(void)close(sp[1]);
495 	if (fd2p)
496 		(void)close(ep[1]);
497 
498 	(void)waitpid(pid, NULL, 0);
499 	return sp[0];
500 }
501 
502 int
rresvport(int * alport)503 rresvport(int *alport)
504 {
505 
506 	_DIAGASSERT(alport != NULL);
507 
508 	return rresvport_af(alport, AF_INET);
509 }
510 
511 int
rresvport_af(int * alport,int family)512 rresvport_af(int *alport, int family)
513 {
514 	return rresvport_af_addr(alport, family, NULL);
515 }
516 
517 int
rresvport_af_addr(int * alport,int family,void * addr)518 rresvport_af_addr(int *alport, int family, void *addr)
519 {
520 	struct sockaddr_storage ss;
521 	struct sockaddr *sa;
522 	socklen_t salen;
523 	int s;
524 	u_int16_t *portp;
525 
526 	_DIAGASSERT(alport != NULL);
527 
528 	memset(&ss, 0, sizeof(ss));
529 	sa = (struct sockaddr *)(void *)&ss;
530 	switch (family) {
531 	case AF_INET:
532 #ifdef BSD4_4
533 		sa->sa_len =
534 #endif
535 		salen = sizeof(struct sockaddr_in);
536 		if (addr)
537 			((struct sockaddr_in *)(void *)sa)->sin_addr =
538 			    ((struct sockaddr_in *)addr)->sin_addr;
539 		portp = &((struct sockaddr_in *)(void *)sa)->sin_port;
540 		break;
541 #ifdef INET6
542 	case AF_INET6:
543 #ifdef BSD4_4
544 		sa->sa_len =
545 #endif
546 		salen = sizeof(struct sockaddr_in6);
547 		if (addr)
548 			((struct sockaddr_in6 *)(void *)sa)->sin6_addr =
549 			    ((struct sockaddr_in6 *)addr)->sin6_addr;
550 		portp = &((struct sockaddr_in6 *)(void *)sa)->sin6_port;
551 		break;
552 #endif
553 	default:
554 		errno = EAFNOSUPPORT;
555 		return -1;
556 	}
557 	sa->sa_family = family;
558 	s = socket(family, SOCK_STREAM, 0);
559 	if (s < 0)
560 		return -1;
561 #if defined(BSD4_4) && !defined(__minix)
562 	switch (family) {
563 	case AF_INET:
564 	case AF_INET6:
565 		*portp = 0;
566 		if (bindresvport(s, (struct sockaddr_in *)(void *)sa) < 0) {
567 			int sverr = errno;
568 
569 			(void)close(s);
570 			errno = sverr;
571 			return -1;
572 		}
573 		*alport = (int)ntohs(*portp);
574 		return s;
575 	default:
576 		/* is it necessary to try keep code for other AFs? */
577 		break;
578 	}
579 #endif
580 	for (;;) {
581 		*portp = htons((u_short)*alport);
582 		if (bind(s, sa, salen) >= 0)
583 			return s;
584 		if (errno != EADDRINUSE) {
585 			(void)close(s);
586 			return -1;
587 		}
588 		(*alport)--;
589 		if (*alport == IPPORT_RESERVED/2) {
590 			(void)close(s);
591 			errno = EAGAIN;		/* close */
592 			return -1;
593 		}
594 	}
595 }
596 
597 int	__check_rhosts_file = 1;
598 const char *__rcmd_errstr;
599 
600 int
ruserok(const char * rhost,int superuser,const char * ruser,const char * luser)601 ruserok(const char *rhost, int superuser, const char *ruser, const char *luser)
602 {
603 	struct addrinfo hints, *res, *r;
604 	int error;
605 
606 	_DIAGASSERT(rhost != NULL);
607 	_DIAGASSERT(ruser != NULL);
608 	_DIAGASSERT(luser != NULL);
609 
610 	memset(&hints, 0, sizeof(hints));
611 	hints.ai_family = PF_UNSPEC;
612 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
613 	error = getaddrinfo(rhost, "0", &hints, &res);
614 	if (error)
615 		return -1;
616 
617 	for (r = res; r; r = r->ai_next) {
618 		if (iruserok_sa(r->ai_addr, (int)r->ai_addrlen, superuser,
619 		    ruser, luser) == 0) {
620 			freeaddrinfo(res);
621 			return 0;
622 		}
623 	}
624 	freeaddrinfo(res);
625 	return -1;
626 }
627 
628 /*
629  * New .rhosts strategy: We are passed an ip address. We spin through
630  * hosts.equiv and .rhosts looking for a match. When the .rhosts only
631  * has ip addresses, we don't have to trust a nameserver.  When it
632  * contains hostnames, we spin through the list of addresses the nameserver
633  * gives us and look for a match.
634  *
635  * Returns 0 if ok, -1 if not ok.
636  */
637 int
iruserok(u_int32_t raddr,int superuser,const char * ruser,const char * luser)638 iruserok(u_int32_t raddr, int superuser, const char *ruser, const char *luser)
639 {
640 	struct sockaddr_in irsin;
641 
642 	memset(&irsin, 0, sizeof(irsin));
643 	irsin.sin_family = AF_INET;
644 #ifdef BSD4_4
645 	irsin.sin_len = sizeof(irsin);
646 #endif
647 	memcpy(&irsin.sin_addr, &raddr, sizeof(irsin.sin_addr));
648 	return iruserok_sa(&irsin, (socklen_t)sizeof(irsin), superuser, ruser,
649 	    luser);
650 }
651 
652 /*
653  * 2nd and 3rd arguments are typed like this, to avoid dependency between
654  * unistd.h and sys/socket.h.  There's no better way.
655  */
656 int
iruserok_sa(const void * raddr,int rlen,int superuser,const char * ruser,const char * luser)657 iruserok_sa(const void *raddr, int rlen, int superuser, const char *ruser,
658     const char *luser)
659 {
660 	const struct sockaddr *sa;
661 	struct stat sbuf;
662 	struct passwd *pwd, pwres;
663 	FILE *hostf;
664 	uid_t uid;
665 	gid_t gid;
666 	int isvaliduser;
667 	char pbuf[MAXPATHLEN];
668 	char pwbuf[1024];
669 
670 	_DIAGASSERT(raddr != NULL);
671 	_DIAGASSERT(ruser != NULL);
672 	_DIAGASSERT(luser != NULL);
673 
674 	sa = raddr;
675 
676 	__rcmd_errstr = NULL;
677 
678 	hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re");
679 
680 	if (hostf) {
681 		if (__ivaliduser_sa(hostf, sa, (socklen_t)rlen, luser,
682 		    ruser) == 0) {
683 			(void)fclose(hostf);
684 			return 0;
685 		}
686 		(void)fclose(hostf);
687 	}
688 
689 	isvaliduser = -1;
690 	if (__check_rhosts_file || superuser) {
691 
692 		if (getpwnam_r(luser, &pwres, pwbuf, sizeof(pwbuf), &pwd) != 0
693 		    || pwd == NULL)
694 			return -1;
695 		(void)strlcpy(pbuf, pwd->pw_dir, sizeof(pbuf));
696 		(void)strlcat(pbuf, "/.rhosts", sizeof(pbuf));
697 
698 		/*
699 		 * Change effective uid while opening and reading .rhosts.
700 		 * If root and reading an NFS mounted file system, can't
701 		 * read files that are protected read/write owner only.
702 		 */
703 		uid = geteuid();
704 		gid = getegid();
705 		(void)setegid(pwd->pw_gid);
706 		(void)initgroups(pwd->pw_name, pwd->pw_gid);
707 		(void)seteuid(pwd->pw_uid);
708 		hostf = fopen(pbuf, "re");
709 
710 		if (hostf != NULL) {
711 			/*
712 			 * If not a regular file, or is owned by someone other
713 			 * than user or root or if writable by anyone but the
714 			 * owner, quit.
715 			 */
716 			if (lstat(pbuf, &sbuf) < 0)
717 				__rcmd_errstr = ".rhosts lstat failed";
718 			else if (!S_ISREG(sbuf.st_mode))
719 				__rcmd_errstr = ".rhosts not regular file";
720 			else if (fstat(fileno(hostf), &sbuf) < 0)
721 				__rcmd_errstr = ".rhosts fstat failed";
722 			else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
723 				__rcmd_errstr = "bad .rhosts owner";
724 			else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
725 				__rcmd_errstr =
726 					".rhosts writable by other than owner";
727 			else
728 				isvaliduser =
729 				    __ivaliduser_sa(hostf, sa, (socklen_t)rlen,
730 						    luser, ruser);
731 
732 			(void)fclose(hostf);
733 		}
734 		(void)seteuid(uid);
735 		(void)setegid(gid);
736 
737 	}
738 	return isvaliduser;
739 }
740 
741 /*
742  * XXX
743  * Don't make static, used by lpd(8).  We will be able to change the function
744  * into static function, when we bump libc major #.
745  *
746  * Returns 0 if ok, -1 if not ok.
747  */
748 #ifdef notdef	/*_LIBC*/
749 static
750 #endif
751 int
__ivaliduser(FILE * hostf,u_int32_t raddr,const char * luser,const char * ruser)752 __ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser,
753     const char *ruser)
754 {
755 	struct sockaddr_in ivusin;
756 
757 	memset(&ivusin, 0, sizeof(ivusin));
758 	ivusin.sin_family = AF_INET;
759 #ifdef BSD4_4
760 	ivusin.sin_len = sizeof(ivusin);
761 #endif
762 	memcpy(&ivusin.sin_addr, &raddr, sizeof(ivusin.sin_addr));
763 	return __ivaliduser_sa(hostf, (struct sockaddr *)(void *)&ivusin,
764 	    (socklen_t)sizeof(ivusin), luser, ruser);
765 }
766 
767 #ifdef notdef	/*_LIBC*/
768 static
769 #endif
770 int
__ivaliduser_sa(FILE * hostf,const struct sockaddr * raddr,socklen_t salen,const char * luser,const char * ruser)771 __ivaliduser_sa(FILE *hostf, const struct sockaddr *raddr, socklen_t salen,
772     const char *luser, const char *ruser)
773 {
774 	char *user, *p;
775 	int ch;
776 	char buf[MAXHOSTNAMELEN + 128];		/* host + login */
777 	const char *auser, *ahost;
778 	int hostok, userok;
779 	char *rhost = NULL;
780 	int firsttime = 1;
781 	char domain[MAXHOSTNAMELEN];
782 
783 	getdomainname(domain, sizeof(domain));
784 
785 	_DIAGASSERT(hostf != NULL);
786 	_DIAGASSERT(luser != NULL);
787 	_DIAGASSERT(ruser != NULL);
788 
789 	while (fgets(buf, (int)sizeof(buf), hostf)) {
790 		p = buf;
791 		/* Skip lines that are too long. */
792 		if (strchr(p, '\n') == NULL) {
793 			while ((ch = getc(hostf)) != '\n' && ch != EOF)
794 				;
795 			continue;
796 		}
797 		while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
798 			*p = isupper((unsigned char)*p) ?
799 			    tolower((unsigned char)*p) : *p;
800 			p++;
801 		}
802 		if (*p == ' ' || *p == '\t') {
803 			*p++ = '\0';
804 			while (*p == ' ' || *p == '\t')
805 				p++;
806 			user = p;
807 			while (*p != '\n' && *p != ' ' &&
808 			    *p != '\t' && *p != '\0')
809 				p++;
810 		} else
811 			user = p;
812 		*p = '\0';
813 
814 		if (p == buf)
815 			continue;
816 
817 		auser = *user ? user : luser;
818 		ahost = buf;
819 
820 		if (ahost[0] == '+')
821 			switch (ahost[1]) {
822 			case '\0':
823 				hostok = 1;
824 				break;
825 
826 			case '@':
827 				if (firsttime) {
828 					rhost = __gethostloop(raddr, salen);
829 					firsttime = 0;
830 				}
831 				if (rhost)
832 					hostok = innetgr(&ahost[2], rhost,
833 					    NULL, domain);
834 				else
835 					hostok = 0;
836 				break;
837 
838 			default:
839 				hostok = __icheckhost(raddr, salen, &ahost[1]);
840 				break;
841 			}
842 		else if (ahost[0] == '-')
843 			switch (ahost[1]) {
844 			case '\0':
845 				hostok = -1;
846 				break;
847 
848 			case '@':
849 				if (firsttime) {
850 					rhost = __gethostloop(raddr, salen);
851 					firsttime = 0;
852 				}
853 				if (rhost)
854 					hostok = -innetgr(&ahost[2], rhost,
855 					    NULL, domain);
856 				else
857 					hostok = 0;
858 				break;
859 
860 			default:
861 				hostok =
862 				    -__icheckhost(raddr, salen, &ahost[1]);
863 				break;
864 			}
865 		else
866 			hostok = __icheckhost(raddr, salen, ahost);
867 
868 
869 		if (auser[0] == '+')
870 			switch (auser[1]) {
871 			case '\0':
872 				userok = 1;
873 				break;
874 
875 			case '@':
876 				userok = innetgr(&auser[2], NULL, ruser,
877 				    domain);
878 				break;
879 
880 			default:
881 				userok = strcmp(ruser, &auser[1]) == 0;
882 				break;
883 			}
884 		else if (auser[0] == '-')
885 			switch (auser[1]) {
886 			case '\0':
887 				userok = -1;
888 				break;
889 
890 			case '@':
891 				userok = -innetgr(&auser[2], NULL, ruser,
892 				    domain);
893 				break;
894 
895 			default:
896 				userok =
897 				    -(strcmp(ruser, &auser[1]) == 0 ? 1 : 0);
898 				break;
899 			}
900 		else
901 			userok = strcmp(ruser, auser) == 0;
902 
903 		/* Check if one component did not match */
904 		if (hostok == 0 || userok == 0)
905 			continue;
906 
907 		/* Check if we got a forbidden pair */
908 		if (userok == -1 || hostok == -1)
909 			return -1;
910 
911 		/* Check if we got a valid pair */
912 		if (hostok == 1 && userok == 1)
913 			return 0;
914 	}
915 	return -1;
916 }
917 
918 /*
919  * Returns "true" if match, 0 if no match.
920  */
921 static int
__icheckhost(const struct sockaddr * raddr,socklen_t salen,const char * lhost)922 __icheckhost(const struct sockaddr *raddr, socklen_t salen, const char *lhost)
923 {
924 	struct addrinfo hints, *res, *r;
925 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
926 	int error;
927 	const int niflags = NI_NUMERICHOST;
928 
929 	_DIAGASSERT(raddr != NULL);
930 	_DIAGASSERT(lhost != NULL);
931 
932 	h1[0] = '\0';
933 	if (getnameinfo(raddr, salen, h1, (socklen_t)sizeof(h1), NULL, 0,
934 	    niflags) != 0)
935 		return 0;
936 
937 	/* Resolve laddr into sockaddr */
938 	memset(&hints, 0, sizeof(hints));
939 	hints.ai_family = raddr->sa_family;
940 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
941 	res = NULL;
942 	error = getaddrinfo(lhost, "0", &hints, &res);
943 	if (error)
944 		return 0;
945 
946 	/*
947 	 * Try string comparisons between raddr and laddr.
948 	 */
949 	for (r = res; r; r = r->ai_next) {
950 		h2[0] = '\0';
951 		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2,
952 		    (socklen_t)sizeof(h2), NULL, 0, niflags) != 0)
953 			continue;
954 		if (strcmp(h1, h2) == 0) {
955 			freeaddrinfo(res);
956 			return 1;
957 		}
958 	}
959 
960 	/* No match. */
961 	freeaddrinfo(res);
962 	return 0;
963 }
964 
965 /*
966  * Return the hostname associated with the supplied address.
967  * Do a reverse lookup as well for security. If a loop cannot
968  * be found, pack the numeric IP address into the string.
969  */
970 static char *
__gethostloop(const struct sockaddr * raddr,socklen_t salen)971 __gethostloop(const struct sockaddr *raddr, socklen_t salen)
972 {
973 	static char remotehost[NI_MAXHOST];
974 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
975 	struct addrinfo hints, *res, *r;
976 	int error;
977 	const int niflags = NI_NUMERICHOST;
978 
979 	_DIAGASSERT(raddr != NULL);
980 
981 	h1[0] = remotehost[0] = '\0';
982 	if (getnameinfo(raddr, salen, remotehost, (socklen_t)sizeof(remotehost),
983 	    NULL, 0, NI_NAMEREQD) != 0)
984 		return NULL;
985 	if (getnameinfo(raddr, salen, h1, (socklen_t)sizeof(h1), NULL, 0,
986 	    niflags) != 0)
987 		return NULL;
988 
989 	/*
990 	 * Look up the name and check that the supplied
991 	 * address is in the list
992 	 */
993 	memset(&hints, 0, sizeof(hints));
994 	hints.ai_family = raddr->sa_family;
995 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
996 	hints.ai_flags = AI_CANONNAME;
997 	res = NULL;
998 	error = getaddrinfo(remotehost, "0", &hints, &res);
999 	if (error)
1000 		return NULL;
1001 
1002 	for (r = res; r; r = r->ai_next) {
1003 		h2[0] = '\0';
1004 		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2,
1005 		    (socklen_t)sizeof(h2), NULL, 0, niflags) != 0)
1006 			continue;
1007 		if (strcmp(h1, h2) == 0) {
1008 			freeaddrinfo(res);
1009 			return remotehost;
1010 		}
1011 	}
1012 
1013 	/*
1014 	 * either the DNS adminstrator has made a configuration
1015 	 * mistake, or someone has attempted to spoof us
1016 	 */
1017 	syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s",
1018 	    h1, res->ai_canonname ? res->ai_canonname : remotehost);
1019 	freeaddrinfo(res);
1020 	return NULL;
1021 }
1022