1 /*
2  * Copyright (C) 1998 WIDE Project.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the project nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 /*
30  * Copyright (c) 1983, 1993, 1994
31  *	The Regents of the University of California.  All rights reserved.
32  *
33  * Redistribution and use in source and binary forms, with or without
34  * modification, are permitted provided that the following conditions
35  * are met:
36  * 1. Redistributions of source code must retain the above copyright
37  *    notice, this list of conditions and the following disclaimer.
38  * 2. Redistributions in binary form must reproduce the above copyright
39  *    notice, this list of conditions and the following disclaimer in the
40  *    documentation and/or other materials provided with the distribution.
41  * 4. Neither the name of the University nor the names of its contributors
42  *    may be used to endorse or promote products derived from this software
43  *    without specific prior written permission.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55  * SUCH DAMAGE.
56  */
57 
58 #include <sys/param.h>
59 #include <sys/poll.h>
60 #include <sys/socket.h>
61 #include <sys/stat.h>
62 
63 #include <netinet/in.h>
64 #include <arpa/inet.h>
65 
66 #include <alloca.h>
67 #include <signal.h>
68 #include <fcntl.h>
69 #include <netdb.h>
70 #include <unistd.h>
71 #include <pwd.h>
72 #include <errno.h>
73 #include <stdio.h>
74 #include <stdio_ext.h>
75 #include <ctype.h>
76 #include <string.h>
77 #include <libintl.h>
78 #include <stdlib.h>
79 #include <wchar.h>
80 #include <sys/uio.h>
81 #include <sigsetops.h>
82 
83 
84 int __ivaliduser (FILE *, uint32_t, const char *, const char *);
85 static int __validuser2_sa (FILE *, struct sockaddr *, size_t,
86 			    const char *, const char *, const char *);
87 static int ruserok2_sa (struct sockaddr *ra, size_t ralen,
88 			int superuser, const char *ruser,
89 			const char *luser, const char *rhost);
90 static int ruserok_sa (struct sockaddr *ra, size_t ralen,
91 			int superuser, const char *ruser,
92 			const char *luser);
93 int iruserok_af (const void *raddr, int superuser, const char *ruser,
94 		 const char *luser, sa_family_t af);
95 int iruserok (uint32_t raddr, int superuser, const char *ruser,
96 	      const char *luser);
97 
98 libc_hidden_proto (iruserok_af)
99 
100 libc_freeres_ptr(static char *ahostbuf);
101 
102 int
rcmd_af(char ** ahost,u_short rport,const char * locuser,const char * remuser,const char * cmd,int * fd2p,sa_family_t af)103 rcmd_af (char **ahost, u_short rport, const char *locuser, const char *remuser,
104 	 const char *cmd, int *fd2p, sa_family_t af)
105 {
106 	char paddr[INET6_ADDRSTRLEN];
107 	struct addrinfo hints, *res, *ai;
108 	union
109 	{
110 		struct sockaddr sa;
111 		struct sockaddr_storage ss;
112 		struct sockaddr_in sin;
113 		struct sockaddr_in6 sin6;
114 	} from;
115 	struct pollfd pfd[2];
116 	sigset_t mask, omask;
117 
118 	pid_t pid;
119 	int s, lport, timo, error;
120 	char c;
121 	int refused;
122 	char num[8];
123 	ssize_t n;
124 
125 	if (af != AF_INET && af != AF_INET6 && af != AF_UNSPEC)
126 	  {
127 	    __set_errno (EAFNOSUPPORT);
128 	    return -1;
129 	  }
130 
131 	pid = __getpid();
132 
133 	memset(&hints, '\0', sizeof(hints));
134 	hints.ai_flags = AI_CANONNAME;
135 	hints.ai_family = af;
136 	hints.ai_socktype = SOCK_STREAM;
137 	(void)__snprintf(num, sizeof(num), "%d", ntohs(rport));
138 	error = getaddrinfo(*ahost, num, &hints, &res);
139 	if (error) {
140 		if (error == EAI_NONAME && *ahost != NULL)
141 			__fxprintf(NULL, "%s: Unknown host\n", *ahost);
142 		else
143 			__fxprintf(NULL, "rcmd: getaddrinfo: %s\n",
144 				   gai_strerror(error));
145 
146 		return -1;
147 	}
148 
149 	pfd[0].events = POLLIN;
150 	pfd[1].events = POLLIN;
151 
152 	if (res->ai_canonname){
153 		free (ahostbuf);
154 		ahostbuf = __strdup (res->ai_canonname);
155 		if (ahostbuf == NULL) {
156 			freeaddrinfo (res);
157 			__fxprintf(NULL, "%s",
158 				   _("rcmd: Cannot allocate memory\n"));
159 			return -1;
160 		}
161 		*ahost = ahostbuf;
162 	} else
163 		*ahost = NULL;
164 	ai = res;
165 	refused = 0;
166 	__sigemptyset(&mask);
167 	__sigaddset(&mask, SIGURG);
168 	__sigprocmask (SIG_BLOCK, &mask, &omask);
169 	for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
170 		char errbuf[200];
171 
172 		s = rresvport_af(&lport, ai->ai_family);
173 		if (s < 0) {
174 			if (errno == EAGAIN)
175 				__fxprintf(NULL, "%s", _("\
176 rcmd: socket: All ports in use\n"));
177 			else
178 				__fxprintf(NULL, "rcmd: socket: %m\n");
179 
180 			__sigprocmask (SIG_SETMASK, &omask, 0);
181 			freeaddrinfo(res);
182 			return -1;
183 		}
184 		__fcntl(s, F_SETOWN, pid);
185 		if (__connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
186 			break;
187 		(void)__close(s);
188 		if (errno == EADDRINUSE) {
189 			lport--;
190 			continue;
191 		}
192 		if (errno == ECONNREFUSED)
193 			refused = 1;
194 		if (ai->ai_next != NULL) {
195 			int oerrno = errno;
196 			char *buf = NULL;
197 
198 			getnameinfo(ai->ai_addr, ai->ai_addrlen,
199 				    paddr, sizeof(paddr),
200 				    NULL, 0,
201 				    NI_NUMERICHOST);
202 
203 			if (__asprintf (&buf, _("connect to address %s: "),
204 					paddr) >= 0)
205 			  {
206 			    __fxprintf(NULL, "%s", buf);
207 			    free (buf);
208 			  }
209 			__set_errno (oerrno);
210 			perror(0);
211 			ai = ai->ai_next;
212 			getnameinfo(ai->ai_addr, ai->ai_addrlen,
213 				    paddr, sizeof(paddr),
214 				    NULL, 0,
215 				    NI_NUMERICHOST);
216 			if (__asprintf (&buf, _("Trying %s...\n"), paddr) >= 0)
217 			  {
218 			    __fxprintf (NULL, "%s", buf);
219 			    free (buf);
220 			  }
221 			continue;
222 		}
223 		if (refused && timo <= 16) {
224 			(void)__sleep(timo);
225 			timo *= 2;
226 			ai = res;
227 			refused = 0;
228 			continue;
229 		}
230 		freeaddrinfo(res);
231 		(void)__fxprintf(NULL, "%s: %s\n", *ahost,
232 				 __strerror_r(errno, errbuf, sizeof (errbuf)));
233 		__sigprocmask (SIG_SETMASK, &omask, 0);
234 		return -1;
235 	}
236 	lport--;
237 	if (fd2p == 0) {
238 		__write(s, "", 1);
239 		lport = 0;
240 	} else {
241 		char num[8];
242 		int s2 = rresvport_af(&lport, ai->ai_family), s3;
243 		socklen_t len = ai->ai_addrlen;
244 
245 		if (s2 < 0)
246 			goto bad;
247 		__listen(s2, 1);
248 		(void)__snprintf(num, sizeof(num), "%d", lport);
249 		if (__write(s, num, strlen(num)+1) != (ssize_t)strlen(num)+1) {
250 			char *buf = NULL;
251 
252 			if (__asprintf (&buf, _("\
253 rcmd: write (setting up stderr): %m\n")) >= 0)
254 			  {
255 			    __fxprintf(NULL, "%s", buf);
256 			    free (buf);
257 			  }
258 			(void)__close(s2);
259 			goto bad;
260 		}
261 		pfd[0].fd = s;
262 		pfd[1].fd = s2;
263 		__set_errno (0);
264 		if (__poll (pfd, 2, -1) < 1 || (pfd[1].revents & POLLIN) == 0){
265 			char *buf = NULL;
266 
267 			if ((errno != 0
268 			     && __asprintf(&buf, _("\
269 rcmd: poll (setting up stderr): %m\n")) >= 0)
270 			    || (errno == 0
271 				&& __asprintf(&buf, _("\
272 poll: protocol failure in circuit setup\n")) >= 0))
273 			  {
274 			    __fxprintf (NULL, "%s", buf);
275 			    free  (buf);
276 			  }
277 			(void)__close(s2);
278 			goto bad;
279 		}
280 		s3 = TEMP_FAILURE_RETRY (accept(s2, &from.sa, &len));
281 		switch (from.sa.sa_family) {
282 		case AF_INET:
283 			rport = ntohs(from.sin.sin_port);
284 			break;
285 		case AF_INET6:
286 			rport = ntohs(from.sin6.sin6_port);
287 			break;
288 		default:
289 			rport = 0;
290 			break;
291 		}
292 		(void)__close(s2);
293 		if (s3 < 0) {
294 			(void)__fxprintf(NULL, "rcmd: accept: %m\n");
295 			lport = 0;
296 			goto bad;
297 		}
298 		*fd2p = s3;
299 
300 		if (rport >= IPPORT_RESERVED || rport < IPPORT_RESERVED / 2){
301 			char *buf = NULL;
302 
303 			if (__asprintf(&buf, _("\
304 socket: protocol failure in circuit setup\n")) >= 0)
305 			  {
306 			    __fxprintf (NULL, "%s", buf);
307 			    free (buf);
308 			  }
309 			goto bad2;
310 		}
311 	}
312 	struct iovec iov[3] =
313 	  {
314 	    [0] = { .iov_base = (void *) locuser,
315 		    .iov_len = strlen (locuser) + 1 },
316 	    [1] = { .iov_base = (void *) remuser,
317 		    .iov_len = strlen (remuser) + 1 },
318 	    [2] = { .iov_base = (void *) cmd,
319 		    .iov_len = strlen (cmd) + 1 }
320 	  };
321 	(void) TEMP_FAILURE_RETRY (__writev (s, iov, 3));
322 	n = TEMP_FAILURE_RETRY (__read(s, &c, 1));
323 	if (n != 1) {
324 		char *buf = NULL;
325 
326 		if ((n == 0
327 		     && __asprintf(&buf, _("rcmd: %s: short read"),
328 				   *ahost) >= 0)
329 		    || (n != 0
330 			&& __asprintf(&buf, "rcmd: %s: %m\n", *ahost) >= 0))
331 		  {
332 		    __fxprintf (NULL, "%s", buf);
333 		    free (buf);
334 		  }
335 		goto bad2;
336 	}
337 	if (c != 0) {
338 		while (__read(s, &c, 1) == 1) {
339 			(void)__write(STDERR_FILENO, &c, 1);
340 			if (c == '\n')
341 				break;
342 		}
343 		goto bad2;
344 	}
345 	__sigprocmask (SIG_SETMASK, &omask, 0);
346 	freeaddrinfo(res);
347 	return s;
348 bad2:
349 	if (lport)
350 		(void)__close(*fd2p);
351 bad:
352 	(void)__close(s);
353 	__sigprocmask (SIG_SETMASK, &omask, 0);
354 	freeaddrinfo(res);
355 	return -1;
356 }
libc_hidden_def(rcmd_af)357 libc_hidden_def (rcmd_af)
358 
359 int
360 rcmd (char **ahost, u_short rport, const char *locuser, const char *remuser,
361       const char *cmd, int *fd2p)
362 {
363   return rcmd_af (ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
364 }
365 
366 int
rresvport_af(int * alport,sa_family_t family)367 rresvport_af (int *alport, sa_family_t family)
368 {
369 	union {
370 		struct sockaddr generic;
371 		struct sockaddr_in in;
372 		struct sockaddr_in6 in6;
373 	} ss;
374 	int s;
375 	size_t len;
376 	uint16_t *sport;
377 
378 	switch(family){
379 	case AF_INET:
380 		len = sizeof(struct sockaddr_in);
381 		sport = &ss.in.sin_port;
382 		break;
383 	case AF_INET6:
384 		len = sizeof(struct sockaddr_in6);
385 		sport = &ss.in6.sin6_port;
386 		break;
387 	default:
388 		__set_errno (EAFNOSUPPORT);
389 		return -1;
390 	}
391 	/* NB: No SOCK_CLOEXEC for backwards compatibility.  */
392 	s = __socket(family, SOCK_STREAM, 0);
393 	if (s < 0)
394 		return -1;
395 
396 	memset (&ss, '\0', sizeof(ss));
397 #ifdef SALEN
398 	ss.generic.__ss_len = len;
399 #endif
400 	ss.generic.sa_family = family;
401 
402 	/* Ignore invalid values.  */
403 	if (*alport < IPPORT_RESERVED / 2)
404 		*alport = IPPORT_RESERVED / 2;
405 	else if (*alport >= IPPORT_RESERVED)
406 		*alport = IPPORT_RESERVED - 1;
407 
408 	int start = *alport;
409 	do {
410 		*sport = htons((uint16_t) *alport);
411 		if (__bind(s, &ss.generic, len) >= 0)
412 			return s;
413 		if (errno != EADDRINUSE) {
414 			(void)__close(s);
415 			return -1;
416 		}
417 		if ((*alport)-- == IPPORT_RESERVED/2)
418 			*alport = IPPORT_RESERVED - 1;
419 	} while (*alport != start);
420 	(void)__close(s);
421 	__set_errno (EAGAIN);
422 	return -1;
423 }
libc_hidden_def(rresvport_af)424 libc_hidden_def (rresvport_af)
425 
426 int
427 rresvport (int *alport)
428 {
429 	return rresvport_af(alport, AF_INET);
430 }
431 
432 int	__check_rhosts_file = 1;
433 char	*__rcmd_errstr;
434 
435 int
ruserok_af(const char * rhost,int superuser,const char * ruser,const char * luser,sa_family_t af)436 ruserok_af (const char *rhost, int superuser, const char *ruser,
437 	    const char *luser, sa_family_t af)
438 {
439 	struct addrinfo hints, *res, *res0;
440 	int gai;
441 	int ret;
442 
443 	memset (&hints, '\0', sizeof(hints));
444 	hints.ai_family = af;
445 	gai = getaddrinfo(rhost, NULL, &hints, &res0);
446 	if (gai)
447 		return -1;
448 	ret = -1;
449 	for (res=res0; res; res=res->ai_next)
450 		if (ruserok2_sa(res->ai_addr, res->ai_addrlen,
451 				superuser, ruser, luser, rhost) == 0){
452 			ret = 0;
453 			break;
454 		}
455 	freeaddrinfo(res0);
456 	return (ret);
457 }
libc_hidden_def(ruserok_af)458 libc_hidden_def (ruserok_af)
459 
460 int
461 ruserok (const char *rhost, int superuser, const char *ruser,
462 	 const char *luser)
463 {
464 	return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
465 }
466 
467 /* Extremely paranoid file open function. */
468 static FILE *
iruserfopen(const char * file,uid_t okuser)469 iruserfopen (const char *file, uid_t okuser)
470 {
471   struct __stat64_t64 st;
472   char *cp = NULL;
473   FILE *res = NULL;
474 
475   /* If not a regular file, if owned by someone other than user or
476      root, if writeable by anyone but the owner, or if hardlinked
477      anywhere, quit.  */
478   if (__lstat64_time64 (file, &st))
479     cp = _("lstat failed");
480   else if (!S_ISREG (st.st_mode))
481     cp = _("not regular file");
482   else
483     {
484       res = fopen (file, "rce");
485       if (!res)
486 	cp = _("cannot open");
487       else if (__fstat64_time64 (fileno (res), &st) < 0)
488 	cp = _("fstat failed");
489       else if (st.st_uid && st.st_uid != okuser)
490 	cp = _("bad owner");
491       else if (st.st_mode & (S_IWGRP|S_IWOTH))
492 	cp = _("writeable by other than owner");
493       else if (st.st_nlink > 1)
494 	cp = _("hard linked somewhere");
495     }
496 
497   /* If there were any problems, quit.  */
498   if (cp != NULL)
499     {
500       __rcmd_errstr = cp;
501       if (res)
502 	fclose (res);
503       return NULL;
504     }
505 
506   /* No threads use this stream.  */
507   __fsetlocking (res, FSETLOCKING_BYCALLER);
508 
509   return res;
510 }
511 
512 /*
513  * New .rhosts strategy: We are passed an ip address. We spin through
514  * hosts.equiv and .rhosts looking for a match. When the .rhosts only
515  * has ip addresses, we don't have to trust a nameserver.  When it
516  * contains hostnames, we spin through the list of addresses the nameserver
517  * gives us and look for a match.
518  *
519  * Returns 0 if ok, -1 if not ok.
520  */
521 static int
ruserok2_sa(struct sockaddr * ra,size_t ralen,int superuser,const char * ruser,const char * luser,const char * rhost)522 ruserok2_sa (struct sockaddr *ra, size_t ralen, int superuser,
523 	     const char *ruser, const char *luser, const char *rhost)
524 {
525   FILE *hostf = NULL;
526   int isbad = -1;
527 
528   if (!superuser)
529     hostf = iruserfopen (_PATH_HEQUIV, 0);
530 
531   if (hostf)
532     {
533       isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
534       fclose (hostf);
535 
536       if (!isbad)
537 	return 0;
538     }
539 
540   if (__check_rhosts_file || superuser)
541     {
542       char *pbuf;
543       struct passwd pwdbuf, *pwd;
544       size_t dirlen;
545       size_t buflen = __sysconf (_SC_GETPW_R_SIZE_MAX);
546       char *buffer = __alloca (buflen);
547       uid_t uid;
548 
549       if (__getpwnam_r (luser, &pwdbuf, buffer, buflen, &pwd) != 0
550 	  || pwd == NULL)
551 	return -1;
552 
553       dirlen = strlen (pwd->pw_dir);
554       pbuf = alloca (dirlen + sizeof "/.rhosts");
555       __mempcpy (__mempcpy (pbuf, pwd->pw_dir, dirlen),
556 		 "/.rhosts", sizeof "/.rhosts");
557 
558        /* Change effective uid while reading .rhosts.  If root and
559 	  reading an NFS mounted file system, can't read files that
560 	  are protected read/write owner only.  */
561        uid = __geteuid ();
562        seteuid (pwd->pw_uid);
563        hostf = iruserfopen (pbuf, pwd->pw_uid);
564 
565        if (hostf != NULL)
566 	 {
567 	   isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
568 	   fclose (hostf);
569 	 }
570 
571        seteuid (uid);
572        return isbad;
573     }
574   return -1;
575 }
576 /*
577  * ruserok_sa() is now discussed on ipng, so
578  * currently disabled for external use
579  */
580 static int
ruserok_sa(struct sockaddr * ra,size_t ralen,int superuser,const char * ruser,const char * luser)581 ruserok_sa (struct sockaddr *ra, size_t ralen, int superuser,
582 	    const char *ruser, const char *luser)
583 {
584   return ruserok2_sa(ra, ralen, superuser, ruser, luser, "-");
585 }
586 
587 /* This is the exported version.  */
588 int
iruserok_af(const void * raddr,int superuser,const char * ruser,const char * luser,sa_family_t af)589 iruserok_af (const void *raddr, int superuser, const char *ruser,
590 	     const char *luser, sa_family_t af)
591 {
592   union {
593     struct sockaddr generic;
594     struct sockaddr_in in;
595     struct sockaddr_in6 in6;
596   } ra;
597   size_t ralen;
598 
599   memset (&ra, '\0', sizeof(ra));
600   switch (af){
601   case AF_INET:
602     ra.in.sin_family = AF_INET;
603     memcpy (&ra.in.sin_addr, raddr, sizeof(struct in_addr));
604     ralen = sizeof(struct sockaddr_in);
605     break;
606   case AF_INET6:
607     ra.in6.sin6_family = AF_INET6;
608     memcpy (&ra.in6.sin6_addr, raddr, sizeof(struct in6_addr));
609     ralen = sizeof(struct sockaddr_in6);
610     break;
611   default:
612     return 0;
613   }
614   return ruserok_sa (&ra.generic, ralen, superuser, ruser, luser);
615 }
libc_hidden_def(iruserok_af)616 libc_hidden_def (iruserok_af)
617 
618 int
619 iruserok (uint32_t raddr, int superuser, const char *ruser, const char *luser)
620 {
621   return iruserok_af (&raddr, superuser, ruser, luser, AF_INET);
622 }
623 
624 /*
625  * XXX
626  * Don't make static, used by lpd(8).
627  *
628  * This function is not used anymore. It is only present because lpd(8)
629  * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
630  * argument. This means that netgroups won't work in .rhost/hosts.equiv
631  * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
632  * or PAM.
633  * Returns 0 if ok, -1 if not ok.
634  */
635 int
__ivaliduser(FILE * hostf,uint32_t raddr,const char * luser,const char * ruser)636 __ivaliduser (FILE *hostf, uint32_t raddr, const char *luser,
637 	      const char *ruser)
638 {
639 	struct sockaddr_in ra;
640 	memset(&ra, '\0', sizeof(ra));
641 	ra.sin_family = AF_INET;
642 	ra.sin_addr.s_addr = raddr;
643 	return __validuser2_sa(hostf, (struct sockaddr *)&ra, sizeof(ra),
644 			       luser, ruser, "-");
645 }
646 
647 
648 /* Returns 1 on positive match, 0 on no match, -1 on negative match.  */
649 static int
__checkhost_sa(struct sockaddr * ra,size_t ralen,char * lhost,const char * rhost)650 __checkhost_sa (struct sockaddr *ra, size_t ralen, char *lhost,
651 		const char *rhost)
652 {
653 	struct addrinfo hints, *res0, *res;
654 	char raddr[INET6_ADDRSTRLEN];
655 	int match;
656 	int negate=1;    /* Multiply return with this to get -1 instead of 1 */
657 
658 	/* Check nis netgroup.  */
659 	if (strncmp ("+@", lhost, 2) == 0)
660 		return innetgr (&lhost[2], rhost, NULL, NULL);
661 
662 	if (strncmp ("-@", lhost, 2) == 0)
663 		return -innetgr (&lhost[2], rhost, NULL, NULL);
664 
665 	/* -host */
666 	if (strncmp ("-", lhost,1) == 0) {
667 		negate = -1;
668 		lhost++;
669 	} else if (strcmp ("+",lhost) == 0) {
670 		return 1;                    /* asking for trouble, but ok.. */
671 	}
672 
673 	/* Try for raw ip address first. */
674 	/* XXX */
675 	if (getnameinfo(ra, ralen,
676 			raddr, sizeof(raddr), NULL, 0,
677 			NI_NUMERICHOST) == 0
678 	    && strcmp(raddr, lhost) == 0)
679 		return negate;
680 
681 	/* Better be a hostname. */
682 	match = 0;
683 	memset(&hints, '\0', sizeof(hints));
684 	hints.ai_family = ra->sa_family;
685 	if (getaddrinfo(lhost, NULL, &hints, &res0) == 0){
686 		/* Spin through ip addresses. */
687 		for (res = res0; res; res = res->ai_next)
688 		  {
689 		    if (res->ai_family == ra->sa_family
690 			&& !memcmp(res->ai_addr, ra, res->ai_addrlen))
691 		      {
692 			match = 1;
693 			break;
694 		      }
695 		  }
696 		freeaddrinfo (res0);
697 	}
698 	return negate * match;
699 }
700 
701 /* Returns 1 on positive match, 0 on no match, -1 on negative match.  */
702 static int
__icheckuser(const char * luser,const char * ruser)703 __icheckuser (const char *luser, const char *ruser)
704 {
705     /*
706       luser is user entry from .rhosts/hosts.equiv file
707       ruser is user id on remote host
708       */
709 
710     /* [-+]@netgroup */
711     if (strncmp ("+@", luser, 2) == 0)
712 	return innetgr (&luser[2], NULL, ruser, NULL);
713 
714     if (strncmp ("-@", luser,2) == 0)
715 	return -innetgr (&luser[2], NULL, ruser, NULL);
716 
717     /* -user */
718     if (strncmp ("-", luser, 1) == 0)
719 	return -(strcmp (&luser[1], ruser) == 0);
720 
721     /* + */
722     if (strcmp ("+", luser) == 0)
723 	return 1;
724 
725     /* simple string match */
726     return strcmp (ruser, luser) == 0;
727 }
728 
729 /*
730  * Returns 1 for blank lines (or only comment lines) and 0 otherwise
731  */
732 static int
__isempty(char * p)733 __isempty (char *p)
734 {
735     while (*p && isspace (*p)) {
736 	++p;
737     }
738 
739     return (*p == '\0' || *p == '#') ? 1 : 0 ;
740 }
741 
742 /*
743  * Returns 0 if positive match, -1 if _not_ ok.
744  */
745 static int
__validuser2_sa(FILE * hostf,struct sockaddr * ra,size_t ralen,const char * luser,const char * ruser,const char * rhost)746 __validuser2_sa (FILE *hostf, struct sockaddr *ra, size_t ralen,
747 		 const char *luser, const char *ruser, const char *rhost)
748 {
749     const char *user;
750     char *p;
751     int hcheck, ucheck;
752     char *buf = NULL;
753     size_t bufsize = 0;
754     int retval = -1;
755 
756     while (__getline (&buf, &bufsize, hostf) > 0) {
757 	buf[bufsize - 1] = '\0'; /* Make sure it's terminated.  */
758 	p = buf;
759 
760 	/* Skip empty or comment lines */
761 	if (__isempty (p)) {
762 	    continue;
763 	}
764 
765 	for (;*p && !isspace(*p); ++p) {
766 	    *p = _tolower (*p);
767 	}
768 
769 	/* Next we want to find the permitted name for the remote user.  */
770 	if (*p == ' ' || *p == '\t') {
771 	    /* <nul> terminate hostname and skip spaces */
772 	    for (*p++='\0'; *p && isspace (*p); ++p);
773 
774 	    user = p;                   /* this is the user's name */
775 	    while (*p && !isspace (*p))
776 		++p;                    /* find end of user's name */
777 	} else
778 	    user = p;
779 
780 	*p = '\0';              /* <nul> terminate username (+host?) */
781 
782 	/* buf -> host(?) ; user -> username(?) */
783 	if (*buf == '\0')
784 	  break;
785 	if (*user == '\0')
786 	  user = luser;
787 
788 	/* First check the user part.  In a naive implementation we
789 	   would check the host part first, then the user.  However,
790 	   if we check the user first and reject the entry we will
791 	   have saved doing any host lookups to normalize the comparison
792 	   and that likely saves several DNS queries.  Therefore we
793 	   check the user first.  */
794 	ucheck = __icheckuser (user, ruser);
795 
796 	/* Either we found the user, or we didn't and this is a
797 	   negative host check.  We must do the negative host lookup
798 	   in order to preserve the semantics of stopping on this line
799 	   before processing others.  */
800 	if (ucheck != 0 || *buf == '-') {
801 
802 	    /* Next check host part.  */
803 	    hcheck = __checkhost_sa (ra, ralen, buf, rhost);
804 
805 	    /* Negative '-host user(?)' match?  */
806 	    if (hcheck < 0)
807 		break;
808 
809 	    /* Positive 'host user' match?  */
810 	    if (hcheck > 0 && ucheck > 0) {
811 		retval = 0;
812 		break;
813 	    }
814 
815 	    /* Negative 'host -user' match?  */
816 	    if (hcheck > 0 && ucheck < 0)
817 	      break;
818 
819 	    /* Neither, go on looking for match.  */
820 	}
821     }
822 
823     free (buf);
824 
825     return retval;
826 }
827