]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/net/rcmd.c
Merge llvm, clang, lld and lldb release_40 branch r292009. Also update
[FreeBSD/FreeBSD.git] / lib / libc / net / rcmd.c
1 /*
2  * Copyright (c) 1983, 1993, 1994
3  *      The Regents of the University of California.  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  * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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 #if defined(LIBC_SCCS) && !defined(lint)
31 static char sccsid[] = "@(#)rcmd.c      8.3 (Berkeley) 3/26/94";
32 #endif /* LIBC_SCCS and not lint */
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include "namespace.h"
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/stat.h>
40
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43
44 #include <signal.h>
45 #include <fcntl.h>
46 #include <netdb.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <pwd.h>
50 #include <errno.h>
51 #include <stdio.h>
52 #include <ctype.h>
53 #include <string.h>
54 #include <rpc/rpc.h>
55 #ifdef YP
56 #include <rpcsvc/yp_prot.h>
57 #include <rpcsvc/ypclnt.h>
58 #endif
59 #include <arpa/nameser.h>
60 #include "un-namespace.h"
61 #include "libc_private.h"
62
63 extern int innetgr( const char *, const char *, const char *, const char * );
64
65 #define max(a, b)       ((a > b) ? a : b)
66
67 int __ivaliduser(FILE *, u_int32_t, const char *, const char *);
68 int __ivaliduser_af(FILE *,const void *, const char *, const char *, int, int);
69 int __ivaliduser_sa(FILE *, const struct sockaddr *, socklen_t, const char *,
70     const char *);
71 static int __icheckhost(const struct sockaddr *, socklen_t, const char *);
72
73 char paddr[NI_MAXHOST];
74
75 int
76 rcmd(char **ahost, int rport, const char *locuser, const char *remuser,
77     const char *cmd, int *fd2p)
78 {
79         return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
80 }
81
82 int
83 rcmd_af(char **ahost, int rport, const char *locuser, const char *remuser,
84     const char *cmd, int *fd2p, int af)
85 {
86         struct addrinfo hints, *res, *ai;
87         struct sockaddr_storage from;
88         fd_set reads;
89         sigset_t oldmask, newmask;
90         pid_t pid;
91         int s, aport, lport, timo, error;
92         char c, *p;
93         int refused, nres;
94         char num[8];
95         static char canonnamebuf[MAXDNAME];     /* is it proper here? */
96
97         /* call rcmdsh() with specified remote shell if appropriate. */
98         if (!issetugid() && (p = getenv("RSH"))) {
99                 struct servent *sp = getservbyname("shell", "tcp");
100
101                 if (sp && sp->s_port == rport)
102                         return (rcmdsh(ahost, rport, locuser, remuser,
103                             cmd, p));
104         }
105
106         /* use rsh(1) if non-root and remote port is shell. */
107         if (geteuid()) {
108                 struct servent *sp = getservbyname("shell", "tcp");
109
110                 if (sp && sp->s_port == rport)
111                         return (rcmdsh(ahost, rport, locuser, remuser,
112                             cmd, NULL));
113         }
114
115         pid = getpid();
116
117         memset(&hints, 0, sizeof(hints));
118         hints.ai_flags = AI_CANONNAME;
119         hints.ai_family = af;
120         hints.ai_socktype = SOCK_STREAM;
121         hints.ai_protocol = 0;
122         (void)snprintf(num, sizeof(num), "%d", ntohs(rport));
123         error = getaddrinfo(*ahost, num, &hints, &res);
124         if (error) {
125                 fprintf(stderr, "rcmd: getaddrinfo: %s\n",
126                         gai_strerror(error));
127                 if (error == EAI_SYSTEM)
128                         fprintf(stderr, "rcmd: getaddrinfo: %s\n",
129                                 strerror(errno));
130                 return (-1);
131         }
132
133         if (res->ai_canonname
134          && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) {
135                 strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf));
136                 *ahost = canonnamebuf;
137         }
138         nres = 0;
139         for (ai = res; ai; ai = ai->ai_next)
140                 nres++;
141         ai = res;
142         refused = 0;
143         sigemptyset(&newmask);
144         sigaddset(&newmask, SIGURG);
145         __libc_sigprocmask(SIG_BLOCK, (const sigset_t *)&newmask, &oldmask);
146         for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
147                 s = rresvport_af(&lport, ai->ai_family);
148                 if (s < 0) {
149                         if (errno != EAGAIN && ai->ai_next) {
150                                 ai = ai->ai_next;
151                                 continue;
152                         }
153                         if (errno == EAGAIN)
154                                 (void)fprintf(stderr,
155                                     "rcmd: socket: All ports in use\n");
156                         else
157                                 (void)fprintf(stderr, "rcmd: socket: %s\n",
158                                     strerror(errno));
159                         freeaddrinfo(res);
160                         __libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask,
161                             NULL);
162                         return (-1);
163                 }
164                 _fcntl(s, F_SETOWN, pid);
165                 if (_connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
166                         break;
167                 (void)_close(s);
168                 if (errno == EADDRINUSE) {
169                         lport--;
170                         continue;
171                 }
172                 if (errno == ECONNREFUSED)
173                         refused = 1;
174                 if (ai->ai_next == NULL && (!refused || timo > 16)) {
175                         (void)fprintf(stderr, "%s: %s\n",
176                                       *ahost, strerror(errno));
177                         freeaddrinfo(res);
178                         __libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask,
179                             NULL);
180                         return (-1);
181                 }
182                 if (nres > 1) {
183                         int oerrno = errno;
184
185                         getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr,
186                             sizeof(paddr), NULL, 0, NI_NUMERICHOST);
187                         (void)fprintf(stderr, "connect to address %s: ",
188                                       paddr);
189                         errno = oerrno;
190                         perror(0);
191                 }
192                 if ((ai = ai->ai_next) == NULL) {
193                         /* refused && timo <= 16 */
194                         struct timespec time_to_sleep, time_remaining;
195
196                         time_to_sleep.tv_sec = timo;
197                         time_to_sleep.tv_nsec = 0;
198                         (void)_nanosleep(&time_to_sleep, &time_remaining);
199                         timo *= 2;
200                         ai = res;
201                         refused = 0;
202                 }
203                 if (nres > 1) {
204                         getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr,
205                             sizeof(paddr), NULL, 0, NI_NUMERICHOST);
206                         fprintf(stderr, "Trying %s...\n", paddr);
207                 }
208         }
209         lport--;
210         if (fd2p == NULL) {
211                 _write(s, "", 1);
212                 lport = 0;
213         } else {
214                 int s2 = rresvport_af(&lport, ai->ai_family), s3;
215                 socklen_t len = ai->ai_addrlen;
216                 int nfds;
217
218                 if (s2 < 0)
219                         goto bad;
220                 _listen(s2, 1);
221                 (void)snprintf(num, sizeof(num), "%d", lport);
222                 if (_write(s, num, strlen(num)+1) != strlen(num)+1) {
223                         (void)fprintf(stderr,
224                             "rcmd: write (setting up stderr): %s\n",
225                             strerror(errno));
226                         (void)_close(s2);
227                         goto bad;
228                 }
229                 nfds = max(s, s2)+1;
230                 if(nfds > FD_SETSIZE) {
231                         fprintf(stderr, "rcmd: too many files\n");
232                         (void)_close(s2);
233                         goto bad;
234                 }
235 again:
236                 FD_ZERO(&reads);
237                 FD_SET(s, &reads);
238                 FD_SET(s2, &reads);
239                 errno = 0;
240                 if (_select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){
241                         if (errno != 0)
242                                 (void)fprintf(stderr,
243                                     "rcmd: select (setting up stderr): %s\n",
244                                     strerror(errno));
245                         else
246                                 (void)fprintf(stderr,
247                                 "select: protocol failure in circuit setup\n");
248                         (void)_close(s2);
249                         goto bad;
250                 }
251                 s3 = _accept(s2, (struct sockaddr *)&from, &len);
252                 switch (from.ss_family) {
253                 case AF_INET:
254                         aport = ntohs(((struct sockaddr_in *)&from)->sin_port);
255                         break;
256 #ifdef INET6
257                 case AF_INET6:
258                         aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
259                         break;
260 #endif
261                 default:
262                         aport = 0;      /* error */
263                         break;
264                 }
265                 /*
266                  * XXX careful for ftp bounce attacks. If discovered, shut them
267                  * down and check for the real auxiliary channel to connect.
268                  */
269                 if (aport == 20) {
270                         _close(s3);
271                         goto again;
272                 }
273                 (void)_close(s2);
274                 if (s3 < 0) {
275                         (void)fprintf(stderr,
276                             "rcmd: accept: %s\n", strerror(errno));
277                         lport = 0;
278                         goto bad;
279                 }
280                 *fd2p = s3;
281                 if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) {
282                         (void)fprintf(stderr,
283                             "socket: protocol failure in circuit setup.\n");
284                         goto bad2;
285                 }
286         }
287         (void)_write(s, locuser, strlen(locuser)+1);
288         (void)_write(s, remuser, strlen(remuser)+1);
289         (void)_write(s, cmd, strlen(cmd)+1);
290         if (_read(s, &c, 1) != 1) {
291                 (void)fprintf(stderr,
292                     "rcmd: %s: %s\n", *ahost, strerror(errno));
293                 goto bad2;
294         }
295         if (c != 0) {
296                 while (_read(s, &c, 1) == 1) {
297                         (void)_write(STDERR_FILENO, &c, 1);
298                         if (c == '\n')
299                                 break;
300                 }
301                 goto bad2;
302         }
303         __libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL);
304         freeaddrinfo(res);
305         return (s);
306 bad2:
307         if (lport)
308                 (void)_close(*fd2p);
309 bad:
310         (void)_close(s);
311         __libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL);
312         freeaddrinfo(res);
313         return (-1);
314 }
315
316 int
317 rresvport(int *port)
318 {
319         return rresvport_af(port, AF_INET);
320 }
321
322 int
323 rresvport_af(int *alport, int family)
324 {
325         int s;
326         struct sockaddr_storage ss;
327         u_short *sport;
328
329         memset(&ss, 0, sizeof(ss));
330         ss.ss_family = family;
331         switch (family) {
332         case AF_INET:
333                 ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in);
334                 sport = &((struct sockaddr_in *)&ss)->sin_port;
335                 ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
336                 break;
337 #ifdef INET6
338         case AF_INET6:
339                 ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6);
340                 sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
341                 ((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any;
342                 break;
343 #endif
344         default:
345                 errno = EAFNOSUPPORT;
346                 return -1;
347         }
348
349         s = _socket(ss.ss_family, SOCK_STREAM, 0);
350         if (s < 0)
351                 return (-1);
352 #if 0 /* compat_exact_traditional_rresvport_semantics */
353         sin.sin_port = htons((u_short)*alport);
354         if (_bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
355                 return (s);
356         if (errno != EADDRINUSE) {
357                 (void)_close(s);
358                 return (-1);
359         }
360 #endif
361         *sport = 0;
362         if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) {
363                 (void)_close(s);
364                 return (-1);
365         }
366         *alport = (int)ntohs(*sport);
367         return (s);
368 }
369
370 int     __check_rhosts_file = 1;
371 char    *__rcmd_errstr;
372
373 int
374 ruserok(const char *rhost, int superuser, const char *ruser, const char *luser)
375 {
376         struct addrinfo hints, *res, *r;
377         int error;
378
379         memset(&hints, 0, sizeof(hints));
380         hints.ai_family = PF_UNSPEC;
381         hints.ai_socktype = SOCK_DGRAM; /*dummy*/
382         error = getaddrinfo(rhost, "0", &hints, &res);
383         if (error)
384                 return (-1);
385
386         for (r = res; r; r = r->ai_next) {
387                 if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser,
388                     luser) == 0) {
389                         freeaddrinfo(res);
390                         return (0);
391                 }
392         }
393         freeaddrinfo(res);
394         return (-1);
395 }
396
397 /*
398  * New .rhosts strategy: We are passed an ip address. We spin through
399  * hosts.equiv and .rhosts looking for a match. When the .rhosts only
400  * has ip addresses, we don't have to trust a nameserver.  When it
401  * contains hostnames, we spin through the list of addresses the nameserver
402  * gives us and look for a match.
403  *
404  * Returns 0 if ok, -1 if not ok.
405  */
406 int
407 iruserok(unsigned long raddr, int superuser, const char *ruser, const char *luser)
408 {
409         struct sockaddr_in sin;
410
411         memset(&sin, 0, sizeof(sin));
412         sin.sin_family = AF_INET;
413         sin.sin_len = sizeof(struct sockaddr_in);
414         memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr));
415         return iruserok_sa((struct sockaddr *)&sin, sin.sin_len, superuser,
416                 ruser, luser);
417 }
418
419 /*
420  * AF independent extension of iruserok.
421  *
422  * Returns 0 if ok, -1 if not ok.
423  */
424 int
425 iruserok_sa(const void *ra, int rlen, int superuser, const char *ruser,
426     const char *luser)
427 {
428         char *cp;
429         struct stat sbuf;
430         struct passwd *pwd;
431         FILE *hostf;
432         uid_t uid;
433         int first;
434         char pbuf[MAXPATHLEN];
435         const struct sockaddr *raddr;
436         struct sockaddr_storage ss;
437
438         /* avoid alignment issue */
439         if (rlen > sizeof(ss)) 
440                 return(-1);
441         memcpy(&ss, ra, rlen);
442         raddr = (struct sockaddr *)&ss;
443
444         first = 1;
445         hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re");
446 again:
447         if (hostf) {
448                 if (__ivaliduser_sa(hostf, raddr, rlen, luser, ruser) == 0) {
449                         (void)fclose(hostf);
450                         return (0);
451                 }
452                 (void)fclose(hostf);
453         }
454         if (first == 1 && (__check_rhosts_file || superuser)) {
455                 first = 0;
456                 if ((pwd = getpwnam(luser)) == NULL)
457                         return (-1);
458                 (void)strcpy(pbuf, pwd->pw_dir);
459                 (void)strcat(pbuf, "/.rhosts");
460
461                 /*
462                  * Change effective uid while opening .rhosts.  If root and
463                  * reading an NFS mounted file system, can't read files that
464                  * are protected read/write owner only.
465                  */
466                 uid = geteuid();
467                 (void)seteuid(pwd->pw_uid);
468                 hostf = fopen(pbuf, "re");
469                 (void)seteuid(uid);
470
471                 if (hostf == NULL)
472                         return (-1);
473                 /*
474                  * If not a regular file, or is owned by someone other than
475                  * user or root or if writeable by anyone but the owner, quit.
476                  */
477                 cp = NULL;
478                 if (lstat(pbuf, &sbuf) < 0)
479                         cp = ".rhosts lstat failed";
480                 else if (!S_ISREG(sbuf.st_mode))
481                         cp = ".rhosts not regular file";
482                 else if (_fstat(fileno(hostf), &sbuf) < 0)
483                         cp = ".rhosts fstat failed";
484                 else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid)
485                         cp = "bad .rhosts owner";
486                 else if (sbuf.st_mode & (S_IWGRP|S_IWOTH))
487                         cp = ".rhosts writeable by other than owner";
488                 /* If there were any problems, quit. */
489                 if (cp) {
490                         __rcmd_errstr = cp;
491                         (void)fclose(hostf);
492                         return (-1);
493                 }
494                 goto again;
495         }
496         return (-1);
497 }
498
499 /*
500  * XXX
501  * Don't make static, used by lpd(8).
502  *
503  * Returns 0 if ok, -1 if not ok.
504  */
505 int
506 __ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser, const char *ruser)
507 {
508         struct sockaddr_in sin;
509
510         memset(&sin, 0, sizeof(sin));
511         sin.sin_family = AF_INET;
512         sin.sin_len = sizeof(struct sockaddr_in);
513         memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr));
514         return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len,
515                 luser, ruser);
516 }
517
518 /*
519  * Returns 0 if ok, -1 if not ok.
520  *
521  * XXX obsolete API.
522  */
523 int
524 __ivaliduser_af(FILE *hostf, const void *raddr, const char *luser,
525     const char *ruser, int af, int len)
526 {
527         struct sockaddr *sa = NULL;
528         struct sockaddr_in *sin = NULL;
529 #ifdef INET6
530         struct sockaddr_in6 *sin6 = NULL;
531 #endif
532         struct sockaddr_storage ss;
533
534         memset(&ss, 0, sizeof(ss));
535         switch (af) {
536         case AF_INET:
537                 if (len != sizeof(sin->sin_addr))
538                         return -1;
539                 sin = (struct sockaddr_in *)&ss;
540                 sin->sin_family = AF_INET;
541                 sin->sin_len = sizeof(struct sockaddr_in);
542                 memcpy(&sin->sin_addr, raddr, sizeof(sin->sin_addr));
543                 break;
544 #ifdef INET6
545         case AF_INET6:
546                 if (len != sizeof(sin6->sin6_addr))
547                         return -1;
548                 /* you will lose scope info */
549                 sin6 = (struct sockaddr_in6 *)&ss;
550                 sin6->sin6_family = AF_INET6;
551                 sin6->sin6_len = sizeof(struct sockaddr_in6);
552                 memcpy(&sin6->sin6_addr, raddr, sizeof(sin6->sin6_addr));
553                 break;
554 #endif
555         default:
556                 return -1;
557         }
558
559         sa = (struct sockaddr *)&ss;
560         return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser);
561 }
562
563 int
564 __ivaliduser_sa(FILE *hostf, const struct sockaddr *raddr, socklen_t salen,
565     const char *luser, const char *ruser)
566 {
567         char *user, *p;
568         int ch;
569         char buf[MAXHOSTNAMELEN + 128];         /* host + login */
570         char hname[MAXHOSTNAMELEN];
571         /* Presumed guilty until proven innocent. */
572         int userok = 0, hostok = 0;
573 #ifdef YP
574         char *ypdomain;
575
576         if (yp_get_default_domain(&ypdomain))
577                 ypdomain = NULL;
578 #else
579 #define ypdomain NULL
580 #endif
581         /* We need to get the damn hostname back for netgroup matching. */
582         if (getnameinfo(raddr, salen, hname, sizeof(hname), NULL, 0,
583                         NI_NAMEREQD) != 0)
584                 hname[0] = '\0';
585
586         while (fgets(buf, sizeof(buf), hostf)) {
587                 p = buf;
588                 /* Skip lines that are too long. */
589                 if (strchr(p, '\n') == NULL) {
590                         while ((ch = getc(hostf)) != '\n' && ch != EOF);
591                         continue;
592                 }
593                 if (*p == '\n' || *p == '#') {
594                         /* comment... */
595                         continue;
596                 }
597                 while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
598                         *p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p;
599                         p++;
600                 }
601                 if (*p == ' ' || *p == '\t') {
602                         *p++ = '\0';
603                         while (*p == ' ' || *p == '\t')
604                                 p++;
605                         user = p;
606                         while (*p != '\n' && *p != ' ' &&
607                             *p != '\t' && *p != '\0')
608                                 p++;
609                 } else
610                         user = p;
611                 *p = '\0';
612                 /*
613                  * Do +/- and +@/-@ checking. This looks really nasty,
614                  * but it matches SunOS's behavior so far as I can tell.
615                  */
616                 switch(buf[0]) {
617                 case '+':
618                         if (!buf[1]) {     /* '+' matches all hosts */
619                                 hostok = 1;
620                                 break;
621                         }
622                         if (buf[1] == '@')  /* match a host by netgroup */
623                                 hostok = hname[0] != '\0' &&
624                                     innetgr(&buf[2], hname, NULL, ypdomain);
625                         else            /* match a host by addr */
626                                 hostok = __icheckhost(raddr, salen,
627                                                       (char *)&buf[1]);
628                         break;
629                 case '-':     /* reject '-' hosts and all their users */
630                         if (buf[1] == '@') {
631                                 if (hname[0] == '\0' ||
632                                     innetgr(&buf[2], hname, NULL, ypdomain))
633                                         return(-1);
634                         } else {
635                                 if (__icheckhost(raddr, salen,
636                                                  (char *)&buf[1]))
637                                         return(-1);
638                         }
639                         break;
640                 default:  /* if no '+' or '-', do a simple match */
641                         hostok = __icheckhost(raddr, salen, buf);
642                         break;
643                 }
644                 switch(*user) {
645                 case '+':
646                         if (!*(user+1)) {      /* '+' matches all users */
647                                 userok = 1;
648                                 break;
649                         }
650                         if (*(user+1) == '@')  /* match a user by netgroup */
651                                 userok = innetgr(user+2, NULL, ruser, ypdomain);
652                         else       /* match a user by direct specification */
653                                 userok = !(strcmp(ruser, user+1));
654                         break;
655                 case '-':               /* if we matched a hostname, */
656                         if (hostok) {   /* check for user field rejections */
657                                 if (!*(user+1))
658                                         return(-1);
659                                 if (*(user+1) == '@') {
660                                         if (innetgr(user+2, NULL,
661                                                         ruser, ypdomain))
662                                                 return(-1);
663                                 } else {
664                                         if (!strcmp(ruser, user+1))
665                                                 return(-1);
666                                 }
667                         }
668                         break;
669                 default:        /* no rejections: try to match the user */
670                         if (hostok)
671                                 userok = !(strcmp(ruser,*user ? user : luser));
672                         break;
673                 }
674                 if (hostok && userok)
675                         return(0);
676         }
677         return (-1);
678 }
679
680 /*
681  * Returns "true" if match, 0 if no match.
682  */
683 static int
684 __icheckhost(const struct sockaddr *raddr, socklen_t salen, const char *lhost)
685 {
686         struct sockaddr_in sin;
687         struct sockaddr_in6 *sin6;
688         struct addrinfo hints, *res, *r;
689         int error;
690         char h1[NI_MAXHOST], h2[NI_MAXHOST];
691
692         if (raddr->sa_family == AF_INET6) {
693                 sin6 = (struct sockaddr_in6 *)raddr;
694                 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
695                         memset(&sin, 0, sizeof(sin));
696                         sin.sin_family = AF_INET;
697                         sin.sin_len = sizeof(struct sockaddr_in);
698                         memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12],
699                                sizeof(sin.sin_addr));
700                         raddr = (struct sockaddr *)&sin;
701                         salen = sin.sin_len;
702                 }
703         }
704
705         h1[0] = '\0';
706         if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0,
707                         NI_NUMERICHOST) != 0)
708                 return (0);
709
710         /* Resolve laddr into sockaddr */
711         memset(&hints, 0, sizeof(hints));
712         hints.ai_family = raddr->sa_family;
713         hints.ai_socktype = SOCK_DGRAM; /*XXX dummy*/
714         res = NULL;
715         error = getaddrinfo(lhost, "0", &hints, &res);
716         if (error)
717                 return (0);
718
719         for (r = res; r ; r = r->ai_next) {
720                 h2[0] = '\0';
721                 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
722                                 NULL, 0, NI_NUMERICHOST) != 0)
723                         continue;
724                 if (strcmp(h1, h2) == 0) {
725                         freeaddrinfo(res);
726                         return (1);
727                 }
728         }
729
730         /* No match. */
731         freeaddrinfo(res);
732         return (0);
733 }