Convert 703 function definitions to prototype style.
[glibc.git] / inet / rcmd.c
blobd3fe3c57fc3ed1074b4a7e86c4673d1d13ed2af6
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 (int *alport, sa_family_t family)
375 union {
376 struct sockaddr generic;
377 struct sockaddr_in in;
378 struct sockaddr_in6 in6;
379 } ss;
380 int s;
381 size_t len;
382 uint16_t *sport;
384 switch(family){
385 case AF_INET:
386 len = sizeof(struct sockaddr_in);
387 sport = &ss.in.sin_port;
388 break;
389 case AF_INET6:
390 len = sizeof(struct sockaddr_in6);
391 sport = &ss.in6.sin6_port;
392 break;
393 default:
394 __set_errno (EAFNOSUPPORT);
395 return -1;
397 s = __socket(family, SOCK_STREAM, 0);
398 if (s < 0)
399 return -1;
401 memset (&ss, '\0', sizeof(ss));
402 #ifdef SALEN
403 ss.generic.__ss_len = len;
404 #endif
405 ss.generic.sa_family = family;
407 /* Ignore invalid values. */
408 if (*alport < IPPORT_RESERVED / 2)
409 *alport = IPPORT_RESERVED / 2;
410 else if (*alport >= IPPORT_RESERVED)
411 *alport = IPPORT_RESERVED - 1;
413 int start = *alport;
414 do {
415 *sport = htons((uint16_t) *alport);
416 if (__bind(s, &ss.generic, len) >= 0)
417 return s;
418 if (errno != EADDRINUSE) {
419 (void)__close(s);
420 return -1;
422 if ((*alport)-- == IPPORT_RESERVED/2)
423 *alport = IPPORT_RESERVED - 1;
424 } while (*alport != start);
425 (void)__close(s);
426 __set_errno (EAGAIN);
427 return -1;
429 libc_hidden_def (rresvport_af)
432 rresvport (int *alport)
434 return rresvport_af(alport, AF_INET);
437 int __check_rhosts_file = 1;
438 char *__rcmd_errstr;
441 ruserok_af(rhost, superuser, ruser, luser, af)
442 const char *rhost, *ruser, *luser;
443 int superuser;
444 sa_family_t af;
446 struct addrinfo hints, *res, *res0;
447 int gai;
448 int ret;
450 memset (&hints, '\0', sizeof(hints));
451 hints.ai_family = af;
452 gai = getaddrinfo(rhost, NULL, &hints, &res0);
453 if (gai)
454 return -1;
455 ret = -1;
456 for (res=res0; res; res=res->ai_next)
457 if (ruserok2_sa(res->ai_addr, res->ai_addrlen,
458 superuser, ruser, luser, rhost) == 0){
459 ret = 0;
460 break;
462 freeaddrinfo(res0);
463 return (ret);
465 libc_hidden_def (ruserok_af)
468 ruserok(rhost, superuser, ruser, luser)
469 const char *rhost, *ruser, *luser;
470 int superuser;
472 return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
475 /* Extremely paranoid file open function. */
476 static FILE *
477 iruserfopen (const char *file, uid_t okuser)
479 struct stat64 st;
480 char *cp = NULL;
481 FILE *res = NULL;
483 /* If not a regular file, if owned by someone other than user or
484 root, if writeable by anyone but the owner, or if hardlinked
485 anywhere, quit. */
486 if (__lxstat64 (_STAT_VER, file, &st))
487 cp = _("lstat failed");
488 else if (!S_ISREG (st.st_mode))
489 cp = _("not regular file");
490 else
492 res = fopen (file, "rce");
493 if (!res)
494 cp = _("cannot open");
495 else if (__fxstat64 (_STAT_VER, fileno (res), &st) < 0)
496 cp = _("fstat failed");
497 else if (st.st_uid && st.st_uid != okuser)
498 cp = _("bad owner");
499 else if (st.st_mode & (S_IWGRP|S_IWOTH))
500 cp = _("writeable by other than owner");
501 else if (st.st_nlink > 1)
502 cp = _("hard linked somewhere");
505 /* If there were any problems, quit. */
506 if (cp != NULL)
508 __rcmd_errstr = cp;
509 if (res)
510 fclose (res);
511 return NULL;
514 /* No threads use this stream. */
515 __fsetlocking (res, FSETLOCKING_BYCALLER);
517 return res;
521 * New .rhosts strategy: We are passed an ip address. We spin through
522 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
523 * has ip addresses, we don't have to trust a nameserver. When it
524 * contains hostnames, we spin through the list of addresses the nameserver
525 * gives us and look for a match.
527 * Returns 0 if ok, -1 if not ok.
529 static int
530 ruserok2_sa (ra, ralen, superuser, ruser, luser, rhost)
531 struct sockaddr *ra;
532 size_t ralen;
533 int superuser;
534 const char *ruser, *luser, *rhost;
536 FILE *hostf = NULL;
537 int isbad = -1;
539 if (!superuser)
540 hostf = iruserfopen (_PATH_HEQUIV, 0);
542 if (hostf)
544 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
545 fclose (hostf);
547 if (!isbad)
548 return 0;
551 if (__check_rhosts_file || superuser)
553 char *pbuf;
554 struct passwd pwdbuf, *pwd;
555 size_t dirlen;
556 size_t buflen = __sysconf (_SC_GETPW_R_SIZE_MAX);
557 char *buffer = __alloca (buflen);
558 uid_t uid;
560 if (__getpwnam_r (luser, &pwdbuf, buffer, buflen, &pwd) != 0
561 || pwd == NULL)
562 return -1;
564 dirlen = strlen (pwd->pw_dir);
565 pbuf = alloca (dirlen + sizeof "/.rhosts");
566 __mempcpy (__mempcpy (pbuf, pwd->pw_dir, dirlen),
567 "/.rhosts", sizeof "/.rhosts");
569 /* Change effective uid while reading .rhosts. If root and
570 reading an NFS mounted file system, can't read files that
571 are protected read/write owner only. */
572 uid = __geteuid ();
573 seteuid (pwd->pw_uid);
574 hostf = iruserfopen (pbuf, pwd->pw_uid);
576 if (hostf != NULL)
578 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
579 fclose (hostf);
582 seteuid (uid);
583 return isbad;
585 return -1;
588 * ruserok_sa() is now discussed on ipng, so
589 * currently disabled for external use
591 static int ruserok_sa(ra, ralen, superuser, ruser, luser)
592 struct sockaddr *ra;
593 size_t ralen;
594 int superuser;
595 const char *ruser, *luser;
597 return ruserok2_sa(ra, ralen, superuser, ruser, luser, "-");
600 /* This is the exported version. */
602 iruserok_af (raddr, superuser, ruser, luser, af)
603 const void *raddr;
604 int superuser;
605 const char *ruser, *luser;
606 sa_family_t af;
608 union {
609 struct sockaddr generic;
610 struct sockaddr_in in;
611 struct sockaddr_in6 in6;
612 } ra;
613 size_t ralen;
615 memset (&ra, '\0', sizeof(ra));
616 switch (af){
617 case AF_INET:
618 ra.in.sin_family = AF_INET;
619 memcpy (&ra.in.sin_addr, raddr, sizeof(struct in_addr));
620 ralen = sizeof(struct sockaddr_in);
621 break;
622 case AF_INET6:
623 ra.in6.sin6_family = AF_INET6;
624 memcpy (&ra.in6.sin6_addr, raddr, sizeof(struct in6_addr));
625 ralen = sizeof(struct sockaddr_in6);
626 break;
627 default:
628 return 0;
630 return ruserok_sa (&ra.generic, ralen, superuser, ruser, luser);
632 libc_hidden_def (iruserok_af)
635 iruserok (raddr, superuser, ruser, luser)
636 u_int32_t raddr;
637 int superuser;
638 const char *ruser, *luser;
640 return iruserok_af (&raddr, superuser, ruser, luser, AF_INET);
644 * XXX
645 * Don't make static, used by lpd(8).
647 * This function is not used anymore. It is only present because lpd(8)
648 * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
649 * argument. This means that netgroups won't work in .rhost/hosts.equiv
650 * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
651 * or PAM.
652 * Returns 0 if ok, -1 if not ok.
655 __ivaliduser(hostf, raddr, luser, ruser)
656 FILE *hostf;
657 u_int32_t raddr;
658 const char *luser, *ruser;
660 struct sockaddr_in ra;
661 memset(&ra, '\0', sizeof(ra));
662 ra.sin_family = AF_INET;
663 ra.sin_addr.s_addr = raddr;
664 return __validuser2_sa(hostf, (struct sockaddr *)&ra, sizeof(ra),
665 luser, ruser, "-");
669 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
670 static int
671 internal_function
672 __checkhost_sa (struct sockaddr *ra, size_t ralen, char *lhost,
673 const char *rhost)
675 struct addrinfo hints, *res0, *res;
676 char raddr[INET6_ADDRSTRLEN];
677 int match;
678 int negate=1; /* Multiply return with this to get -1 instead of 1 */
680 /* Check nis netgroup. */
681 if (strncmp ("+@", lhost, 2) == 0)
682 return innetgr (&lhost[2], rhost, NULL, NULL);
684 if (strncmp ("-@", lhost, 2) == 0)
685 return -innetgr (&lhost[2], rhost, NULL, NULL);
687 /* -host */
688 if (strncmp ("-", lhost,1) == 0) {
689 negate = -1;
690 lhost++;
691 } else if (strcmp ("+",lhost) == 0) {
692 return 1; /* asking for trouble, but ok.. */
695 /* Try for raw ip address first. */
696 /* XXX */
697 if (getnameinfo(ra, ralen,
698 raddr, sizeof(raddr), NULL, 0,
699 NI_NUMERICHOST) == 0
700 && strcmp(raddr, lhost) == 0)
701 return negate;
703 /* Better be a hostname. */
704 match = 0;
705 memset(&hints, '\0', sizeof(hints));
706 hints.ai_family = ra->sa_family;
707 if (getaddrinfo(lhost, NULL, &hints, &res0) == 0){
708 /* Spin through ip addresses. */
709 for (res = res0; res; res = res->ai_next)
711 if (res->ai_family == ra->sa_family
712 && !memcmp(res->ai_addr, ra, res->ai_addrlen))
714 match = 1;
715 break;
718 freeaddrinfo (res0);
720 return negate * match;
723 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
724 static int
725 internal_function
726 __icheckuser (const char *luser, const char *ruser)
729 luser is user entry from .rhosts/hosts.equiv file
730 ruser is user id on remote host
733 /* [-+]@netgroup */
734 if (strncmp ("+@", luser, 2) == 0)
735 return innetgr (&luser[2], NULL, ruser, NULL);
737 if (strncmp ("-@", luser,2) == 0)
738 return -innetgr (&luser[2], NULL, ruser, NULL);
740 /* -user */
741 if (strncmp ("-", luser, 1) == 0)
742 return -(strcmp (&luser[1], ruser) == 0);
744 /* + */
745 if (strcmp ("+", luser) == 0)
746 return 1;
748 /* simple string match */
749 return strcmp (ruser, luser) == 0;
753 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
755 static int
756 __isempty (char *p)
758 while (*p && isspace (*p)) {
759 ++p;
762 return (*p == '\0' || *p == '#') ? 1 : 0 ;
766 * Returns 0 if positive match, -1 if _not_ ok.
768 static int
769 __validuser2_sa(hostf, ra, ralen, luser, ruser, rhost)
770 FILE *hostf;
771 struct sockaddr *ra;
772 size_t ralen;
773 const char *luser, *ruser, *rhost;
775 const char *user;
776 char *p;
777 int hcheck, ucheck;
778 char *buf = NULL;
779 size_t bufsize = 0;
780 int retval = -1;
782 while (__getline (&buf, &bufsize, hostf) > 0) {
783 buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */
784 p = buf;
786 /* Skip empty or comment lines */
787 if (__isempty (p)) {
788 continue;
791 for (;*p && !isspace(*p); ++p) {
792 *p = _tolower (*p);
795 /* Next we want to find the permitted name for the remote user. */
796 if (*p == ' ' || *p == '\t') {
797 /* <nul> terminate hostname and skip spaces */
798 for (*p++='\0'; *p && isspace (*p); ++p);
800 user = p; /* this is the user's name */
801 while (*p && !isspace (*p))
802 ++p; /* find end of user's name */
803 } else
804 user = p;
806 *p = '\0'; /* <nul> terminate username (+host?) */
808 /* buf -> host(?) ; user -> username(?) */
809 if (*buf == '\0')
810 break;
811 if (*user == '\0')
812 user = luser;
814 /* First check the user part. In a naive implementation we
815 would check the host part first, then the user. However,
816 if we check the user first and reject the entry we will
817 have saved doing any host lookups to normalize the comparison
818 and that likely saves several DNS queries. Therefore we
819 check the user first. */
820 ucheck = __icheckuser (user, ruser);
822 /* Either we found the user, or we didn't and this is a
823 negative host check. We must do the negative host lookup
824 in order to preserve the semantics of stopping on this line
825 before processing others. */
826 if (ucheck != 0 || *buf == '-') {
828 /* Next check host part. */
829 hcheck = __checkhost_sa (ra, ralen, buf, rhost);
831 /* Negative '-host user(?)' match? */
832 if (hcheck < 0)
833 break;
835 /* Positive 'host user' match? */
836 if (hcheck > 0 && ucheck > 0) {
837 retval = 0;
838 break;
841 /* Negative 'host -user' match? */
842 if (hcheck > 0 && ucheck < 0)
843 break;
845 /* Neither, go on looking for match. */
849 free (buf);
851 return retval;