Fix fmtmsg addseverity namespace (bug 18539).
[glibc.git] / inet / rcmd.c
blob98b3735d1fce2a69f42a7dd33daddb39ed42d506
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(?) */
813 /* First check host part */
814 hcheck = __checkhost_sa (ra, ralen, buf, rhost);
816 if (hcheck < 0)
817 break;
819 if (hcheck) {
820 /* Then check user part */
821 if (! (*user))
822 user = luser;
824 ucheck = __icheckuser (user, ruser);
826 /* Positive 'host user' match? */
827 if (ucheck > 0) {
828 retval = 0;
829 break;
832 /* Negative 'host -user' match? */
833 if (ucheck < 0)
834 break;
836 /* Neither, go on looking for match */
840 free (buf);
842 return retval;