Cache the host port like we cache the task port
[glibc.git] / inet / rcmd.c
blob035cb0d87d80df2932b23b01ab8abb91bf0624ed
1 /*
2 * Copyright (C) 1998 WIDE Project.
3 * All rights reserved.
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.
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.
30 * Copyright (c) 1983, 1993, 1994
31 * The Regents of the University of California. All rights reserved.
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.
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.
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 */
62 #include <sys/param.h>
63 #include <sys/poll.h>
64 #include <sys/socket.h>
65 #include <sys/stat.h>
67 #include <netinet/in.h>
68 #include <arpa/inet.h>
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 #include <wchar.h>
84 #include <sys/uio.h>
87 int __ivaliduser (FILE *, u_int32_t, const char *, const char *);
88 static int __validuser2_sa (FILE *, struct sockaddr *, size_t,
89 const char *, const char *, const char *);
90 static int ruserok2_sa (struct sockaddr *ra, size_t ralen,
91 int superuser, const char *ruser,
92 const char *luser, const char *rhost);
93 static int ruserok_sa (struct sockaddr *ra, size_t ralen,
94 int superuser, const char *ruser,
95 const char *luser);
96 int iruserok_af (const void *raddr, int superuser, const char *ruser,
97 const char *luser, sa_family_t af);
98 int iruserok (u_int32_t raddr, int superuser, const char *ruser,
99 const char *luser);
101 libc_hidden_proto (iruserok_af)
103 libc_freeres_ptr(static char *ahostbuf);
106 rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, af)
107 char **ahost;
108 u_short rport;
109 const char *locuser, *remuser, *cmd;
110 int *fd2p;
111 sa_family_t af;
113 char paddr[INET6_ADDRSTRLEN];
114 struct addrinfo hints, *res, *ai;
115 union
117 struct sockaddr sa;
118 struct sockaddr_storage ss;
119 struct sockaddr_in sin;
120 struct sockaddr_in6 sin6;
121 } from;
122 struct pollfd pfd[2];
123 int32_t oldmask;
124 pid_t pid;
125 int s, lport, timo, error;
126 char c;
127 int refused;
128 char num[8];
129 ssize_t n;
131 if (af != AF_INET && af != AF_INET6 && af != AF_UNSPEC)
133 __set_errno (EAFNOSUPPORT);
134 return -1;
137 pid = __getpid();
139 memset(&hints, '\0', sizeof(hints));
140 hints.ai_flags = AI_CANONNAME;
141 hints.ai_family = af;
142 hints.ai_socktype = SOCK_STREAM;
143 (void)__snprintf(num, sizeof(num), "%d", ntohs(rport));
144 error = getaddrinfo(*ahost, num, &hints, &res);
145 if (error) {
146 if (error == EAI_NONAME && *ahost != NULL)
147 __fxprintf(NULL, "%s: Unknown host\n", *ahost);
148 else
149 __fxprintf(NULL, "rcmd: getaddrinfo: %s\n",
150 gai_strerror(error));
152 return -1;
155 pfd[0].events = POLLIN;
156 pfd[1].events = POLLIN;
158 if (res->ai_canonname){
159 free (ahostbuf);
160 ahostbuf = strdup (res->ai_canonname);
161 if (ahostbuf == NULL) {
162 __fxprintf(NULL, "%s",
163 _("rcmd: Cannot allocate memory\n"));
164 return -1;
166 *ahost = ahostbuf;
167 } else
168 *ahost = NULL;
169 ai = res;
170 refused = 0;
171 oldmask = __sigblock(sigmask(SIGURG));
172 for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
173 char errbuf[200];
175 s = rresvport_af(&lport, ai->ai_family);
176 if (s < 0) {
177 if (errno == EAGAIN)
178 __fxprintf(NULL, "%s", _("\
179 rcmd: socket: All ports in use\n"));
180 else
181 __fxprintf(NULL, "rcmd: socket: %m\n");
183 __sigsetmask(oldmask);
184 freeaddrinfo(res);
185 return -1;
187 __fcntl(s, F_SETOWN, pid);
188 if (__connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
189 break;
190 (void)__close(s);
191 if (errno == EADDRINUSE) {
192 lport--;
193 continue;
195 if (errno == ECONNREFUSED)
196 refused = 1;
197 if (ai->ai_next != NULL) {
198 int oerrno = errno;
199 char *buf = NULL;
201 getnameinfo(ai->ai_addr, ai->ai_addrlen,
202 paddr, sizeof(paddr),
203 NULL, 0,
204 NI_NUMERICHOST);
206 if (__asprintf (&buf, _("connect to address %s: "),
207 paddr) >= 0)
209 __fxprintf(NULL, "%s", buf);
210 free (buf);
212 __set_errno (oerrno);
213 perror(0);
214 ai = ai->ai_next;
215 getnameinfo(ai->ai_addr, ai->ai_addrlen,
216 paddr, sizeof(paddr),
217 NULL, 0,
218 NI_NUMERICHOST);
219 if (__asprintf (&buf, _("Trying %s...\n"), paddr) >= 0)
221 __fxprintf (NULL, "%s", buf);
222 free (buf);
224 continue;
226 if (refused && timo <= 16) {
227 (void)__sleep(timo);
228 timo *= 2;
229 ai = res;
230 refused = 0;
231 continue;
233 freeaddrinfo(res);
234 (void)__fxprintf(NULL, "%s: %s\n", *ahost,
235 __strerror_r(errno, errbuf, sizeof (errbuf)));
236 __sigsetmask(oldmask);
237 return -1;
239 lport--;
240 if (fd2p == 0) {
241 __write(s, "", 1);
242 lport = 0;
243 } else {
244 char num[8];
245 int s2 = rresvport_af(&lport, ai->ai_family), s3;
246 socklen_t len = ai->ai_addrlen;
248 if (s2 < 0)
249 goto bad;
250 __listen(s2, 1);
251 (void)__snprintf(num, sizeof(num), "%d", lport);
252 if (__write(s, num, strlen(num)+1) != (ssize_t)strlen(num)+1) {
253 char *buf = NULL;
255 if (__asprintf (&buf, _("\
256 rcmd: write (setting up stderr): %m\n")) >= 0)
258 __fxprintf(NULL, "%s", buf);
259 free (buf);
261 (void)__close(s2);
262 goto bad;
264 pfd[0].fd = s;
265 pfd[1].fd = s2;
266 __set_errno (0);
267 if (__poll (pfd, 2, -1) < 1 || (pfd[1].revents & POLLIN) == 0){
268 char *buf = NULL;
270 if ((errno != 0
271 && __asprintf(&buf, _("\
272 rcmd: poll (setting up stderr): %m\n")) >= 0)
273 || (errno == 0
274 && __asprintf(&buf, _("\
275 poll: protocol failure in circuit setup\n")) >= 0))
277 __fxprintf (NULL, "%s", buf);
278 free (buf);
280 (void)__close(s2);
281 goto bad;
283 s3 = TEMP_FAILURE_RETRY (accept(s2, &from.sa, &len));
284 switch (from.sa.sa_family) {
285 case AF_INET:
286 rport = ntohs(from.sin.sin_port);
287 break;
288 case AF_INET6:
289 rport = ntohs(from.sin6.sin6_port);
290 break;
291 default:
292 rport = 0;
293 break;
295 (void)__close(s2);
296 if (s3 < 0) {
297 (void)__fxprintf(NULL, "rcmd: accept: %m\n");
298 lport = 0;
299 goto bad;
301 *fd2p = s3;
303 if (rport >= IPPORT_RESERVED || rport < IPPORT_RESERVED / 2){
304 char *buf = NULL;
306 if (__asprintf(&buf, _("\
307 socket: protocol failure in circuit setup\n")) >= 0)
309 __fxprintf (NULL, "%s", buf);
310 free (buf);
312 goto bad2;
315 struct iovec iov[3] =
317 [0] = { .iov_base = (void *) locuser,
318 .iov_len = strlen (locuser) + 1 },
319 [1] = { .iov_base = (void *) remuser,
320 .iov_len = strlen (remuser) + 1 },
321 [2] = { .iov_base = (void *) cmd,
322 .iov_len = strlen (cmd) + 1 }
324 (void) TEMP_FAILURE_RETRY (__writev (s, iov, 3));
325 n = TEMP_FAILURE_RETRY (__read(s, &c, 1));
326 if (n != 1) {
327 char *buf = NULL;
329 if ((n == 0
330 && __asprintf(&buf, _("rcmd: %s: short read"),
331 *ahost) >= 0)
332 || (n != 0
333 && __asprintf(&buf, "rcmd: %s: %m\n", *ahost) >= 0))
335 __fxprintf (NULL, "%s", buf);
336 free (buf);
338 goto bad2;
340 if (c != 0) {
341 while (__read(s, &c, 1) == 1) {
342 (void)__write(STDERR_FILENO, &c, 1);
343 if (c == '\n')
344 break;
346 goto bad2;
348 __sigsetmask(oldmask);
349 freeaddrinfo(res);
350 return s;
351 bad2:
352 if (lport)
353 (void)__close(*fd2p);
354 bad:
355 (void)__close(s);
356 __sigsetmask(oldmask);
357 freeaddrinfo(res);
358 return -1;
360 libc_hidden_def (rcmd_af)
363 rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
364 char **ahost;
365 u_short rport;
366 const char *locuser, *remuser, *cmd;
367 int *fd2p;
369 return rcmd_af (ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
373 rresvport_af(alport, family)
374 int *alport;
375 sa_family_t family;
377 union {
378 struct sockaddr generic;
379 struct sockaddr_in in;
380 struct sockaddr_in6 in6;
381 } ss;
382 int s;
383 size_t len;
384 uint16_t *sport;
386 switch(family){
387 case AF_INET:
388 len = sizeof(struct sockaddr_in);
389 sport = &ss.in.sin_port;
390 break;
391 case AF_INET6:
392 len = sizeof(struct sockaddr_in6);
393 sport = &ss.in6.sin6_port;
394 break;
395 default:
396 __set_errno (EAFNOSUPPORT);
397 return -1;
399 s = __socket(family, SOCK_STREAM, 0);
400 if (s < 0)
401 return -1;
403 memset (&ss, '\0', sizeof(ss));
404 #ifdef SALEN
405 ss.generic.__ss_len = len;
406 #endif
407 ss.generic.sa_family = family;
409 /* Ignore invalid values. */
410 if (*alport < IPPORT_RESERVED / 2)
411 *alport = IPPORT_RESERVED / 2;
412 else if (*alport >= IPPORT_RESERVED)
413 *alport = IPPORT_RESERVED - 1;
415 int start = *alport;
416 do {
417 *sport = htons((uint16_t) *alport);
418 if (__bind(s, &ss.generic, len) >= 0)
419 return s;
420 if (errno != EADDRINUSE) {
421 (void)__close(s);
422 return -1;
424 if ((*alport)-- == IPPORT_RESERVED/2)
425 *alport = IPPORT_RESERVED - 1;
426 } while (*alport != start);
427 (void)__close(s);
428 __set_errno (EAGAIN);
429 return -1;
431 libc_hidden_def (rresvport_af)
434 rresvport(alport)
435 int *alport;
437 return rresvport_af(alport, AF_INET);
440 int __check_rhosts_file = 1;
441 char *__rcmd_errstr;
444 ruserok_af(rhost, superuser, ruser, luser, af)
445 const char *rhost, *ruser, *luser;
446 int superuser;
447 sa_family_t af;
449 struct addrinfo hints, *res, *res0;
450 int gai;
451 int ret;
453 memset (&hints, '\0', sizeof(hints));
454 hints.ai_family = af;
455 gai = getaddrinfo(rhost, NULL, &hints, &res0);
456 if (gai)
457 return -1;
458 ret = -1;
459 for (res=res0; res; res=res->ai_next)
460 if (ruserok2_sa(res->ai_addr, res->ai_addrlen,
461 superuser, ruser, luser, rhost) == 0){
462 ret = 0;
463 break;
465 freeaddrinfo(res0);
466 return (ret);
468 libc_hidden_def (ruserok_af)
471 ruserok(rhost, superuser, ruser, luser)
472 const char *rhost, *ruser, *luser;
473 int superuser;
475 return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
478 /* Extremely paranoid file open function. */
479 static FILE *
480 iruserfopen (const char *file, uid_t okuser)
482 struct stat64 st;
483 char *cp = NULL;
484 FILE *res = NULL;
486 /* If not a regular file, if owned by someone other than user or
487 root, if writeable by anyone but the owner, or if hardlinked
488 anywhere, quit. */
489 if (__lxstat64 (_STAT_VER, file, &st))
490 cp = _("lstat failed");
491 else if (!S_ISREG (st.st_mode))
492 cp = _("not regular file");
493 else
495 res = fopen (file, "rce");
496 if (!res)
497 cp = _("cannot open");
498 else if (__fxstat64 (_STAT_VER, fileno (res), &st) < 0)
499 cp = _("fstat failed");
500 else if (st.st_uid && st.st_uid != okuser)
501 cp = _("bad owner");
502 else if (st.st_mode & (S_IWGRP|S_IWOTH))
503 cp = _("writeable by other than owner");
504 else if (st.st_nlink > 1)
505 cp = _("hard linked somewhere");
508 /* If there were any problems, quit. */
509 if (cp != NULL)
511 __rcmd_errstr = cp;
512 if (res)
513 fclose (res);
514 return NULL;
517 /* No threads use this stream. */
518 __fsetlocking (res, FSETLOCKING_BYCALLER);
520 return res;
524 * New .rhosts strategy: We are passed an ip address. We spin through
525 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
526 * has ip addresses, we don't have to trust a nameserver. When it
527 * contains hostnames, we spin through the list of addresses the nameserver
528 * gives us and look for a match.
530 * Returns 0 if ok, -1 if not ok.
532 static int
533 ruserok2_sa (ra, ralen, superuser, ruser, luser, rhost)
534 struct sockaddr *ra;
535 size_t ralen;
536 int superuser;
537 const char *ruser, *luser, *rhost;
539 FILE *hostf = NULL;
540 int isbad = -1;
542 if (!superuser)
543 hostf = iruserfopen (_PATH_HEQUIV, 0);
545 if (hostf)
547 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
548 fclose (hostf);
550 if (!isbad)
551 return 0;
554 if (__check_rhosts_file || superuser)
556 char *pbuf;
557 struct passwd pwdbuf, *pwd;
558 size_t dirlen;
559 size_t buflen = __sysconf (_SC_GETPW_R_SIZE_MAX);
560 char *buffer = __alloca (buflen);
561 uid_t uid;
563 if (__getpwnam_r (luser, &pwdbuf, buffer, buflen, &pwd) != 0
564 || pwd == NULL)
565 return -1;
567 dirlen = strlen (pwd->pw_dir);
568 pbuf = alloca (dirlen + sizeof "/.rhosts");
569 __mempcpy (__mempcpy (pbuf, pwd->pw_dir, dirlen),
570 "/.rhosts", sizeof "/.rhosts");
572 /* Change effective uid while reading .rhosts. If root and
573 reading an NFS mounted file system, can't read files that
574 are protected read/write owner only. */
575 uid = __geteuid ();
576 seteuid (pwd->pw_uid);
577 hostf = iruserfopen (pbuf, pwd->pw_uid);
579 if (hostf != NULL)
581 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
582 fclose (hostf);
585 seteuid (uid);
586 return isbad;
588 return -1;
591 * ruserok_sa() is now discussed on ipng, so
592 * currently disabled for external use
594 static int ruserok_sa(ra, ralen, superuser, ruser, luser)
595 struct sockaddr *ra;
596 size_t ralen;
597 int superuser;
598 const char *ruser, *luser;
600 return ruserok2_sa(ra, ralen, superuser, ruser, luser, "-");
603 /* This is the exported version. */
605 iruserok_af (raddr, superuser, ruser, luser, af)
606 const void *raddr;
607 int superuser;
608 const char *ruser, *luser;
609 sa_family_t af;
611 union {
612 struct sockaddr generic;
613 struct sockaddr_in in;
614 struct sockaddr_in6 in6;
615 } ra;
616 size_t ralen;
618 memset (&ra, '\0', sizeof(ra));
619 switch (af){
620 case AF_INET:
621 ra.in.sin_family = AF_INET;
622 memcpy (&ra.in.sin_addr, raddr, sizeof(struct in_addr));
623 ralen = sizeof(struct sockaddr_in);
624 break;
625 case AF_INET6:
626 ra.in6.sin6_family = AF_INET6;
627 memcpy (&ra.in6.sin6_addr, raddr, sizeof(struct in6_addr));
628 ralen = sizeof(struct sockaddr_in6);
629 break;
630 default:
631 return 0;
633 return ruserok_sa (&ra.generic, ralen, superuser, ruser, luser);
635 libc_hidden_def (iruserok_af)
638 iruserok (raddr, superuser, ruser, luser)
639 u_int32_t raddr;
640 int superuser;
641 const char *ruser, *luser;
643 return iruserok_af (&raddr, superuser, ruser, luser, AF_INET);
647 * XXX
648 * Don't make static, used by lpd(8).
650 * This function is not used anymore. It is only present because lpd(8)
651 * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
652 * argument. This means that netgroups won't work in .rhost/hosts.equiv
653 * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
654 * or PAM.
655 * Returns 0 if ok, -1 if not ok.
658 __ivaliduser(hostf, raddr, luser, ruser)
659 FILE *hostf;
660 u_int32_t raddr;
661 const char *luser, *ruser;
663 struct sockaddr_in ra;
664 memset(&ra, '\0', sizeof(ra));
665 ra.sin_family = AF_INET;
666 ra.sin_addr.s_addr = raddr;
667 return __validuser2_sa(hostf, (struct sockaddr *)&ra, sizeof(ra),
668 luser, ruser, "-");
672 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
673 static int
674 internal_function
675 __checkhost_sa (struct sockaddr *ra, size_t ralen, char *lhost,
676 const char *rhost)
678 struct addrinfo hints, *res0, *res;
679 char raddr[INET6_ADDRSTRLEN];
680 int match;
681 int negate=1; /* Multiply return with this to get -1 instead of 1 */
683 /* Check nis netgroup. */
684 if (strncmp ("+@", lhost, 2) == 0)
685 return innetgr (&lhost[2], rhost, NULL, NULL);
687 if (strncmp ("-@", lhost, 2) == 0)
688 return -innetgr (&lhost[2], rhost, NULL, NULL);
690 /* -host */
691 if (strncmp ("-", lhost,1) == 0) {
692 negate = -1;
693 lhost++;
694 } else if (strcmp ("+",lhost) == 0) {
695 return 1; /* asking for trouble, but ok.. */
698 /* Try for raw ip address first. */
699 /* XXX */
700 if (getnameinfo(ra, ralen,
701 raddr, sizeof(raddr), NULL, 0,
702 NI_NUMERICHOST) == 0
703 && strcmp(raddr, lhost) == 0)
704 return negate;
706 /* Better be a hostname. */
707 match = 0;
708 memset(&hints, '\0', sizeof(hints));
709 hints.ai_family = ra->sa_family;
710 if (getaddrinfo(lhost, NULL, &hints, &res0) == 0){
711 /* Spin through ip addresses. */
712 for (res = res0; res; res = res->ai_next)
714 if (res->ai_family == ra->sa_family
715 && !memcmp(res->ai_addr, ra, res->ai_addrlen))
717 match = 1;
718 break;
721 freeaddrinfo (res0);
723 return negate * match;
726 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
727 static int
728 internal_function
729 __icheckuser (const char *luser, const char *ruser)
732 luser is user entry from .rhosts/hosts.equiv file
733 ruser is user id on remote host
736 /* [-+]@netgroup */
737 if (strncmp ("+@", luser, 2) == 0)
738 return innetgr (&luser[2], NULL, ruser, NULL);
740 if (strncmp ("-@", luser,2) == 0)
741 return -innetgr (&luser[2], NULL, ruser, NULL);
743 /* -user */
744 if (strncmp ("-", luser, 1) == 0)
745 return -(strcmp (&luser[1], ruser) == 0);
747 /* + */
748 if (strcmp ("+", luser) == 0)
749 return 1;
751 /* simple string match */
752 return strcmp (ruser, luser) == 0;
756 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
758 static int
759 __isempty (char *p)
761 while (*p && isspace (*p)) {
762 ++p;
765 return (*p == '\0' || *p == '#') ? 1 : 0 ;
769 * Returns 0 if positive match, -1 if _not_ ok.
771 static int
772 __validuser2_sa(hostf, ra, ralen, luser, ruser, rhost)
773 FILE *hostf;
774 struct sockaddr *ra;
775 size_t ralen;
776 const char *luser, *ruser, *rhost;
778 const char *user;
779 char *p;
780 int hcheck, ucheck;
781 char *buf = NULL;
782 size_t bufsize = 0;
783 int retval = -1;
785 while (__getline (&buf, &bufsize, hostf) > 0) {
786 buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */
787 p = buf;
789 /* Skip empty or comment lines */
790 if (__isempty (p)) {
791 continue;
794 for (;*p && !isspace(*p); ++p) {
795 *p = _tolower (*p);
798 /* Next we want to find the permitted name for the remote user. */
799 if (*p == ' ' || *p == '\t') {
800 /* <nul> terminate hostname and skip spaces */
801 for (*p++='\0'; *p && isspace (*p); ++p);
803 user = p; /* this is the user's name */
804 while (*p && !isspace (*p))
805 ++p; /* find end of user's name */
806 } else
807 user = p;
809 *p = '\0'; /* <nul> terminate username (+host?) */
811 /* buf -> host(?) ; user -> username(?) */
812 if (*buf == '\0')
813 break;
814 if (*user == '\0')
815 user = luser;
817 /* First check the user part. In a naive implementation we
818 would check the host part first, then the user. However,
819 if we check the user first and reject the entry we will
820 have saved doing any host lookups to normalize the comparison
821 and that likely saves several DNS queries. Therefore we
822 check the user first. */
823 ucheck = __icheckuser (user, ruser);
825 /* Either we found the user, or we didn't and this is a
826 negative host check. We must do the negative host lookup
827 in order to preserve the semantics of stopping on this line
828 before processing others. */
829 if (ucheck != 0 || *buf == '-') {
831 /* Next check host part. */
832 hcheck = __checkhost_sa (ra, ralen, buf, rhost);
834 /* Negative '-host user(?)' match? */
835 if (hcheck < 0)
836 break;
838 /* Positive 'host user' match? */
839 if (hcheck > 0 && ucheck > 0) {
840 retval = 0;
841 break;
844 /* Negative 'host -user' match? */
845 if (hcheck > 0 && ucheck < 0)
846 break;
848 /* Neither, go on looking for match. */
852 free (buf);
854 return retval;