* include/libc-symbols.h (__libc_freeres_fn_section, libc_freeres_fn):
[platform/upstream/glibc.git] / inet / rcmd.c
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 #if defined(LIBC_SCCS) && !defined(lint)
59 static char sccsid[] = "@(#)rcmd.c      8.3 (Berkeley) 3/26/94";
60 #endif /* LIBC_SCCS and not lint */
61
62 #include <sys/param.h>
63 #include <sys/poll.h>
64 #include <sys/socket.h>
65 #include <sys/stat.h>
66
67 #include <netinet/in.h>
68 #include <arpa/inet.h>
69
70 #include <alloca.h>
71 #include <signal.h>
72 #include <fcntl.h>
73 #include <netdb.h>
74 #include <unistd.h>
75 #include <pwd.h>
76 #include <errno.h>
77 #include <stdio.h>
78 #include <stdio_ext.h>
79 #include <ctype.h>
80 #include <string.h>
81 #include <libintl.h>
82 #include <stdlib.h>
83 #ifdef USE_IN_LIBIO
84 # include <wchar.h>
85 #endif
86
87
88 int __ivaliduser (FILE *, u_int32_t, const char *, const char *);
89 static int __validuser2_sa (FILE *, struct sockaddr *, size_t,
90                             const char *, const char *, const char *);
91 static int ruserok2_sa (struct sockaddr *ra, size_t ralen,
92                         int superuser, const char *ruser,
93                         const char *luser, const char *rhost);
94 static int ruserok_sa (struct sockaddr *ra, size_t ralen,
95                         int superuser, const char *ruser,
96                         const char *luser);
97 int iruserok_af (const void *raddr, int superuser, const char *ruser,
98                  const char *luser, sa_family_t af);
99 int iruserok (u_int32_t raddr, int superuser, const char *ruser,
100               const char *luser);
101
102 libc_hidden_proto (iruserok_af)
103
104 libc_freeres_ptr(static char *ahostbuf);
105
106 int
107 rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af)
108         char **ahost;
109         u_short rport;
110         const char *locuser, *remuser, *cmd;
111         int *fd2p;
112         sa_family_t af;
113 {
114         char paddr[INET6_ADDRSTRLEN];
115         struct addrinfo hints, *res, *ai;
116         struct sockaddr_storage from;
117         struct pollfd pfd[2];
118         int32_t oldmask;
119         pid_t pid;
120         int s, lport, timo, error;
121         char c;
122         int refused;
123         char num[8];
124         ssize_t n;
125
126         if (af != AF_INET && af != AF_INET6 && af != AF_UNSPEC)
127           {
128             __set_errno (EAFNOSUPPORT);
129             return -1;
130           }
131
132         pid = __getpid();
133
134         memset(&hints, '\0', sizeof(hints));
135         hints.ai_flags = AI_CANONNAME;
136         hints.ai_family = af;
137         hints.ai_socktype = SOCK_STREAM;
138         (void)__snprintf(num, sizeof(num), "%d", ntohs(rport));
139         error = getaddrinfo(*ahost, num, &hints, &res);
140         if (error) {
141 #ifdef USE_IN_LIBIO
142                 if (_IO_fwide (stderr, 0) > 0)
143                         __fwprintf(stderr, L"rcmd: getaddrinfo: %s\n",
144                                    gai_strerror(error));
145                 else
146 #endif
147                         fprintf(stderr, "rcmd: getaddrinfo: %s\n",
148                                 gai_strerror(error));
149                 return (-1);
150         }
151
152         pfd[0].events = POLLIN;
153         pfd[1].events = POLLIN;
154
155         if (res->ai_canonname){
156                 free (ahostbuf);
157                 ahostbuf = strdup (res->ai_canonname);
158                 if (ahostbuf == NULL) {
159 #ifdef USE_IN_LIBIO
160                         if (_IO_fwide (stderr, 0) > 0)
161                                 __fwprintf(stderr, L"%s",
162                                            _("rcmd: Cannot allocate memory\n"));
163                         else
164 #endif
165                                 fputs(_("rcmd: Cannot allocate memory\n"),
166                                       stderr);
167                         return (-1);
168                 }
169                 *ahost = ahostbuf;
170         } else
171                 *ahost = NULL;
172         ai = res;
173         refused = 0;
174         oldmask = __sigblock(sigmask(SIGURG));
175         for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
176                 char errbuf[200];
177
178                 s = rresvport_af(&lport, ai->ai_family);
179                 if (s < 0) {
180                         if (errno == EAGAIN) {
181 #ifdef USE_IN_LIBIO
182                                 if (_IO_fwide (stderr, 0) > 0)
183                                         __fwprintf(stderr, L"%s",
184                                                    _("rcmd: socket: All ports in use\n"));
185                                 else
186 #endif
187                                         fputs(_("rcmd: socket: All ports in use\n"),
188                                               stderr);
189                         } else {
190 #ifdef USE_IN_LIBIO
191                                 if (_IO_fwide (stderr, 0) > 0)
192                                         __fwprintf(stderr,
193                                                    L"rcmd: socket: %m\n");
194                                 else
195 #endif
196                                         fprintf(stderr, "rcmd: socket: %m\n");
197                         }
198                         __sigsetmask(oldmask);
199                         freeaddrinfo(res);
200                         return -1;
201                 }
202                 __fcntl(s, F_SETOWN, pid);
203                 if (__connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
204                         break;
205                 (void)__close(s);
206                 if (errno == EADDRINUSE) {
207                         lport--;
208                         continue;
209                 }
210                 if (errno == ECONNREFUSED)
211                         refused = 1;
212                 if (ai->ai_next != NULL) {
213                         int oerrno = errno;
214                         char *buf = NULL;
215
216                         getnameinfo(ai->ai_addr, ai->ai_addrlen,
217                                     paddr, sizeof(paddr),
218                                     NULL, 0,
219                                     NI_NUMERICHOST);
220
221                         if (__asprintf (&buf, _("connect to address %s: "),
222                                         paddr) >= 0)
223                           {
224 #ifdef USE_IN_LIBIO
225                             if (_IO_fwide (stderr, 0) > 0)
226                               __fwprintf(stderr, L"%s", buf);
227                             else
228 #endif
229                               fputs (buf, stderr);
230                             free (buf);
231                           }
232                         __set_errno (oerrno);
233                         perror(0);
234                         ai = ai->ai_next;
235                         getnameinfo(ai->ai_addr, ai->ai_addrlen,
236                                     paddr, sizeof(paddr),
237                                     NULL, 0,
238                                     NI_NUMERICHOST);
239                         if (__asprintf (&buf, _("Trying %s...\n"), paddr) >= 0)
240                           {
241 #ifdef USE_IN_LIBIO
242                             if (_IO_fwide (stderr, 0) > 0)
243                               __fwprintf (stderr, L"%s", buf);
244                             else
245 #endif
246                               fputs (buf, stderr);
247                             free (buf);
248                           }
249                         continue;
250                 }
251                 if (refused && timo <= 16) {
252                         (void)__sleep(timo);
253                         timo *= 2;
254                         ai = res;
255                         refused = 0;
256                         continue;
257                 }
258                 freeaddrinfo(res);
259 #ifdef USE_IN_LIBIO
260                 if (_IO_fwide (stderr, 0) > 0)
261                         (void)__fwprintf(stderr, L"%s: %s\n", *ahost,
262                                          __strerror_r(errno,
263                                                       errbuf, sizeof (errbuf)));
264                 else
265 #endif
266                         (void)fprintf(stderr, "%s: %s\n", *ahost,
267                                       __strerror_r(errno,
268                                                    errbuf, sizeof (errbuf)));
269                 __sigsetmask(oldmask);
270                 return -1;
271         }
272         lport--;
273         if (fd2p == 0) {
274                 __write(s, "", 1);
275                 lport = 0;
276         } else {
277                 char num[8];
278                 int s2 = rresvport_af(&lport, ai->ai_family), s3;
279                 socklen_t len = ai->ai_addrlen;
280
281                 if (s2 < 0)
282                         goto bad;
283                 __listen(s2, 1);
284                 (void)__snprintf(num, sizeof(num), "%d", lport);
285                 if (__write(s, num, strlen(num)+1) != (ssize_t)strlen(num)+1) {
286                         char *buf = NULL;
287
288                         if (__asprintf (&buf, _("\
289 rcmd: write (setting up stderr): %m\n")) >= 0)
290                           {
291 #ifdef USE_IN_LIBIO
292                             if (_IO_fwide (stderr, 0) > 0)
293                               __fwprintf(stderr, L"%s", buf);
294                             else
295 #endif
296                               fputs (buf, stderr);
297                             free (buf);
298                           }
299                         (void)__close(s2);
300                         goto bad;
301                 }
302                 pfd[0].fd = s;
303                 pfd[1].fd = s2;
304                 __set_errno (0);
305                 if (__poll (pfd, 2, -1) < 1 || (pfd[1].revents & POLLIN) == 0){
306                         char *buf = NULL;
307
308                         if ((errno != 0
309                              && __asprintf(&buf, _("\
310 rcmd: poll (setting up stderr): %m\n")) >= 0)
311                             || (errno == 0
312                                 && __asprintf(&buf, _("\
313 poll: protocol failure in circuit setup\n")) >= 0))
314                           {
315 #ifdef USE_IN_LIBIO
316                             if (_IO_fwide (stderr, 0) > 0)
317                               __fwprintf (stderr, L"%s", buf);
318                             else
319 #endif
320                               fputs (buf, stderr);
321                             free  (buf);
322                           }
323                         (void)__close(s2);
324                         goto bad;
325                 }
326                 s3 = accept(s2, (struct sockaddr *)&from, &len);
327                 switch (from.ss_family) {
328                 case AF_INET:
329                         rport = ntohs(((struct sockaddr_in *)&from)->sin_port);
330                         break;
331                 case AF_INET6:
332                         rport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
333                         break;
334                 default:
335                         rport = 0;
336                         break;
337                 }
338                 (void)__close(s2);
339                 if (s3 < 0) {
340 #ifdef USE_IN_LIBIO
341                         if (_IO_fwide (stderr, 0) > 0)
342                                 (void)__fwprintf(stderr,
343                                                  L"rcmd: accept: %m\n");
344                         else
345 #endif
346                                 (void)fprintf(stderr,
347                                               "rcmd: accept: %m\n");
348                         lport = 0;
349                         goto bad;
350                 }
351                 *fd2p = s3;
352
353                 if (rport >= IPPORT_RESERVED || rport < IPPORT_RESERVED / 2){
354                         char *buf = NULL;
355
356                         if (__asprintf(&buf, _("\
357 socket: protocol failure in circuit setup\n")) >= 0)
358                           {
359 #ifdef USE_IN_LIBIO
360                             if (_IO_fwide (stderr, 0) > 0)
361                               __fwprintf (stderr, L"%s", buf);
362                             else
363 #endif
364                               fputs (buf, stderr);
365                             free (buf);
366                           }
367                         goto bad2;
368                 }
369         }
370         (void)__write(s, locuser, strlen(locuser)+1);
371         (void)__write(s, remuser, strlen(remuser)+1);
372         (void)__write(s, cmd, strlen(cmd)+1);
373         n = __read(s, &c, 1);
374         if (n != 1) {
375                 char *buf = NULL;
376
377                 if ((n == 0
378                      && __asprintf(&buf, _("rcmd: %s: short read"),
379                                    *ahost) >= 0)
380                     || (n != 0
381                         && __asprintf(&buf, "rcmd: %s: %m\n", *ahost) >= 0))
382                   {
383 #ifdef USE_IN_LIBIO
384                     if (_IO_fwide (stderr, 0) > 0)
385                       __fwprintf (stderr, L"%s", buf);
386                     else
387 #endif
388                       fputs (buf, stderr);
389                     free (buf);
390                   }
391                 goto bad2;
392         }
393         if (c != 0) {
394                 while (__read(s, &c, 1) == 1) {
395                         (void)__write(STDERR_FILENO, &c, 1);
396                         if (c == '\n')
397                                 break;
398                 }
399                 goto bad2;
400         }
401         __sigsetmask(oldmask);
402         freeaddrinfo(res);
403         return s;
404 bad2:
405         if (lport)
406                 (void)__close(*fd2p);
407 bad:
408         (void)__close(s);
409         __sigsetmask(oldmask);
410         freeaddrinfo(res);
411         return -1;
412 }
413 libc_hidden_def (rcmd_af)
414
415 int
416 rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
417         char **ahost;
418         u_short rport;
419         const char *locuser, *remuser, *cmd;
420         int *fd2p;
421 {
422   return rcmd_af (ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
423 }
424
425 int
426 rresvport_af(alport, family)
427         int *alport;
428         sa_family_t family;
429 {
430         struct sockaddr_storage ss;
431         int s;
432         size_t len;
433         uint16_t *sport;
434
435         switch(family){
436         case AF_INET:
437                 len = sizeof(struct sockaddr_in);
438                 sport = &((struct sockaddr_in *)&ss)->sin_port;
439                 break;
440         case AF_INET6:
441                 len = sizeof(struct sockaddr_in6);
442                 sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
443                 break;
444         default:
445                 __set_errno (EAFNOSUPPORT);
446                 return -1;
447         }
448         s = __socket(family, SOCK_STREAM, 0);
449         if (s < 0)
450                 return -1;
451
452         memset (&ss, '\0', sizeof(ss));
453 #ifdef SALEN
454         ss.__ss_len = len;
455 #endif
456         ss.ss_family = family;
457
458         for (;;) {
459                 *sport = htons((uint16_t) *alport);
460                 if (__bind(s, (struct sockaddr *)&ss, len) >= 0)
461                         return s;
462                 if (errno != EADDRINUSE) {
463                         (void)__close(s);
464                         return -1;
465                 }
466                 (*alport)--;
467                 if (*alport == IPPORT_RESERVED/2)
468                         break;
469         }
470         (void)__close(s);
471         __set_errno (EAGAIN);
472         return -1;
473 }
474 libc_hidden_def (rresvport_af)
475
476 int
477 rresvport(alport)
478         int *alport;
479 {
480         return rresvport_af(alport, AF_INET);
481 }
482
483 int     __check_rhosts_file = 1;
484 char    *__rcmd_errstr;
485
486 int
487 ruserok_af(rhost, superuser, ruser, luser, af)
488         const char *rhost, *ruser, *luser;
489         int superuser;
490         sa_family_t af;
491 {
492         struct addrinfo hints, *res, *res0;
493         int gai;
494         int ret;
495
496         memset (&hints, '\0', sizeof(hints));
497         hints.ai_family = af;
498         gai = getaddrinfo(rhost, NULL, &hints, &res0);
499         if (gai)
500                 return -1;
501         ret = -1;
502         for (res=res0; res; res=res->ai_next)
503                 if (ruserok2_sa(res->ai_addr, res->ai_addrlen,
504                                 superuser, ruser, luser, rhost) == 0){
505                         ret = 0;
506                         break;
507                 }
508         freeaddrinfo(res0);
509         return (ret);
510 }
511 libc_hidden_def (ruserok_af)
512
513 int
514 ruserok(rhost, superuser, ruser, luser)
515         const char *rhost, *ruser, *luser;
516         int superuser;
517 {
518         return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
519 }
520
521 /* Extremely paranoid file open function. */
522 static FILE *
523 iruserfopen (const char *file, uid_t okuser)
524 {
525   struct stat64 st;
526   char *cp = NULL;
527   FILE *res = NULL;
528
529   /* If not a regular file, if owned by someone other than user or
530      root, if writeable by anyone but the owner, or if hardlinked
531      anywhere, quit.  */
532   cp = NULL;
533   if (__lxstat64 (_STAT_VER, file, &st))
534     cp = _("lstat failed");
535   else if (!S_ISREG (st.st_mode))
536     cp = _("not regular file");
537   else
538     {
539       res = fopen (file, "r");
540       if (!res)
541         cp = _("cannot open");
542       else if (__fxstat64 (_STAT_VER, fileno (res), &st) < 0)
543         cp = _("fstat failed");
544       else if (st.st_uid && st.st_uid != okuser)
545         cp = _("bad owner");
546       else if (st.st_mode & (S_IWGRP|S_IWOTH))
547         cp = _("writeable by other than owner");
548       else if (st.st_nlink > 1)
549         cp = _("hard linked somewhere");
550     }
551
552   /* If there were any problems, quit.  */
553   if (cp != NULL)
554     {
555       __rcmd_errstr = cp;
556       if (res)
557         fclose (res);
558       return NULL;
559     }
560
561   /* No threads use this stream.  */
562   __fsetlocking (res, FSETLOCKING_BYCALLER);
563
564   return res;
565 }
566
567 /*
568  * New .rhosts strategy: We are passed an ip address. We spin through
569  * hosts.equiv and .rhosts looking for a match. When the .rhosts only
570  * has ip addresses, we don't have to trust a nameserver.  When it
571  * contains hostnames, we spin through the list of addresses the nameserver
572  * gives us and look for a match.
573  *
574  * Returns 0 if ok, -1 if not ok.
575  */
576 static int
577 ruserok2_sa (ra, ralen, superuser, ruser, luser, rhost)
578      struct sockaddr *ra;
579      size_t ralen;
580      int superuser;
581      const char *ruser, *luser, *rhost;
582 {
583   FILE *hostf = NULL;
584   int isbad = -1;
585
586   if (!superuser)
587     hostf = iruserfopen (_PATH_HEQUIV, 0);
588
589   if (hostf)
590     {
591       isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
592       fclose (hostf);
593
594       if (!isbad)
595         return 0;
596     }
597
598   if (__check_rhosts_file || superuser)
599     {
600       char *pbuf;
601       struct passwd pwdbuf, *pwd;
602       size_t dirlen;
603       size_t buflen = __sysconf (_SC_GETPW_R_SIZE_MAX);
604       char *buffer = __alloca (buflen);
605       uid_t uid;
606
607       if (__getpwnam_r (luser, &pwdbuf, buffer, buflen, &pwd) != 0
608           || pwd == NULL)
609         return -1;
610
611       dirlen = strlen (pwd->pw_dir);
612       pbuf = alloca (dirlen + sizeof "/.rhosts");
613       __mempcpy (__mempcpy (pbuf, pwd->pw_dir, dirlen),
614                  "/.rhosts", sizeof "/.rhosts");
615
616        /* Change effective uid while reading .rhosts.  If root and
617           reading an NFS mounted file system, can't read files that
618           are protected read/write owner only.  */
619        uid = __geteuid ();
620        seteuid (pwd->pw_uid);
621        hostf = iruserfopen (pbuf, pwd->pw_uid);
622
623        if (hostf != NULL)
624          {
625            isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
626            fclose (hostf);
627          }
628
629        seteuid (uid);
630        return isbad;
631     }
632   return -1;
633 }
634 /*
635  * ruserok_sa() is now discussed on ipng, so
636  * currently disabled for external use
637  */
638 static int ruserok_sa(ra, ralen, superuser, ruser, luser)
639      struct sockaddr *ra;
640      size_t ralen;
641      int superuser;
642      const char *ruser, *luser;
643 {
644   return ruserok2_sa(ra, ralen, superuser, ruser, luser, "-");
645 }
646
647 /* This is the exported version.  */
648 int
649 iruserok_af (raddr, superuser, ruser, luser, af)
650      const void *raddr;
651      int superuser;
652      const char *ruser, *luser;
653      sa_family_t af;
654 {
655   struct sockaddr_storage ra;
656   size_t ralen;
657
658   memset (&ra, '\0', sizeof(ra));
659   switch (af){
660   case AF_INET:
661     ((struct sockaddr_in *)&ra)->sin_family = AF_INET;
662     memcpy (&(((struct sockaddr_in *)&ra)->sin_addr), raddr,
663             sizeof(struct in_addr));
664     ralen = sizeof(struct sockaddr_in);
665     break;
666   case AF_INET6:
667     ((struct sockaddr_in6 *)&ra)->sin6_family = AF_INET6;
668     memcpy (&(((struct sockaddr_in6 *)&ra)->sin6_addr), raddr,
669             sizeof(struct in6_addr));
670     ralen = sizeof(struct sockaddr_in6);
671     break;
672   default:
673     return 0;
674   }
675   return ruserok_sa ((struct sockaddr *)&ra, ralen, superuser, ruser, luser);
676 }
677 libc_hidden_def (iruserok_af)
678
679 int
680 iruserok (raddr, superuser, ruser, luser)
681      u_int32_t raddr;
682      int superuser;
683      const char *ruser, *luser;
684 {
685   return iruserok_af (&raddr, superuser, ruser, luser, AF_INET);
686 }
687
688 /*
689  * XXX
690  * Don't make static, used by lpd(8).
691  *
692  * This function is not used anymore. It is only present because lpd(8)
693  * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
694  * argument. This means that netgroups won't work in .rhost/hosts.equiv
695  * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
696  * or PAM.
697  * Returns 0 if ok, -1 if not ok.
698  */
699 int
700 __ivaliduser(hostf, raddr, luser, ruser)
701         FILE *hostf;
702         u_int32_t raddr;
703         const char *luser, *ruser;
704 {
705         struct sockaddr_in ra;
706         memset(&ra, '\0', sizeof(ra));
707         ra.sin_family = AF_INET;
708         ra.sin_addr.s_addr = raddr;
709         return __validuser2_sa(hostf, (struct sockaddr *)&ra, sizeof(ra),
710                                luser, ruser, "-");
711 }
712
713
714 /* Returns 1 on positive match, 0 on no match, -1 on negative match.  */
715 static int
716 internal_function
717 __checkhost_sa (struct sockaddr *ra, size_t ralen, char *lhost,
718                 const char *rhost)
719 {
720         struct addrinfo hints, *res0, *res;
721         char raddr[INET6_ADDRSTRLEN];
722         int match;
723         int negate=1;    /* Multiply return with this to get -1 instead of 1 */
724
725         /* Check nis netgroup.  */
726         if (strncmp ("+@", lhost, 2) == 0)
727                 return innetgr (&lhost[2], rhost, NULL, NULL);
728
729         if (strncmp ("-@", lhost, 2) == 0)
730                 return -innetgr (&lhost[2], rhost, NULL, NULL);
731
732         /* -host */
733         if (strncmp ("-", lhost,1) == 0) {
734                 negate = -1;
735                 lhost++;
736         } else if (strcmp ("+",lhost) == 0) {
737                 return 1;                    /* asking for trouble, but ok.. */
738         }
739
740         /* Try for raw ip address first. */
741         /* XXX */
742         if (getnameinfo(ra, ralen,
743                         raddr, sizeof(raddr), NULL, 0,
744                         NI_NUMERICHOST) == 0
745             && strcmp(raddr, lhost) == 0)
746                 return negate;
747
748         /* Better be a hostname. */
749         match = 0;
750         memset(&hints, '\0', sizeof(hints));
751         hints.ai_family = ra->sa_family;
752         if (getaddrinfo(lhost, NULL, &hints, &res0) == 0){
753                 /* Spin through ip addresses. */
754                 for (res = res0; res; res = res->ai_next)
755                   {
756                     if (res->ai_family == ra->sa_family
757                         && !memcmp(res->ai_addr, ra, res->ai_addrlen))
758                       {
759                         match = 1;
760                         break;
761                       }
762                   }
763                 freeaddrinfo (res0);
764         }
765         return negate * match;
766 }
767
768 /* Returns 1 on positive match, 0 on no match, -1 on negative match.  */
769 static int
770 internal_function
771 __icheckuser (const char *luser, const char *ruser)
772 {
773     /*
774       luser is user entry from .rhosts/hosts.equiv file
775       ruser is user id on remote host
776       */
777
778     /* [-+]@netgroup */
779     if (strncmp ("+@", luser, 2) == 0)
780         return innetgr (&luser[2], NULL, ruser, NULL);
781
782     if (strncmp ("-@", luser,2) == 0)
783         return -innetgr (&luser[2], NULL, ruser, NULL);
784
785     /* -user */
786     if (strncmp ("-", luser, 1) == 0)
787         return -(strcmp (&luser[1], ruser) == 0);
788
789     /* + */
790     if (strcmp ("+", luser) == 0)
791         return 1;
792
793     /* simple string match */
794     return strcmp (ruser, luser) == 0;
795 }
796
797 /*
798  * Returns 1 for blank lines (or only comment lines) and 0 otherwise
799  */
800 static int
801 __isempty (char *p)
802 {
803     while (*p && isspace (*p)) {
804         ++p;
805     }
806
807     return (*p == '\0' || *p == '#') ? 1 : 0 ;
808 }
809
810 /*
811  * Returns 0 if positive match, -1 if _not_ ok.
812  */
813 static int
814 __validuser2_sa(hostf, ra, ralen, luser, ruser, rhost)
815         FILE *hostf;
816         struct sockaddr *ra;
817         size_t ralen;
818         const char *luser, *ruser, *rhost;
819 {
820     register const char *user;
821     register char *p;
822     int hcheck, ucheck;
823     char *buf = NULL;
824     size_t bufsize = 0;
825     int retval = -1;
826
827     while (__getline (&buf, &bufsize, hostf) > 0) {
828         buf[bufsize - 1] = '\0'; /* Make sure it's terminated.  */
829         p = buf;
830
831         /* Skip empty or comment lines */
832         if (__isempty (p)) {
833             continue;
834         }
835
836         /* Skip lines that are too long. */
837         if (strchr (p, '\n') == NULL) {
838             int ch = getc_unlocked (hostf);
839
840             while (ch != '\n' && ch != EOF)
841                 ch = getc_unlocked (hostf);
842             continue;
843         }
844
845         for (;*p && !isspace(*p); ++p) {
846             *p = _tolower (*p);
847         }
848
849         /* Next we want to find the permitted name for the remote user.  */
850         if (*p == ' ' || *p == '\t') {
851             /* <nul> terminate hostname and skip spaces */
852             for (*p++='\0'; *p && isspace (*p); ++p);
853
854             user = p;                   /* this is the user's name */
855             while (*p && !isspace (*p))
856                 ++p;                    /* find end of user's name */
857         } else
858             user = p;
859
860         *p = '\0';              /* <nul> terminate username (+host?) */
861
862         /* buf -> host(?) ; user -> username(?) */
863
864         /* First check host part */
865         hcheck = __checkhost_sa (ra, ralen, buf, rhost);
866
867         if (hcheck < 0)
868             break;
869
870         if (hcheck) {
871             /* Then check user part */
872             if (! (*user))
873                 user = luser;
874
875             ucheck = __icheckuser (user, ruser);
876
877             /* Positive 'host user' match? */
878             if (ucheck > 0) {
879                 retval = 0;
880                 break;
881             }
882
883             /* Negative 'host -user' match? */
884             if (ucheck < 0)
885                 break;
886
887             /* Neither, go on looking for match */
888         }
889     }
890
891     if (buf != NULL)
892       free (buf);
893
894     return retval;
895 }