2004-09-12 Roland McGrath <roland@frob.com>
[glibc.git] / inet / rcmd.c
blob0bcb731a164deecd9ac9b7cd7d6bb80dfa8b0d5f
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 struct sockaddr_storage from;
116 struct pollfd pfd[2];
117 int32_t oldmask;
118 pid_t pid;
119 int s, lport, timo, error;
120 char c;
121 int refused;
122 char num[8];
123 ssize_t n;
125 if (af != AF_INET && af != AF_INET6 && af != AF_UNSPEC)
127 __set_errno (EAFNOSUPPORT);
128 return -1;
131 pid = __getpid();
133 memset(&hints, '\0', sizeof(hints));
134 hints.ai_flags = AI_CANONNAME;
135 hints.ai_family = af;
136 hints.ai_socktype = SOCK_STREAM;
137 (void)__snprintf(num, sizeof(num), "%d", ntohs(rport));
138 error = getaddrinfo(*ahost, num, &hints, &res);
139 if (error) {
140 if (error == EAI_NONAME && *ahost != NULL) {
141 if (_IO_fwide (stderr, 0) > 0)
142 __fwprintf(stderr, L"%s: Unknown host\n",
143 *ahost);
144 else
145 fprintf(stderr, "%s: Unknown host\n", *ahost);
146 } else {
147 if (_IO_fwide (stderr, 0) > 0)
148 __fwprintf(stderr, L"rcmd: getaddrinfo: %s\n",
149 gai_strerror(error));
150 else
151 fprintf(stderr, "rcmd: getaddrinfo: %s\n",
152 gai_strerror(error));
154 return (-1);
157 pfd[0].events = POLLIN;
158 pfd[1].events = POLLIN;
160 if (res->ai_canonname){
161 free (ahostbuf);
162 ahostbuf = strdup (res->ai_canonname);
163 if (ahostbuf == NULL) {
164 if (_IO_fwide (stderr, 0) > 0)
165 __fwprintf(stderr, L"%s",
166 _("rcmd: Cannot allocate memory\n"));
167 else
168 fputs(_("rcmd: Cannot allocate memory\n"),
169 stderr);
170 return (-1);
172 *ahost = ahostbuf;
173 } else
174 *ahost = NULL;
175 ai = res;
176 refused = 0;
177 oldmask = __sigblock(sigmask(SIGURG));
178 for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
179 char errbuf[200];
181 s = rresvport_af(&lport, ai->ai_family);
182 if (s < 0) {
183 if (errno == EAGAIN) {
184 if (_IO_fwide (stderr, 0) > 0)
185 __fwprintf(stderr, L"%s",
186 _("rcmd: socket: All ports in use\n"));
187 else
188 fputs(_("rcmd: socket: All ports in use\n"),
189 stderr);
190 } else {
191 if (_IO_fwide (stderr, 0) > 0)
192 __fwprintf(stderr,
193 L"rcmd: socket: %m\n");
194 else
195 fprintf(stderr, "rcmd: socket: %m\n");
197 __sigsetmask(oldmask);
198 freeaddrinfo(res);
199 return -1;
201 __fcntl(s, F_SETOWN, pid);
202 if (__connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
203 break;
204 (void)__close(s);
205 if (errno == EADDRINUSE) {
206 lport--;
207 continue;
209 if (errno == ECONNREFUSED)
210 refused = 1;
211 if (ai->ai_next != NULL) {
212 int oerrno = errno;
213 char *buf = NULL;
215 getnameinfo(ai->ai_addr, ai->ai_addrlen,
216 paddr, sizeof(paddr),
217 NULL, 0,
218 NI_NUMERICHOST);
220 if (__asprintf (&buf, _("connect to address %s: "),
221 paddr) >= 0)
223 if (_IO_fwide (stderr, 0) > 0)
224 __fwprintf(stderr, L"%s", buf);
225 else
226 fputs (buf, stderr);
227 free (buf);
229 __set_errno (oerrno);
230 perror(0);
231 ai = ai->ai_next;
232 getnameinfo(ai->ai_addr, ai->ai_addrlen,
233 paddr, sizeof(paddr),
234 NULL, 0,
235 NI_NUMERICHOST);
236 if (__asprintf (&buf, _("Trying %s...\n"), paddr) >= 0)
238 if (_IO_fwide (stderr, 0) > 0)
239 __fwprintf (stderr, L"%s", buf);
240 else
241 fputs (buf, stderr);
242 free (buf);
244 continue;
246 if (refused && timo <= 16) {
247 (void)__sleep(timo);
248 timo *= 2;
249 ai = res;
250 refused = 0;
251 continue;
253 freeaddrinfo(res);
254 if (_IO_fwide (stderr, 0) > 0)
255 (void)__fwprintf(stderr, L"%s: %s\n", *ahost,
256 __strerror_r(errno,
257 errbuf, sizeof (errbuf)));
258 else
259 (void)fprintf(stderr, "%s: %s\n", *ahost,
260 __strerror_r(errno,
261 errbuf, sizeof (errbuf)));
262 __sigsetmask(oldmask);
263 return -1;
265 lport--;
266 if (fd2p == 0) {
267 __write(s, "", 1);
268 lport = 0;
269 } else {
270 char num[8];
271 int s2 = rresvport_af(&lport, ai->ai_family), s3;
272 socklen_t len = ai->ai_addrlen;
274 if (s2 < 0)
275 goto bad;
276 __listen(s2, 1);
277 (void)__snprintf(num, sizeof(num), "%d", lport);
278 if (__write(s, num, strlen(num)+1) != (ssize_t)strlen(num)+1) {
279 char *buf = NULL;
281 if (__asprintf (&buf, _("\
282 rcmd: write (setting up stderr): %m\n")) >= 0)
284 if (_IO_fwide (stderr, 0) > 0)
285 __fwprintf(stderr, L"%s", buf);
286 else
287 fputs (buf, stderr);
288 free (buf);
290 (void)__close(s2);
291 goto bad;
293 pfd[0].fd = s;
294 pfd[1].fd = s2;
295 __set_errno (0);
296 if (__poll (pfd, 2, -1) < 1 || (pfd[1].revents & POLLIN) == 0){
297 char *buf = NULL;
299 if ((errno != 0
300 && __asprintf(&buf, _("\
301 rcmd: poll (setting up stderr): %m\n")) >= 0)
302 || (errno == 0
303 && __asprintf(&buf, _("\
304 poll: protocol failure in circuit setup\n")) >= 0))
306 if (_IO_fwide (stderr, 0) > 0)
307 __fwprintf (stderr, L"%s", buf);
308 else
309 fputs (buf, stderr);
310 free (buf);
312 (void)__close(s2);
313 goto bad;
315 s3 = TEMP_FAILURE_RETRY (accept(s2, (struct sockaddr *)&from,
316 &len));
317 switch (from.ss_family) {
318 case AF_INET:
319 rport = ntohs(((struct sockaddr_in *)&from)->sin_port);
320 break;
321 case AF_INET6:
322 rport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
323 break;
324 default:
325 rport = 0;
326 break;
328 (void)__close(s2);
329 if (s3 < 0) {
330 if (_IO_fwide (stderr, 0) > 0)
331 (void)__fwprintf(stderr,
332 L"rcmd: accept: %m\n");
333 else
334 (void)fprintf(stderr,
335 "rcmd: accept: %m\n");
336 lport = 0;
337 goto bad;
339 *fd2p = s3;
341 if (rport >= IPPORT_RESERVED || rport < IPPORT_RESERVED / 2){
342 char *buf = NULL;
344 if (__asprintf(&buf, _("\
345 socket: protocol failure in circuit setup\n")) >= 0)
347 if (_IO_fwide (stderr, 0) > 0)
348 __fwprintf (stderr, L"%s", buf);
349 else
350 fputs (buf, stderr);
351 free (buf);
353 goto bad2;
356 struct iovec iov[3] =
358 [0] = { .iov_base = (void *) locuser,
359 .iov_len = strlen (locuser) + 1 },
360 [1] = { .iov_base = (void *) remuser,
361 .iov_len = strlen (remuser) + 1 },
362 [2] = { .iov_base = (void *) cmd,
363 .iov_len = strlen (cmd) + 1 }
365 (void) TEMP_FAILURE_RETRY (__writev (s, iov, 3));
366 n = TEMP_FAILURE_RETRY (__read(s, &c, 1));
367 if (n != 1) {
368 char *buf = NULL;
370 if ((n == 0
371 && __asprintf(&buf, _("rcmd: %s: short read"),
372 *ahost) >= 0)
373 || (n != 0
374 && __asprintf(&buf, "rcmd: %s: %m\n", *ahost) >= 0))
376 if (_IO_fwide (stderr, 0) > 0)
377 __fwprintf (stderr, L"%s", buf);
378 else
379 fputs (buf, stderr);
380 free (buf);
382 goto bad2;
384 if (c != 0) {
385 while (__read(s, &c, 1) == 1) {
386 (void)__write(STDERR_FILENO, &c, 1);
387 if (c == '\n')
388 break;
390 goto bad2;
392 __sigsetmask(oldmask);
393 freeaddrinfo(res);
394 return s;
395 bad2:
396 if (lport)
397 (void)__close(*fd2p);
398 bad:
399 (void)__close(s);
400 __sigsetmask(oldmask);
401 freeaddrinfo(res);
402 return -1;
404 libc_hidden_def (rcmd_af)
407 rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
408 char **ahost;
409 u_short rport;
410 const char *locuser, *remuser, *cmd;
411 int *fd2p;
413 return rcmd_af (ahost, rport, locuser, remuser, cmd, fd2p, AF_INET);
417 rresvport_af(alport, family)
418 int *alport;
419 sa_family_t family;
421 struct sockaddr_storage ss;
422 int s;
423 size_t len;
424 uint16_t *sport;
426 switch(family){
427 case AF_INET:
428 len = sizeof(struct sockaddr_in);
429 sport = &((struct sockaddr_in *)&ss)->sin_port;
430 break;
431 case AF_INET6:
432 len = sizeof(struct sockaddr_in6);
433 sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
434 break;
435 default:
436 __set_errno (EAFNOSUPPORT);
437 return -1;
439 s = __socket(family, SOCK_STREAM, 0);
440 if (s < 0)
441 return -1;
443 memset (&ss, '\0', sizeof(ss));
444 #ifdef SALEN
445 ss.__ss_len = len;
446 #endif
447 ss.ss_family = family;
449 /* Ignore invalid values. */
450 if (*alport < IPPORT_RESERVED / 2)
451 *alport = IPPORT_RESERVED / 2;
452 else if (*alport >= IPPORT_RESERVED)
453 *alport = IPPORT_RESERVED - 1;
455 int start = *alport;
456 do {
457 *sport = htons((uint16_t) *alport);
458 if (__bind(s, (struct sockaddr *)&ss, len) >= 0)
459 return s;
460 if (errno != EADDRINUSE) {
461 (void)__close(s);
462 return -1;
464 if ((*alport)-- == IPPORT_RESERVED/2)
465 *alport = IPPORT_RESERVED - 1;
466 } while (*alport != start);
467 (void)__close(s);
468 __set_errno (EAGAIN);
469 return -1;
471 libc_hidden_def (rresvport_af)
474 rresvport(alport)
475 int *alport;
477 return rresvport_af(alport, AF_INET);
480 int __check_rhosts_file = 1;
481 char *__rcmd_errstr;
484 ruserok_af(rhost, superuser, ruser, luser, af)
485 const char *rhost, *ruser, *luser;
486 int superuser;
487 sa_family_t af;
489 struct addrinfo hints, *res, *res0;
490 int gai;
491 int ret;
493 memset (&hints, '\0', sizeof(hints));
494 hints.ai_family = af;
495 gai = getaddrinfo(rhost, NULL, &hints, &res0);
496 if (gai)
497 return -1;
498 ret = -1;
499 for (res=res0; res; res=res->ai_next)
500 if (ruserok2_sa(res->ai_addr, res->ai_addrlen,
501 superuser, ruser, luser, rhost) == 0){
502 ret = 0;
503 break;
505 freeaddrinfo(res0);
506 return (ret);
508 libc_hidden_def (ruserok_af)
511 ruserok(rhost, superuser, ruser, luser)
512 const char *rhost, *ruser, *luser;
513 int superuser;
515 return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
518 /* Extremely paranoid file open function. */
519 static FILE *
520 iruserfopen (const char *file, uid_t okuser)
522 struct stat64 st;
523 char *cp = NULL;
524 FILE *res = NULL;
526 /* If not a regular file, if owned by someone other than user or
527 root, if writeable by anyone but the owner, or if hardlinked
528 anywhere, quit. */
529 cp = NULL;
530 if (__lxstat64 (_STAT_VER, file, &st))
531 cp = _("lstat failed");
532 else if (!S_ISREG (st.st_mode))
533 cp = _("not regular file");
534 else
536 res = fopen (file, "rc");
537 if (!res)
538 cp = _("cannot open");
539 else if (__fxstat64 (_STAT_VER, fileno (res), &st) < 0)
540 cp = _("fstat failed");
541 else if (st.st_uid && st.st_uid != okuser)
542 cp = _("bad owner");
543 else if (st.st_mode & (S_IWGRP|S_IWOTH))
544 cp = _("writeable by other than owner");
545 else if (st.st_nlink > 1)
546 cp = _("hard linked somewhere");
549 /* If there were any problems, quit. */
550 if (cp != NULL)
552 __rcmd_errstr = cp;
553 if (res)
554 fclose (res);
555 return NULL;
558 /* No threads use this stream. */
559 __fsetlocking (res, FSETLOCKING_BYCALLER);
561 return res;
565 * New .rhosts strategy: We are passed an ip address. We spin through
566 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
567 * has ip addresses, we don't have to trust a nameserver. When it
568 * contains hostnames, we spin through the list of addresses the nameserver
569 * gives us and look for a match.
571 * Returns 0 if ok, -1 if not ok.
573 static int
574 ruserok2_sa (ra, ralen, superuser, ruser, luser, rhost)
575 struct sockaddr *ra;
576 size_t ralen;
577 int superuser;
578 const char *ruser, *luser, *rhost;
580 FILE *hostf = NULL;
581 int isbad = -1;
583 if (!superuser)
584 hostf = iruserfopen (_PATH_HEQUIV, 0);
586 if (hostf)
588 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
589 fclose (hostf);
591 if (!isbad)
592 return 0;
595 if (__check_rhosts_file || superuser)
597 char *pbuf;
598 struct passwd pwdbuf, *pwd;
599 size_t dirlen;
600 size_t buflen = __sysconf (_SC_GETPW_R_SIZE_MAX);
601 char *buffer = __alloca (buflen);
602 uid_t uid;
604 if (__getpwnam_r (luser, &pwdbuf, buffer, buflen, &pwd) != 0
605 || pwd == NULL)
606 return -1;
608 dirlen = strlen (pwd->pw_dir);
609 pbuf = alloca (dirlen + sizeof "/.rhosts");
610 __mempcpy (__mempcpy (pbuf, pwd->pw_dir, dirlen),
611 "/.rhosts", sizeof "/.rhosts");
613 /* Change effective uid while reading .rhosts. If root and
614 reading an NFS mounted file system, can't read files that
615 are protected read/write owner only. */
616 uid = __geteuid ();
617 seteuid (pwd->pw_uid);
618 hostf = iruserfopen (pbuf, pwd->pw_uid);
620 if (hostf != NULL)
622 isbad = __validuser2_sa (hostf, ra, ralen, luser, ruser, rhost);
623 fclose (hostf);
626 seteuid (uid);
627 return isbad;
629 return -1;
632 * ruserok_sa() is now discussed on ipng, so
633 * currently disabled for external use
635 static int ruserok_sa(ra, ralen, superuser, ruser, luser)
636 struct sockaddr *ra;
637 size_t ralen;
638 int superuser;
639 const char *ruser, *luser;
641 return ruserok2_sa(ra, ralen, superuser, ruser, luser, "-");
644 /* This is the exported version. */
646 iruserok_af (raddr, superuser, ruser, luser, af)
647 const void *raddr;
648 int superuser;
649 const char *ruser, *luser;
650 sa_family_t af;
652 struct sockaddr_storage ra;
653 size_t ralen;
655 memset (&ra, '\0', sizeof(ra));
656 switch (af){
657 case AF_INET:
658 ((struct sockaddr_in *)&ra)->sin_family = AF_INET;
659 memcpy (&(((struct sockaddr_in *)&ra)->sin_addr), raddr,
660 sizeof(struct in_addr));
661 ralen = sizeof(struct sockaddr_in);
662 break;
663 case AF_INET6:
664 ((struct sockaddr_in6 *)&ra)->sin6_family = AF_INET6;
665 memcpy (&(((struct sockaddr_in6 *)&ra)->sin6_addr), raddr,
666 sizeof(struct in6_addr));
667 ralen = sizeof(struct sockaddr_in6);
668 break;
669 default:
670 return 0;
672 return ruserok_sa ((struct sockaddr *)&ra, ralen, superuser, ruser, luser);
674 libc_hidden_def (iruserok_af)
677 iruserok (raddr, superuser, ruser, luser)
678 u_int32_t raddr;
679 int superuser;
680 const char *ruser, *luser;
682 return iruserok_af (&raddr, superuser, ruser, luser, AF_INET);
686 * XXX
687 * Don't make static, used by lpd(8).
689 * This function is not used anymore. It is only present because lpd(8)
690 * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
691 * argument. This means that netgroups won't work in .rhost/hosts.equiv
692 * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
693 * or PAM.
694 * Returns 0 if ok, -1 if not ok.
697 __ivaliduser(hostf, raddr, luser, ruser)
698 FILE *hostf;
699 u_int32_t raddr;
700 const char *luser, *ruser;
702 struct sockaddr_in ra;
703 memset(&ra, '\0', sizeof(ra));
704 ra.sin_family = AF_INET;
705 ra.sin_addr.s_addr = raddr;
706 return __validuser2_sa(hostf, (struct sockaddr *)&ra, sizeof(ra),
707 luser, ruser, "-");
711 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
712 static int
713 internal_function
714 __checkhost_sa (struct sockaddr *ra, size_t ralen, char *lhost,
715 const char *rhost)
717 struct addrinfo hints, *res0, *res;
718 char raddr[INET6_ADDRSTRLEN];
719 int match;
720 int negate=1; /* Multiply return with this to get -1 instead of 1 */
722 /* Check nis netgroup. */
723 if (strncmp ("+@", lhost, 2) == 0)
724 return innetgr (&lhost[2], rhost, NULL, NULL);
726 if (strncmp ("-@", lhost, 2) == 0)
727 return -innetgr (&lhost[2], rhost, NULL, NULL);
729 /* -host */
730 if (strncmp ("-", lhost,1) == 0) {
731 negate = -1;
732 lhost++;
733 } else if (strcmp ("+",lhost) == 0) {
734 return 1; /* asking for trouble, but ok.. */
737 /* Try for raw ip address first. */
738 /* XXX */
739 if (getnameinfo(ra, ralen,
740 raddr, sizeof(raddr), NULL, 0,
741 NI_NUMERICHOST) == 0
742 && strcmp(raddr, lhost) == 0)
743 return negate;
745 /* Better be a hostname. */
746 match = 0;
747 memset(&hints, '\0', sizeof(hints));
748 hints.ai_family = ra->sa_family;
749 if (getaddrinfo(lhost, NULL, &hints, &res0) == 0){
750 /* Spin through ip addresses. */
751 for (res = res0; res; res = res->ai_next)
753 if (res->ai_family == ra->sa_family
754 && !memcmp(res->ai_addr, ra, res->ai_addrlen))
756 match = 1;
757 break;
760 freeaddrinfo (res0);
762 return negate * match;
765 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
766 static int
767 internal_function
768 __icheckuser (const char *luser, const char *ruser)
771 luser is user entry from .rhosts/hosts.equiv file
772 ruser is user id on remote host
775 /* [-+]@netgroup */
776 if (strncmp ("+@", luser, 2) == 0)
777 return innetgr (&luser[2], NULL, ruser, NULL);
779 if (strncmp ("-@", luser,2) == 0)
780 return -innetgr (&luser[2], NULL, ruser, NULL);
782 /* -user */
783 if (strncmp ("-", luser, 1) == 0)
784 return -(strcmp (&luser[1], ruser) == 0);
786 /* + */
787 if (strcmp ("+", luser) == 0)
788 return 1;
790 /* simple string match */
791 return strcmp (ruser, luser) == 0;
795 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
797 static int
798 __isempty (char *p)
800 while (*p && isspace (*p)) {
801 ++p;
804 return (*p == '\0' || *p == '#') ? 1 : 0 ;
808 * Returns 0 if positive match, -1 if _not_ ok.
810 static int
811 __validuser2_sa(hostf, ra, ralen, luser, ruser, rhost)
812 FILE *hostf;
813 struct sockaddr *ra;
814 size_t ralen;
815 const char *luser, *ruser, *rhost;
817 register const char *user;
818 register char *p;
819 int hcheck, ucheck;
820 char *buf = NULL;
821 size_t bufsize = 0;
822 int retval = -1;
824 while (__getline (&buf, &bufsize, hostf) > 0) {
825 buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */
826 p = buf;
828 /* Skip empty or comment lines */
829 if (__isempty (p)) {
830 continue;
833 for (;*p && !isspace(*p); ++p) {
834 *p = _tolower (*p);
837 /* Next we want to find the permitted name for the remote user. */
838 if (*p == ' ' || *p == '\t') {
839 /* <nul> terminate hostname and skip spaces */
840 for (*p++='\0'; *p && isspace (*p); ++p);
842 user = p; /* this is the user's name */
843 while (*p && !isspace (*p))
844 ++p; /* find end of user's name */
845 } else
846 user = p;
848 *p = '\0'; /* <nul> terminate username (+host?) */
850 /* buf -> host(?) ; user -> username(?) */
852 /* First check host part */
853 hcheck = __checkhost_sa (ra, ralen, buf, rhost);
855 if (hcheck < 0)
856 break;
858 if (hcheck) {
859 /* Then check user part */
860 if (! (*user))
861 user = luser;
863 ucheck = __icheckuser (user, ruser);
865 /* Positive 'host user' match? */
866 if (ucheck > 0) {
867 retval = 0;
868 break;
871 /* Negative 'host -user' match? */
872 if (ucheck < 0)
873 break;
875 /* Neither, go on looking for match */
879 if (buf != NULL)
880 free (buf);
882 return retval;