2 * Copyright (C) 1998 WIDE Project.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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
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
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>
64 #include <sys/socket.h>
67 #include <netinet/in.h>
68 #include <arpa/inet.h>
78 #include <stdio_ext.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
,
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
,
101 libc_hidden_proto (iruserok_af
)
103 libc_freeres_ptr(static char *ahostbuf
);
106 rcmd_af(ahost
, rport
, locuser
, remuser
, cmd
, fd2p
, af
)
109 const char *locuser
, *remuser
, *cmd
;
113 char paddr
[INET6_ADDRSTRLEN
];
114 struct addrinfo hints
, *res
, *ai
;
118 struct sockaddr_storage ss
;
119 struct sockaddr_in sin
;
120 struct sockaddr_in6 sin6
;
122 struct pollfd pfd
[2];
125 int s
, lport
, timo
, error
;
131 if (af
!= AF_INET
&& af
!= AF_INET6
&& af
!= AF_UNSPEC
)
133 __set_errno (EAFNOSUPPORT
);
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
);
146 if (error
== EAI_NONAME
&& *ahost
!= NULL
)
147 __fxprintf(NULL
, "%s: Unknown host\n", *ahost
);
149 __fxprintf(NULL
, "rcmd: getaddrinfo: %s\n",
150 gai_strerror(error
));
155 pfd
[0].events
= POLLIN
;
156 pfd
[1].events
= POLLIN
;
158 if (res
->ai_canonname
){
160 ahostbuf
= strdup (res
->ai_canonname
);
161 if (ahostbuf
== NULL
) {
162 __fxprintf(NULL
, "%s",
163 _("rcmd: Cannot allocate memory\n"));
171 oldmask
= __sigblock(sigmask(SIGURG
));
172 for (timo
= 1, lport
= IPPORT_RESERVED
- 1;;) {
175 s
= rresvport_af(&lport
, ai
->ai_family
);
178 __fxprintf(NULL
, "%s", _("\
179 rcmd: socket: All ports in use\n"));
181 __fxprintf(NULL
, "rcmd: socket: %m\n");
183 __sigsetmask(oldmask
);
187 __fcntl(s
, F_SETOWN
, pid
);
188 if (__connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) >= 0)
191 if (errno
== EADDRINUSE
) {
195 if (errno
== ECONNREFUSED
)
197 if (ai
->ai_next
!= NULL
) {
201 getnameinfo(ai
->ai_addr
, ai
->ai_addrlen
,
202 paddr
, sizeof(paddr
),
206 if (__asprintf (&buf
, _("connect to address %s: "),
209 __fxprintf(NULL
, "%s", buf
);
212 __set_errno (oerrno
);
215 getnameinfo(ai
->ai_addr
, ai
->ai_addrlen
,
216 paddr
, sizeof(paddr
),
219 if (__asprintf (&buf
, _("Trying %s...\n"), paddr
) >= 0)
221 __fxprintf (NULL
, "%s", buf
);
226 if (refused
&& timo
<= 16) {
234 (void)__fxprintf(NULL
, "%s: %s\n", *ahost
,
235 __strerror_r(errno
, errbuf
, sizeof (errbuf
)));
236 __sigsetmask(oldmask
);
245 int s2
= rresvport_af(&lport
, ai
->ai_family
), s3
;
246 socklen_t len
= ai
->ai_addrlen
;
251 (void)__snprintf(num
, sizeof(num
), "%d", lport
);
252 if (__write(s
, num
, strlen(num
)+1) != (ssize_t
)strlen(num
)+1) {
255 if (__asprintf (&buf
, _("\
256 rcmd: write (setting up stderr): %m\n")) >= 0)
258 __fxprintf(NULL
, "%s", buf
);
267 if (__poll (pfd
, 2, -1) < 1 || (pfd
[1].revents
& POLLIN
) == 0){
271 && __asprintf(&buf
, _("\
272 rcmd: poll (setting up stderr): %m\n")) >= 0)
274 && __asprintf(&buf
, _("\
275 poll: protocol failure in circuit setup\n")) >= 0))
277 __fxprintf (NULL
, "%s", buf
);
283 s3
= TEMP_FAILURE_RETRY (accept(s2
, &from
.sa
, &len
));
284 switch (from
.sa
.sa_family
) {
286 rport
= ntohs(from
.sin
.sin_port
);
289 rport
= ntohs(from
.sin6
.sin6_port
);
297 (void)__fxprintf(NULL
, "rcmd: accept: %m\n");
303 if (rport
>= IPPORT_RESERVED
|| rport
< IPPORT_RESERVED
/ 2){
306 if (__asprintf(&buf
, _("\
307 socket: protocol failure in circuit setup\n")) >= 0)
309 __fxprintf (NULL
, "%s", buf
);
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));
330 && __asprintf(&buf
, _("rcmd: %s: short read"),
333 && __asprintf(&buf
, "rcmd: %s: %m\n", *ahost
) >= 0))
335 __fxprintf (NULL
, "%s", buf
);
341 while (__read(s
, &c
, 1) == 1) {
342 (void)__write(STDERR_FILENO
, &c
, 1);
348 __sigsetmask(oldmask
);
353 (void)__close(*fd2p
);
356 __sigsetmask(oldmask
);
360 libc_hidden_def (rcmd_af
)
363 rcmd(ahost
, rport
, locuser
, remuser
, cmd
, fd2p
)
366 const char *locuser
, *remuser
, *cmd
;
369 return rcmd_af (ahost
, rport
, locuser
, remuser
, cmd
, fd2p
, AF_INET
);
373 rresvport_af (int *alport
, sa_family_t family
)
376 struct sockaddr generic
;
377 struct sockaddr_in in
;
378 struct sockaddr_in6 in6
;
386 len
= sizeof(struct sockaddr_in
);
387 sport
= &ss
.in
.sin_port
;
390 len
= sizeof(struct sockaddr_in6
);
391 sport
= &ss
.in6
.sin6_port
;
394 __set_errno (EAFNOSUPPORT
);
397 s
= __socket(family
, SOCK_STREAM
, 0);
401 memset (&ss
, '\0', sizeof(ss
));
403 ss
.generic
.__ss_len
= len
;
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;
415 *sport
= htons((uint16_t) *alport
);
416 if (__bind(s
, &ss
.generic
, len
) >= 0)
418 if (errno
!= EADDRINUSE
) {
422 if ((*alport
)-- == IPPORT_RESERVED
/2)
423 *alport
= IPPORT_RESERVED
- 1;
424 } while (*alport
!= start
);
426 __set_errno (EAGAIN
);
429 libc_hidden_def (rresvport_af
)
432 rresvport (int *alport
)
434 return rresvport_af(alport
, AF_INET
);
437 int __check_rhosts_file
= 1;
441 ruserok_af(rhost
, superuser
, ruser
, luser
, af
)
442 const char *rhost
, *ruser
, *luser
;
446 struct addrinfo hints
, *res
, *res0
;
450 memset (&hints
, '\0', sizeof(hints
));
451 hints
.ai_family
= af
;
452 gai
= getaddrinfo(rhost
, NULL
, &hints
, &res0
);
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){
465 libc_hidden_def (ruserok_af
)
468 ruserok(rhost
, superuser
, ruser
, luser
)
469 const char *rhost
, *ruser
, *luser
;
472 return ruserok_af(rhost
, superuser
, ruser
, luser
, AF_INET
);
475 /* Extremely paranoid file open function. */
477 iruserfopen (const char *file
, uid_t okuser
)
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
486 if (__lxstat64 (_STAT_VER
, file
, &st
))
487 cp
= _("lstat failed");
488 else if (!S_ISREG (st
.st_mode
))
489 cp
= _("not regular file");
492 res
= fopen (file
, "rce");
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
)
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. */
514 /* No threads use this stream. */
515 __fsetlocking (res
, FSETLOCKING_BYCALLER
);
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.
530 ruserok2_sa (ra
, ralen
, superuser
, ruser
, luser
, rhost
)
534 const char *ruser
, *luser
, *rhost
;
540 hostf
= iruserfopen (_PATH_HEQUIV
, 0);
544 isbad
= __validuser2_sa (hostf
, ra
, ralen
, luser
, ruser
, rhost
);
551 if (__check_rhosts_file
|| superuser
)
554 struct passwd pwdbuf
, *pwd
;
556 size_t buflen
= __sysconf (_SC_GETPW_R_SIZE_MAX
);
557 char *buffer
= __alloca (buflen
);
560 if (__getpwnam_r (luser
, &pwdbuf
, buffer
, buflen
, &pwd
) != 0
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. */
573 seteuid (pwd
->pw_uid
);
574 hostf
= iruserfopen (pbuf
, pwd
->pw_uid
);
578 isbad
= __validuser2_sa (hostf
, ra
, ralen
, luser
, ruser
, rhost
);
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
)
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
)
605 const char *ruser
, *luser
;
609 struct sockaddr generic
;
610 struct sockaddr_in in
;
611 struct sockaddr_in6 in6
;
615 memset (&ra
, '\0', sizeof(ra
));
618 ra
.in
.sin_family
= AF_INET
;
619 memcpy (&ra
.in
.sin_addr
, raddr
, sizeof(struct in_addr
));
620 ralen
= sizeof(struct sockaddr_in
);
623 ra
.in6
.sin6_family
= AF_INET6
;
624 memcpy (&ra
.in6
.sin6_addr
, raddr
, sizeof(struct in6_addr
));
625 ralen
= sizeof(struct sockaddr_in6
);
630 return ruserok_sa (&ra
.generic
, ralen
, superuser
, ruser
, luser
);
632 libc_hidden_def (iruserok_af
)
635 iruserok (raddr
, superuser
, ruser
, luser
)
638 const char *ruser
, *luser
;
640 return iruserok_af (&raddr
, superuser
, ruser
, luser
, AF_INET
);
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()
652 * Returns 0 if ok, -1 if not ok.
655 __ivaliduser(hostf
, raddr
, luser
, ruser
)
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
),
669 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
672 __checkhost_sa (struct sockaddr
*ra
, size_t ralen
, char *lhost
,
675 struct addrinfo hints
, *res0
, *res
;
676 char raddr
[INET6_ADDRSTRLEN
];
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
);
688 if (strncmp ("-", lhost
,1) == 0) {
691 } else if (strcmp ("+",lhost
) == 0) {
692 return 1; /* asking for trouble, but ok.. */
695 /* Try for raw ip address first. */
697 if (getnameinfo(ra
, ralen
,
698 raddr
, sizeof(raddr
), NULL
, 0,
700 && strcmp(raddr
, lhost
) == 0)
703 /* Better be a hostname. */
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
))
720 return negate
* match
;
723 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
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
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
);
741 if (strncmp ("-", luser
, 1) == 0)
742 return -(strcmp (&luser
[1], ruser
) == 0);
745 if (strcmp ("+", luser
) == 0)
748 /* simple string match */
749 return strcmp (ruser
, luser
) == 0;
753 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
758 while (*p
&& isspace (*p
)) {
762 return (*p
== '\0' || *p
== '#') ? 1 : 0 ;
766 * Returns 0 if positive match, -1 if _not_ ok.
769 __validuser2_sa(hostf
, ra
, ralen
, luser
, ruser
, rhost
)
773 const char *luser
, *ruser
, *rhost
;
782 while (__getline (&buf
, &bufsize
, hostf
) > 0) {
783 buf
[bufsize
- 1] = '\0'; /* Make sure it's terminated. */
786 /* Skip empty or comment lines */
791 for (;*p
&& !isspace(*p
); ++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 */
806 *p
= '\0'; /* <nul> terminate username (+host?) */
808 /* buf -> host(?) ; user -> username(?) */
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? */
835 /* Positive 'host user' match? */
836 if (hcheck
> 0 && ucheck
> 0) {
841 /* Negative 'host -user' match? */
842 if (hcheck
> 0 && ucheck
< 0)
845 /* Neither, go on looking for match. */