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
59 static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94";
60 #endif /* LIBC_SCCS and not lint */
62 #define __UCLIBC_HIDE_DEPRECATED__
64 #include <sys/param.h>
66 #include <sys/socket.h>
69 #include <netinet/in.h>
70 #include <arpa/inet.h>
80 #include <stdio_ext.h>
85 #ifdef __UCLIBC_HAS_WCHAR__
90 /* Experimentally off - libc_hidden_proto(memcmp) */
91 /* Experimentally off - libc_hidden_proto(strcat) */
92 /* Experimentally off - libc_hidden_proto(strchr) */
93 /* Experimentally off - libc_hidden_proto(strcmp) */
94 /* Experimentally off - libc_hidden_proto(strcpy) */
95 /* Experimentally off - libc_hidden_proto(strlen) */
96 /* Experimentally off - libc_hidden_proto(strncmp) */
97 /* Experimentally off - libc_hidden_proto(memmove) */
98 /* libc_hidden_proto(getpid) */
99 /* libc_hidden_proto(socket) */
100 /* libc_hidden_proto(close) */
101 /* libc_hidden_proto(fcntl) */
102 /* libc_hidden_proto(read) */
103 /* libc_hidden_proto(write) */
104 /* libc_hidden_proto(perror) */
105 /* libc_hidden_proto(lstat) */
106 /* libc_hidden_proto(fstat) */
107 /* libc_hidden_proto(tolower) */
108 /* libc_hidden_proto(sysconf) */
109 /* libc_hidden_proto(getline) */
110 /* libc_hidden_proto(geteuid) */
111 /* libc_hidden_proto(seteuid) */
112 /* libc_hidden_proto(getpwnam_r) */
113 /* libc_hidden_proto(gethostbyname) */
114 /* libc_hidden_proto(gethostbyname_r) */
115 /* libc_hidden_proto(fileno) */
116 /* libc_hidden_proto(sleep) */
117 /* libc_hidden_proto(inet_addr) */
118 /* libc_hidden_proto(inet_ntoa) */
119 /* libc_hidden_proto(herror) */
120 /* libc_hidden_proto(bind) */
121 /* libc_hidden_proto(connect) */
122 /* libc_hidden_proto(sigblock) */
123 /* libc_hidden_proto(snprintf) */
124 /* libc_hidden_proto(poll) */
125 /* libc_hidden_proto(accept) */
126 /* libc_hidden_proto(listen) */
127 /* libc_hidden_proto(sigsetmask) */
128 /* libc_hidden_proto(getc_unlocked) */
129 /* libc_hidden_proto(__fgetc_unlocked) */
130 /* libc_hidden_proto(fopen) */
131 /* libc_hidden_proto(fclose) */
132 /* libc_hidden_proto(fprintf) */
133 /* libc_hidden_proto(__h_errno_location) */
134 #ifdef __UCLIBC_HAS_XLOCALE__
135 /* libc_hidden_proto(__ctype_b_loc) */
136 /* libc_hidden_proto(__ctype_tolower_loc) */
137 #elif defined __UCLIBC_HAS_CTYPE_TABLES__
138 /* libc_hidden_proto(__ctype_b) */
139 /* libc_hidden_proto(__ctype_tolower) */
142 /* libc_hidden_proto(rresvport) */
144 /* some forward declarations */
145 static int __ivaliduser2(FILE *hostf, u_int32_t raddr,
146 const char *luser, const char *ruser, const char *rhost);
147 static int iruserok2 (u_int32_t raddr, int superuser, const char *ruser,
148 const char *luser, const char *rhost);
151 int rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
154 const char *locuser, *remuser, *cmd;
157 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
159 struct hostent hostbuf;
164 struct sockaddr_in sin, from;
165 struct pollfd pfd[2];
173 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
175 #ifdef __ARCH_USE_MMU__
176 tmphstbuf = alloca (hstbuflen);
178 tmphstbuf = malloc (hstbuflen);
181 while (gethostbyname_r (*ahost, &hostbuf, tmphstbuf,
182 hstbuflen, &hp, &herr) != 0 || hp == NULL)
184 if (herr != NETDB_INTERNAL || errno != ERANGE)
186 __set_h_errno (herr);
187 #ifndef __ARCH_USE_MMU__
195 /* Enlarge the buffer. */
197 #ifdef __ARCH_USE_MMU__
198 tmphstbuf = alloca (hstbuflen);
201 tmphstbuf = malloc (hstbuflen);
205 #ifndef __ARCH_USE_MMU__
208 #else /* call the non-reentrant version */
209 if ((hp = gethostbyname(*ahost)) == NULL) {
213 pfd[0].events = POLLIN;
214 pfd[1].events = POLLIN;
217 oldmask = sigblock(sigmask(SIGURG)); /* __sigblock */
218 for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
219 s = rresvport(&lport);
222 (void)fprintf(stderr,
223 "rcmd: socket: All ports in use\n");
225 (void)fprintf(stderr, "rcmd: socket: %m\n");
226 sigsetmask(oldmask); /* sigsetmask */
229 fcntl(s, F_SETOWN, pid);
230 sin.sin_family = hp->h_addrtype;
231 memmove(&sin.sin_addr, hp->h_addr_list[0],
232 MIN (sizeof (sin.sin_addr), hp->h_length));
233 sin.sin_port = rport;
234 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) /* __connect */
237 if (errno == EADDRINUSE) {
241 if (errno == ECONNREFUSED && timo <= 16) {
242 (void)sleep(timo); /* __sleep */
246 if (hp->h_addr_list[1] != NULL) {
249 (void)fprintf(stderr, "connect to address %s: ",
250 inet_ntoa(sin.sin_addr));
251 __set_errno (oerrno);
254 memmove(&sin.sin_addr, hp->h_addr_list[0],
255 MIN (sizeof (sin.sin_addr), hp->h_length));
256 (void)fprintf(stderr, "Trying %s...\n",
257 inet_ntoa(sin.sin_addr));
260 (void)fprintf(stderr, "%s: %m\n", hp->h_name);
261 sigsetmask(oldmask); /* __sigsetmask */
270 int s2 = rresvport(&lport), s3;
271 socklen_t len = sizeof(from);
276 (void)snprintf(num, sizeof(num), "%d", lport); /* __snprintf */
277 if (write(s, num, strlen(num)+1) != strlen(num)+1) {
278 (void)fprintf(stderr,
279 "rcmd: write (setting up stderr): %m\n");
286 if (poll (pfd, 2, -1) < 1 || (pfd[1].revents & POLLIN) == 0){
288 (void)fprintf(stderr, "rcmd: poll (setting up stderr): %m\n");
290 (void)fprintf(stderr, "poll: protocol failure in circuit setup\n");
294 s3 = accept(s2, (struct sockaddr *)&from, &len);
297 (void)fprintf(stderr,
298 "rcmd: accept: %m\n");
303 from.sin_port = ntohs((u_short)from.sin_port);
304 if (from.sin_family != AF_INET ||
305 from.sin_port >= IPPORT_RESERVED ||
306 from.sin_port < IPPORT_RESERVED / 2) {
307 (void)fprintf(stderr,
308 "socket: protocol failure in circuit setup\n");
312 (void)write(s, locuser, strlen(locuser)+1);
313 (void)write(s, remuser, strlen(remuser)+1);
314 (void)write(s, cmd, strlen(cmd)+1);
315 if (read(s, &c, 1) != 1) {
316 (void)fprintf(stderr,
317 "rcmd: %s: %m\n", *ahost);
321 while (read(s, &c, 1) == 1) {
322 (void)write(STDERR_FILENO, &c, 1);
339 int rresvport(int *alport)
341 struct sockaddr_in sin;
344 sin.sin_family = AF_INET;
345 sin.sin_addr.s_addr = INADDR_ANY;
346 s = socket(AF_INET, SOCK_STREAM, 0);
350 sin.sin_port = htons((u_short)*alport);
351 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
353 if (errno != EADDRINUSE) {
358 if (*alport == IPPORT_RESERVED/2) {
360 __set_errno (EAGAIN); /* close */
367 libc_hidden_def(rresvport)
369 /* This needs to be exported ... while it is not a documented interface
370 * for rcp related apps, it's a required one that is used to control the
371 * rhost behavior. Legacy sucks.
373 int __check_rhosts_file = 1;
375 int ruserok(rhost, superuser, ruser, luser)
376 const char *rhost, *ruser, *luser;
382 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
386 struct hostent hostbuf;
389 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
391 #ifdef __ARCH_USE_MMU__
392 buffer = alloca (buflen);
394 buffer = malloc (buflen);
397 while (gethostbyname_r (rhost, &hostbuf, buffer,
398 buflen, &hp, &herr) != 0 || hp == NULL)
400 if (herr != NETDB_INTERNAL || errno != ERANGE) {
401 #ifndef __ARCH_USE_MMU__
407 /* Enlarge the buffer. */
409 #ifdef __ARCH_USE_MMU__
410 buffer = alloca (buflen);
413 buffer = malloc (buflen);
417 #ifndef __ARCH_USE_MMU__
421 if ((hp = gethostbyname(rhost)) == NULL) {
425 for (ap = hp->h_addr_list; *ap; ++ap) {
426 memmove(&addr, *ap, sizeof(addr));
427 if (iruserok2(addr, superuser, ruser, luser, rhost) == 0)
434 /* Extremely paranoid file open function. */
436 iruserfopen (const char *file, uid_t okuser)
442 /* If not a regular file, if owned by someone other than user or
443 root, if writeable by anyone but the owner, or if hardlinked
445 if (lstat (file, &st))
447 else if (!S_ISREG (st.st_mode))
448 cp = "not regular file";
451 res = fopen (file, "r");
454 else if (fstat (fileno (res), &st) < 0)
456 else if (st.st_uid && st.st_uid != okuser)
458 else if (st.st_mode & (S_IWGRP|S_IWOTH))
459 cp = "writeable by other than owner";
460 else if (st.st_nlink > 1)
461 cp = "hard linked somewhere";
464 /* If there were any problems, quit. */
477 * New .rhosts strategy: We are passed an ip address. We spin through
478 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
479 * has ip addresses, we don't have to trust a nameserver. When it
480 * contains hostnames, we spin through the list of addresses the nameserver
481 * gives us and look for a match.
483 * Returns 0 if ok, -1 if not ok.
486 iruserok2 (raddr, superuser, ruser, luser, rhost)
489 const char *ruser, *luser, *rhost;
495 hostf = iruserfopen (_PATH_HEQUIV, 0);
498 isbad = __ivaliduser2 (hostf, raddr, luser, ruser, rhost);
505 if (__check_rhosts_file || superuser) {
511 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
512 size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
513 struct passwd pwdbuf;
514 #ifdef __ARCH_USE_MMU__
515 char *buffer = alloca (buflen);
517 char *buffer = malloc (buflen);
520 if (getpwnam_r (luser, &pwdbuf, buffer,
521 buflen, &pwd) != 0 || pwd == NULL)
523 #ifndef __ARCH_USE_MMU__
528 #ifndef __ARCH_USE_MMU__
532 if ((pwd = getpwnam(luser)) == NULL)
536 dirlen = strlen (pwd->pw_dir);
537 pbuf = malloc (dirlen + sizeof "/.rhosts");
538 strcpy (pbuf, pwd->pw_dir);
539 strcat (pbuf, "/.rhosts");
541 /* Change effective uid while reading .rhosts. If root and
542 reading an NFS mounted file system, can't read files that
543 are protected read/write owner only. */
545 seteuid (pwd->pw_uid);
546 hostf = iruserfopen (pbuf, pwd->pw_uid);
550 isbad = __ivaliduser2 (hostf, raddr, luser, ruser, rhost);
560 /* This is the exported version. */
561 int iruserok (u_int32_t raddr, int superuser, const char * ruser, const char * luser);
562 int iruserok (u_int32_t raddr, int superuser, const char * ruser, const char * luser)
564 return iruserok2 (raddr, superuser, ruser, luser, "-");
570 * Don't make static, used by lpd(8).
572 * This function is not used anymore. It is only present because lpd(8)
573 * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
574 * argument. This means that netgroups won't work in .rhost/hosts.equiv
575 * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
577 * Returns 0 if ok, -1 if not ok.
580 __ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser, const char *ruser);
582 __ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser, const char *ruser)
584 return __ivaliduser2(hostf, raddr, luser, ruser, "-");
588 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
590 __icheckhost (u_int32_t raddr, char *lhost, const char *rhost)
594 int negate=1; /* Multiply return with this to get -1 instead of 1 */
597 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
601 struct hostent hostbuf;
606 /* Check nis netgroup. */
607 if (strncmp ("+@", lhost, 2) == 0)
608 return innetgr (&lhost[2], rhost, NULL, NULL);
610 if (strncmp ("-@", lhost, 2) == 0)
611 return -innetgr (&lhost[2], rhost, NULL, NULL);
612 #endif /* HAVE_NETGROUP */
615 if (strncmp ("-", lhost,1) == 0) {
618 } else if (strcmp ("+",lhost) == 0) {
619 return 1; /* asking for trouble, but ok.. */
622 /* Try for raw ip address first. */
623 if (isdigit (*lhost) && (laddr = inet_addr (lhost)) != INADDR_NONE)
624 return negate * (! (raddr ^ laddr));
626 /* Better be a hostname. */
627 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
629 buffer = malloc(buflen);
632 while (gethostbyname_r (lhost, &hostbuf, buffer, buflen, &hp, &herr)
638 __set_errno (save_errno);
640 hp = gethostbyname(lhost);
641 #endif /* __UCLIBC_HAS_REENTRANT_RPC__ */
646 /* Spin through ip addresses. */
647 for (pp = hp->h_addr_list; *pp; ++pp)
648 if (!memcmp (&raddr, *pp, sizeof (u_int32_t)))
655 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
657 __icheckuser (const char *luser, const char *ruser)
661 luser is user entry from .rhosts/hosts.equiv file
662 ruser is user id on remote host
667 if (strncmp ("+@", luser, 2) == 0)
668 return innetgr (&luser[2], NULL, ruser, NULL);
670 if (strncmp ("-@", luser,2) == 0)
671 return -innetgr (&luser[2], NULL, ruser, NULL);
672 #endif /* HAVE_NETGROUP */
675 if (strncmp ("-", luser, 1) == 0)
676 return -(strcmp (&luser[1], ruser) == 0);
679 if (strcmp ("+", luser) == 0)
682 /* simple string match */
683 return strcmp (ruser, luser) == 0;
687 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
692 while (*p && isspace (*p)) {
696 return (*p == '\0' || *p == '#') ? 1 : 0 ;
700 * Returns 0 if positive match, -1 if _not_ ok.
703 __ivaliduser2(hostf, raddr, luser, ruser, rhost)
706 const char *luser, *ruser, *rhost;
708 register const char *user;
715 while (getline (&buf, &bufsize, hostf) > 0) {
716 buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */
719 /* Skip empty or comment lines */
724 /* Skip lines that are too long. */
725 if (strchr (p, '\n') == NULL) {
726 int ch = getc_unlocked (hostf);
728 while (ch != '\n' && ch != EOF)
729 ch = getc_unlocked (hostf);
733 for (;*p && !isspace(*p); ++p) {
737 /* Next we want to find the permitted name for the remote user. */
738 if (*p == ' ' || *p == '\t') {
739 /* <nul> terminate hostname and skip spaces */
740 for (*p++='\0'; *p && isspace (*p); ++p);
742 user = p; /* this is the user's name */
743 while (*p && !isspace (*p))
744 ++p; /* find end of user's name */
748 *p = '\0'; /* <nul> terminate username (+host?) */
750 /* buf -> host(?) ; user -> username(?) */
752 /* First check host part */
753 hcheck = __icheckhost (raddr, buf, rhost);
759 /* Then check user part */
763 ucheck = __icheckuser (user, ruser);
765 /* Positive 'host user' match? */
771 /* Negative 'host -user' match? */
775 /* Neither, go on looking for match */