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>
83 #ifdef __UCLIBC_HAS_WCHAR__
87 #include <bits/uClibc_alloc.h>
89 /* sigsetmask and sigblock are not provided anymore, until this file is corrected,
90 * include the sources */
91 #include "../../signal/sigblock.c"
92 #include "../../signal/sigsetmask.c"
95 /* some forward declarations */
96 static int __ivaliduser2(FILE *hostf, u_int32_t raddr,
97 const char *luser, const char *ruser, const char *rhost);
98 static int iruserok2 (u_int32_t raddr, int superuser, const char *ruser,
99 const char *luser, const char *rhost);
102 int rcmd(char **ahost, u_short rport, const char *locuser, const char *remuser,
103 const char *cmd, int *fd2p)
105 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
107 struct hostent hostbuf;
112 struct sockaddr_in sin, from;
113 struct pollfd pfd[2];
121 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
123 tmphstbuf = stack_heap_alloc(hstbuflen);
125 while (gethostbyname_r (*ahost, &hostbuf, tmphstbuf,
126 hstbuflen, &hp, &herr) != 0 || hp == NULL)
128 if (herr != NETDB_INTERNAL || errno != ERANGE)
130 __set_h_errno (herr);
131 stack_heap_free(tmphstbuf);
137 /* Enlarge the buffer. */
139 stack_heap_free(tmphstbuf);
140 tmphstbuf = stack_heap_alloc(hstbuflen);
143 stack_heap_free(tmphstbuf);
144 #else /* call the non-reentrant version */
145 if ((hp = gethostbyname(*ahost)) == NULL) {
149 pfd[0].events = POLLIN;
150 pfd[1].events = POLLIN;
153 oldmask = sigblock(__sigmask(SIGURG)); /* sigblock */
154 for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
155 s = rresvport(&lport);
158 (void)fprintf(stderr,
159 "rcmd: socket: All ports in use\n");
161 (void)fprintf(stderr, "rcmd: socket: %m\n");
162 sigsetmask(oldmask); /* sigsetmask */
165 fcntl(s, F_SETOWN, pid);
166 sin.sin_family = hp->h_addrtype;
167 memmove(&sin.sin_addr, hp->h_addr_list[0],
168 MIN (sizeof (sin.sin_addr), hp->h_length));
169 sin.sin_port = rport;
170 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) /* __connect */
173 if (errno == EADDRINUSE) {
177 if (errno == ECONNREFUSED && timo <= 16) {
178 (void)sleep(timo); /* __sleep */
182 if (hp->h_addr_list[1] != NULL) {
185 (void)fprintf(stderr, "connect to address %s: ",
186 inet_ntoa(sin.sin_addr));
187 __set_errno (oerrno);
190 memmove(&sin.sin_addr, hp->h_addr_list[0],
191 MIN (sizeof (sin.sin_addr), hp->h_length));
192 (void)fprintf(stderr, "Trying %s...\n",
193 inet_ntoa(sin.sin_addr));
196 (void)fprintf(stderr, "%s: %m\n", hp->h_name);
197 sigsetmask(oldmask); /* __sigsetmask */
206 int s2 = rresvport(&lport), s3;
207 socklen_t len = sizeof(from);
212 (void)snprintf(num, sizeof(num), "%d", lport); /* __snprintf */
213 if (write(s, num, strlen(num)+1) != strlen(num)+1) {
214 (void)fprintf(stderr,
215 "rcmd: write (setting up stderr): %m\n");
222 if (poll (pfd, 2, -1) < 1 || (pfd[1].revents & POLLIN) == 0){
224 (void)fprintf(stderr, "rcmd: poll (setting up stderr): %m\n");
226 (void)fprintf(stderr, "poll: protocol failure in circuit setup\n");
230 s3 = accept(s2, (struct sockaddr *)&from, &len);
233 (void)fprintf(stderr,
234 "rcmd: accept: %m\n");
239 from.sin_port = ntohs((u_short)from.sin_port);
240 if (from.sin_family != AF_INET ||
241 from.sin_port >= IPPORT_RESERVED ||
242 from.sin_port < IPPORT_RESERVED / 2) {
243 (void)fprintf(stderr,
244 "socket: protocol failure in circuit setup\n");
248 (void)write(s, locuser, strlen(locuser)+1);
249 (void)write(s, remuser, strlen(remuser)+1);
250 (void)write(s, cmd, strlen(cmd)+1);
251 if (read(s, &c, 1) != 1) {
252 (void)fprintf(stderr,
253 "rcmd: %s: %m\n", *ahost);
257 while (read(s, &c, 1) == 1) {
258 (void)write(STDERR_FILENO, &c, 1);
275 int rresvport(int *alport)
277 struct sockaddr_in sin;
280 sin.sin_family = AF_INET;
281 sin.sin_addr.s_addr = INADDR_ANY;
282 s = socket(AF_INET, SOCK_STREAM, 0);
286 sin.sin_port = htons((u_short)*alport);
287 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
289 if (errno != EADDRINUSE) {
294 if (*alport == IPPORT_RESERVED/2) {
296 __set_errno (EAGAIN); /* close */
303 libc_hidden_def(rresvport)
305 /* This needs to be exported ... while it is not a documented interface
306 * for rcp related apps, it's a required one that is used to control the
307 * rhost behavior. Legacy sucks.
309 int __check_rhosts_file = 1;
311 int ruserok(const char *rhost, int superuser, const char *ruser,
317 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
321 struct hostent hostbuf;
324 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
326 buffer = stack_heap_alloc(buflen);
328 while (gethostbyname_r (rhost, &hostbuf, buffer,
329 buflen, &hp, &herr) != 0 || hp == NULL)
331 if (herr != NETDB_INTERNAL || errno != ERANGE) {
332 stack_heap_free(buffer);
336 /* Enlarge the buffer. */
338 stack_heap_free(buffer);
339 buffer = stack_heap_alloc(buflen);
342 stack_heap_free(buffer);
344 if ((hp = gethostbyname(rhost)) == NULL) {
348 for (ap = hp->h_addr_list; *ap; ++ap) {
349 memmove(&addr, *ap, sizeof(addr));
350 if (iruserok2(addr, superuser, ruser, luser, rhost) == 0)
357 /* Extremely paranoid file open function. */
359 iruserfopen (const char *file, uid_t okuser)
365 /* If not a regular file, if owned by someone other than user or
366 root, if writeable by anyone but the owner, or if hardlinked
368 if (lstat (file, &st))
370 else if (!S_ISREG (st.st_mode))
371 cp = "not regular file";
374 res = fopen (file, "r");
377 else if (fstat (fileno (res), &st) < 0)
379 else if (st.st_uid && st.st_uid != okuser)
381 else if (st.st_mode & (S_IWGRP|S_IWOTH))
382 cp = "writeable by other than owner";
383 else if (st.st_nlink > 1)
384 cp = "hard linked somewhere";
387 /* If there were any problems, quit. */
400 * New .rhosts strategy: We are passed an ip address. We spin through
401 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
402 * has ip addresses, we don't have to trust a nameserver. When it
403 * contains hostnames, we spin through the list of addresses the nameserver
404 * gives us and look for a match.
406 * Returns 0 if ok, -1 if not ok.
409 iruserok2 (u_int32_t raddr, int superuser, const char *ruser, const char *luser,
416 hostf = iruserfopen (_PATH_HEQUIV, 0);
419 isbad = __ivaliduser2 (hostf, raddr, luser, ruser, rhost);
426 if (__check_rhosts_file || superuser) {
432 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
433 size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
434 struct passwd pwdbuf;
435 char *buffer = stack_heap_alloc(buflen);
437 if (getpwnam_r (luser, &pwdbuf, buffer,
438 buflen, &pwd) != 0 || pwd == NULL)
440 stack_heap_free(buffer);
443 stack_heap_free(buffer);
445 if ((pwd = getpwnam(luser)) == NULL)
449 dirlen = strlen (pwd->pw_dir);
450 pbuf = malloc (dirlen + sizeof "/.rhosts");
451 strcpy (pbuf, pwd->pw_dir);
452 strcat (pbuf, "/.rhosts");
454 /* Change effective uid while reading .rhosts. If root and
455 reading an NFS mounted file system, can't read files that
456 are protected read/write owner only. */
458 seteuid (pwd->pw_uid);
459 hostf = iruserfopen (pbuf, pwd->pw_uid);
463 isbad = __ivaliduser2 (hostf, raddr, luser, ruser, rhost);
473 /* This is the exported version. */
474 int iruserok (u_int32_t raddr, int superuser, const char * ruser, const char * luser);
475 int iruserok (u_int32_t raddr, int superuser, const char * ruser, const char * luser)
477 return iruserok2 (raddr, superuser, ruser, luser, "-");
483 * Don't make static, used by lpd(8).
485 * This function is not used anymore. It is only present because lpd(8)
486 * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
487 * argument. This means that netgroups won't work in .rhost/hosts.equiv
488 * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
490 * Returns 0 if ok, -1 if not ok.
493 __ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser, const char *ruser);
495 __ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser, const char *ruser)
497 return __ivaliduser2(hostf, raddr, luser, ruser, "-");
501 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
503 __icheckhost (u_int32_t raddr, char *lhost, const char *rhost)
507 int negate=1; /* Multiply return with this to get -1 instead of 1 */
510 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
514 struct hostent hostbuf;
519 /* Check nis netgroup. */
520 if (strncmp ("+@", lhost, 2) == 0)
521 return innetgr (&lhost[2], rhost, NULL, NULL);
523 if (strncmp ("-@", lhost, 2) == 0)
524 return -innetgr (&lhost[2], rhost, NULL, NULL);
525 #endif /* HAVE_NETGROUP */
528 if (strncmp ("-", lhost,1) == 0) {
531 } else if (strcmp ("+",lhost) == 0) {
532 return 1; /* asking for trouble, but ok.. */
535 /* Try for raw ip address first. */
536 if (isdigit (*lhost) && (laddr = inet_addr (lhost)) != INADDR_NONE)
537 return negate * (! (raddr ^ laddr));
539 /* Better be a hostname. */
540 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
542 buffer = malloc(buflen);
545 while (gethostbyname_r (lhost, &hostbuf, buffer, buflen, &hp, &herr)
551 __set_errno (save_errno);
553 hp = gethostbyname(lhost);
554 #endif /* __UCLIBC_HAS_REENTRANT_RPC__ */
559 /* Spin through ip addresses. */
560 for (pp = hp->h_addr_list; *pp; ++pp)
561 if (!memcmp (&raddr, *pp, sizeof (u_int32_t)))
568 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
570 __icheckuser (const char *luser, const char *ruser)
574 luser is user entry from .rhosts/hosts.equiv file
575 ruser is user id on remote host
580 if (strncmp ("+@", luser, 2) == 0)
581 return innetgr (&luser[2], NULL, ruser, NULL);
583 if (strncmp ("-@", luser,2) == 0)
584 return -innetgr (&luser[2], NULL, ruser, NULL);
585 #endif /* HAVE_NETGROUP */
588 if (strncmp ("-", luser, 1) == 0)
589 return -(strcmp (&luser[1], ruser) == 0);
592 if (strcmp ("+", luser) == 0)
595 /* simple string match */
596 return strcmp (ruser, luser) == 0;
600 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
605 while (*p && isspace (*p)) {
609 return (*p == '\0' || *p == '#') ? 1 : 0 ;
613 * Returns 0 if positive match, -1 if _not_ ok.
616 __ivaliduser2(FILE *hostf, u_int32_t raddr, const char *luser,
617 const char *ruser, const char *rhost)
619 register const char *user;
626 while (getline (&buf, &bufsize, hostf) > 0) {
627 buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */
630 /* Skip empty or comment lines */
635 /* Skip lines that are too long. */
636 if (strchr (p, '\n') == NULL) {
637 int ch = getc_unlocked (hostf);
639 while (ch != '\n' && ch != EOF)
640 ch = getc_unlocked (hostf);
644 for (;*p && !isspace(*p); ++p) {
648 /* Next we want to find the permitted name for the remote user. */
649 if (*p == ' ' || *p == '\t') {
650 /* <nul> terminate hostname and skip spaces */
651 for (*p++='\0'; *p && isspace (*p); ++p);
653 user = p; /* this is the user's name */
654 while (*p && !isspace (*p))
655 ++p; /* find end of user's name */
659 *p = '\0'; /* <nul> terminate username (+host?) */
661 /* buf -> host(?) ; user -> username(?) */
663 /* First check host part */
664 hcheck = __icheckhost (raddr, buf, rhost);
670 /* Then check user part */
674 ucheck = __icheckuser (user, ruser);
676 /* Positive 'host user' match? */
682 /* Negative 'host -user' match? */
686 /* Neither, go on looking for match */