- check for "*" when looking up wildcard in LDAP.
- fix couple of edge case parse fails of timeout option.
- add SEARCH_BASE configuration option.
- add random selection as a master map entry option.
- re-read config on HUP signal.
- add LDAP_URI, LDAP_TIMEOUT and LDAP_NETWORK_TIMEOUT configuration
options.
- fix deadlock in submount mount module.
- fix lack of ferror() checking when reading files.
- fix typo in autofs(5) man page.
- fix map entry expansion when undefined macro is present.
- remove unused export validation code.
- add dynamic logging (adapted from v4 patch from Jeff Moyer).
- fix recursive loopback mounts (Matthias Koenig).
- add map re-load to verbose logging.
- fix handling of LDAP base dns with spaces.
- handle MTAB_NOTUPDATED status return from mount.
- when default master map, auto.master, is used also check for auto_master.
- update negative mount timeout handling.
- fix large group handling (Ryan Thomas).
- fix for dynamic logging breaking non-sasl build (Guillaume Rousse).
- eliminate NULL proc ping for singleton host or local mounts.
612 lines
13 KiB
Diff
612 lines
13 KiB
Diff
diff --git a/lib/rpc_subs.c b/lib/rpc_subs.c
|
|
index 831d456..d79a94f 100644
|
|
--- a/lib/rpc_subs.c
|
|
+++ b/lib/rpc_subs.c
|
|
@@ -52,10 +52,7 @@
|
|
/* Get numeric value of the n bits starting at position p */
|
|
#define getbits(x, p, n) ((x >> (p + 1 - n)) & ~(~0 << n))
|
|
|
|
-static char *domain = NULL;
|
|
-
|
|
inline void dump_core(void);
|
|
-static pthread_mutex_t networks_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
/*
|
|
* Create a UDP RPC client
|
|
@@ -764,573 +761,6 @@ void rpc_exports_free(exports list)
|
|
return;
|
|
}
|
|
|
|
-static int masked_match(const char *addr, const char *mask)
|
|
-{
|
|
- char buf[MAX_IFC_BUF], *ptr;
|
|
- struct sockaddr_in saddr;
|
|
- struct sockaddr_in6 saddr6;
|
|
- struct ifconf ifc;
|
|
- struct ifreq *ifr;
|
|
- int sock, cl_flags, ret, i, is_ipv4, is_ipv6;
|
|
- unsigned int msize;
|
|
-
|
|
- sock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
- if (sock < 0) {
|
|
- char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
- error(LOGOPT_ANY, "socket creation failed: %s", estr);
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if ((cl_flags = fcntl(sock, F_GETFD, 0)) != -1) {
|
|
- cl_flags |= FD_CLOEXEC;
|
|
- fcntl(sock, F_SETFD, cl_flags);
|
|
- }
|
|
-
|
|
- ifc.ifc_len = sizeof(buf);
|
|
- ifc.ifc_req = (struct ifreq *) buf;
|
|
- ret = ioctl(sock, SIOCGIFCONF, &ifc);
|
|
- if (ret == -1) {
|
|
- close(sock);
|
|
- char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
- error(LOGOPT_ANY, "ioctl: %s", estr);
|
|
- return 0;
|
|
- }
|
|
-
|
|
- is_ipv4 = is_ipv6 = 0;
|
|
- is_ipv4 = inet_pton(AF_INET, addr, &saddr.sin_addr);
|
|
- if (!is_ipv4)
|
|
- is_ipv6 = inet_pton(AF_INET6, addr, &saddr6.sin6_addr);
|
|
-
|
|
- if (strchr(mask, '.')) {
|
|
- struct sockaddr_in maddr;
|
|
- uint32_t ma;
|
|
- int i = 0;
|
|
-
|
|
- ret = inet_aton(mask, &maddr.sin_addr);
|
|
- if (!ret) {
|
|
- close(sock);
|
|
- return 0;
|
|
- }
|
|
-
|
|
- ma = ntohl((uint32_t) maddr.sin_addr.s_addr);
|
|
- while (!(ma & 1)) {
|
|
- i++;
|
|
- ma = ma >> 1;
|
|
- }
|
|
-
|
|
- msize = i;
|
|
- } else
|
|
- msize = atoi(mask);
|
|
-
|
|
- i = 0;
|
|
- ptr = (char *) &ifc.ifc_buf[0];
|
|
-
|
|
- while (ptr < buf + ifc.ifc_len) {
|
|
- ifr = (struct ifreq *) ptr;
|
|
-
|
|
- switch (ifr->ifr_addr.sa_family) {
|
|
- case AF_INET:
|
|
- {
|
|
- struct sockaddr_in *if_addr;
|
|
- uint32_t m, ia, ha;
|
|
-
|
|
- if (!is_ipv4 || msize > 32)
|
|
- break;
|
|
-
|
|
- m = -1;
|
|
- m = m << (32 - msize);
|
|
- ha = ntohl((uint32_t) saddr.sin_addr.s_addr);
|
|
-
|
|
- if_addr = (struct sockaddr_in *) &ifr->ifr_addr;
|
|
- ia = ntohl((uint32_t) if_addr->sin_addr.s_addr);
|
|
-
|
|
- if ((ia & m) == (ha & m)) {
|
|
- close(sock);
|
|
- return 1;
|
|
- }
|
|
- break;
|
|
- }
|
|
-
|
|
- /* glibc rpc only understands IPv4 atm */
|
|
- case AF_INET6:
|
|
- break;
|
|
-
|
|
- default:
|
|
- break;
|
|
- }
|
|
-
|
|
- i++;
|
|
- ptr = (char *) &ifc.ifc_req[i];
|
|
- }
|
|
-
|
|
- close(sock);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/*
|
|
- * This function has been adapted from the match_patern function
|
|
- * found in OpenSSH and is used in accordance with the copyright
|
|
- * notice found their.
|
|
- *
|
|
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland.
|
|
- */
|
|
-/*
|
|
- * Returns true if the given string matches the pattern (which
|
|
- * may contain ? and * as wildcards), and zero if it does not
|
|
- * match.
|
|
- */
|
|
-static int pattern_match(const char *s, const char *pattern)
|
|
-{
|
|
- for (;;) {
|
|
- /* If at end of pattern, accept if also at end of string. */
|
|
- if (!*pattern)
|
|
- return !*s;
|
|
-
|
|
- if (*pattern == '*') {
|
|
- /* Skip the asterisk. */
|
|
- pattern++;
|
|
-
|
|
- /* If at end of pattern, accept immediately. */
|
|
- if (!*pattern)
|
|
- return 1;
|
|
-
|
|
- /* If next character in pattern is known, optimize. */
|
|
- if (*pattern != '?' && *pattern != '*') {
|
|
- /*
|
|
- * Look instances of the next character in
|
|
- * pattern, and try to match starting from
|
|
- * those.
|
|
- */
|
|
- for (; *s; s++)
|
|
- if (*s == *pattern &&
|
|
- pattern_match(s + 1, pattern + 1))
|
|
- return 1;
|
|
-
|
|
- /* Failed. */
|
|
- return 0;
|
|
- }
|
|
- /*
|
|
- * Move ahead one character at a time and try to
|
|
- * match at each position.
|
|
- */
|
|
- for (; *s; s++)
|
|
- if (pattern_match(s, pattern))
|
|
- return 1;
|
|
- /* Failed. */
|
|
- return 0;
|
|
- }
|
|
- /*
|
|
- * There must be at least one more character in the string.
|
|
- * If we are at the end, fail.
|
|
- */
|
|
- if (!*s)
|
|
- return 0;
|
|
-
|
|
- /* Check if the next character of the string is acceptable. */
|
|
- if (*pattern != '?' && *pattern != *s)
|
|
- return 0;
|
|
-
|
|
- /* Move to the next character, both in string and in pattern. */
|
|
- s++;
|
|
- pattern++;
|
|
- }
|
|
- /* NOTREACHED */
|
|
-}
|
|
-
|
|
-static int name_match(const char *name, const char *pattern)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- if (strchr(pattern, '*') || strchr(pattern, '?'))
|
|
- ret = pattern_match(name, pattern);
|
|
- else {
|
|
- ret = !memcmp(name, pattern, strlen(pattern));
|
|
- /* Name could still be a netgroup (Solaris) */
|
|
- if (!ret)
|
|
- ret = innetgr(pattern, name, NULL, domain);
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int fqdn_match(const char *pattern)
|
|
-{
|
|
- char buf[MAX_IFC_BUF], *ptr;
|
|
- struct ifconf ifc;
|
|
- struct ifreq *ifr;
|
|
- int sock, cl_flags, ret, i;
|
|
- char fqdn[NI_MAXHOST + 1];
|
|
-
|
|
- sock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
- if (sock < 0) {
|
|
- char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
- error(LOGOPT_ANY, "socket creation failed: %s", estr);
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if ((cl_flags = fcntl(sock, F_GETFD, 0)) != -1) {
|
|
- cl_flags |= FD_CLOEXEC;
|
|
- fcntl(sock, F_SETFD, cl_flags);
|
|
- }
|
|
-
|
|
- ifc.ifc_len = sizeof(buf);
|
|
- ifc.ifc_req = (struct ifreq *) buf;
|
|
- ret = ioctl(sock, SIOCGIFCONF, &ifc);
|
|
- if (ret == -1) {
|
|
- close(sock);
|
|
- char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
- error(LOGOPT_ANY, "ioctl: %s", estr);
|
|
- return 0;
|
|
- }
|
|
-
|
|
- i = 0;
|
|
- ptr = (char *) &ifc.ifc_buf[0];
|
|
-
|
|
- while (ptr < buf + ifc.ifc_len) {
|
|
- ifr = (struct ifreq *) ptr;
|
|
-
|
|
- switch (ifr->ifr_addr.sa_family) {
|
|
- case AF_INET:
|
|
- {
|
|
- socklen_t slen = sizeof(struct sockaddr);
|
|
-
|
|
- ret = getnameinfo(&ifr->ifr_addr, slen, fqdn,
|
|
- NI_MAXHOST, NULL, 0, NI_NAMEREQD);
|
|
- if (!ret) {
|
|
- ret = name_match(fqdn, pattern);
|
|
- if (ret) {
|
|
- close(sock);
|
|
- return 1;
|
|
- }
|
|
- }
|
|
- break;
|
|
- }
|
|
-
|
|
- /* glibc rpc only understands IPv4 atm */
|
|
- case AF_INET6:
|
|
- break;
|
|
-
|
|
- default:
|
|
- break;
|
|
- }
|
|
-
|
|
- i++;
|
|
- ptr = (char *) &ifc.ifc_req[i];
|
|
- }
|
|
-
|
|
- close(sock);
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int string_match(const char *myname, const char *pattern)
|
|
-{
|
|
- struct addrinfo hints, *ni;
|
|
- int ret;
|
|
-
|
|
- /* Try simple name match first */
|
|
- ret = name_match(myname, pattern);
|
|
- if (ret)
|
|
- goto done;
|
|
-
|
|
- memset(&hints, 0, sizeof(hints));
|
|
- hints.ai_flags = AI_CANONNAME;
|
|
- hints.ai_family = 0;
|
|
- hints.ai_socktype = 0;
|
|
-
|
|
- /* See if our canonical name matches */
|
|
- if (getaddrinfo(myname, NULL, &hints, &ni) == 0) {
|
|
- ret = name_match(ni->ai_canonname, pattern);
|
|
- freeaddrinfo(ni);
|
|
- } else
|
|
- warn(LOGOPT_ANY, "name lookup failed: %s", gai_strerror(ret));
|
|
- if (ret)
|
|
- goto done;
|
|
-
|
|
- /* Lastly see if the name of an interfaces matches */
|
|
- ret = fqdn_match(pattern);
|
|
-done:
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static unsigned int inet_get_net_len(uint32_t net)
|
|
-{
|
|
- int i;
|
|
-
|
|
- for (i = 0; i < 32; i += 8) {
|
|
- if (getbits(net, i + 7, 8))
|
|
- break;
|
|
- }
|
|
-
|
|
- return (unsigned int) 32 - i;
|
|
-}
|
|
-
|
|
-static char *inet_fill_net(const char *net_num, char *net)
|
|
-{
|
|
- char *np;
|
|
- unsigned int dots = 3;
|
|
-
|
|
- if (strlen(net_num) > INET_ADDRSTRLEN)
|
|
- return NULL;
|
|
-
|
|
- if (!isdigit(*net_num))
|
|
- return NULL;
|
|
-
|
|
- *net = '\0';
|
|
- strcpy(net, net_num);
|
|
-
|
|
- np = net;
|
|
- while (*np++) {
|
|
- if (*np == '.') {
|
|
- np++;
|
|
- dots--;
|
|
- if (!*np && dots)
|
|
- strcat(net, "0");
|
|
- continue;
|
|
- }
|
|
-
|
|
- if ((*np && !isdigit(*np)) || dots < 0) {
|
|
- *net = '\0';
|
|
- return NULL;
|
|
- }
|
|
- }
|
|
-
|
|
- while (dots--)
|
|
- strcat(net, ".0");
|
|
-
|
|
- return net;
|
|
-}
|
|
-
|
|
-static int match_network(const char *network)
|
|
-{
|
|
- struct netent *pnent, nent;
|
|
- const char *pcnet;
|
|
- char *net, cnet[MAX_NETWORK_LEN], mask[4], *pmask;
|
|
- unsigned int size;
|
|
- size_t l_network = strlen(network) + 1;
|
|
- int status;
|
|
-
|
|
- if (l_network > MAX_NETWORK_LEN) {
|
|
- error(LOGOPT_ANY,
|
|
- "match string \"%s\" too long", network);
|
|
- return 0;
|
|
- }
|
|
-
|
|
- net = alloca(l_network);
|
|
- if (!net)
|
|
- return 0;
|
|
- memset(net, 0, l_network);
|
|
- strcpy(net, network);
|
|
-
|
|
- if ((pmask = strchr(net, '/')))
|
|
- *pmask++ = '\0';
|
|
-
|
|
- status = pthread_mutex_lock(&networks_mutex);
|
|
- if (status)
|
|
- fatal(status);
|
|
-
|
|
- pnent = getnetbyname(net);
|
|
- if (pnent)
|
|
- memcpy(&nent, pnent, sizeof(struct netent));
|
|
-
|
|
- status = pthread_mutex_unlock(&networks_mutex);
|
|
- if (status)
|
|
- fatal(status);
|
|
-
|
|
- if (pnent) {
|
|
- uint32_t n_net;
|
|
-
|
|
- switch (nent.n_addrtype) {
|
|
- case AF_INET:
|
|
- n_net = ntohl(nent.n_net);
|
|
- pcnet = inet_ntop(AF_INET, &n_net, cnet, INET_ADDRSTRLEN);
|
|
- if (!pcnet)
|
|
- return 0;
|
|
-
|
|
- if (!pmask) {
|
|
- size = inet_get_net_len(nent.n_net);
|
|
- if (!size)
|
|
- return 0;
|
|
- }
|
|
- break;
|
|
-
|
|
- case AF_INET6:
|
|
- return 0;
|
|
-
|
|
- default:
|
|
- return 0;
|
|
- }
|
|
- } else {
|
|
- int ret;
|
|
-
|
|
- if (strchr(net, ':')) {
|
|
- return 0;
|
|
- } else {
|
|
- struct in_addr addr;
|
|
-
|
|
- pcnet = inet_fill_net(net, cnet);
|
|
- if (!pcnet)
|
|
- return 0;
|
|
-
|
|
- ret = inet_pton(AF_INET, pcnet, &addr);
|
|
- if (ret <= 0)
|
|
- return 0;
|
|
-
|
|
- if (!pmask) {
|
|
- uint32_t nl_addr = htonl(addr.s_addr);
|
|
- size = inet_get_net_len(nl_addr);
|
|
- if (!size)
|
|
- return 0;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- if (!pmask) {
|
|
- if (sprintf(mask, "%u", size) <= 0)
|
|
- return 0;
|
|
- pmask = mask;
|
|
- }
|
|
-
|
|
- debug(LOGOPT_ANY, "pcnet %s pmask %s", pcnet, pmask);
|
|
-
|
|
- return masked_match(pcnet, pmask);
|
|
-}
|
|
-
|
|
-/*
|
|
- * Two export formats need to be understood to cater for different
|
|
- * NFS server exports.
|
|
- *
|
|
- * (host|wildcard|network[/mask]|@netgroup)
|
|
- *
|
|
- * A host name which can be cannonical.
|
|
- * A wildcard host name containing "*" and "?" with the usual meaning.
|
|
- * A network in numbers and dots form with optional mask given as
|
|
- * either a length or as numbers and dots.
|
|
- * A netgroup identified by the prefix "@".
|
|
- *
|
|
- * [-](host|domain suffix|netgroup|@network[/mask])
|
|
- *
|
|
- * A host name which can be cannonical.
|
|
- * A domain suffix identified by a leading "." which will match all
|
|
- * hosts in the given domain.
|
|
- * A netgroup.
|
|
- * A network identified by the prefix "@" given in numbers and dots
|
|
- * form or as a network name with optional mask given as either a
|
|
- * length or as numbers and dots.
|
|
- * A "-" prefix can be appended to indicate access is denied.
|
|
- */
|
|
-static int host_match(char *pattern)
|
|
-{
|
|
- unsigned int negate = (*pattern == '-');
|
|
- const char *m_pattern = (negate ? pattern + 1 : pattern);
|
|
- char myname[MAXHOSTNAMELEN + 1] = "\0";
|
|
- int ret = 0;
|
|
-
|
|
- if (gethostname(myname, MAXHOSTNAMELEN))
|
|
- return 0;
|
|
-
|
|
- if (yp_get_default_domain(&domain))
|
|
- domain = NULL;
|
|
-
|
|
- if (*m_pattern == '@') {
|
|
- /*
|
|
- * The pattern begins with an "@" so it's a network
|
|
- * spec or it's a netgroup.
|
|
- */
|
|
- ret = match_network(m_pattern + 1);
|
|
- if (!ret)
|
|
- ret = innetgr(m_pattern + 1, myname, NULL, domain);
|
|
- } else if (*m_pattern == '.') {
|
|
- size_t m_len = strlen(m_pattern);
|
|
- char *has_dot = strchr(myname, '.');
|
|
- /*
|
|
- * The pattern starts with a "." so it's a domain spec
|
|
- * of some sort.
|
|
- *
|
|
- * If the host name contains a dot then it must be either
|
|
- * a cannonical name or a simple NIS name.domain. So
|
|
- * perform a string match. Otherwise, append the domain
|
|
- * pattern to our simple name and try a wildcard pattern
|
|
- * match against the interfaces.
|
|
- */
|
|
- if (has_dot) {
|
|
- if (strlen(has_dot) == m_len)
|
|
- ret = !memcmp(has_dot, m_pattern, m_len);
|
|
- } else {
|
|
- char *w_pattern = alloca(m_len + 2);
|
|
- if (w_pattern) {
|
|
- strcpy(w_pattern, "*");
|
|
- strcat(w_pattern, m_pattern);
|
|
- ret = fqdn_match(w_pattern);
|
|
- }
|
|
- }
|
|
- } else if (!strcmp(m_pattern, "gss/krb5")) {
|
|
- /* Leave this to the GSS layer */
|
|
- return 1;
|
|
- } else {
|
|
- /*
|
|
- * Otherwise it's a network name or host name
|
|
- */
|
|
- ret = match_network(m_pattern);
|
|
- if (!ret)
|
|
- /* if not then try to match host name */
|
|
- ret = string_match(myname, m_pattern);
|
|
- }
|
|
-
|
|
- if (negate && ret)
|
|
- ret = -1;
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static int rpc_export_allowed(groups grouplist)
|
|
-{
|
|
- groups grp = grouplist;
|
|
-
|
|
- /* NULL group list => everyone */
|
|
- if (!grp)
|
|
- return 1;
|
|
-
|
|
- while (grp) {
|
|
- int allowed = host_match(grp->gr_name);
|
|
- /* Explicitly denied access */
|
|
- if (allowed == -1)
|
|
- return 0;
|
|
- else if (allowed)
|
|
- return 1;
|
|
- grp = grp->gr_next;
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-exports rpc_exports_prune(exports list)
|
|
-{
|
|
- exports head = list;
|
|
- exports exp;
|
|
- exports last;
|
|
- int res;
|
|
-
|
|
- exp = list;
|
|
- last = NULL;
|
|
- while (exp) {
|
|
- res = rpc_export_allowed(exp->ex_groups);
|
|
- if (!res) {
|
|
- if (last == NULL) {
|
|
- head = exp->ex_next;
|
|
- rpc_export_free(exp);
|
|
- exp = head;
|
|
- } else {
|
|
- last->ex_next = exp->ex_next;
|
|
- rpc_export_free(exp);
|
|
- exp = last->ex_next;
|
|
- }
|
|
- continue;
|
|
- }
|
|
- last = exp;
|
|
- exp = exp->ex_next;
|
|
- }
|
|
- return head;
|
|
-}
|
|
-
|
|
exports rpc_get_exports(const char *host, long seconds, long micros, unsigned int option)
|
|
{
|
|
struct conn_info info;
|
|
diff --git a/modules/lookup_hosts.c b/modules/lookup_hosts.c
|
|
index 1f8fa15..d711611 100644
|
|
--- a/modules/lookup_hosts.c
|
|
+++ b/modules/lookup_hosts.c
|
|
@@ -45,7 +45,6 @@ struct lookup_context {
|
|
int lookup_version = AUTOFS_LOOKUP_VERSION; /* Required by protocol */
|
|
|
|
exports rpc_get_exports(const char *host, long seconds, long micros, unsigned int option);
|
|
-exports rpc_exports_prune(exports list);
|
|
void rpc_exports_free(exports list);
|
|
|
|
int lookup_init(const char *mapfmt, int argc, const char *const *argv, void **context)
|
|
@@ -207,9 +206,6 @@ done:
|
|
|
|
exp = rpc_get_exports(name, 10, 0, RPC_CLOSE_NOLINGER);
|
|
|
|
- /* Check exports for obvious ones we don't have access to */
|
|
- /*exp = rpc_exports_prune(exp);*/
|
|
-
|
|
mapent = NULL;
|
|
while (exp) {
|
|
if (mapent) {
|