From e9648d2ce662fb0d79072496682efb0718c01e66 Mon Sep 17 00:00:00 2001 From: guenther <> Date: Wed, 18 Nov 2009 07:43:22 +0000 Subject: More shrinkage, a bit for ramdisks but mostly for static binaries: - wrap with #ifndef NO_LOG_BAD_DNS_RESPONSES libc code that uses p_class() and p_type() for diagnostics, then add that define to libstub to avoid pulling in res_debug_syms.o - split rcmd() and ruserok() into separate files, as nothing uses both - split readdir_r() to its own file - split syslog_r() from syslog(), as the latter needs localtime(); many binaries no longer need to pull in all the time code after this; switch from usleep() to nanosleep() while we're at it (The profit of analysis of -Wl,-M,--cref output) Chops 888kB from /bin and /sbin on i386 ok deraadt@, miod@ --- src/lib/libc/net/ruserok.c | 437 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 src/lib/libc/net/ruserok.c (limited to 'src/lib/libc/net/ruserok.c') diff --git a/src/lib/libc/net/ruserok.c b/src/lib/libc/net/ruserok.c new file mode 100644 index 0000000000..5ef078ac95 --- /dev/null +++ b/src/lib/libc/net/ruserok.c @@ -0,0 +1,437 @@ +/* + * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved. + * 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. 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. + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int __ivaliduser(FILE *, in_addr_t, const char *, const char *); +int __ivaliduser_sa(FILE *, struct sockaddr *, socklen_t, + const char *, const char *); +static int __icheckhost(struct sockaddr *, socklen_t, const char *); +static char *__gethostloop(struct sockaddr *, socklen_t); + +int __check_rhosts_file = 1; +char *__rcmd_errstr; + +int +ruserok(const char *rhost, int superuser, const char *ruser, const char *luser) +{ + struct addrinfo hints, *res, *r; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + error = getaddrinfo(rhost, "0", &hints, &res); + if (error) + return (-1); + + for (r = res; r; r = r->ai_next) { + if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser, + luser) == 0) { + freeaddrinfo(res); + return (0); + } + } + freeaddrinfo(res); + return (-1); +} + +/* + * 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. + */ +int +iruserok(u_int32_t raddr, int superuser, const char *ruser, const char *luser) +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); + return iruserok_sa(&sin, sizeof(struct sockaddr_in), superuser, ruser, + luser); +} + +int +iruserok_sa(const void *raddr, int rlen, int superuser, const char *ruser, + const char *luser) +{ + struct sockaddr *sa; + char *cp; + struct stat sbuf; + struct passwd *pwd; + FILE *hostf; + uid_t uid; + int first; + char pbuf[MAXPATHLEN]; + + sa = (struct sockaddr *)raddr; + first = 1; + hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); +again: + if (hostf) { + if (__ivaliduser_sa(hostf, sa, rlen, luser, ruser) == 0) { + (void)fclose(hostf); + return (0); + } + (void)fclose(hostf); + } + if (first == 1 && (__check_rhosts_file || superuser)) { + int len; + + first = 0; + if ((pwd = getpwnam(luser)) == NULL) + return (-1); + len = snprintf(pbuf, sizeof pbuf, "%s/.rhosts", pwd->pw_dir); + if (len < 0 || len >= sizeof pbuf) + return (-1); + + /* + * Change effective uid while opening .rhosts. If root and + * reading an NFS mounted file system, can't read files that + * are protected read/write owner only. + */ + uid = geteuid(); + (void)seteuid(pwd->pw_uid); + hostf = fopen(pbuf, "r"); + (void)seteuid(uid); + + if (hostf == NULL) + return (-1); + /* + * If not a regular file, or is owned by someone other than + * user or root or if writeable by anyone but the owner, quit. + */ + cp = NULL; + if (lstat(pbuf, &sbuf) < 0) + cp = ".rhosts lstat failed"; + else if (!S_ISREG(sbuf.st_mode)) + cp = ".rhosts not regular file"; + else if (fstat(fileno(hostf), &sbuf) < 0) + cp = ".rhosts fstat failed"; + else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) + cp = "bad .rhosts owner"; + else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) + cp = ".rhosts writable by other than owner"; + /* If there were any problems, quit. */ + if (cp) { + __rcmd_errstr = cp; + (void)fclose(hostf); + return (-1); + } + goto again; + } + return (-1); +} + +/* + * XXX + * Don't make static, used by lpd(8). + * + * Returns 0 if ok, -1 if not ok. + */ +int +__ivaliduser(FILE *hostf, in_addr_t raddrl, const char *luser, + const char *ruser) +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_len = sizeof(struct sockaddr_in); + memcpy(&sin.sin_addr, &raddrl, sizeof(sin.sin_addr)); + return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len, + luser, ruser); +} + +int +__ivaliduser_sa(FILE *hostf, struct sockaddr *raddr, socklen_t salen, + const char *luser, const char *ruser) +{ + char *user, *p; + char *buf; + const char *auser, *ahost; + int hostok, userok; + char *rhost = (char *)-1; + char domain[MAXHOSTNAMELEN]; + size_t buflen; + + getdomainname(domain, sizeof(domain)); + + while ((buf = fgetln(hostf, &buflen))) { + p = buf; + if (*p == '#') + continue; + while (p < buf + buflen && *p != '\n' && *p != ' ' && *p != '\t') { + if (!isprint(*p)) + goto bail; + *p = isupper(*p) ? tolower(*p) : *p; + p++; + } + if (p >= buf + buflen) + continue; + if (*p == ' ' || *p == '\t') { + *p++ = '\0'; + while (p < buf + buflen && (*p == ' ' || *p == '\t')) + p++; + if (p >= buf + buflen) + continue; + user = p; + while (p < buf + buflen && *p != '\n' && *p != ' ' && + *p != '\t') { + if (!isprint(*p)) + goto bail; + p++; + } + } else + user = p; + *p = '\0'; + + if (p == buf) + continue; + + auser = *user ? user : luser; + ahost = buf; + + if (strlen(ahost) >= MAXHOSTNAMELEN) + continue; + + /* + * innetgr() must lookup a hostname (we do not attempt + * to change the semantics so that netgroups may have + * #.#.#.# addresses in the list.) + */ + if (ahost[0] == '+') + switch (ahost[1]) { + case '\0': + hostok = 1; + break; + case '@': + if (rhost == (char *)-1) + rhost = __gethostloop(raddr, salen); + hostok = 0; + if (rhost) + hostok = innetgr(&ahost[2], rhost, + NULL, domain); + break; + default: + hostok = __icheckhost(raddr, salen, &ahost[1]); + break; + } + else if (ahost[0] == '-') + switch (ahost[1]) { + case '\0': + hostok = -1; + break; + case '@': + if (rhost == (char *)-1) + rhost = __gethostloop(raddr, salen); + hostok = 0; + if (rhost) + hostok = -innetgr(&ahost[2], rhost, + NULL, domain); + break; + default: + hostok = -__icheckhost(raddr, salen, &ahost[1]); + break; + } + else + hostok = __icheckhost(raddr, salen, ahost); + + + if (auser[0] == '+') + switch (auser[1]) { + case '\0': + userok = 1; + break; + case '@': + userok = innetgr(&auser[2], NULL, ruser, + domain); + break; + default: + userok = strcmp(ruser, &auser[1]) ? 0 : 1; + break; + } + else if (auser[0] == '-') + switch (auser[1]) { + case '\0': + userok = -1; + break; + case '@': + userok = -innetgr(&auser[2], NULL, ruser, + domain); + break; + default: + userok = strcmp(ruser, &auser[1]) ? 0 : -1; + break; + } + else + userok = strcmp(ruser, auser) ? 0 : 1; + + /* Check if one component did not match */ + if (hostok == 0 || userok == 0) + continue; + + /* Check if we got a forbidden pair */ + if (userok <= -1 || hostok <= -1) + return (-1); + + /* Check if we got a valid pair */ + if (hostok >= 1 && userok >= 1) + return (0); + } +bail: + return (-1); +} + +/* + * Returns "true" if match, 0 if no match. If we do not find any + * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work. + */ +static int +__icheckhost(struct sockaddr *raddr, socklen_t salen, const char *lhost) +{ + struct addrinfo hints, *res, *r; + char h1[NI_MAXHOST], h2[NI_MAXHOST]; + int error; + const int niflags = NI_NUMERICHOST; + + h1[0] = '\0'; + if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, + niflags) != 0) + return (0); + + /* Resolve laddr into sockaddr */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = raddr->sa_family; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + res = NULL; + error = getaddrinfo(lhost, "0", &hints, &res); + if (error) + return (0); + + /* + * Try string comparisons between raddr and laddr. + */ + for (r = res; r; r = r->ai_next) { + h2[0] = '\0'; + if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), + NULL, 0, niflags) != 0) + continue; + if (strcmp(h1, h2) == 0) { + freeaddrinfo(res); + return (1); + } + } + + /* No match. */ + freeaddrinfo(res); + return (0); +} + +/* + * Return the hostname associated with the supplied address. + * Do a reverse lookup as well for security. If a loop cannot + * be found, pack the result of inet_ntoa() into the string. + */ +static char * +__gethostloop(struct sockaddr *raddr, socklen_t salen) +{ + static char remotehost[NI_MAXHOST]; + char h1[NI_MAXHOST], h2[NI_MAXHOST]; + struct addrinfo hints, *res, *r; + int error; + const int niflags = NI_NUMERICHOST; + + h1[0] = remotehost[0] = '\0'; + if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost), + NULL, 0, NI_NAMEREQD) != 0) + return (NULL); + if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, + niflags) != 0) + return (NULL); + + /* + * Look up the name and check that the supplied + * address is in the list + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = raddr->sa_family; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_CANONNAME; + res = NULL; + error = getaddrinfo(remotehost, "0", &hints, &res); + if (error) + return (NULL); + + for (r = res; r; r = r->ai_next) { + h2[0] = '\0'; + if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), + NULL, 0, niflags) != 0) + continue; + if (strcmp(h1, h2) == 0) { + freeaddrinfo(res); + return (remotehost); + } + } + + /* + * either the DNS adminstrator has made a configuration + * mistake, or someone has attempted to spoof us + */ + syslog(LOG_NOTICE, "rcmd: address %s not listed for host %s", + h1, res->ai_canonname ? res->ai_canonname : remotehost); + freeaddrinfo(res); + return (NULL); +} -- cgit v1.2.3-55-g6feb