diff options
author | Eric Andersen <andersen@codepoet.org> | 2002-01-02 13:59:31 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2002-01-02 13:59:31 +0000 |
commit | b4bb6f662bdfc73778b08404c82b04a29ea24a79 (patch) | |
tree | 646ba55f2cff221cb9f3d58d6f984aeea89212ea | |
parent | c1bb4ddffbee3c12824765dc0ff2439ffa032c2b (diff) |
Patch to add rcmd support, from Stefan Soucek, which I'd long
ago forgotten. Oops. Needed some minor scrubbing for bitrot,
-Erik
-rw-r--r-- | libc/inet/rpc/Makefile | 2 | ||||
-rw-r--r-- | libc/inet/rpc/rcmd.c | 669 |
2 files changed, 670 insertions, 1 deletions
diff --git a/libc/inet/rpc/Makefile b/libc/inet/rpc/Makefile index 08782e1a3..c3d6498dd 100644 --- a/libc/inet/rpc/Makefile +++ b/libc/inet/rpc/Makefile @@ -34,7 +34,7 @@ CSRC = auth_none.c auth_unix.c authunix_prot.c \ rpc_dtablesize.c rpc_prot.c svc.c svc_auth.c \ svc_auth_unix.c svc_raw.c svc_run.c svc_simple.c \ svc_tcp.c svc_udp.c xdr.c xdr_array.c xdr_float.c \ - xdr_mem.c xdr_rec.c xdr_reference.c xdr_stdio.c + xdr_mem.c xdr_rec.c xdr_reference.c xdr_stdio.c rcmd.c COBJS=$(patsubst %.c,%.o, $(CSRC)) OBJS=$(COBJS) diff --git a/libc/inet/rpc/rcmd.c b/libc/inet/rpc/rcmd.c new file mode 100644 index 000000000..511d28593 --- /dev/null +++ b/libc/inet/rpc/rcmd.c @@ -0,0 +1,669 @@ +/* + * Copyright (c) 1983, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; +#endif /* LIBC_SCCS and not lint */ + +#define __FORCE_GLIBC +#include <features.h> +#include <sys/param.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ctype.h> +#define __USE_GNU +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <alloca.h> +#include <signal.h> +#include <fcntl.h> +#include <netdb.h> +#include <unistd.h> +#include <pwd.h> + +#include <netdb.h> + +/* hmm. uClibc seems to have that, but it doesn't work for some reason */ +#define getc_unlocked getc + + +/* some forward declarations */ +static int __ivaliduser2(FILE *hostf, u_int32_t raddr, + const char *luser, const char *ruser, const char *rhost); +static int iruserok2 (u_int32_t raddr, int superuser, const char *ruser, + const char *luser, const char *rhost); + + +int rcmd(ahost, rport, locuser, remuser, cmd, fd2p) + char **ahost; + u_short rport; + const char *locuser, *remuser, *cmd; + int *fd2p; +{ +#ifdef _LIBC_REENTRANT + int herr; + struct hostent hostbuf; + size_t hstbuflen; + char *tmphstbuf; +#endif + struct hostent *hp; + struct sockaddr_in sin, from; + struct pollfd pfd[2]; + int32_t oldmask; + pid_t pid; + int s, lport, timo; + char c; + + pid = getpid(); + +#ifdef _LIBC_REENTRANT + hstbuflen = 1024; + tmphstbuf = alloca (hstbuflen); + + while (__gethostbyname_r (*ahost, &hostbuf, tmphstbuf, hstbuflen, + &hp, &herr) != 0 + || hp == NULL) + if (herr != NETDB_INTERNAL || errno != ERANGE) + { + __set_h_errno (herr); + herror(*ahost); + return -1; + } + else + { + /* Enlarge the buffer. */ + hstbuflen *= 2; + tmphstbuf = alloca (hstbuflen); + } +#else /* call the non-reentrant version */ + if ((hp = gethostbyname(*ahost)) == NULL) { + return -1; + } +#endif + pfd[0].events = POLLIN; + pfd[1].events = POLLIN; + + *ahost = hp->h_name; + oldmask = sigblock(sigmask(SIGURG)); /* __sigblock */ + for (timo = 1, lport = IPPORT_RESERVED - 1;;) { + s = rresvport(&lport); + if (s < 0) { + if (errno == EAGAIN) + (void)fprintf(stderr, + "rcmd: socket: All ports in use\n"); + else + (void)fprintf(stderr, "rcmd: socket: %m\n"); + sigsetmask(oldmask); /* sigsetmask */ + return -1; + } + fcntl(s, F_SETOWN, pid); /* __fcntl */ + sin.sin_family = hp->h_addrtype; + bcopy(hp->h_addr_list[0], &sin.sin_addr, + MIN (sizeof (sin.sin_addr), hp->h_length)); + sin.sin_port = rport; + if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) /* __connect */ + break; + (void)close(s); /* __close */ + if (errno == EADDRINUSE) { + lport--; + continue; + } + if (errno == ECONNREFUSED && timo <= 16) { + (void)sleep(timo); /* __sleep */ + timo *= 2; + continue; + } + if (hp->h_addr_list[1] != NULL) { + int oerrno = errno; + + (void)fprintf(stderr, "connect to address %s: ", + inet_ntoa(sin.sin_addr)); + __set_errno (oerrno); + perror(0); + hp->h_addr_list++; + bcopy(hp->h_addr_list[0], &sin.sin_addr, + MIN (sizeof (sin.sin_addr), hp->h_length)); + (void)fprintf(stderr, "Trying %s...\n", + inet_ntoa(sin.sin_addr)); + continue; + } + (void)fprintf(stderr, "%s: %m\n", hp->h_name); + sigsetmask(oldmask); /* __sigsetmask */ + return -1; + } + lport--; + if (fd2p == 0) { + write(s, "", 1); /* __write */ + lport = 0; + } else { + char num[8]; + int s2 = rresvport(&lport), s3; + size_t len = sizeof(from); + + if (s2 < 0) + goto bad; + listen(s2, 1); + (void)snprintf(num, sizeof(num), "%d", lport); /* __snprintf */ + if (write(s, num, strlen(num)+1) != strlen(num)+1) { + (void)fprintf(stderr, + "rcmd: write (setting up stderr): %m\n"); + (void)close(s2); /* __close */ + goto bad; + } + pfd[0].fd = s; + pfd[1].fd = s2; + __set_errno (0); + if (poll (pfd, 2, -1) < 1 || (pfd[1].revents & POLLIN) == 0){ + if (errno != 0) + (void)fprintf(stderr, "rcmd: poll (setting up stderr): %m\n"); + else + (void)fprintf(stderr, "poll: protocol failure in circuit setup\n"); + (void)close(s2); + goto bad; + } + s3 = accept(s2, (struct sockaddr *)&from, &len); + (void)close(s2); + if (s3 < 0) { + (void)fprintf(stderr, + "rcmd: accept: %m\n"); + lport = 0; + goto bad; + } + *fd2p = s3; + from.sin_port = ntohs((u_short)from.sin_port); + if (from.sin_family != AF_INET || + from.sin_port >= IPPORT_RESERVED || + from.sin_port < IPPORT_RESERVED / 2) { + (void)fprintf(stderr, + "socket: protocol failure in circuit setup\n"); + goto bad2; + } + } + (void)write(s, locuser, strlen(locuser)+1); + (void)write(s, remuser, strlen(remuser)+1); + (void)write(s, cmd, strlen(cmd)+1); + if (read(s, &c, 1) != 1) { + (void)fprintf(stderr, + "rcmd: %s: %m\n", *ahost); + goto bad2; + } + if (c != 0) { + while (read(s, &c, 1) == 1) { + (void)write(STDERR_FILENO, &c, 1); + if (c == '\n') + break; + } + goto bad2; + } + sigsetmask(oldmask); + return s; +bad2: + if (lport) + (void)close(*fd2p); +bad: + (void)close(s); + sigsetmask(oldmask); + return -1; +} + +int rresvport(int *alport) +{ + struct sockaddr_in sin; + int s; + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + return -1; + for (;;) { + sin.sin_port = htons((u_short)*alport); + if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) + return s; + if (errno != EADDRINUSE) { + (void)close(s); + return -1; + } + (*alport)--; + if (*alport == IPPORT_RESERVED/2) { + (void)close(s); + __set_errno (EAGAIN); /* close */ + return -1; + } + } + + return -1; +} + +int __check_rhosts_file = 1; +char *__rcmd_errstr; + +int ruserok(rhost, superuser, ruser, luser) + const char *rhost, *ruser, *luser; + int superuser; +{ + struct hostent *hp; + size_t buflen; + char *buffer; + u_int32_t addr; + char **ap; +#ifdef _LIBC_REENTRANT + int herr; + struct hostent hostbuf; +#endif + + buflen = 1024; + buffer = alloca (buflen); + +#ifdef _LIBC_REENTRANT + while (__gethostbyname_r (rhost, &hostbuf, buffer, buflen, &hp, &herr) + != 0 + || hp == NULL) + if (herr != NETDB_INTERNAL || errno != ERANGE) + return -1; + else + { + /* Enlarge the buffer. */ + buflen *= 2; + buffer = alloca (buflen); + } +#else + if ((hp = gethostbyname(rhost)) == NULL) { + return -1; + } +#endif + for (ap = hp->h_addr_list; *ap; ++ap) { + bcopy(*ap, &addr, sizeof(addr)); + if (iruserok2(addr, superuser, ruser, luser, rhost) == 0) + return 0; + } + return -1; +} + + +/* Extremely paranoid file open function. */ +static FILE * +iruserfopen (char *file, uid_t okuser) +{ + struct stat st; + char *cp = NULL; + FILE *res = NULL; + + /* If not a regular file, if owned by someone other than user or + root, if writeable by anyone but the owner, or if hardlinked + anywhere, quit. */ + cp = NULL; + if (lstat (file, &st)) + cp = "lstat failed"; + else if (!S_ISREG (st.st_mode)) + cp = "not regular file"; + else + { + res = fopen (file, "r"); + if (!res) + cp = "cannot open"; + else if (fstat (fileno (res), &st) < 0) + cp = "fstat failed"; + else if (st.st_uid && st.st_uid != okuser) + cp = "bad owner"; + else if (st.st_mode & (S_IWGRP|S_IWOTH)) + cp = "writeable by other than owner"; + else if (st.st_nlink > 1) + cp = "hard linked somewhere"; + } + + /* If there were any problems, quit. */ + if (cp != NULL) + { + __rcmd_errstr = cp; + if (res) + fclose (res); + return NULL; + } + + return res; +} + + +/* + * New .rhosts strategy: We are passed an ip address. We spin through + * hosts.equiv and .rhosts looking for a match. When the .rhosts only + * has ip addresses, we don't have to trust a nameserver. When it + * contains hostnames, we spin through the list of addresses the nameserver + * gives us and look for a match. + * + * Returns 0 if ok, -1 if not ok. + */ +static int +iruserok2 (raddr, superuser, ruser, luser, rhost) + u_int32_t raddr; + int superuser; + const char *ruser, *luser, *rhost; +{ + FILE *hostf = NULL; + int isbad = -1; + + if (!superuser) + hostf = iruserfopen (_PATH_HEQUIV, 0); + + if (hostf) { + isbad = __ivaliduser2 (hostf, raddr, luser, ruser, rhost); + fclose (hostf); + + if (!isbad) + return 0; + } + if (__check_rhosts_file || superuser) { + char *pbuf; + struct passwd *pwd; + size_t dirlen; + uid_t uid; + +#ifdef _LIBC_REENTRANT + size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX); + char *buffer = alloca (buflen); + struct passwd pwdbuf; + + if (__getpwnam_r (luser, &pwdbuf, buffer, buflen, &pwd) != 0 + || pwd == NULL) + return -1; +#else + if ((pwd = getpwnam(luser)) == NULL) + return -1; +#endif + + dirlen = strlen (pwd->pw_dir); + pbuf = alloca (dirlen + sizeof "/.rhosts"); + strcpy (pbuf, pwd->pw_dir); + strcat (pbuf, "/.rhosts"); + + /* Change effective uid while reading .rhosts. If root and + reading an NFS mounted file system, can't read files that + are protected read/write owner only. */ + uid = geteuid (); + seteuid (pwd->pw_uid); + hostf = iruserfopen (pbuf, pwd->pw_uid); + + if (hostf != NULL) { + isbad = __ivaliduser2 (hostf, raddr, luser, ruser, rhost); + fclose (hostf); + } + + seteuid (uid); + return isbad; + } + return -1; +} + +/* This is the exported version. */ +int iruserok (raddr, superuser, ruser, luser) + u_int32_t raddr; + int superuser; + const char *ruser, *luser; +{ + return iruserok2 (raddr, superuser, ruser, luser, "-"); +} + + +/* + * XXX + * Don't make static, used by lpd(8). + * + * This function is not used anymore. It is only present because lpd(8) + * calls it (!?!). We simply call __invaliduser2() with an illegal rhost + * argument. This means that netgroups won't work in .rhost/hosts.equiv + * files. If you want lpd to work with netgroups, fix lpd to use ruserok() + * or PAM. + * Returns 0 if ok, -1 if not ok. + */ +int +__ivaliduser(hostf, raddr, luser, ruser) + FILE *hostf; + u_int32_t raddr; + const char *luser, *ruser; +{ + return __ivaliduser2(hostf, raddr, luser, ruser, "-"); +} + + +/* Returns 1 on positive match, 0 on no match, -1 on negative match. */ +static int +__icheckhost (raddr, lhost, rhost) + u_int32_t raddr; + char *lhost; + const char *rhost; +{ + struct hostent *hp; + size_t buflen; + char *buffer; + int save_errno; + u_int32_t laddr; + int negate=1; /* Multiply return with this to get -1 instead of 1 */ + char **pp; +#ifdef _LIBC_REENTRANT + struct hostent hostbuf; + int herr; +#endif + +#ifdef HAVE_NETGROUP + /* Check nis netgroup. */ + if (strncmp ("+@", lhost, 2) == 0) + return innetgr (&lhost[2], rhost, NULL, NULL); + + if (strncmp ("-@", lhost, 2) == 0) + return -innetgr (&lhost[2], rhost, NULL, NULL); +#endif /* HAVE_NETGROUP */ + + /* -host */ + if (strncmp ("-", lhost,1) == 0) { + negate = -1; + lhost++; + } else if (strcmp ("+",lhost) == 0) { + return 1; /* asking for trouble, but ok.. */ + } + + /* Try for raw ip address first. */ + if (isdigit (*lhost) && (long) (laddr = inet_addr (lhost)) != -1) + return negate * (! (raddr ^ laddr)); + + /* Better be a hostname. */ + buflen = 1024; + buffer = alloca (buflen); + save_errno = errno; + +#ifdef _LIBC_REENTRANT + while (__gethostbyname_r (lhost, &hostbuf, buffer, buflen, &hp, &herr) + != 0) + if (herr != NETDB_INTERNAL || errno != ERANGE) + return (0); + else { + /* Enlarge the buffer. */ + buflen *= 2; + buffer = alloca (buflen); + __set_errno (0); + } + __set_errno (save_errno); + if (hp == NULL) + return 0; +#else + if ((hp = gethostbyname(lhost)) == NULL) + return 0; +#endif /* _LIBC_REENTRANT */ + + /* Spin through ip addresses. */ + for (pp = hp->h_addr_list; *pp; ++pp) + if (!memcmp (&raddr, *pp, sizeof (u_int32_t))) + return negate; + + /* No match. */ + return (0); +} + +/* Returns 1 on positive match, 0 on no match, -1 on negative match. */ +static int +__icheckuser (luser, ruser) + const char *luser, *ruser; +{ + + /* + luser is user entry from .rhosts/hosts.equiv file + ruser is user id on remote host + */ + +#ifdef HAVE_NETGROUP + /* [-+]@netgroup */ + if (strncmp ("+@", luser, 2) == 0) + return innetgr (&luser[2], NULL, ruser, NULL); + + if (strncmp ("-@", luser,2) == 0) + return -innetgr (&luser[2], NULL, ruser, NULL); +#endif /* HAVE_NETGROUP */ + + /* -user */ + if (strncmp ("-", luser, 1) == 0) + return -(strcmp (&luser[1], ruser) == 0); + + /* + */ + if (strcmp ("+", luser) == 0) + return 1; + + /* simple string match */ + return strcmp (ruser, luser) == 0; +} + +/* + * Returns 1 for blank lines (or only comment lines) and 0 otherwise + */ +static int +__isempty(p) + char *p; +{ + while (*p && isspace (*p)) { + ++p; + } + + return (*p == '\0' || *p == '#') ? 1 : 0 ; +} + +/* + * Returns 0 if positive match, -1 if _not_ ok. + */ +static int +__ivaliduser2(hostf, raddr, luser, ruser, rhost) + FILE *hostf; + u_int32_t raddr; + const char *luser, *ruser, *rhost; +{ + register const char *user; + register char *p; + int hcheck, ucheck; + char *buf = NULL; + size_t bufsize = 0; + int retval = -1; + + while (getline (&buf, &bufsize, hostf) > 0) { + buf[bufsize - 1] = '\0'; /* Make sure it's terminated. */ + p = buf; + + /* Skip empty or comment lines */ + if (__isempty (p)) { + continue; + } + + /* Skip lines that are too long. */ + if (strchr (p, '\n') == NULL) { + int ch = getc_unlocked (hostf); + + while (ch != '\n' && ch != EOF) + ch = getc_unlocked (hostf); + continue; + } + + for (;*p && !isspace(*p); ++p) { + *p = tolower (*p); + } + + /* Next we want to find the permitted name for the remote user. */ + if (*p == ' ' || *p == '\t') { + /* <nul> terminate hostname and skip spaces */ + for (*p++='\0'; *p && isspace (*p); ++p); + + user = p; /* this is the user's name */ + while (*p && !isspace (*p)) + ++p; /* find end of user's name */ + } else + user = p; + + *p = '\0'; /* <nul> terminate username (+host?) */ + + /* buf -> host(?) ; user -> username(?) */ + + /* First check host part */ + hcheck = __icheckhost (raddr, buf, rhost); + + if (hcheck < 0) + break; + + if (hcheck) { + /* Then check user part */ + if (! (*user)) + user = luser; + + ucheck = __icheckuser (user, ruser); + + /* Positive 'host user' match? */ + if (ucheck > 0) { + retval = 0; + break; + } + + /* Negative 'host -user' match? */ + if (ucheck < 0) + break; + + /* Neither, go on looking for match */ + } + } + + if (buf != NULL) + free (buf); + + return retval; +} |