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