2 * Copyright (c) 1983, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
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. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94";
36 #endif /* LIBC_SCCS and not lint */
38 #define __UCLIBC_HIDE_DEPRECATED__
51 #include <sys/param.h>
53 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
59 libc_hidden_proto(memcmp)
60 libc_hidden_proto(strcat)
61 libc_hidden_proto(strchr)
62 libc_hidden_proto(strcmp)
63 libc_hidden_proto(strcpy)
64 libc_hidden_proto(strlen)
65 libc_hidden_proto(strncmp)
66 libc_hidden_proto(strnlen)
67 libc_hidden_proto(bcopy)
68 libc_hidden_proto(getpid)
69 libc_hidden_proto(socket)
70 libc_hidden_proto(close)
71 libc_hidden_proto(fcntl)
72 libc_hidden_proto(read)
73 libc_hidden_proto(write)
74 libc_hidden_proto(perror)
75 libc_hidden_proto(lstat)
76 libc_hidden_proto(fstat)
77 libc_hidden_proto(tolower)
78 libc_hidden_proto(sysconf)
79 libc_hidden_proto(getline)
80 libc_hidden_proto(geteuid)
81 libc_hidden_proto(seteuid)
82 libc_hidden_proto(getpwnam_r)
83 libc_hidden_proto(gethostbyname)
84 libc_hidden_proto(gethostbyname_r)
85 libc_hidden_proto(fileno)
86 libc_hidden_proto(sleep)
87 libc_hidden_proto(inet_addr)
88 libc_hidden_proto(inet_ntoa)
89 libc_hidden_proto(herror)
90 libc_hidden_proto(bind)
91 libc_hidden_proto(connect)
92 libc_hidden_proto(sigblock)
93 libc_hidden_proto(snprintf)
94 libc_hidden_proto(poll)
95 libc_hidden_proto(accept)
96 libc_hidden_proto(listen)
97 libc_hidden_proto(sigsetmask)
98 libc_hidden_proto(getc_unlocked)
99 libc_hidden_proto(__fgetc_unlocked)
100 libc_hidden_proto(fopen)
101 libc_hidden_proto(fclose)
102 libc_hidden_proto(fprintf)
103 libc_hidden_proto(__h_errno_location)
104 libc_hidden_proto(stderr)
105 #ifdef __UCLIBC_HAS_XLOCALE__
106 libc_hidden_proto(__ctype_b_loc)
107 libc_hidden_proto(__ctype_tolower_loc)
109 libc_hidden_proto(__ctype_b)
110 libc_hidden_proto(__ctype_tolower)
113 libc_hidden_proto(rresvport)
115 /* some forward declarations */
116 static int __ivaliduser2(FILE *hostf, u_int32_t raddr,
117 const char *luser, const char *ruser, const char *rhost);
118 static int iruserok2 (u_int32_t raddr, int superuser, const char *ruser,
119 const char *luser, const char *rhost);
122 int rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
125 const char *locuser, *remuser, *cmd;
128 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
130 struct hostent hostbuf;
135 struct sockaddr_in sin, from;
136 struct pollfd pfd[2];
144 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
146 #ifdef __ARCH_HAS_MMU__
147 tmphstbuf = alloca (hstbuflen);
149 tmphstbuf = malloc (hstbuflen);
152 while (gethostbyname_r (*ahost, &hostbuf, tmphstbuf,
153 hstbuflen, &hp, &herr) != 0 || hp == NULL)
155 if (herr != NETDB_INTERNAL || errno != ERANGE)
157 __set_h_errno (herr);
158 #ifndef __ARCH_HAS_MMU__
166 /* Enlarge the buffer. */
168 #ifdef __ARCH_HAS_MMU__
169 tmphstbuf = alloca (hstbuflen);
174 tmphstbuf = malloc (hstbuflen);
178 #ifndef __ARCH_HAS_MMU__
181 #else /* call the non-reentrant version */
182 if ((hp = gethostbyname(*ahost)) == NULL) {
186 pfd[0].events = POLLIN;
187 pfd[1].events = POLLIN;
190 oldmask = sigblock(sigmask(SIGURG)); /* __sigblock */
191 for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
192 s = rresvport(&lport);
195 (void)fprintf(stderr,
196 "rcmd: socket: All ports in use\n");
198 (void)fprintf(stderr, "rcmd: socket: %m\n");
199 sigsetmask(oldmask); /* sigsetmask */
202 fcntl(s, F_SETOWN, pid);
203 sin.sin_family = hp->h_addrtype;
204 bcopy(hp->h_addr_list[0], &sin.sin_addr,
205 MIN (sizeof (sin.sin_addr), hp->h_length));
206 sin.sin_port = rport;
207 if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) /* __connect */
210 if (errno == EADDRINUSE) {
214 if (errno == ECONNREFUSED && timo <= 16) {
215 (void)sleep(timo); /* __sleep */
219 if (hp->h_addr_list[1] != NULL) {
222 (void)fprintf(stderr, "connect to address %s: ",
223 inet_ntoa(sin.sin_addr));
224 __set_errno (oerrno);
227 bcopy(hp->h_addr_list[0], &sin.sin_addr,
228 MIN (sizeof (sin.sin_addr), hp->h_length));
229 (void)fprintf(stderr, "Trying %s...\n",
230 inet_ntoa(sin.sin_addr));
233 (void)fprintf(stderr, "%s: %m\n", hp->h_name);
234 sigsetmask(oldmask); /* __sigsetmask */
243 int s2 = rresvport(&lport), s3;
244 socklen_t len = sizeof(from);
249 (void)snprintf(num, sizeof(num), "%d", lport); /* __snprintf */
250 if (write(s, num, strlen(num)+1) != strlen(num)+1) {
251 (void)fprintf(stderr,
252 "rcmd: write (setting up stderr): %m\n");
259 if (poll (pfd, 2, -1) < 1 || (pfd[1].revents & POLLIN) == 0){
261 (void)fprintf(stderr, "rcmd: poll (setting up stderr): %m\n");
263 (void)fprintf(stderr, "poll: protocol failure in circuit setup\n");
267 s3 = accept(s2, (struct sockaddr *)&from, &len);
270 (void)fprintf(stderr,
271 "rcmd: accept: %m\n");
276 from.sin_port = ntohs((u_short)from.sin_port);
277 if (from.sin_family != AF_INET ||
278 from.sin_port >= IPPORT_RESERVED ||
279 from.sin_port < IPPORT_RESERVED / 2) {
280 (void)fprintf(stderr,
281 "socket: protocol failure in circuit setup\n");
285 (void)write(s, locuser, strlen(locuser)+1);
286 (void)write(s, remuser, strlen(remuser)+1);
287 (void)write(s, cmd, strlen(cmd)+1);
288 if (read(s, &c, 1) != 1) {
289 (void)fprintf(stderr,
290 "rcmd: %s: %m\n", *ahost);
294 while (read(s, &c, 1) == 1) {
295 (void)write(STDERR_FILENO, &c, 1);
312 int rresvport(int *alport)
314 struct sockaddr_in sin;
317 sin.sin_family = AF_INET;
318 sin.sin_addr.s_addr = INADDR_ANY;
319 s = socket(AF_INET, SOCK_STREAM, 0);
323 sin.sin_port = htons((u_short)*alport);
324 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
326 if (errno != EADDRINUSE) {
331 if (*alport == IPPORT_RESERVED/2) {
333 __set_errno (EAGAIN); /* close */
340 libc_hidden_def(rresvport)
342 static int __check_rhosts_file = 1;
343 static char *__rcmd_errstr;
345 int ruserok(rhost, superuser, ruser, luser)
346 const char *rhost, *ruser, *luser;
352 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
356 struct hostent hostbuf;
359 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
361 #ifdef __ARCH_HAS_MMU__
362 buffer = alloca (buflen);
364 buffer = malloc (buflen);
367 while (gethostbyname_r (rhost, &hostbuf, buffer,
368 buflen, &hp, &herr) != 0 || hp == NULL)
370 if (herr != NETDB_INTERNAL || errno != ERANGE) {
371 #ifndef __ARCH_HAS_MMU__
377 /* Enlarge the buffer. */
379 #ifdef __ARCH_HAS_MMU__
380 buffer = alloca (buflen);
385 buffer = malloc (buflen);
389 #ifndef __ARCH_HAS_MMU__
393 if ((hp = gethostbyname(rhost)) == NULL) {
397 for (ap = hp->h_addr_list; *ap; ++ap) {
398 bcopy(*ap, &addr, sizeof(addr));
399 if (iruserok2(addr, superuser, ruser, luser, rhost) == 0)
406 /* Extremely paranoid file open function. */
408 iruserfopen (char *file, uid_t okuser)
414 /* If not a regular file, if owned by someone other than user or
415 root, if writeable by anyone but the owner, or if hardlinked
418 if (lstat (file, &st))
420 else if (!S_ISREG (st.st_mode))
421 cp = "not regular file";
424 res = fopen (file, "r");
427 else if (fstat (fileno (res), &st) < 0)
429 else if (st.st_uid && st.st_uid != okuser)
431 else if (st.st_mode & (S_IWGRP|S_IWOTH))
432 cp = "writeable by other than owner";
433 else if (st.st_nlink > 1)
434 cp = "hard linked somewhere";
437 /* If there were any problems, quit. */
451 * New .rhosts strategy: We are passed an ip address. We spin through
452 * hosts.equiv and .rhosts looking for a match. When the .rhosts only
453 * has ip addresses, we don't have to trust a nameserver. When it
454 * contains hostnames, we spin through the list of addresses the nameserver
455 * gives us and look for a match.
457 * Returns 0 if ok, -1 if not ok.
460 iruserok2 (raddr, superuser, ruser, luser, rhost)
463 const char *ruser, *luser, *rhost;
469 hostf = iruserfopen (_PATH_HEQUIV, 0);
472 isbad = __ivaliduser2 (hostf, raddr, luser, ruser, rhost);
479 if (__check_rhosts_file || superuser) {
485 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
486 size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
487 struct passwd pwdbuf;
488 #ifdef __ARCH_HAS_MMU__
489 char *buffer = alloca (buflen);
491 char *buffer = malloc (buflen);
494 if (getpwnam_r (luser, &pwdbuf, buffer,
495 buflen, &pwd) != 0 || pwd == NULL)
497 #ifndef __ARCH_HAS_MMU__
502 #ifndef __ARCH_HAS_MMU__
506 if ((pwd = getpwnam(luser)) == NULL)
510 dirlen = strlen (pwd->pw_dir);
511 pbuf = malloc (dirlen + sizeof "/.rhosts");
512 strcpy (pbuf, pwd->pw_dir);
513 strcat (pbuf, "/.rhosts");
515 /* Change effective uid while reading .rhosts. If root and
516 reading an NFS mounted file system, can't read files that
517 are protected read/write owner only. */
519 seteuid (pwd->pw_uid);
520 hostf = iruserfopen (pbuf, pwd->pw_uid);
524 isbad = __ivaliduser2 (hostf, raddr, luser, ruser, rhost);
534 /* This is the exported version. */
535 int iruserok (u_int32_t raddr, int superuser, const char * ruser, const char * luser)
537 return iruserok2 (raddr, superuser, ruser, luser, "-");
543 * Don't make static, used by lpd(8).
545 * This function is not used anymore. It is only present because lpd(8)
546 * calls it (!?!). We simply call __invaliduser2() with an illegal rhost
547 * argument. This means that netgroups won't work in .rhost/hosts.equiv
548 * files. If you want lpd to work with netgroups, fix lpd to use ruserok()
550 * Returns 0 if ok, -1 if not ok.
553 __ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser, const char *ruser)
555 return __ivaliduser2(hostf, raddr, luser, ruser, "-");
559 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
561 __icheckhost (u_int32_t raddr, char *lhost, const char *rhost)
565 int negate=1; /* Multiply return with this to get -1 instead of 1 */
568 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
572 struct hostent hostbuf;
577 /* Check nis netgroup. */
578 if (strncmp ("+@", lhost, 2) == 0)
579 return innetgr (&lhost[2], rhost, NULL, NULL);
581 if (strncmp ("-@", lhost, 2) == 0)
582 return -innetgr (&lhost[2], rhost, NULL, NULL);
583 #endif /* HAVE_NETGROUP */
586 if (strncmp ("-", lhost,1) == 0) {
589 } else if (strcmp ("+",lhost) == 0) {
590 return 1; /* asking for trouble, but ok.. */
593 /* Try for raw ip address first. */
594 if (isdigit (*lhost) && (laddr = inet_addr (lhost)) != INADDR_NONE)
595 return negate * (! (raddr ^ laddr));
597 /* Better be a hostname. */
598 #ifdef __UCLIBC_HAS_REENTRANT_RPC__
600 buffer = malloc(buflen);
603 while (gethostbyname_r (lhost, &hostbuf, buffer, buflen, &hp, &herr)
609 __set_errno (save_errno);
611 hp = gethostbyname(lhost);
612 #endif /* __UCLIBC_HAS_REENTRANT_RPC__ */
617 /* Spin through ip addresses. */
618 for (pp = hp->h_addr_list; *pp; ++pp)
619 if (!memcmp (&raddr, *pp, sizeof (u_int32_t)))
626 /* Returns 1 on positive match, 0 on no match, -1 on negative match. */
628 __icheckuser (const char *luser, const char *ruser)
632 luser is user entry from .rhosts/hosts.equiv file
633 ruser is user id on remote host
638 if (strncmp ("+@", luser, 2) == 0)
639 return innetgr (&luser[2], NULL, ruser, NULL);
641 if (strncmp ("-@", luser,2) == 0)
642 return -innetgr (&luser[2], NULL, ruser, NULL);
643 #endif /* HAVE_NETGROUP */
646 if (strncmp ("-", luser, 1) == 0)
647 return -(strcmp (&luser[1], ruser) == 0);
650 if (strcmp ("+", luser) == 0)
653 /* simple string match */
654 return strcmp (ruser, luser) == 0;
658 * Returns 1 for blank lines (or only comment lines) and 0 otherwise
663 while (*p && isspace (*p)) {
667 return (*p == '\0' || *p == '#') ? 1 : 0 ;
671 * Returns 0 if positive match, -1 if _not_ ok.
674 __ivaliduser2(hostf, raddr, luser, ruser, rhost)
677 const char *luser, *ruser, *rhost;
679 register const char *user;
686 while (getline (&buf, &bufsize, hostf) > 0) {
687 buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */
690 /* Skip empty or comment lines */
695 /* Skip lines that are too long. */
696 if (strchr (p, '\n') == NULL) {
697 int ch = getc_unlocked (hostf);
699 while (ch != '\n' && ch != EOF)
700 ch = getc_unlocked (hostf);
704 for (;*p && !isspace(*p); ++p) {
708 /* Next we want to find the permitted name for the remote user. */
709 if (*p == ' ' || *p == '\t') {
710 /* <nul> terminate hostname and skip spaces */
711 for (*p++='\0'; *p && isspace (*p); ++p);
713 user = p; /* this is the user's name */
714 while (*p && !isspace (*p))
715 ++p; /* find end of user's name */
719 *p = '\0'; /* <nul> terminate username (+host?) */
721 /* buf -> host(?) ; user -> username(?) */
723 /* First check host part */
724 hcheck = __icheckhost (raddr, buf, rhost);
730 /* Then check user part */
734 ucheck = __icheckuser (user, ruser);
736 /* Positive 'host user' match? */
742 /* Negative 'host -user' match? */
746 /* Neither, go on looking for match */