diff --git a/aclocal/libcap.m4 b/aclocal/libcap.m4 index eabe507..68a624c 100644 --- a/aclocal/libcap.m4 +++ b/aclocal/libcap.m4 @@ -5,11 +5,19 @@ AC_DEFUN([AC_LIBCAP], [ dnl look for prctl AC_CHECK_FUNC([prctl], , ) - dnl look for the library; do not add to LIBS if found - AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) - AC_SUBST(LIBCAP) + AC_ARG_ENABLE([caps], + [AS_HELP_STRING([--disable-caps], [Disable capabilities support])]) + + LIBCAP= + + if test "x$enable_caps" != "xno" ; then + dnl look for the library; do not add to LIBS if found + AC_CHECK_LIB([cap], [cap_get_proc], [LIBCAP=-lcap], ,) - AC_CHECK_HEADERS([sys/capability.h], , - [AC_MSG_ERROR([libcap headers not found.])]) + AC_CHECK_HEADERS([sys/capability.h], , + [test "x$enable_caps" = "xyes" && AC_MSG_ERROR([libcap headers not found.])]) + fi + + AC_SUBST(LIBCAP) ])dnl diff --git a/autogen.sh b/autogen.sh old mode 100644 new mode 100755 diff --git a/configure.ac b/configure.ac index b7520d8..7c9e61a 100644 --- a/configure.ac +++ b/configure.ac @@ -81,7 +81,7 @@ AC_ARG_ENABLE(nfsv41, if test "$enable_nfsv41" = yes; then AC_DEFINE(NFS41_SUPPORTED, 1, [Define this if you want NFSv41 support compiled in]) else - enable_nfsv4= + enable_nfsv41= fi AC_SUBST(enable_nfsv41) AM_CONDITIONAL(CONFIG_NFSV41, [test "$enable_nfsv41" = "yes"]) @@ -400,7 +400,7 @@ case $host in ARCHFLAGS="" ;; esac -my_am_cflags="-Wall -Wstrict-prototypes $ARCHFLAGS -pipe" +my_am_cflags="-Wall -Wextra -Wstrict-prototypes $ARCHFLAGS -pipe" AC_SUBST([AM_CFLAGS], ["$my_am_cflags"]) @@ -425,6 +425,8 @@ AC_CONFIG_FILES([ tools/nlmtest/Makefile tools/rpcdebug/Makefile tools/rpcgen/Makefile + tools/mountstats/Makefile + tools/nfs-iostat/Makefile utils/Makefile utils/exportfs/Makefile utils/gssd/Makefile diff --git a/support/export/client.c b/support/export/client.c index 6236561..c74961e 100644 --- a/support/export/client.c +++ b/support/export/client.c @@ -17,7 +17,9 @@ #include #include #include -#include "xmalloc.h" +#include + +#include "sockaddr.h" #include "misc.h" #include "nfslib.h" #include "exportfs.h" @@ -28,58 +30,211 @@ #if !defined(__GLIBC__) || __GLIBC__ < 2 extern int innetgr(char *netgr, char *host, char *, char *); #endif -static void client_init(nfs_client *clp, const char *hname, - struct hostent *hp); -static int client_checkaddr(nfs_client *clp, struct in_addr addr); + +static char *add_name(char *old, const char *add); nfs_client *clientlist[MCL_MAXTYPES] = { NULL, }; -/* if canonical is set, then we *know* this is already a canonical name - * so hostname lookup is avoided. - * This is used when reading /proc/fs/nfs/exports +static void +init_addrlist(nfs_client *clp, const struct addrinfo *ai) +{ + int i; + + if (ai == NULL) + return; + + for (i = 0; (ai != NULL) && (i < NFSCLNT_ADDRMAX); i++) { + set_addrlist(clp, i, ai->ai_addr); + ai = ai->ai_next; + } + + clp->m_naddr = i; +} + +static void +client_free(nfs_client *clp) +{ + free(clp->m_hostname); + free(clp); +} + +static int +init_netmask(nfs_client *clp, const char *slash, const sa_family_t family) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + }; + unsigned long prefixlen; + uint32_t shift; +#ifdef IPV6_SUPPORTED + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + }; + int i; +#endif + + /* No slash present; assume netmask is all ones */ + if (slash == NULL) { + switch (family) { + case AF_INET: + prefixlen = 32; + break; +#ifdef IPV6_SUPPORTED + case AF_INET6: + prefixlen = 128; + break; +#endif + default: + goto out_badfamily; + } + } else { + char *endptr; + + /* A spelled out netmask address, perhaps? */ + if (strchr(slash + 1, '.') != NULL) { + if (inet_pton(AF_INET, slash + 1, + &sin.sin_addr.s_addr) == 0) + goto out_badmask; + set_addrlist_in(clp, 1, &sin); + return 1; + } +#ifdef IPV6_SUPPORTED + if (strchr(slash + 1, ':')) { + if (!inet_pton(AF_INET6, slash + 1, &sin6.sin6_addr)) + goto out_badmask; + set_addrlist_in6(clp, 1, &sin6); + return 1; + } +#endif + + /* A prefixlen was given */ + prefixlen = strtoul(slash + 1, &endptr, 10); + if (*endptr != '\0' && prefixlen != ULONG_MAX && errno != ERANGE) + goto out_badprefix; + } + + switch (family) { + case AF_INET: + if (prefixlen > 32) + goto out_badprefix; + shift = 32 - (uint32_t)prefixlen; + sin.sin_addr.s_addr = htonl((uint32_t)~0 << shift); + set_addrlist_in(clp, 1, &sin); + return 1; +#ifdef IPV6_SUPPORTED + case AF_INET6: + if (prefixlen > 128) + goto out_badprefix; + for (i = 0; prefixlen > 32; i++) { + sin6.sin6_addr.s6_addr32[i] = 0xffffffff; + prefixlen -= 32; + } + shift = 32 - (uint32_t)prefixlen; + sin6.sin6_addr.s6_addr32[i] = htonl((uint32_t)~0 << shift); + set_addrlist_in6(clp, 1, &sin6); + return 1; +#endif + } + +out_badfamily: + xlog(L_ERROR, "Unsupported address family for %s", clp->m_hostname); + return 0; + +out_badmask: + xlog(L_ERROR, "Invalid netmask `%s' for %s", slash + 1, clp->m_hostname); + return 0; + +out_badprefix: + xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname); + return 0; +} + +static int +init_subnetwork(nfs_client *clp) +{ + struct addrinfo *ai; + sa_family_t family; + char *slash; + + slash = strchr(clp->m_hostname, '/'); + if (slash != NULL) { + *slash = '\0'; + ai = host_pton(clp->m_hostname); + *slash = '/'; + } else + ai = host_pton(clp->m_hostname); + if (ai == NULL) { + xlog(L_ERROR, "Invalid IP address %s", clp->m_hostname); + return false; + } + + set_addrlist(clp, 0, ai->ai_addr); + family = ai->ai_addr->sa_family; + + freeaddrinfo(ai); + + return init_netmask(clp, slash, family); +} + +static int +client_init(nfs_client *clp, const char *hname, const struct addrinfo *ai) +{ + clp->m_hostname = strdup(hname); + if (clp->m_hostname == NULL) + return 0; + + clp->m_exported = 0; + clp->m_count = 0; + clp->m_naddr = 0; + + if (clp->m_type == MCL_SUBNETWORK) + return init_subnetwork(clp); + + init_addrlist(clp, ai); + return 1; +} + +static void +client_add(nfs_client *clp) +{ + nfs_client **cpp; + + cpp = &clientlist[clp->m_type]; + while (*cpp != NULL) + cpp = &((*cpp)->m_next); + clp->m_next = NULL; + *cpp = clp; +} + +/** + * client_lookup - look for @hname in our list of cached nfs_clients + * @hname: '\0'-terminated ASCII string containing hostname to look for + * @canonical: if set, @hname is known to be canonical DNS name + * + * Returns pointer to a matching or freshly created nfs_client. NULL + * is returned if some problem occurs. */ nfs_client * client_lookup(char *hname, int canonical) { nfs_client *clp = NULL; int htype; - struct hostent *hp = NULL; + struct addrinfo *ai = NULL; htype = client_gettype(hname); if (htype == MCL_FQDN && !canonical) { - struct hostent *hp2; - hp = gethostbyname(hname); - if (hp == NULL || hp->h_addrtype != AF_INET) { - xlog(L_ERROR, "%s has non-inet addr", hname); - return NULL; + ai = host_addrinfo(hname); + if (!ai) { + xlog(L_ERROR, "Failed to resolve %s", hname); + goto out; } - /* make sure we have canonical name */ - hp2 = hostent_dup(hp); - hp = gethostbyaddr(hp2->h_addr, hp2->h_length, - hp2->h_addrtype); - if (hp) { - hp = hostent_dup(hp); - /* but now we might not have all addresses... */ - if (hp2->h_addr_list[1]) { - struct hostent *hp3 = - gethostbyname(hp->h_name); - if (hp3) { - free(hp); - hp = hostent_dup(hp3); - } - } - free(hp2); - } else - hp = hp2; - - hname = (char *) hp->h_name; + hname = ai->ai_canonname; - for (clp = clientlist[htype]; clp; clp = clp->m_next) { - if (client_check(clp, hp)) + for (clp = clientlist[htype]; clp; clp = clp->m_next) + if (client_check(clp, ai)) break; - } } else { for (clp = clientlist[htype]; clp; clp = clp->m_next) { if (strcasecmp(hname, clp->m_hostname)==0) @@ -87,106 +242,60 @@ client_lookup(char *hname, int canonical) } } - if (!clp) { - clp = (nfs_client *) xmalloc(sizeof(*clp)); - memset(clp, 0, sizeof(*clp)); + if (clp == NULL) { + clp = calloc(1, sizeof(*clp)); + if (clp == NULL) + goto out; clp->m_type = htype; - client_init(clp, hname, NULL); + if (!client_init(clp, hname, NULL)) { + client_free(clp); + clp = NULL; + goto out; + } client_add(clp); } - if (htype == MCL_FQDN && clp->m_naddr == 0 && hp != NULL) { - char **ap = hp->h_addr_list; - int i; - - for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) - clp->m_addrlist[i] = *(struct in_addr *)*ap; - clp->m_naddr = i; - } - - if (hp) - free (hp); + if (htype == MCL_FQDN && clp->m_naddr == 0) + init_addrlist(clp, ai); +out: + freeaddrinfo(ai); return clp; } +/** + * client_dup - create a copy of an nfs_client + * @clp: pointer to nfs_client to copy + * @ai: pointer to addrinfo used to initialize the new client's addrlist + * + * Returns a dynamically allocated nfs_client if successful, or + * NULL if some problem occurs. Caller must free the returned + * nfs_client with free(3). + */ nfs_client * -client_dup(nfs_client *clp, struct hostent *hp) +client_dup(const nfs_client *clp, const struct addrinfo *ai) { nfs_client *new; - new = (nfs_client *) xmalloc(sizeof(*new)); + new = (nfs_client *)malloc(sizeof(*new)); + if (new == NULL) + return NULL; memcpy(new, clp, sizeof(*new)); new->m_type = MCL_FQDN; new->m_hostname = NULL; - client_init(new, (char *) hp->h_name, hp); + if (!client_init(new, ai->ai_canonname, ai)) { + client_free(new); + return NULL; + } client_add(new); return new; } -static void -client_init(nfs_client *clp, const char *hname, struct hostent *hp) -{ - xfree(clp->m_hostname); - if (hp) - clp->m_hostname = xstrdup(hp->h_name); - else - clp->m_hostname = xstrdup(hname); - - clp->m_exported = 0; - clp->m_count = 0; - - if (clp->m_type == MCL_SUBNETWORK) { - char *cp = strchr(clp->m_hostname, '/'); - static char slash32[] = "/32"; - - if(!cp) cp = slash32; - *cp = '\0'; - clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname); - if (strchr(cp + 1, '.')) { - clp->m_addrlist[1].s_addr = inet_addr(cp+1); - } - else { - int netmask = atoi(cp + 1); - if (0 < netmask && netmask <= 32) { - clp->m_addrlist[1].s_addr = - htonl ((uint32_t) ~0 << (32 - netmask)); - } - else { - xlog(L_FATAL, "invalid netmask `%s' for %s", - cp + 1, clp->m_hostname); - } - } - *cp = '/'; - clp->m_naddr = 0; - } else if (!hp) { - clp->m_naddr = 0; - } else { - char **ap = hp->h_addr_list; - int i; - - for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) { - clp->m_addrlist[i] = *(struct in_addr *)*ap; - } - clp->m_naddr = i; - } -} - -void -client_add(nfs_client *clp) -{ - nfs_client **cpp; - - if (clp->m_type < 0 || clp->m_type >= MCL_MAXTYPES) - xlog(L_FATAL, "unknown client type in client_add"); - cpp = clientlist + clp->m_type; - while (*cpp) - cpp = &((*cpp)->m_next); - clp->m_next = NULL; - *cpp = clp; -} - +/** + * client_release - drop a reference to an nfs_client record + * + */ void client_release(nfs_client *clp) { @@ -195,6 +304,10 @@ client_release(nfs_client *clp) clp->m_count--; } +/** + * client_freeall - deallocate all nfs_client records + * + */ void client_freeall(void) { @@ -205,57 +318,45 @@ client_freeall(void) head = clientlist + i; while (*head) { *head = (clp = *head)->m_next; - xfree(clp->m_hostname); - xfree(clp); - } - } -} - -nfs_client * -client_find(struct hostent *hp) -{ - nfs_client *clp; - int i; - - for (i = 0; i < MCL_MAXTYPES; i++) { - for (clp = clientlist[i]; clp; clp = clp->m_next) { - if (!client_check(clp, hp)) - continue; -#ifdef notdef - if (clp->m_type == MCL_FQDN) - return clp; - return client_dup(clp, hp); -#else - return clp; -#endif + client_free(clp); } } - return NULL; } -struct hostent * -client_resolve(struct in_addr addr) +/** + * client_resolve - look up an IP address + * @sap: pointer to socket address to resolve + * + * Returns an addrinfo structure, or NULL if some problem occurred. + * Caller must free the result with freeaddrinfo(3). + */ +struct addrinfo * +client_resolve(const struct sockaddr *sap) { - struct hostent *he = NULL; + struct addrinfo *ai = NULL; if (clientlist[MCL_WILDCARD] || clientlist[MCL_NETGROUP]) - he = get_reliable_hostbyaddr((const char*)&addr, sizeof(addr), AF_INET); - if (he == NULL) - he = get_hostent((const char*)&addr, sizeof(addr), AF_INET); + ai = host_reliable_addrinfo(sap); + if (ai == NULL) + ai = host_numeric_addrinfo(sap); - return he; + return ai; } -/* - * Find client name given an IP address - * This is found by gathering all known names that match that IP address, - * sorting them and joining them with '+' +/** + * client_compose - Make a list of cached hostnames that match an IP address + * @ai: pointer to addrinfo containing IP address information to match + * + * Gather all known client hostnames that match the IP address, and sort + * the result into a comma-separated list. * + * Returns a '\0'-terminated ASCII string containing a comma-separated + * sorted list of client hostnames, or NULL if no client records matched + * the IP address or memory could not be allocated. Caller must free the + * returned string with free(3). */ -static char *add_name(char *old, char *add); - char * -client_compose(struct hostent *he) +client_compose(const struct addrinfo *ai) { char *name = NULL; int i; @@ -263,7 +364,7 @@ client_compose(struct hostent *he) for (i = 0 ; i < MCL_MAXTYPES; i++) { nfs_client *clp; for (clp = clientlist[i]; clp ; clp = clp->m_next) { - if (!client_check(clp, he)) + if (!client_check(clp, ai)) continue; name = add_name(name, clp->m_hostname); } @@ -271,13 +372,19 @@ client_compose(struct hostent *he) return name; } +/** + * client_member - check if @name is contained in the list @client + * @client: '\0'-terminated ASCII string containing + * comma-separated list of hostnames + * @name: '\0'-terminated ASCII string containing hostname to look for + * + * Returns 1 if @name was found in @client, otherwise zero is returned. + */ int -client_member(char *client, char *name) +client_member(const char *client, const char *name) { - /* check if "client" (a ',' separated list of names) - * contains 'name' as a member - */ - int l = strlen(name); + size_t l = strlen(name); + while (*client) { if (strncmp(client, name, l) == 0 && (client[l] == ',' || client[l] == '\0')) @@ -290,9 +397,8 @@ client_member(char *client, char *name) return 0; } - -int -name_cmp(char *a, char *b) +static int +name_cmp(const char *a, const char *b) { /* compare strings a and b, but only upto ',' in a */ while (*a && *b && *a != ',' && *a == *b) @@ -305,9 +411,9 @@ name_cmp(char *a, char *b) } static char * -add_name(char *old, char *add) +add_name(char *old, const char *add) { - int len = strlen(add)+2; + size_t len = strlen(add) + 2; char *new; char *cp; if (old) len += strlen(old); @@ -340,108 +446,257 @@ add_name(char *old, char *add) } /* - * Match a host (given its hostent record) to a client record. This - * is usually called from mountd. + * Check each address listed in @ai against each address + * stored in @clp. Return 1 if a match is found, otherwise + * zero. */ -int -client_check(nfs_client *clp, struct hostent *hp) +static int +check_fqdn(const nfs_client *clp, const struct addrinfo *ai) { - char *hname = (char *) hp->h_name; - char *cname = clp->m_hostname; - char **ap; + int i; - switch (clp->m_type) { - case MCL_FQDN: - case MCL_SUBNETWORK: - for (ap = hp->h_addr_list; *ap; ap++) { - if (client_checkaddr(clp, *(struct in_addr *) *ap)) + for (; ai; ai = ai->ai_next) + for (i = 0; i < clp->m_naddr; i++) + if (nfs_compare_sockaddr(ai->ai_addr, + get_addrlist(clp, i))) return 1; - } - return 0; - case MCL_WILDCARD: - if (wildmat(hname, cname)) + + return 0; +} + +static _Bool +mask_match(const uint32_t a, const uint32_t b, const uint32_t m) +{ + return ((a ^ b) & m) == 0; +} + +static int +check_subnet_v4(const struct sockaddr_in *address, + const struct sockaddr_in *mask, const struct addrinfo *ai) +{ + for (; ai; ai = ai->ai_next) { + struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; + + if (sin->sin_family != AF_INET) + continue; + + if (mask_match(address->sin_addr.s_addr, + sin->sin_addr.s_addr, + mask->sin_addr.s_addr)) return 1; - else { - for (ap = hp->h_aliases; *ap; ap++) - if (wildmat(*ap, cname)) - return 1; - } - return 0; - case MCL_NETGROUP: -#ifdef HAVE_INNETGR - { - char *dot; - int match, i; - struct hostent *nhp = NULL; - struct sockaddr_in addr; - - /* First, try to match the hostname without - * splitting off the domain */ - if (innetgr(cname+1, hname, NULL, NULL)) - return 1; + } + return 0; +} - /* try the aliases as well */ - for (i = 0; hp->h_aliases[i]; i++) { - if (innetgr(cname+1, hp->h_aliases[i], NULL, NULL)) - return 1; - } +#ifdef IPV6_SUPPORTED +static int +check_subnet_v6(const struct sockaddr_in6 *address, + const struct sockaddr_in6 *mask, const struct addrinfo *ai) +{ + for (; ai; ai = ai->ai_next) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; + + if (sin6->sin6_family != AF_INET6) + continue; + + if (mask_match(address->sin6_addr.s6_addr32[0], + sin6->sin6_addr.s6_addr32[0], + mask->sin6_addr.s6_addr32[0]) && + mask_match(address->sin6_addr.s6_addr32[1], + sin6->sin6_addr.s6_addr32[1], + mask->sin6_addr.s6_addr32[1]) && + mask_match(address->sin6_addr.s6_addr32[2], + sin6->sin6_addr.s6_addr32[2], + mask->sin6_addr.s6_addr32[2]) && + mask_match(address->sin6_addr.s6_addr32[3], + sin6->sin6_addr.s6_addr32[3], + mask->sin6_addr.s6_addr32[3])) + return 1; + } + return 0; +} +#else /* !IPV6_SUPPORTED */ +static int +check_subnet_v6(const struct sockaddr_in6 *UNUSED(address), + const struct sockaddr_in6 *UNUSED(mask), + const struct addrinfo *UNUSED(ai)) +{ + return 0; +} +#endif /* !IPV6_SUPPORTED */ - /* If hname is ip address convert to FQDN */ - if (inet_aton(hname, &addr.sin_addr) && - (nhp = gethostbyaddr((const char *)&(addr.sin_addr), - sizeof(addr.sin_addr), AF_INET))) { - hname = (char *)nhp->h_name; - if (innetgr(cname+1, hname, NULL, NULL)) - return 1; - } +/* + * Check each address listed in @ai against the subnetwork or + * host address stored in @clp. Return 1 if an address in @hp + * matches the host address stored in @clp, otherwise zero. + */ +static int +check_subnetwork(const nfs_client *clp, const struct addrinfo *ai) +{ + switch (get_addrlist(clp, 0)->sa_family) { + case AF_INET: + return check_subnet_v4(get_addrlist_in(clp, 0), + get_addrlist_in(clp, 1), ai); + case AF_INET6: + return check_subnet_v6(get_addrlist_in6(clp, 0), + get_addrlist_in6(clp, 1), ai); + } - /* Okay, strip off the domain (if we have one) */ - if ((dot = strchr(hname, '.')) == NULL) - return 0; + return 0; +} - *dot = '\0'; - match = innetgr(cname+1, hname, NULL, NULL); - *dot = '.'; +/* + * Check if a wildcard nfs_client record matches the canonical name + * or the aliases of a host. Return 1 if a match is found, otherwise + * zero. + */ +static int +check_wildcard(const nfs_client *clp, const struct addrinfo *ai) +{ + char *cname = clp->m_hostname; + char *hname = ai->ai_canonname; + struct hostent *hp; + char **ap; - return match; - } -#else - return 0; -#endif - case MCL_ANONYMOUS: + if (wildmat(hname, cname)) return 1; - case MCL_GSS: - return 0; - default: - xlog(L_FATAL, "internal: bad client type %d", clp->m_type); + + /* See if hname aliases listed in /etc/hosts or nis[+] + * match the requested wildcard */ + hp = gethostbyname(hname); + if (hp != NULL) { + for (ap = hp->h_aliases; *ap; ap++) + if (wildmat(*ap, cname)) + return 1; } return 0; } +/* + * Check if @ai's hostname or aliases fall in a given netgroup. + * Return 1 if @ai represents a host in the netgroup, otherwise + * zero. + */ +#ifdef HAVE_INNETGR +static int +check_netgroup(const nfs_client *clp, const struct addrinfo *ai) +{ + const char *netgroup = clp->m_hostname + 1; + struct addrinfo *tmp = NULL; + struct hostent *hp; + char *dot, *hname; + int i, match; + + match = 0; + + hname = strdup(ai->ai_canonname); + if (hname == NULL) { + xlog(D_GENERAL, "%s: no memory for strdup", __func__); + goto out; + } + + /* First, try to match the hostname without + * splitting off the domain */ + if (innetgr(netgroup, hname, NULL, NULL)) { + match = 1; + goto out; + } + + /* See if hname aliases listed in /etc/hosts or nis[+] + * match the requested netgroup */ + hp = gethostbyname(hname); + if (hp != NULL) { + for (i = 0; hp->h_aliases[i]; i++) + if (innetgr(netgroup, hp->h_aliases[i], NULL, NULL)) { + match = 1; + goto out; + } + } + + /* If hname happens to be an IP address, convert it + * to a the canonical DNS name bound to this address. */ + tmp = host_pton(hname); + if (tmp != NULL) { + char *cname = host_canonname(tmp->ai_addr); + freeaddrinfo(tmp); + + /* The resulting FQDN may be in our netgroup. */ + if (cname != NULL) { + free(hname); + hname = cname; + if (innetgr(netgroup, hname, NULL, NULL)) { + match = 1; + goto out; + } + } + } + + /* Okay, strip off the domain (if we have one) */ + dot = strchr(hname, '.'); + if (dot == NULL) + goto out; + + *dot = '\0'; + match = innetgr(netgroup, hname, NULL, NULL); + +out: + free(hname); + return match; +} +#else /* !HAVE_INNETGR */ static int -client_checkaddr(nfs_client *clp, struct in_addr addr) +check_netgroup(__attribute__((unused)) const nfs_client *clp, + __attribute__((unused)) const struct addrinfo *ai) { - int i; + return 0; +} +#endif /* !HAVE_INNETGR */ +/** + * client_check - check if IP address information matches a cached nfs_client + * @clp: pointer to a cached nfs_client record + * @ai: pointer to addrinfo to compare it with + * + * Returns 1 if the address information matches the cached nfs_client, + * otherwise zero. + */ +int +client_check(const nfs_client *clp, const struct addrinfo *ai) +{ switch (clp->m_type) { case MCL_FQDN: - for (i = 0; i < clp->m_naddr; i++) { - if (clp->m_addrlist[i].s_addr == addr.s_addr) - return 1; - } - return 0; + return check_fqdn(clp, ai); case MCL_SUBNETWORK: - return !((clp->m_addrlist[0].s_addr ^ addr.s_addr) - & clp->m_addrlist[1].s_addr); + return check_subnetwork(clp, ai); + case MCL_WILDCARD: + return check_wildcard(clp, ai); + case MCL_NETGROUP: + return check_netgroup(clp, ai); + case MCL_ANONYMOUS: + return 1; + case MCL_GSS: + return 0; + default: + xlog(D_GENERAL, "%s: unrecognized client type: %d", + __func__, clp->m_type); } + return 0; } +/** + * client_gettype - determine type of nfs_client given an identifier + * @ident: '\0'-terminated ASCII string containing a client identifier + * + * Returns the type of nfs_client record that would be used for + * this client. + */ int client_gettype(char *ident) { - char *sp; + struct addrinfo *ai; + char *sp; if (ident[0] == '\0' || strcmp(ident, "*")==0) return MCL_ANONYMOUS; @@ -461,12 +716,16 @@ client_gettype(char *ident) if (*sp == '\\' && sp[1]) sp++; } - /* check for N.N.N.N */ - sp = ident; - if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN; - sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN; - sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN; - sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '\0') return MCL_FQDN; - /* we lie here a bit. but technically N.N.N.N == N.N.N.N/32 :) */ - return MCL_SUBNETWORK; + + /* + * Treat unadorned IP addresses as MCL_SUBNETWORK. + * Everything else is MCL_FQDN. + */ + ai = host_pton(ident); + if (ai != NULL) { + freeaddrinfo(ai); + return MCL_SUBNETWORK; + } + + return MCL_FQDN; } diff --git a/support/export/export.c b/support/export/export.c index 2943466..4fda30a 100644 --- a/support/export/export.c +++ b/support/export/export.c @@ -24,9 +24,25 @@ static int export_hash(char *); static void export_init(nfs_export *exp, nfs_client *clp, struct exportent *nep); -static int export_check(nfs_export *, struct hostent *, char *); +static void export_add(nfs_export *exp); +static int export_check(const nfs_export *exp, const struct addrinfo *ai, + const char *path); static nfs_export * - export_allowed_internal(struct hostent *hp, char *path); + export_allowed_internal(const struct addrinfo *ai, + const char *path); + +static void +export_free(nfs_export *exp) +{ + xfree(exp->m_export.e_squids); + xfree(exp->m_export.e_sqgids); + free(exp->m_export.e_mountpoint); + free(exp->m_export.e_fslocdata); + free(exp->m_export.e_uuid); + + xfree(exp->m_export.e_hostname); + xfree(exp); +} static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) { @@ -44,7 +60,12 @@ static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) } } -int +/** + * export_read - read entries from /etc/exports + * @fname: name of file to read from + * + */ +void export_read(char *fname) { struct exportent *eep; @@ -59,11 +80,15 @@ export_read(char *fname) warn_duplicated_exports(exp, eep); } endexportent(); - return 0; } -/* - * Create an in-core export struct from an export entry. +/** + * export_create - create an in-core nfs_export record from an export entry + * @xep: export entry to lookup + * @canonical: if set, e_hostname is known to be canonical DNS name + * + * Returns a freshly instantiated export record, or NULL if + * a problem occurred. */ nfs_export * export_create(struct exportent *xep, int canonical) @@ -105,8 +130,8 @@ export_init(nfs_export *exp, nfs_client *clp, struct exportent *nep) * original hostname from /etc/exports, while the in-core client struct * gets the newly found FQDN. */ -nfs_export * -export_dup(nfs_export *exp, struct hostent *hp) +static nfs_export * +export_dup(nfs_export *exp, const struct addrinfo *ai) { nfs_export *new; nfs_client *clp; @@ -116,7 +141,11 @@ export_dup(nfs_export *exp, struct hostent *hp) dupexportent(&new->m_export, &exp->m_export); if (exp->m_export.e_hostname) new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname); - clp = client_dup(exp->m_client, hp); + clp = client_dup(exp->m_client, ai); + if (clp == NULL) { + export_free(new); + return NULL; + } clp->m_count++; new->m_client = clp; new->m_mayexport = exp->m_mayexport; @@ -128,10 +157,8 @@ export_dup(nfs_export *exp, struct hostent *hp) return new; } -/* - * Add export entry to hash table - */ -void + +static void export_add(nfs_export *exp) { exp_hash_table *p_tbl; @@ -159,19 +186,27 @@ export_add(nfs_export *exp) } } +/** + * export_find - find or create a suitable nfs_export for @ai and @path + * @ai: pointer to addrinfo for client + * @path: '\0'-terminated ASCII string containing export path + * + * Returns a pointer to nfs_export data matching @ai and @path, + * or NULL if an error occurs. + */ nfs_export * -export_find(struct hostent *hp, char *path) +export_find(const struct addrinfo *ai, const char *path) { nfs_export *exp; int i; for (i = 0; i < MCL_MAXTYPES; i++) { for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { - if (!export_check(exp, hp, path)) + if (!export_check(exp, ai, path)) continue; if (exp->m_client->m_type == MCL_FQDN) return exp; - return export_dup(exp, hp); + return export_dup(exp, ai); } } @@ -179,7 +214,7 @@ export_find(struct hostent *hp, char *path) } static nfs_export * -export_allowed_internal (struct hostent *hp, char *path) +export_allowed_internal(const struct addrinfo *ai, const char *path) { nfs_export *exp; int i; @@ -187,7 +222,7 @@ export_allowed_internal (struct hostent *hp, char *path) for (i = 0; i < MCL_MAXTYPES; i++) { for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { if (!exp->m_mayexport || - !export_check(exp, hp, path)) + !export_check(exp, ai, path)) continue; return exp; } @@ -196,8 +231,16 @@ export_allowed_internal (struct hostent *hp, char *path) return NULL; } +/** + * export_allowed - determine if this export is allowed + * @ai: pointer to addrinfo for client + * @path: '\0'-terminated ASCII string containing export path + * + * Returns a pointer to nfs_export data matching @ai and @path, + * or NULL if the export is not allowed. + */ nfs_export * -export_allowed(struct hostent *hp, char *path) +export_allowed(const struct addrinfo *ai, const char *path) { nfs_export *exp; char epath[MAXPATHLEN+1]; @@ -210,7 +253,7 @@ export_allowed(struct hostent *hp, char *path) /* Try the longest matching exported pathname. */ while (1) { - exp = export_allowed_internal (hp, epath); + exp = export_allowed_internal(ai, epath); if (exp) return exp; /* We have to treat the root, "/", specially. */ @@ -223,11 +266,17 @@ export_allowed(struct hostent *hp, char *path) return NULL; } -/* - * Search hash table for export entry. - */ +/** + * export_lookup - search hash table for export entry + * @hname: '\0'-terminated ASCII string containing client hostname to look for + * @path: '\0'-terminated ASCII string containing export path to look for + * @canonical: if set, @hname is known to be canonical DNS name + * + * Returns a pointer to nfs_export record matching @hname and @path, + * or NULL if the export was not found. + */ nfs_export * -export_lookup(char *hname, char *path, int canonical) +export_lookup(char *hname, char *path, int canonical) { nfs_client *clp; nfs_export *exp; @@ -251,14 +300,18 @@ export_lookup(char *hname, char *path, int canonical) } static int -export_check(nfs_export *exp, struct hostent *hp, char *path) +export_check(const nfs_export *exp, const struct addrinfo *ai, const char *path) { if (strcmp(path, exp->m_export.e_path)) return 0; - return client_check(exp->m_client, hp); + return client_check(exp->m_client, ai); } +/** + * export_freeall - deallocate all nfs_export records + * + */ void export_freeall(void) { @@ -269,22 +322,13 @@ export_freeall(void) for (exp = exportlist[i].p_head; exp; exp = nxt) { nxt = exp->m_next; client_release(exp->m_client); - if (exp->m_export.e_squids) - xfree(exp->m_export.e_squids); - if (exp->m_export.e_sqgids) - xfree(exp->m_export.e_sqgids); - if (exp->m_export.e_mountpoint) - free(exp->m_export.e_mountpoint); - if (exp->m_export.e_fslocdata) - xfree(exp->m_export.e_fslocdata); - xfree(exp->m_export.e_hostname); - xfree(exp); + export_free(exp); + } + for (j = 0; j < HASH_TABLE_SIZE; j++) { + exportlist[i].entries[j].p_first = NULL; + exportlist[i].entries[j].p_last = NULL; } - for(j = 0; j < HASH_TABLE_SIZE; j++) { - exportlist[i].entries[j].p_first = NULL; - exportlist[i].entries[j].p_last = NULL; - } - exportlist[i].p_head = NULL; + exportlist[i].p_head = NULL; } client_freeall(); } diff --git a/support/export/hostname.c b/support/export/hostname.c index 8a23a89..3c55ce7 100644 --- a/support/export/hostname.c +++ b/support/export/hostname.c @@ -1,315 +1,376 @@ /* - * support/export/hostname.c + * Copyright 2010 Oracle. All rights reserved. * - * Functions for hostname. + * This file is part of nfs-utils. * + * nfs-utils is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * nfs-utils is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with nfs-utils. If not, see . */ #ifdef HAVE_CONFIG_H #include #endif -/* -#define TEST -*/ - #include -#include -#include -#include #include -#include -#ifdef TEST -#define xmalloc malloc -#else -#include "xmalloc.h" -#include "misc.h" -#endif +#include +#include +#include -#define ALIGNMENT sizeof (char *) +#include "sockaddr.h" +#include "exportfs.h" -static int -align (int len, int al) +#ifndef HAVE_DECL_AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif + +/** + * host_ntop - generate presentation address given a sockaddr + * @sap: pointer to socket address + * @buf: working storage + * @buflen: size of @buf in bytes + * + * Returns a pointer to a @buf. + */ +#ifdef HAVE_GETNAMEINFO +char * +host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen) { - int i; - i = len % al; - if (i) - len += al - i; - return len; -} + socklen_t salen = nfs_sockaddr_length(sap); + int error; + + memset(buf, 0, buflen); + + if (salen == 0) { + (void)strncpy(buf, "bad family", buflen - 1); + return buf; + } + + error = getnameinfo(sap, salen, buf, (socklen_t)buflen, + NULL, 0, NI_NUMERICHOST); + if (error != 0) { + buf[0] = '\0'; + (void)strncpy(buf, "bad address", buflen - 1); + } -struct hostent * -get_hostent (const char *addr, int len, int type) + return buf; +} +#else /* !HAVE_GETNAMEINFO */ +char * +host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen) { - struct hostent *cp; - int len_ent; - const char *name; - int len_name; - int num_aliases = 1; - int len_aliases = sizeof (char *); - int num_addr_list = 1; - int len_addr_list = sizeof (char *); - int pos; - struct in_addr *ipv4; - - switch (type) - { - case AF_INET: - ipv4 = (struct in_addr *) addr; - name = inet_ntoa (*ipv4); - break; - - default: - return NULL; - } - - len_ent = align (sizeof (*cp), ALIGNMENT); - len_name = align (strlen (name) + 1, ALIGNMENT); - - num_addr_list++; - len_addr_list += align (len, ALIGNMENT) + sizeof (char *); - - cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases - + len_addr_list); - - cp->h_addrtype = type; - cp->h_length = len; - pos = len_ent; - cp->h_name = (char *) &(((char *) cp) [pos]); - strcpy (cp->h_name, name); - - pos += len_name; - cp->h_aliases = (char **) &(((char *) cp) [pos]); - pos += num_aliases * sizeof (char *); - cp->h_aliases [0] = NULL; - - pos = len_ent + len_name + len_aliases; - cp->h_addr_list = (char **) &(((char *) cp) [pos]); - pos += num_addr_list * sizeof (char *); - cp->h_addr_list [0] = (char *) &(((char *) cp) [pos]); - memcpy (cp->h_addr_list [0], addr, cp->h_length); - pos += align (cp->h_length, ALIGNMENT); - cp->h_addr_list [1] = NULL; - - return cp; + const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap; + + memset(buf, 0, buflen); + + if (sin->sin_family != AF_INET) + (void)strncpy(buf, "bad family", buflen - 1); + return buf; + } + + if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, buflen) != NULL) + return buf; + + buf[0] = '\0'; + (void)strncpy(buf, "bad address", buflen - 1); + return buf; } +#endif /* !HAVE_GETNAMEINFO */ -struct hostent * -hostent_dup (struct hostent *hp) +/** + * host_pton - return addrinfo for a given presentation address + * @paddr: pointer to a '\0'-terminated ASCII string containing an + * IP presentation address + * + * Returns address info structure, or NULL if an error occurs. Caller + * must free the returned structure with freeaddrinfo(3). + */ +__attribute_malloc__ +struct addrinfo * +host_pton(const char *paddr) { - int len_ent = align (sizeof (*hp), ALIGNMENT); - int len_name = align (strlen (hp->h_name) + 1, ALIGNMENT); - int num_aliases = 1; - int len_aliases = sizeof (char *); - int num_addr_list = 1; - int len_addr_list = sizeof (char *); - int pos, i; - char **sp; - struct hostent *cp; - - for (sp = hp->h_aliases; sp && *sp; sp++) - { - num_aliases++; - len_aliases += align (strlen (*sp) + 1, ALIGNMENT) - + sizeof (char *); - } - - for (sp = hp->h_addr_list; *sp; sp++) - { - num_addr_list++; - len_addr_list += align (hp->h_length, ALIGNMENT) - + sizeof (char *); - } - - cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases - + len_addr_list); - - *cp = *hp; - pos = len_ent; - cp->h_name = (char *) &(((char *) cp) [pos]); - strcpy (cp->h_name, hp->h_name); - - pos += len_name; - cp->h_aliases = (char **) &(((char *) cp) [pos]); - pos += num_aliases * sizeof (char *); - for (sp = hp->h_aliases, i = 0; i < num_aliases; i++, sp++) - if (sp && *sp) - { - cp->h_aliases [i] = (char *) &(((char *) cp) [pos]); - strcpy (cp->h_aliases [i], *sp); - pos += align (strlen (*sp) + 1, ALIGNMENT); - } - else - cp->h_aliases [i] = NULL; - - pos = len_ent + len_name + len_aliases; - cp->h_addr_list = (char **) &(((char *) cp) [pos]); - pos += num_addr_list * sizeof (char *); - for (sp = hp->h_addr_list, i = 0; i < num_addr_list; i++, sp++) - if (*sp) - { - cp->h_addr_list [i] = (char *) &(((char *) cp) [pos]); - memcpy (cp->h_addr_list [i], *sp, hp->h_length); - pos += align (hp->h_length, ALIGNMENT); - } - else - cp->h_addr_list [i] = *sp; - - return cp; + struct addrinfo *ai = NULL; + struct addrinfo hint = { + /* don't return duplicates */ + .ai_protocol = (int)IPPROTO_UDP, + .ai_flags = AI_NUMERICHOST, + .ai_family = AF_UNSPEC, + }; + struct sockaddr_in sin; + int error, inet4; + + /* + * Although getaddrinfo(3) is easier to use and supports + * IPv6, it recognizes incomplete addresses like "10.4" + * as valid AF_INET addresses. It also accepts presentation + * addresses that end with a blank. + * + * inet_pton(3) is much stricter. Use it to be certain we + * have a real AF_INET presentation address, before invoking + * getaddrinfo(3) to generate the full addrinfo list. + */ + inet4 = 1; + if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0) + inet4 = 0; + + error = getaddrinfo(paddr, NULL, &hint, &ai); + switch (error) { + case 0: + if (!inet4 && ai->ai_addr->sa_family == AF_INET) { + freeaddrinfo(ai); + break; + } + return ai; + case EAI_NONAME: + if (paddr == NULL) + xlog(D_GENERAL, "%s: passed a NULL presentation address", + __func__); + break; + case EAI_SYSTEM: + xlog(D_GENERAL, "%s: failed to convert %s: (%d) %m", + __func__, paddr, errno); + break; + default: + xlog(D_GENERAL, "%s: failed to convert %s: %s", + __func__, paddr, gai_strerror(error)); + break; + } + + return NULL; } -static int -is_hostname(const char *sp) +/** + * host_addrinfo - return addrinfo for a given hostname + * @hostname: pointer to a '\0'-terminated ASCII string containing a hostname + * + * Returns address info structure with ai_canonname filled in, or NULL + * if no information is available for @hostname. Caller must free the + * returned structure with freeaddrinfo(3). + */ +__attribute_malloc__ +struct addrinfo * +host_addrinfo(const char *hostname) { - if (*sp == '\0' || *sp == '@') - return 0; - - for (; *sp; sp++) - { - if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/') - return 0; - if (*sp == '\\' && sp[1]) - sp++; - } - - return 1; + struct addrinfo *ai = NULL; + struct addrinfo hint = { +#ifdef IPV6_SUPPORTED + .ai_family = AF_UNSPEC, +#else + .ai_family = AF_INET, +#endif + /* don't return duplicates */ + .ai_protocol = (int)IPPROTO_UDP, + .ai_flags = AI_ADDRCONFIG | AI_CANONNAME, + }; + int error; + + error = getaddrinfo(hostname, NULL, &hint, &ai); + switch (error) { + case 0: + return ai; + case EAI_SYSTEM: + xlog(D_GENERAL, "%s: failed to resolve %s: (%d) %m", + __func__, hostname, errno); + break; + default: + xlog(D_GENERAL, "%s: failed to resolve %s: %s", + __func__, hostname, gai_strerror(error)); + break; + } + + return NULL; } -int -matchhostname (const char *h1, const char *h2) +/** + * host_canonname - return canonical hostname bound to an address + * @sap: pointer to socket address to look up + * + * Discover the canonical hostname associated with the given socket + * address. The host's reverse mapping is verified in the process. + * + * Returns a '\0'-terminated ASCII string containing a hostname, or + * NULL if no hostname can be found for @sap. Caller must free + * the string. + */ +#ifdef HAVE_GETNAMEINFO +__attribute_malloc__ +char * +host_canonname(const struct sockaddr *sap) { - struct hostent *hp1, *hp2; - int status; - - if (strcasecmp (h1, h2) == 0) - return 1; - - if (!is_hostname (h1) || !is_hostname (h2)) - return 0; - - hp1 = gethostbyname (h1); - if (hp1 == NULL) - return 0; - - hp1 = hostent_dup (hp1); - - hp2 = gethostbyname (h2); - if (hp2) - { - if (strcasecmp (hp1->h_name, hp2->h_name) == 0) - status = 1; - else - { - char **ap1, **ap2; - - status = 0; - for (ap1 = hp1->h_addr_list; *ap1 && status == 0; ap1++) - for (ap2 = hp2->h_addr_list; *ap2; ap2++) - if (memcmp (*ap1, *ap2, sizeof (struct in_addr)) == 0) - { - status = 1; - break; - } + socklen_t salen = nfs_sockaddr_length(sap); + char buf[NI_MAXHOST]; + int error; + + if (salen == 0) { + xlog(D_GENERAL, "%s: unsupported address family %d", + __func__, sap->sa_family); + return NULL; + } + + memset(buf, 0, sizeof(buf)); + error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), + NULL, 0, NI_NAMEREQD); + switch (error) { + case 0: + break; + case EAI_SYSTEM: + xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m", + __func__, errno); + return NULL; + default: + (void)getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), + NULL, 0, NI_NUMERICHOST); + xlog(D_GENERAL, "%s: failed to resolve %s: %s", + __func__, buf, gai_strerror(error)); + return NULL; } - } - else - status = 0; - free (hp1); - return status; + return strdup(buf); } +#else /* !HAVE_GETNAMEINFO */ +__attribute_malloc__ +char * +host_canonname(const struct sockaddr *sap) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap; + const struct in_addr *addr = &sin->sin_addr; + struct hostent *hp; + + if (sap->sa_family != AF_INET) + return NULL; + hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET); + if (hp == NULL) + return NULL; + + return strdup(hp->h_name); +} +#endif /* !HAVE_GETNAMEINFO */ -/* Map IP to hostname, and then map back to addr to make sure it is a - * reliable hostname +/** + * host_reliable_addrinfo - return addrinfo for a given address + * @sap: pointer to socket address to look up + * + * Reverse and forward lookups are performed to ensure the address has + * proper forward and reverse mappings. + * + * Returns address info structure with ai_canonname filled in, or NULL + * if no information is available for @sap. Caller must free the returned + * structure with freeaddrinfo(3). */ -struct hostent * -get_reliable_hostbyaddr(const char *addr, int len, int type) +__attribute_malloc__ +struct addrinfo * +host_reliable_addrinfo(const struct sockaddr *sap) { - struct hostent *hp = NULL; + struct addrinfo *ai; + char *hostname; - struct hostent *reverse; - struct hostent *forward; - char **sp; - - reverse = gethostbyaddr (addr, len, type); - if (!reverse) + hostname = host_canonname(sap); + if (hostname == NULL) return NULL; - /* must make sure the hostent is authorative. */ + ai = host_addrinfo(hostname); - reverse = hostent_dup (reverse); - forward = gethostbyname (reverse->h_name); + free(hostname); + return ai; +} - if (forward) { - /* now make sure the "addr" is in the list */ - for (sp = forward->h_addr_list ; *sp ; sp++) { - if (memcmp (*sp, addr, forward->h_length) == 0) - break; - } +/** + * host_numeric_addrinfo - return addrinfo without doing DNS queries + * @sap: pointer to socket address + * + * Returns address info structure, or NULL if an error occurred. + * Caller must free the returned structure with freeaddrinfo(3). + */ +#ifdef HAVE_GETNAMEINFO +__attribute_malloc__ +struct addrinfo * +host_numeric_addrinfo(const struct sockaddr *sap) +{ + socklen_t salen = nfs_sockaddr_length(sap); + char buf[INET6_ADDRSTRLEN]; + struct addrinfo *ai; + int error; + + if (salen == 0) { + xlog(D_GENERAL, "%s: unsupported address family %d", + __func__, sap->sa_family); + return NULL; + } - if (*sp) { - /* it's valid */ - hp = hostent_dup (forward); - } - else { - /* it was a FAKE */ - xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't match reverse", - reverse->h_name, inet_ntoa(*(struct in_addr*)addr)); - } + memset(buf, 0, sizeof(buf)); + error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), + NULL, 0, NI_NUMERICHOST); + switch (error) { + case 0: + break; + case EAI_SYSTEM: + xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m", + __func__, errno); + return NULL; + default: + xlog(D_GENERAL, "%s: getnameinfo(3) failed: %s", + __func__, gai_strerror(error)); + return NULL; } - else { - /* never heard of it. misconfigured DNS? */ - xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't exist", - reverse->h_name, inet_ntoa(*(struct in_addr*)addr)); + + ai = host_pton(buf); + + /* + * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname + */ + if (ai != NULL) { + free(ai->ai_canonname); /* just in case */ + ai->ai_canonname = strdup(buf); + if (ai->ai_canonname == NULL) { + freeaddrinfo(ai); + ai = NULL; + } } - free (reverse); - return hp; + return ai; } +#else /* !HAVE_GETNAMEINFO */ +__attribute_malloc__ +struct addrinfo * +host_numeric_addrinfo(const struct sockaddr *sap) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + const struct in_addr *addr = &sin->sin_addr; + char buf[INET_ADDRSTRLEN]; + struct addrinfo *ai; + if (sap->sa_family != AF_INET) + return NULL; -#ifdef TEST -void -print_host (struct hostent *hp) -{ - char **sp; - - if (hp) - { - printf ("official hostname: %s\n", hp->h_name); - printf ("aliases:\n"); - for (sp = hp->h_aliases; *sp; sp++) - printf (" %s\n", *sp); - printf ("IP addresses:\n"); - for (sp = hp->h_addr_list; *sp; sp++) - printf (" %s\n", inet_ntoa (*(struct in_addr *) *sp)); - } - else - printf ("Not host information\n"); -} + memset(buf, 0, sizeof(buf)); + if (inet_ntop(AF_INET, (char *)addr, buf, + (socklen_t)sizeof(buf)) == NULL) + return NULL; -int -main (int argc, char **argv) -{ - struct hostent *hp = gethostbyname (argv [1]); - struct hostent *cp; - struct in_addr addr; - - print_host (hp); - - if (hp) - { - cp = hostent_dup (hp); - print_host (cp); - free (cp); - } - printf ("127.0.0.1 == %s: %d\n", argv [1], - matchhostname ("127.0.0.1", argv [1])); - addr.s_addr = inet_addr(argv [2]); - printf ("%s\n", inet_ntoa (addr)); - cp = get_hostent ((const char *)&addr, sizeof(addr), AF_INET); - print_host (cp); - return 0; + ai = host_pton(buf); + + /* + * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname + */ + if (ai != NULL) { + ai->ai_canonname = strdup(buf); + if (ai->ai_canonname == NULL) { + freeaddrinfo(ai); + ai = NULL; + } + } + + return ai; } -#endif +#endif /* !HAVE_GETNAMEINFO */ diff --git a/support/export/nfsctl.c b/support/export/nfsctl.c index e2877b9..f89c644 100644 --- a/support/export/nfsctl.c +++ b/support/export/nfsctl.c @@ -66,7 +66,7 @@ str_tolower(char *s) static int cltsetup(struct nfsctl_client *cltarg, nfs_client *clp) { - int i; + int i, j; if (clp->m_type != MCL_FQDN) { xlog(L_ERROR, "internal: can't export non-FQDN host"); @@ -76,10 +76,19 @@ cltsetup(struct nfsctl_client *cltarg, nfs_client *clp) strncpy(cltarg->cl_ident, clp->m_hostname, sizeof (cltarg->cl_ident) - 1); str_tolower(cltarg->cl_ident); - cltarg->cl_naddr = clp->m_naddr; - for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) - cltarg->cl_addrlist[i] = clp->m_addrlist[i]; + j = 0; + for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) { + const struct sockaddr_in *sin = get_addrlist_in(clp, i); + if (sin->sin_family == AF_INET) + cltarg->cl_addrlist[j++] = sin->sin_addr; + } + if (j == 0) { + xlog(L_ERROR, "internal: no supported addresses in nfs_client"); + return 0; + } + + cltarg->cl_naddr = j; return 1; } @@ -100,7 +109,7 @@ expsetup(struct nfsctl_export *exparg, nfs_export *exp, int unexport) str_tolower(exparg->ex_client); exparg->ex_flags = exp->m_export.e_flags; exparg->ex_dev = (!unexport && (exp->m_export.e_flags & NFSEXP_FSID)) ? - exp->m_export.e_fsid : stb.st_dev; + (__nfsd_dev_t)exp->m_export.e_fsid : stb.st_dev; exparg->ex_ino = stb.st_ino; exparg->ex_anon_uid = exp->m_export.e_anonuid; exparg->ex_anon_gid = exp->m_export.e_anongid; diff --git a/support/export/rmtab.c b/support/export/rmtab.c index b49e1aa..31c0f50 100644 --- a/support/export/rmtab.c +++ b/support/export/rmtab.c @@ -19,39 +19,54 @@ #include "xio.h" #include "xlog.h" +/* + * See if the entry already exists. If not, + * this was an instantiated wild card, and we + * must add it. + */ +static void +rmtab_read_wildcard(struct rmtabent *rep) +{ + nfs_export *exp, *exp2; + struct addrinfo *ai; + + ai = host_addrinfo(rep->r_client); + if (ai == NULL) + return; + + exp = export_allowed(ai, rep->r_path); + freeaddrinfo(ai); + if (exp == NULL) + return; + + exp2 = export_lookup(rep->r_client, exp->m_export.e_path, 0); + if (exp2 == NULL) { + struct exportent ee; + + memset(&ee, 0, sizeof(ee)); + dupexportent(&ee, &exp->m_export); + + ee.e_hostname = rep->r_client; + exp2 = export_create(&ee, 0); + exp2->m_changed = exp->m_changed; + } + exp2->m_mayexport = 1; +} + int rmtab_read(void) { struct rmtabent *rep; - nfs_export *exp = NULL; setrmtabent("r"); while ((rep = getrmtabent(1, NULL)) != NULL) { - struct hostent *hp = NULL; int htype; - + htype = client_gettype(rep->r_client); - if ((htype == MCL_FQDN || htype == MCL_SUBNETWORK) - && (hp = gethostbyname (rep->r_client)) - && (hp = hostent_dup (hp), - exp = export_allowed (hp, rep->r_path))) { - /* see if the entry already exists, otherwise this was an instantiated - * wild card, and we must add it - */ - nfs_export *exp2 = export_lookup(rep->r_client, - exp->m_export.e_path, 0); - if (!exp2) { - struct exportent ee; - dupexportent(&ee, &exp->m_export); - ee.e_hostname = rep->r_client; - exp2 = export_create(&ee, 0); - exp2->m_changed = exp->m_changed; - } - free (hp); - exp2->m_mayexport = 1; - } else if (hp) /* export_allowed failed */ - free(hp); + if (htype == MCL_FQDN || htype == MCL_SUBNETWORK) + rmtab_read_wildcard(rep); } + if (errno == EINVAL) { /* Something goes wrong. We need to fix the rmtab file. */ diff --git a/support/include/exportfs.h b/support/include/exportfs.h index 470b2ec..3cf1ee8 100644 --- a/support/include/exportfs.h +++ b/support/include/exportfs.h @@ -10,6 +10,8 @@ #define EXPORTFS_H #include + +#include "sockaddr.h" #include "nfslib.h" enum { @@ -35,11 +37,56 @@ typedef struct mclient { char * m_hostname; int m_type; int m_naddr; - struct in_addr m_addrlist[NFSCLNT_ADDRMAX]; + union nfs_sockaddr m_addrlist[NFSCLNT_ADDRMAX]; int m_exported; /* exported to nfsd */ int m_count; } nfs_client; +static inline const struct sockaddr * +get_addrlist(const nfs_client *clp, const int i) +{ + return &clp->m_addrlist[i].sa; +} + +static inline const struct sockaddr_in * +get_addrlist_in(const nfs_client *clp, const int i) +{ + return &clp->m_addrlist[i].s4; +} + +static inline const struct sockaddr_in6 * +get_addrlist_in6(const nfs_client *clp, const int i) +{ + return &clp->m_addrlist[i].s6; +} + +static inline void +set_addrlist_in(nfs_client *clp, const int i, const struct sockaddr_in *sin) +{ + memcpy(&clp->m_addrlist[i].s4, sin, sizeof(*sin)); +} + +static inline void +set_addrlist_in6(nfs_client *clp, const int i, const struct sockaddr_in6 *sin6) +{ + memcpy(&clp->m_addrlist[i].s6, sin6, sizeof(*sin6)); +} + +static inline void +set_addrlist(nfs_client *clp, const int i, const struct sockaddr *sap) +{ + switch (sap->sa_family) { + case AF_INET: + memcpy(&clp->m_addrlist[i].s4, sap, sizeof(struct sockaddr_in)); + break; +#ifdef IPV6_SUPPORTED + case AF_INET6: + memcpy(&clp->m_addrlist[i].s6, sap, sizeof(struct sockaddr_in6)); + break; +#endif + } +} + typedef struct mexport { struct mexport * m_next; struct mclient * m_client; @@ -69,26 +116,26 @@ extern exp_hash_table exportlist[MCL_MAXTYPES]; extern nfs_client * clientlist[MCL_MAXTYPES]; nfs_client * client_lookup(char *hname, int canonical); -nfs_client * client_find(struct hostent *); -void client_add(nfs_client *); -nfs_client * client_dup(nfs_client *, struct hostent *); +nfs_client * client_dup(const nfs_client *clp, + const struct addrinfo *ai); int client_gettype(char *hname); -int client_check(nfs_client *, struct hostent *); -int client_match(nfs_client *, char *hname); +int client_check(const nfs_client *clp, + const struct addrinfo *ai); void client_release(nfs_client *); void client_freeall(void); -char * client_compose(struct hostent *he); -struct hostent * client_resolve(struct in_addr addr); -int client_member(char *client, char *name); +char * client_compose(const struct addrinfo *ai); +struct addrinfo * client_resolve(const struct sockaddr *sap); +int client_member(const char *client, + const char *name); -int export_read(char *fname); -void export_add(nfs_export *); +void export_read(char *fname); void export_reset(nfs_export *); nfs_export * export_lookup(char *hname, char *path, int caconical); -nfs_export * export_find(struct hostent *, char *path); -nfs_export * export_allowed(struct hostent *, char *path); +nfs_export * export_find(const struct addrinfo *ai, + const char *path); +nfs_export * export_allowed(const struct addrinfo *ai, + const char *path); nfs_export * export_create(struct exportent *, int canonical); -nfs_export * export_dup(nfs_export *, struct hostent *); void export_freeall(void); int export_export(nfs_export *); int export_unexport(nfs_export *); @@ -101,6 +148,19 @@ void xtab_append(nfs_export *); int secinfo_addflavor(struct flav_info *, struct exportent *); +char * host_ntop(const struct sockaddr *sap, + char *buf, const size_t buflen); +__attribute_malloc__ +struct addrinfo * host_pton(const char *paddr); +__attribute_malloc__ +struct addrinfo * host_addrinfo(const char *hostname); +__attribute_malloc__ +char * host_canonname(const struct sockaddr *sap); +__attribute_malloc__ +struct addrinfo * host_reliable_addrinfo(const struct sockaddr *sap); +__attribute_malloc__ +struct addrinfo * host_numeric_addrinfo(const struct sockaddr *sap); + int rmtab_read(void); struct nfskey * key_lookup(char *hname); diff --git a/support/include/misc.h b/support/include/misc.h index 9a1b25d..bc5ba23 100644 --- a/support/include/misc.h +++ b/support/include/misc.h @@ -15,13 +15,6 @@ int randomkey(unsigned char *keyout, int len); int weakrandomkey(unsigned char *keyout, int len); -int matchhostname(const char *h1, const char *h2); - -struct hostent; -struct hostent *hostent_dup(struct hostent *hp); -struct hostent *get_hostent (const char *addr, int len, int type); -struct hostent *get_reliable_hostbyaddr(const char *addr, int len, int type); - extern int is_mountpoint(char *path); #endif /* MISC_H */ diff --git a/support/include/nfslib.h b/support/include/nfslib.h index 537a31e..3db5bec 100644 --- a/support/include/nfslib.h +++ b/support/include/nfslib.h @@ -83,7 +83,7 @@ struct exportent { int e_nsquids; int * e_sqgids; int e_nsqgids; - int e_fsid; + unsigned int e_fsid; char * e_mountpoint; int e_fslocmethod; char * e_fslocdata; @@ -134,9 +134,12 @@ int nfsaddclient(struct nfsctl_client *clp); int nfsdelclient(struct nfsctl_client *clp); int nfsexport(struct nfsctl_export *exp); int nfsunexport(struct nfsctl_export *exp); -struct nfs_fh_len * getfh_old(struct sockaddr *addr, dev_t dev, ino_t ino); -struct nfs_fh_len * getfh(struct sockaddr *addr, const char *); -struct nfs_fh_len * getfh_size(struct sockaddr *addr, const char *, int size); + +struct nfs_fh_len * getfh_old(const struct sockaddr_in *sin, + const dev_t dev, const ino_t ino); +struct nfs_fh_len * getfh(const struct sockaddr_in *sin, const char *path); +struct nfs_fh_len * getfh_size(const struct sockaddr_in *sin, + const char *path, int const size); void qword_print(FILE *f, char *str); void qword_printhex(FILE *f, char *str, int slen); @@ -152,10 +155,15 @@ void qword_addhex(char **bpp, int *lp, char *buf, int blen); void qword_addint(char **bpp, int *lp, int n); void qword_adduint(char **bpp, int *lp, unsigned int n); void qword_addeol(char **bpp, int *lp); +int qword_get_uint(char **bpp, unsigned int *anint); +void qword_printuint(FILE *f, unsigned int num); void closeall(int min); int svctcp_socket (u_long __number, int __reuse); -int svcudp_socket (u_long __number, int __reuse); +int svcudp_socket (u_long __number); + + +#define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #endif /* NFSLIB_H */ diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h index 4db35ab..6ebefca 100644 --- a/support/include/nfsrpc.h +++ b/support/include/nfsrpc.h @@ -160,4 +160,7 @@ extern int nfs_rpc_ping(const struct sockaddr *sap, const unsigned short protocol, const struct timeval *timeout); +/* create AUTH_SYS handle with no supplemental groups */ +extern AUTH * nfs_authsys_create(void); + #endif /* !__NFS_UTILS_NFSRPC_H */ diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h index 1b8f411..c5847fa 100644 --- a/support/include/rpcmisc.h +++ b/support/include/rpcmisc.h @@ -60,12 +60,12 @@ extern int _rpcsvcdirty; static inline struct sockaddr_in *nfs_getrpccaller_in(SVCXPRT *xprt) { - return (struct sockaddr_in *)svc_getcaller(xprt); + return (struct sockaddr_in *)(char *)svc_getcaller(xprt); } static inline struct sockaddr *nfs_getrpccaller(SVCXPRT *xprt) { - return (struct sockaddr *)svc_getcaller(xprt); + return (struct sockaddr *)(char *)svc_getcaller(xprt); } #endif /* RPCMISC_H */ diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c index bdf5d84..9bad8e6 100644 --- a/support/nfs/cacheio.c +++ b/support/nfs/cacheio.c @@ -148,6 +148,11 @@ void qword_printint(FILE *f, int num) fprintf(f, "%d ", num); } +void qword_printuint(FILE *f, unsigned int num) +{ + fprintf(f, "%u ", num); +} + int qword_eol(FILE *f) { int err; @@ -236,6 +241,20 @@ int qword_get_int(char **bpp, int *anint) return 0; } +int qword_get_uint(char **bpp, unsigned int *anint) +{ + char buf[50]; + char *ep; + unsigned int rv; + int len = qword_get(bpp, buf, 50); + if (len < 0) return -1; + if (len ==0) return -1; + rv = strtoul(buf, &ep, 0); + if (*ep) return -1; + *anint = rv; + return 0; +} + #define READLINE_BUFFER_INCREMENT 2048 int readline(int fd, char **buf, int *lenp) @@ -330,7 +349,7 @@ cache_flush(int force) sprintf(path, "/proc/net/rpc/%s/flush", cachelist[c]); fd = open(path, O_RDWR); if (fd >= 0) { - if (write(fd, stime, strlen(stime)) != strlen(stime)) { + if (write(fd, stime, strlen(stime)) != (ssize_t)strlen(stime)) { xlog_warn("Writing to '%s' failed: errno %d (%s)", path, errno, strerror(errno)); } diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c index b277c2a..24640f4 100644 --- a/support/nfs/conffile.c +++ b/support/nfs/conffile.c @@ -49,7 +49,7 @@ #include "conffile.h" #include "xlog.h" -static void conf_load_defaults (int); +static void conf_load_defaults(void); static int conf_set(int , char *, char *, char *, char *, int , int ); @@ -212,7 +212,7 @@ conf_parse_line(int trans, char *line, size_t sz) { char *val, *ptr; size_t i; - int j; + size_t j; static char *section = 0; static char *arg = 0; static int ln = 0; @@ -353,7 +353,7 @@ conf_parse(int trans, char *buf, size_t sz) } static void -conf_load_defaults(int tr) +conf_load_defaults(void) { /* No defaults */ return; @@ -412,7 +412,7 @@ conf_reinit(void) trans = conf_begin(); /* Load default configuration values. */ - conf_load_defaults(trans); + conf_load_defaults(); /* Free potential existing configuration. */ if (conf_addr) { diff --git a/support/nfs/exports.c b/support/nfs/exports.c index a93941c..1744ed6 100644 --- a/support/nfs/exports.c +++ b/support/nfs/exports.c @@ -332,6 +332,8 @@ dupexportent(struct exportent *dst, struct exportent *src) dst->e_mountpoint = strdup(src->e_mountpoint); if (src->e_fslocdata) dst->e_fslocdata = strdup(src->e_fslocdata); + if (src->e_uuid) + dst->e_uuid = strdup(src->e_uuid); dst->e_hostname = NULL; } diff --git a/support/nfs/getfh.c b/support/nfs/getfh.c index 81266fd..611459b 100644 --- a/support/nfs/getfh.c +++ b/support/nfs/getfh.c @@ -19,60 +19,112 @@ #include #include "nfslib.h" +/** + * getfh_old - ask the kernel for an NFSv2 file handle via nfsctl() + * @sin: pointer to IPv4 address of a client + * @dev: device number of device where requested object resides + * @ino: inode number of requested object + * + * Returns a pointer to an NFSv2 file handle, or NULL if some error + * occurred. errno is set to reflect the specifics of the error. + */ struct nfs_fh_len * -getfh_old (struct sockaddr *addr, dev_t dev, ino_t ino) +getfh_old(const struct sockaddr_in *sin, const dev_t dev, const ino_t ino) { union nfsctl_res res; struct nfsctl_arg arg; static struct nfs_fh_len rfh; + if (sin->sin_family != AF_INET) { + errno = EAFNOSUPPORT; + return NULL; + } + + memset(&arg, 0, sizeof(arg)); + memset(&res, 0, sizeof(res)); + arg.ca_version = NFSCTL_VERSION; arg.ca_getfh.gf_version = 2; /* obsolete */ arg.ca_getfh.gf_dev = dev; arg.ca_getfh.gf_ino = ino; - memcpy(&arg.ca_getfh.gf_addr, addr, sizeof(struct sockaddr_in)); + memcpy(&arg.ca_getfh.gf_addr, sin, sizeof(*sin)); if (nfsctl(NFSCTL_GETFH, &arg, &res) < 0) return NULL; + memset(&rfh, 0, sizeof(rfh)); rfh.fh_size = 32; memcpy(rfh.fh_handle, &res.cr_getfh, 32); return &rfh; } +/** + * getfh - ask the kernel for an NFSv2 file handle via nfsctl() + * @sin: pointer to IPv4 address of a client + * @path: pointer to a '\0'-terminated ASCII string containing an pathname + * + * Returns a pointer to an NFSv2 file handle, or NULL if some error + * occurred. errno is set to reflect the specifics of the error. + */ struct nfs_fh_len * -getfh(struct sockaddr *addr, const char *path) +getfh(const struct sockaddr_in *sin, const char *path) { static union nfsctl_res res; struct nfsctl_arg arg; static struct nfs_fh_len rfh; + if (sin->sin_family != AF_INET) { + errno = EAFNOSUPPORT; + return NULL; + } + + memset(&arg, 0, sizeof(arg)); + memset(&res, 0, sizeof(res)); + arg.ca_version = NFSCTL_VERSION; arg.ca_getfd.gd_version = 2; /* obsolete */ strncpy(arg.ca_getfd.gd_path, path, sizeof(arg.ca_getfd.gd_path) - 1); arg.ca_getfd.gd_path[sizeof (arg.ca_getfd.gd_path) - 1] = '\0'; - memcpy(&arg.ca_getfd.gd_addr, addr, sizeof(struct sockaddr_in)); + memcpy(&arg.ca_getfd.gd_addr, sin, sizeof(*sin)); if (nfsctl(NFSCTL_GETFD, &arg, &res) < 0) return NULL; + memset(&rfh, 0, sizeof(rfh)); rfh.fh_size = 32; memcpy(rfh.fh_handle, &res.cr_getfh, 32); return &rfh; } +/** + * getfh_size - ask the kernel for a file handle via nfsctl() + * @sin: pointer to IPv4 address of a client + * @path: pointer to a '\0'-terminated ASCII string containing an pathname + * @size: maximum size, in bytes, of the returned file handle + * + * Returns a pointer to an NFSv3 file handle, or NULL if some error + * occurred. errno is set to reflect the specifics of the error. + */ struct nfs_fh_len * -getfh_size(struct sockaddr *addr, const char *path, int size) +getfh_size(const struct sockaddr_in *sin, const char *path, const int size) { static union nfsctl_res res; struct nfsctl_arg arg; + if (sin->sin_family != AF_INET) { + errno = EAFNOSUPPORT; + return NULL; + } + + memset(&arg, 0, sizeof(arg)); + memset(&res, 0, sizeof(res)); + arg.ca_version = NFSCTL_VERSION; strncpy(arg.ca_getfs.gd_path, path, sizeof(arg.ca_getfs.gd_path) - 1); arg.ca_getfs.gd_path[sizeof (arg.ca_getfs.gd_path) - 1] = '\0'; - memcpy(&arg.ca_getfs.gd_addr, addr, sizeof(struct sockaddr_in)); + memcpy(&arg.ca_getfs.gd_addr, sin, sizeof(*sin)); arg.ca_getfs.gd_maxlen = size; if (nfsctl(NFSCTL_GETFS, &arg, &res) < 0) diff --git a/support/nfs/nfs_mntent.c b/support/nfs/nfs_mntent.c index a3fecfc..9c73ae0 100644 --- a/support/nfs/nfs_mntent.c +++ b/support/nfs/nfs_mntent.c @@ -28,7 +28,7 @@ static char * mangle(const char *arg) { const unsigned char *s = (const unsigned char *)arg; char *ss, *sp; - int n; + unsigned int n; n = strlen(arg); ss = sp = xmalloc(4*n+1); diff --git a/support/nfs/rmtab.c b/support/nfs/rmtab.c index a28abf3..ca789a3 100644 --- a/support/nfs/rmtab.c +++ b/support/nfs/rmtab.c @@ -19,6 +19,18 @@ #include #include "nfslib.h" +/* + * Colons in incoming IPv6 presentation addresses have to + * replaced with another character, since rmtab already + * uses colons to delineate fields. + * + * Use a printable character, but one that would never be + * found in a presentation address or domain name + */ +#define IPV6_COLON ';' + +#define LINELEN (2048) + static FILE *rmfp = NULL; int @@ -56,7 +68,8 @@ struct rmtabent * fgetrmtabent(FILE *fp, int log, long *pos) { static struct rmtabent re; - char buf[2048], *count, *host, *path; + char *count, *host, *path, *c; + static char buf[LINELEN]; errno = 0; if (!fp) @@ -84,10 +97,16 @@ fgetrmtabent(FILE *fp, int log, long *pos) else re.r_count = 1; } while (0); + strncpy(re.r_client, host, sizeof (re.r_client) - 1); re.r_client[sizeof (re.r_client) - 1] = '\0'; + for (c = re.r_client; *c != '\0'; c++) + if (*c == IPV6_COLON) + *c = ':'; + strncpy(re.r_path, path, sizeof (re.r_path) - 1); re.r_path[sizeof (re.r_path) - 1] = '\0'; + return &re; } @@ -100,10 +119,27 @@ putrmtabent(struct rmtabent *rep, long *pos) void fputrmtabent(FILE *fp, struct rmtabent *rep, long *pos) { + static char buf[LINELEN]; + char *c; + if (!fp || (pos && fseek (fp, *pos, SEEK_SET) != 0)) return; - fprintf(fp, "%s:%s:0x%.8x\n", rep->r_client, rep->r_path, - rep->r_count); + + /* + * To avoid confusing the token parser in fgetrmtabent(), + * convert colons in incoming IPv6 presentation addresses + * to semicolons. + */ + if (strlen(rep->r_client) > sizeof(buf)) { + xlog(L_ERROR, "client name too large"); + return; + } + strncpy(buf, rep->r_client, sizeof(buf)); + for (c = buf; *c != '\0'; c++) + if (*c == ':') + *c = IPV6_COLON; + + (void)fprintf(fp, "%s:%s:0x%.8x\n", buf, rep->r_path, rep->r_count); } void diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c index 0e20824..c14efe8 100644 --- a/support/nfs/rpc_socket.c +++ b/support/nfs/rpc_socket.c @@ -557,3 +557,24 @@ rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[]) return program; } + +/* + * AUTH_SYS doesn't allow more than 16 gids in the supplemental group list. + * If there are more than that, trying to determine which ones to include + * in the list is problematic. This function creates an auth handle that + * only has the primary gid in the supplemental gids list. It's intended to + * be used for protocols where credentials really don't matter much (the MNT + * protocol, for instance). + */ +AUTH * +nfs_authsys_create(void) +{ + char machname[MAXHOSTNAMELEN + 1]; + uid_t uid = geteuid(); + gid_t gid = getegid(); + + if (gethostname(machname, sizeof(machname)) == -1) + return NULL; + + return authunix_create(machname, uid, gid, 1, &gid); +} diff --git a/support/nfs/rpcdispatch.c b/support/nfs/rpcdispatch.c index 502fc5f..984c646 100644 --- a/support/nfs/rpcdispatch.c +++ b/support/nfs/rpcdispatch.c @@ -27,12 +27,12 @@ rpc_dispatch(struct svc_req *rqstp, SVCXPRT *transp, { struct rpc_dentry *dent; - if (rqstp->rq_vers > nvers) { + if (((int)rqstp->rq_vers) > nvers) { svcerr_progvers(transp, 1, nvers); return; } dtable += (rqstp->rq_vers - 1); - if (rqstp->rq_proc > dtable->nproc) { + if (((int)rqstp->rq_proc) > dtable->nproc) { svcerr_noproc(transp); return; } diff --git a/support/nfs/rpcmisc.c b/support/nfs/rpcmisc.c index a0854e7..b73187a 100644 --- a/support/nfs/rpcmisc.c +++ b/support/nfs/rpcmisc.c @@ -154,7 +154,7 @@ rpc_init(char *name, int prog, int vers, sock = makesock(defport, IPPROTO_UDP); } if (sock == RPC_ANYSOCK) - sock = svcudp_socket (prog, 1); + sock = svcudp_socket (prog); transp = svcudp_create(sock); if (transp == NULL) { xlog(L_FATAL, "cannot create udp service."); diff --git a/support/nfs/svc_socket.c b/support/nfs/svc_socket.c index f44217a..03a5325 100644 --- a/support/nfs/svc_socket.c +++ b/support/nfs/svc_socket.c @@ -157,9 +157,9 @@ svctcp_socket (u_long number, int reuse) * Create and bind a UDP socket based on program number */ int -svcudp_socket (u_long number, int reuse) +svcudp_socket (u_long number) { - return svc_socket (number, SOCK_DGRAM, IPPROTO_UDP, 0); + return svc_socket (number, SOCK_DGRAM, IPPROTO_UDP, FALSE); } #ifdef TEST @@ -174,7 +174,7 @@ check (u_long number, u_short port, int protocol, int reuse) if (protocol == IPPROTO_TCP) socket = svctcp_socket (number, reuse); else - socket = svcudp_socket (number, reuse); + socket = svcudp_socket (number); if (socket < 0) return 1; diff --git a/support/nsm/file.c b/support/nsm/file.c index d469219..f4baeb9 100644 --- a/support/nsm/file.c +++ b/support/nsm/file.c @@ -67,7 +67,9 @@ #endif #include +#ifdef HAVE_SYS_CAPABILITY_H #include +#endif #include #include @@ -347,6 +349,7 @@ nsm_is_default_parentdir(void) static _Bool nsm_clear_capabilities(void) { +#ifdef HAVE_SYS_CAPABILITY_H cap_t caps; caps = cap_from_text("cap_net_bind_service=ep"); @@ -362,6 +365,7 @@ nsm_clear_capabilities(void) } (void)cap_free(caps); +#endif return true; } diff --git a/tests/t0001-statd-basic-mon-unmon.sh b/tests/t0001-statd-basic-mon-unmon.sh old mode 100644 new mode 100755 diff --git a/tools/Makefile.am b/tools/Makefile.am index db15346..f2ce282 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -6,6 +6,6 @@ if CONFIG_RPCGEN OPTDIRS += rpcgen endif -SUBDIRS = locktest rpcdebug nlmtest $(OPTDIRS) +SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS) MAINTAINERCLEANFILES = Makefile.in diff --git a/tools/mountstats/Makefile.am b/tools/mountstats/Makefile.am new file mode 100644 index 0000000..ca617a2 --- /dev/null +++ b/tools/mountstats/Makefile.am @@ -0,0 +1,13 @@ +## Process this file with automake to produce Makefile.in +PYTHON_FILES = mountstats.py + +man8_MANS = mountstats.man + +EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) + +all-local: $(PYTHON_FILES) + +install-data-hook: + $(INSTALL) --mode 755 mountstats.py $(DESTDIR)$(sbindir)/mountstats + +MAINTAINERCLEANFILES=Makefile.in diff --git a/tools/mountstats/mountstats.man b/tools/mountstats/mountstats.man new file mode 100644 index 0000000..0de31b7 --- /dev/null +++ b/tools/mountstats/mountstats.man @@ -0,0 +1,32 @@ +.\" +.\" mountstats(8) +.\" +.TH mountstats 8 "15 Apr 2010" +.SH NAME +mountstats \- Displays NFS client per-mount statistics +.SH SYNOPSIS +.BI "mountstats [" "] " " [ " "]" +.SH DESCRIPTION +The +.B mountstats +command displays NFS client statisitics on each given +.I +.SH OPTIONS +.TP +.B " \-\-nfs +display only the NFS statistics +.TP +.B " \-\-rpc +display only the RPC statistics +.TP +.B " \-\-version +display the version of this command +.SH FILES +.TP +.B /proc/self/mountstats +.SH SEE ALSO +.BR iostat (8), +.BR nfsiostat (8), +.BR nfsstat(8) +.SH AUTHOR +Chuck Lever diff --git a/tools/nfs-iostat/Makefile.am b/tools/nfs-iostat/Makefile.am new file mode 100644 index 0000000..30f4054 --- /dev/null +++ b/tools/nfs-iostat/Makefile.am @@ -0,0 +1,13 @@ +## Process this file with automake to produce Makefile.in +PYTHON_FILES = nfs-iostat.py + +man8_MANS = nfsiostat.man + +EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) + +all-local: $(PYTHON_FILES) + +install-data-hook: + $(INSTALL) --mode 755 nfs-iostat.py $(DESTDIR)$(sbindir)/nfsiostat + +MAINTAINERCLEANFILES=Makefile.in diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py index 2d0b143..1207674 100644 --- a/tools/nfs-iostat/nfs-iostat.py +++ b/tools/nfs-iostat/nfs-iostat.py @@ -366,6 +366,12 @@ class DeviceData: sends = float(self.__rpc_data['rpcsends']) if sample_time == 0: sample_time = float(self.__nfs_data['age']) + # sample_time could still be zero if the export was just mounted. + # Set it to 1 to avoid divide by zero errors in this case since we'll + # likely still have relevant mount statistics to show. + # + if sample_time == 0: + sample_time = 1; if sends != 0: backlog = (float(self.__rpc_data['backlogutil']) / sends) / sample_time else: diff --git a/tools/nfs-iostat/nfsiostat.man b/tools/nfs-iostat/nfsiostat.man new file mode 100644 index 0000000..99e04fb --- /dev/null +++ b/tools/nfs-iostat/nfsiostat.man @@ -0,0 +1,71 @@ +.\" +.\" nfsiostat(8) +.\" +.TH nfsiostat 8 "15 Apr 2010" +.SH NAME +nfsiostat \- Emulate iostat for NFS mount points using /proc/self/mountstats +.SH SYNOPSIS +.BI "nfsiostat [[" "] [" "]] [" "][" "] +.SH DESCRIPTION +The +.B nfsiostat +command displays NFS client per-mount statisitics. +.TP + +specifies the amount of time in seconds between each report. +The first report contains statistics for the time since each file +system was mounted. Each subsequent report contains statistics collected +during the interval since the previous report. +.TP + +If the +.I +parameter is +specified, the value of +.I +determines the number of reports generated at +. +seconds apart. if the interval parameter is +specified without the +.I +parameter, the command generates reports continuously. +.TP + +Define below +.TP + +If one or more +.I +names are specified, statistics for only these mount points will +be displayed. Otherwise, all NFS mount points on the client are listed. +.SH OPTIONS +.TP +.B \-a " or " \-\-attr +displays statistics related to the attribute cache +.TP +.B \-d " or " \-\-dir +displays statistics related to directory operations +.TP +.B \-h " or " \-\-help +shows help message and exit +.TP +.B \-l LIST or " \-\-list=LIST +only print stats for first LIST mount points +.TP +.B \-p " or " \-\-page +displays statistics related to the page cache +.TP +.B \-s " or " \-\-sort +Sort NFS mount points by ops/second +.TP +.B \-\-version +show program's version number and exit +.SH FILES +.TP +.B /proc/self/mountstats +.SH SEE ALSO +.BR iostat (8), +.BR mountstats (8), +.BR nfsstat(8) +.SH AUTHOR +Chuck Lever diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c index 331e57e..b78957f 100644 --- a/utils/exportfs/exportfs.c +++ b/utils/exportfs/exportfs.c @@ -12,20 +12,24 @@ #include #endif +#include +#include #include #include #include +#include #include #include #include #include +#include #include #include -#include "xmalloc.h" + +#include "sockaddr.h" #include "misc.h" #include "nfslib.h" #include "exportfs.h" -#include "xmalloc.h" #include "xlog.h" static void export_all(int verbose); @@ -34,13 +38,15 @@ static void unexportfs(char *arg, int verbose); static void exports_update(int verbose); static void dump(int verbose); static void error(nfs_export *exp, int err); -static void usage(void); +static void usage(const char *progname); static void validate_export(nfs_export *exp); +static int matchhostname(const char *hostname1, const char *hostname2); int main(int argc, char **argv) { char *options = NULL; + char *progname = NULL; int f_export = 1; int f_all = 0; int f_verbose = 0; @@ -50,7 +56,14 @@ main(int argc, char **argv) int new_cache = 0; int force_flush = 0; - xlog_open("exportfs"); + if ((progname = strrchr(argv[0], '/')) != NULL) + progname++; + else + progname = argv[0]; + + xlog_open(progname); + xlog_stderr(1); + xlog_syslog(0); export_errno = 0; @@ -79,21 +92,21 @@ main(int argc, char **argv) force_flush = 1; break; default: - usage(); + usage(progname); break; } } if (optind != argc && f_all) { - fprintf(stderr,"exportfs: extra arguments are not permitted with -a or -r.\n"); + xlog(L_ERROR, "extra arguments are not permitted with -a or -r"); return 1; } if (f_ignore && (f_all || ! f_export)) { - fprintf(stderr,"exportfs: -i not meaningful with -a, -r or -u.\n"); + xlog(L_ERROR, "-i not meaningful with -a, -r or -u"); return 1; } if (f_reexport && ! f_export) { - fprintf(stderr, "exportfs: -r and -u are incompatible.\n"); + xlog(L_ERROR, "-r and -u are incompatible"); return 1; } new_cache = check_new_cache(); @@ -102,8 +115,10 @@ main(int argc, char **argv) if (new_cache) cache_flush(1); else { - fprintf(stderr, "exportfs: -f: only available with new cache controls: mount /proc/fs/nfsd first\n"); - exit(1); + xlog(L_ERROR, "-f is available only " + "with new cache controls. " + "Mount /proc/fs/nfsd first"); + return 1; } return 0; } else { @@ -232,7 +247,7 @@ exportfs(char *arg, char *options, int verbose) { struct exportent *eep; nfs_export *exp; - struct hostent *hp = NULL; + struct addrinfo *ai = NULL; char *path; char *hname = arg; int htype; @@ -241,36 +256,25 @@ exportfs(char *arg, char *options, int verbose) *path++ = '\0'; if (!path || *path != '/') { - fprintf(stderr, "Invalid exporting option: %s\n", arg); + xlog(L_ERROR, "Invalid exporting option: %s", arg); return; } - if ((htype = client_gettype(hname)) == MCL_FQDN && - (hp = gethostbyname(hname)) != NULL) { - struct hostent *hp2 = hostent_dup (hp); - hp = gethostbyaddr(hp2->h_addr, hp2->h_length, - hp2->h_addrtype); - if (hp) { - free(hp2); - hp = hostent_dup(hp); - } else - hp = hp2; - exp = export_find(hp, path); - hname = hp->h_name; - } else { + if ((htype = client_gettype(hname)) == MCL_FQDN) { + ai = host_addrinfo(hname); + if (ai != NULL) { + exp = export_find(ai, path); + hname = ai->ai_canonname; + } + } else exp = export_lookup(hname, path, 0); - } if (!exp) { if (!(eep = mkexportent(hname, path, options)) || - !(exp = export_create(eep, 0))) { - if (hp) free (hp); - return; - } - } else if (!updateexportent(&exp->m_export, options)) { - if (hp) free (hp); - return; - } + !(exp = export_create(eep, 0))) + goto out; + } else if (!updateexportent(&exp->m_export, options)) + goto out; if (verbose) printf("exporting %s:%s\n", exp->m_client->m_hostname, @@ -280,14 +284,16 @@ exportfs(char *arg, char *options, int verbose) exp->m_changed = 1; exp->m_warned = 0; validate_export(exp); - if (hp) free (hp); + +out: + freeaddrinfo(ai); } static void unexportfs(char *arg, int verbose) { nfs_export *exp; - struct hostent *hp = NULL; + struct addrinfo *ai = NULL; char *path; char *hname = arg; int htype; @@ -296,16 +302,14 @@ unexportfs(char *arg, int verbose) *path++ = '\0'; if (!path || *path != '/') { - fprintf(stderr, "Invalid unexporting option: %s\n", - arg); + xlog(L_ERROR, "Invalid unexporting option: %s", arg); return; } if ((htype = client_gettype(hname)) == MCL_FQDN) { - if ((hp = gethostbyname(hname)) != 0) { - hp = hostent_dup (hp); - hname = (char *) hp->h_name; - } + ai = host_addrinfo(hname); + if (ai) + hname = ai->ai_canonname; } for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) { @@ -341,7 +345,7 @@ unexportfs(char *arg, int verbose) exp->m_mayexport = 0; } - if (hp) free (hp); + freeaddrinfo(ai); } static int can_test(void) @@ -393,14 +397,12 @@ validate_export(nfs_export *exp) int fs_has_fsid = 0; if (stat(path, &stb) < 0) { - fprintf(stderr, "exportfs: Warning: %s does not exist\n", - path); + xlog(L_ERROR, "Failed to stat %s: %m \n", path); return; } if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { - fprintf(stderr, "exportfs: Warning: %s is neither " - "a directory nor a file.\n" - " remote access will fail\n", path); + xlog(L_ERROR, "%s is neither a directory nor a file. " + "Remote access will fail", path); return; } if (!can_test()) @@ -413,24 +415,75 @@ validate_export(nfs_export *exp) if ((exp->m_export.e_flags & NFSEXP_FSID) || exp->m_export.e_uuid || fs_has_fsid) { if ( !test_export(path, 1)) { - fprintf(stderr, "exportfs: Warning: %s does not " - "support NFS export.\n", - path); + xlog(L_ERROR, "%s does not support NFS export", path); return; } } else if ( ! test_export(path, 0)) { if (test_export(path, 1)) - fprintf(stderr, "exportfs: Warning: %s requires fsid= " - "for NFS export\n", path); + xlog(L_ERROR, "%s requires fsid= for NFS export", path); else - fprintf(stderr, "exportfs: Warning: %s does not " - "support NFS export.\n", - path); + xlog(L_ERROR, "%s does not support NFS export", path); return; } } +static _Bool +is_hostname(const char *sp) +{ + if (*sp == '\0' || *sp == '@') + return false; + + for (; *sp != '\0'; sp++) { + if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/') + return false; + if (*sp == '\\' && sp[1] != '\0') + sp++; + } + + return true; +} + +static int +matchhostname(const char *hostname1, const char *hostname2) +{ + struct addrinfo *results1 = NULL, *results2 = NULL; + struct addrinfo *ai1, *ai2; + int result = 0; + + if (strcasecmp(hostname1, hostname2) == 0) + return 1; + + /* + * Don't pass export wildcards or netgroup names to DNS + */ + if (!is_hostname(hostname1) || !is_hostname(hostname2)) + return 0; + + results1 = host_addrinfo(hostname1); + if (results1 == NULL) + goto out; + results2 = host_addrinfo(hostname2); + if (results2 == NULL) + goto out; + + if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) { + result = 1; + goto out; + } + + for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next) + for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next) + if (nfs_compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) { + result = 1; + break; + } + +out: + freeaddrinfo(results1); + freeaddrinfo(results2); + return result; +} static char dumpopt(char c, char *fmt, ...) @@ -532,13 +585,13 @@ dump(int verbose) static void error(nfs_export *exp, int err) { - fprintf(stderr, "%s:%s: %s\n", exp->m_client->m_hostname, + xlog(L_ERROR, "%s:%s: %s\n", exp->m_client->m_hostname, exp->m_export.e_path, strerror(err)); } static void -usage(void) +usage(const char *progname) { - fprintf(stderr, "usage: exportfs [-aruv] [host:/path]\n"); + fprintf(stderr, "usage: %s [-aruv] [host:/path]\n", progname); exit(1); } diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man index c7b230a..089f75b 100644 --- a/utils/exportfs/exportfs.man +++ b/utils/exportfs/exportfs.man @@ -1,11 +1,11 @@ +.\"@(#)exportfs.8" .\" -.\" exportfs(8) -.\" .\" Copyright (C) 1995 Olaf Kirch .\" Modifications 1999-2003 Neil Brown -.TH exportfs 8 "18 July 2003" +.\" +.TH exportfs 8 "31 December 2009" .SH NAME -exportfs \- maintain list of NFS exported file systems +exportfs \- maintain table of exported NFS file systems .SH SYNOPSIS .BI "/usr/sbin/exportfs [-avi] [-o " "options,.." "] [" "client:/path" " ..] .br @@ -18,229 +18,236 @@ exportfs \- maintain list of NFS exported file systems .BI "/usr/sbin/exportfs -f" .br .SH DESCRIPTION +An NFS server maintains a table of local physical file systems +that are accessible to NFS clients. +Each file system in this table is referred to as an +.IR "exported file system" , +or +.IR export , +for short. +.PP The .B exportfs -command is used to maintain the current table of exported file systems for -NFS. This list is kept in a separate file named -.BR /var/lib/nfs/etab -which is read by -.B mountd -when a remote host requests access to mount a file tree, and parts of -the list which are active are kept in the kernel's export table. -.P -Normally this -.B etab -file is initialized with the list of all file systems named in -.B /etc/exports +command maintains the current table of exports for the NFS server. +The master export table is kept in a file named +.IR /var/lib/nfs/etab . +This file is read by +.B rpc.mountd +when a client sends an NFS MOUNT request. +.PP +Normally the master export table is initialized with the contents of +.I /etc/exports by invoking .BR "exportfs -a" . -.P -However, administrators can choose to add and delete individual file systems -without modifying -.B /etc/exports -using -.BR exportfs . -.P +However, a system administrator can choose to add or delete +exports without modifying +.I /etc/exports +by using the +.B exportfs +command. +.PP .B exportfs -and it's partner program -.B mountd -work in one of two modes, a legacy mode which applies to 2.4 and +and its partner program +.B rpc.mountd +work in one of two modes: a legacy mode which applies to 2.4 and earlier versions of the Linux kernel, and a new mode which applies to -2.6 and later versions providing the +2.6 and later versions, providing the .B nfsd virtual filesystem has been mounted at -.B /proc/fs/nfsd +.I /proc/fs/nfsd or -.BR /proc/fs/nfs . -If this filesystem is not mounted in 2.6, the legacy mode is used. -.P +.IR /proc/fs/nfs . +On 2.6 kernels, if this filesystem is not mounted, the legacy mode is used. +.PP In the new mode, .B exportfs -does not give any information to the kernel but only provides it to -.B mountd +does not give any information to the kernel, but provides it only to +.B rpc.mountd through the -.B /var/lib/nfs/etab +.I /var/lib/nfs/etab file. -.B mountd -will listen to requests from the kernel and will provide information -as needed. -.P +.B rpc.mountd +then manages kernel requests for information about exports, as needed. +.PP In the legacy mode, -any export requests which identify a specific host (rather than a -subnet or netgroup etc) are entered directly into the kernel's export -table as well as being written to -.BR /var/lib/nfs/etab . -Further, any mount points listed in -.B /var/lib/nfs/rmtab +exports which identify a specific host, rather than a subnet or netgroup, +are entered directly into the kernel's export table, +as well as being written to +.IR /var/lib/nfs/etab . +Further, exports listed in +.I /var/lib/nfs/rmtab which match a non host-specific export request will cause an appropriate export entry for the host given in -.B rmtab -to be entered -into the kernel's export table. +.I rmtab +to be added to the kernel's export table. .SH OPTIONS -.TP +.TP .B -a Export or unexport all directories. .TP .BI "-o " options,... Specify a list of export options in the same manner as in -.BR exports(5) . +.BR exports (5). .TP .B -i Ignore the -.B /etc/exports -file, so that only default options and options given on the command -line are used. +.I /etc/exports +file. Only default options and options given on the command line are used. .TP .B -r -Reexport all directories. It synchronizes /var/lib/nfs/etab -with /etc/exports. It removes entries in /var/lib/nfs/etab -which are deleted from /etc/exports, and remove any entries from the +Reexport all directories, synchronizing +.I /var/lib/nfs/etab +with +.IR /etc/exports . +This option removes entries in +.I /var/lib/nfs/etab +which have been deleted from +.I /etc/exports, and removes any entries from the kernel export table which are no longer valid. .TP .B -u Unexport one or more directories. .TP .B -f -In 'new' mode, flush everything out of the kernels export table. Any -clients that are active will get new entries added by -.B mountd -when they make their next request. +If +.I /proc/fs/nfsd +or +.I /proc/fs/nfs +is mounted, flush everything out of the kernel's export table. +Fresh entries for active clients are added to the kernel's export table by +.B rpc.mountd +when they make their next NFS mount request. .TP .B -v Be verbose. When exporting or unexporting, show what's going on. When displaying the current export list, also display the list of export options. .SH DISCUSSION -.\" -------------------- Exporting Directories -------------------- .SS Exporting Directories -The first synopsis shows how to invoke the command when adding new -entries to the export table. When using +The first synopsis shows how to invoke +.B exportfs +when adding new entries to the export table. When using .BR "exportfs -a" , -all directories in -.B exports(5) +all exports listed in +.I /etc/exports are added to -.B etab -and the resulting list is pushed into the kernel. -.P +.IR /var/lib/nfs/etab . +The kernel's export table is also updated as needed. +.PP The .I host:/path -argument specifies the directory to export along with the host or hosts to -export it to. All formats described in +argument specifies a local directory to export, +along with the client or clients who are permitted to access it. +See .B exports(5) -are supported; to export a directory to the world, simply specify +for a description of supported options and access list formats. +To export a directory to the world, simply specify .IR :/path . -.P +.PP The export options for a particular host/directory pair derive from -several sources. There is a set of default options which can be overridden by -entries in -.B /etc/exports -(unless the -.B -i -option is given). -In addition, the administrator may override any options from these sources -using the +several sources. +The default export options are +.BR sync,ro,root_squash,wdelay . +These can be overridden by entries in +.IR /etc/exports . +.PP +A system administrator may override options from these sources using the .B -o -argument which takes a comma-separated list of options in the same fashion +command-line option on +.BR exportfs . +This option takes a comma-separated list of options in the same fashion as one would specify them in -.BR exports(5) . -Thus, +.IR /etc/exports . +In this way .B exportfs -can also be used to modify the export options of an already exported -directory. -.P -Modifications of the kernel export table used by -.B nfsd(8) -take place immediately after parsing the command line and updating the -.B etab -file. -.P -The default export options are -.BR sync,ro,root_squash,wdelay . -.\" -------------------- Unexporting Directories ------------------ +can be used to modify the export options of an already exported directory. .SS Unexporting Directories The third synopsis shows how to unexported a currently exported directory. When using .BR "exportfs -ua" , all entries listed in -.B etab +.I /var/lib/nfs/etab are removed from the kernel export tables, and the file is cleared. This effectively shuts down all NFS activity. -.P -To remove an export to a host, specify a +.PP +To remove an export, specify a .I host:/path pair. This deletes the specified entry from -.B etab +.I /var/lib/nfs/etab and removes the corresponding kernel entry (if any). -To remove one or more exports to several hosts, use -.BR "exportfs -ua" . -.P -.\" -------------------- Dumping the Export Table ----------------- -.SS Dumping the Export Table +.PP +.SS Dumping the Export Table Invoking .B exportfs -without further options shows the current list of exported file systems. -When giving the +without options shows the current list of exported file systems. +Adding the .B -v -option, the list of flags pertaining to each export are shown in addition. -.\" -------------------- EXAMPLES --------------------------------- +option causes +.B exportfs +to display the export options for each export. .SH EXAMPLES The following adds all directories listed in -.B /etc/exports +.I /etc/exports to -.B /var/lib/nfs/etab +.I /var/lib/nfs/etab and pushes the resulting export entries into the kernel: -.P +.PP .nf .B "# exportfs -a .fi -.P +.PP To export the -.B /usr/tmp -directory to host +.I /usr/tmp +directory to host .BR django , -allowing asynchronous writes, one would do this: -.P +allowing insecure file locking requests from clients: +.PP .nf -.B "# exportfs -o async django:/usr/tmp +.B "# exportfs -o insecure_locks django:/usr/tmp .fi -.P +.PP To unexport the -.B /usr/tmp +.I /usr/tmp directory: -.P +.PP .nf .B "# exportfs -u django:/usr/tmp .fi -.P -To unexport all the directories listed in -.B /etc/exports: -.P +.PP +To unexport all exports listed in +.IR /etc/exports : +.PP .nf .B "# exportfs -au .fi -.\" -------------------- DEPENDENCIES ----------------------------- -.SH DEPENDENCIES -Exporting to IP networks, DNS and NIS domains does not enable clients -from these groups to access NFS immediately; rather, these sorts of -exports are hints to -.B mountd(8) +.SH USAGE NOTES +Exporting to IP networks or DNS and NIS domains does not enable clients +from these groups to access NFS immediately. +Rather, these sorts of exports are hints to +.BR rpc.mountd (8) to grant any mount requests from these clients. -This is usually not a big problem, because any existing mounts are preserved -in -.B rmtab +This is usually not a problem, because any existing mounts are preserved in +.I rmtab across reboots. -.P +.PP When unexporting a network or domain entry, any current exports to members of this group will be checked against the remaining valid exports and if they themselves are no longer valid they will be removed. -.P -.\" -------------------- SEE ALSO -------------------------------- +.SH FILES +.TP 2.5i +.I /etc/exports +input file listing exports, export options, and access control lists +.TP 2.5i +.I /var/lib/nfs/etab +master table of exports +.TP 2.5i +.I /var/lib/nfs/rmtab +table of clients accessing server's exports .SH SEE ALSO -.BR exports(5) ", " mountd(8) -.\" -------------------- AUTHOR ---------------------------------- +.BR exports (5), +.BR rpc.mountd (8), +.BR netgroup (5) .SH AUTHORS -Olaf Kirch, +Olaf Kirch .br -Neil Brown, - +Neil Brown diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man index ea28ca8..c726dd9 100644 --- a/utils/exportfs/exports.man +++ b/utils/exportfs/exports.man @@ -1,18 +1,22 @@ -.TH EXPORTS 5 "4 March 2005" "Linux" "Linux File Formats Manual" +.\"@(#)exports.5" +.\" +.TH exports 5 "31 December 2009" .SH NAME -exports \- NFS file systems being exported (for Kernel based NFS) -.SH SYNOPSIS -.B /etc/exports +exports \- NFS server export table .SH DESCRIPTION The file .I /etc/exports -serves as the access control list for file systems which may be -exported to NFS clients. It is used by -.IR exportfs (8) +contains a table of local physical file systems on an NFS server +that are accessible to NFS clients. +The contents of the file are maintained by the server's system +administrator. +.PP +Each file system in this table has a list of options and an +access control list. +The table is used by +.BR exportfs (8) to give information to -.IR mountd (8) -and to the kernel based NFS file server daemon -.IR nfsd (8). +.BR mountd (8). .PP The file format is similar to the SunOS .I exports @@ -34,7 +38,9 @@ double quotes. You can also specify spaces or other unusual character in the export name using a backslash followed by the character code as three octal digits. .PP -To apply changes to this file, run exportfs \-ra or restart the NFS server. +To apply changes to this file, run +.BR exportfs \-ra +or restart the NFS server. .PP .SS Machine Name Formats NFS clients may be specified in a number of ways: @@ -61,9 +67,10 @@ simultaneously. This is done by specifying an IP address and netmask pair as .IR address/netmask where the netmask can be specified in dotted-decimal format, or as a -contiguous mask length (for example, either `/255.255.252.0' or `/22' appended -to the network base address result in identical subnetworks with 10 bits of -host). Wildcard characters generally do not work on IP addresses, though they +contiguous mask length. +For example, either `/255.255.252.0' or `/22' appended +to the network base IPv4 address results in identical subnetworks with 10 bits of +host. Wildcard characters generally do not work on IP addresses, though they may work by accident when reverse DNS lookups fail. '''.TP '''.B =public @@ -106,7 +113,7 @@ preceding sec= option. The only options that are permitted to vary in this way are ro, rw, no_root_squash, root_squash, and all_squash. .PP .SS General Options -.IR exportfs +.BR exportfs understands the following export options: .TP .IR secure "\*d @@ -144,7 +151,8 @@ default. In all releases after 1.0.0, is the default, and .I async must be explicitly requested if needed. -To help make system administrators aware of this change, 'exportfs' +To help make system administrators aware of this change, +.B exportfs will issue a warning if neither .I sync nor @@ -188,7 +196,7 @@ The option is currently only effective on .I "single host exports. It does not work reliably with netgroup, subnet, or wildcard -exports. +exports. This option can be very useful in some situations, but it should be used with due care, and only after confirming that the client system @@ -246,7 +254,7 @@ If you genuinely require subtree checking, you should explicitly put that option in the .B exports file. If you put neither option, -.I exportfs +.B exportfs will warn you that the change is pending. .TP @@ -272,7 +280,9 @@ or .TP .IR no_acl On some specially patched kernels, and when exporting filesystems that -support ACLs, this option tells nfsd not to reveal ACLs to clients, so +support ACLs, this option tells +.B nfsd +not to reveal ACLs to clients, so they will see only a subset of actual permissions on the given file system. This option is safe for filesystems used by NFSv2 clients and old NFSv3 clients that perform access decisions locally. Current @@ -381,7 +391,7 @@ of the filesystem must be handled elsewhere.) .SS User ID Mapping .PP -.I nfsd +.B nfsd bases its access control to files on the server machine on the uid and gid provided in each NFS RPC request. The normal behavior a user would expect is that she can access her files on the server just as she would @@ -399,19 +409,19 @@ and can be turned off with .IR no_root_squash . .PP By default, -'''.I nfsd +'''.B nfsd '''tries to obtain the anonymous uid and gid by looking up user '''.I nobody '''in the password file at startup time. If it isn't found, a uid and gid -.I exportfs +.B exportfs chooses a uid and gid of 65534 for squashed access. These values can also be overridden by the .IR anonuid " and " anongid options. '''.PP -'''In addition to this, -'''.I nfsd +'''In addition to this, +'''.B nfsd '''lets you specify arbitrary uids and gids that should be mapped to user '''nobody as well. Finally, you can map all user requests to the @@ -424,7 +434,7 @@ Here's the complete list of mapping options: Map requests from uid/gid 0 to the anonymous uid/gid. Note that this does not apply to any other uids or gids that might be equally sensitive, such as user -.IR bin +.IR bin or group .IR staff . .TP @@ -434,7 +444,7 @@ Turn off root squashing. This option is mainly useful for diskless clients. .IR all_squash Map all uids and gids to the anonymous user. Useful for NFS-exported public FTP directories, news spool directories, etc. The opposite option -is +is .IR no_all_squash , which is the default setting. .TP @@ -468,7 +478,7 @@ and netgroups (this is the entry `@trusted'). The fourth line shows the entry for the PC/NFS client discussed above. Line 5 exports the public FTP directory to every host in the world, executing all requests under the nobody account. The -.I insecure +.I insecure option in this entry also allows clients with NFS implementations that don't use a reserved port for NFS. The sixth line exports a directory read-write to the machine 'server' @@ -478,15 +488,15 @@ all three mounts with the `sync' option enabled. '''access to the private directory. '''.SH CAVEATS '''Unlike other NFS server implementations, this -'''.I nfsd +'''.B nfsd '''allows you to export both a directory and a subdirectory thereof to -'''the same host, for instance +'''the same host, for instance '''.IR /usr " and " /usr/X11R6 . '''In this case, the mount options of the most specific entry apply. For -'''instance, when a user on the client host accesses a file in +'''instance, when a user on the client host accesses a file in '''.IR /usr/X11R6 , -'''the mount options given in the -'''.I /usr/X11R6 +'''the mount options given in the +'''.I /usr/X11R6 '''entry apply. This is also true when the latter is a wildcard or netgroup '''entry. .SH FILES @@ -499,7 +509,15 @@ all three mounts with the `sync' option enabled. .BR showmount (8). '''.SH DIAGNOSTICS '''An error parsing the file is reported using syslogd(8) as level NOTICE from -'''a DAEMON whenever nfsd(8) or mountd(8) is started up. Any unknown +'''a DAEMON whenever +'''.BR nfsd (8) +'''or +'''.BR mountd (8) +'''is started up. Any unknown '''host is reported at that time, but often not all hosts are not yet known -'''to named(8) at boot time, thus as hosts are found they are reported -'''with the same syslogd(8) parameters. +'''to +'''.BR named (8) +'''at boot time, thus as hosts are found they are reported +'''with the same +'''.BR syslogd (8) +'''parameters. diff --git a/utils/gssd/context.h b/utils/gssd/context.h index be47f9c..c9cb0bd 100644 --- a/utils/gssd/context.h +++ b/utils/gssd/context.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2004 The Regents of the University of Michigan. + Copyright (c) 2004,2008 The Regents of the University of Michigan. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -36,6 +36,10 @@ /* Hopefully big enough to hold any serialized context */ #define MAX_CTX_LEN 4096 +/* New context format flag values */ +#define KRB5_CTX_FLAG_INITIATOR 0x00000001 +#define KRB5_CTX_FLAG_CFX 0x00000002 +#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 int serialize_context_for_kernel(gss_ctx_id_t ctx, gss_buffer_desc *buf, gss_OID mech, int32_t *endtime); diff --git a/utils/gssd/context_lucid.c b/utils/gssd/context_lucid.c index 4a682ae..b8d4734 100644 --- a/utils/gssd/context_lucid.c +++ b/utils/gssd/context_lucid.c @@ -42,6 +42,7 @@ #include #include #include +#include #include @@ -76,7 +77,7 @@ prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx, unsigned char fakeseed[FAKESEED_SIZE]; uint32_t word_send_seq; gss_krb5_lucid_key_t enc_key; - int i; + uint32_t i; char *skd, *dkd; gss_buffer_desc fakeoid; @@ -119,15 +120,13 @@ prepare_krb5_rfc1964_buffer(gss_krb5_lucid_context_v1_t *lctx, * Note that the rfc1964 version only supports DES enctypes. */ if (lctx->rfc1964_kd.ctx_key.type != 4) { - printerr(1, "prepare_krb5_rfc1964_buffer: " - "overriding heimdal keytype (%d => %d)\n", - lctx->rfc1964_kd.ctx_key.type, 4); + printerr(2, "%s: overriding heimdal keytype (%d => %d)\n", + __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, 4); lctx->rfc1964_kd.ctx_key.type = 4; } #endif - printerr(2, "prepare_krb5_rfc1964_buffer: serializing keys with " - "enctype %d and length %d\n", - lctx->rfc1964_kd.ctx_key.type, + printerr(2, "%s: serializing keys with enctype %d and length %d\n", + __FUNCTION__, lctx->rfc1964_kd.ctx_key.type, lctx->rfc1964_kd.ctx_key.length); /* derive the encryption key and copy it into buffer */ @@ -158,11 +157,102 @@ out_err: return -1; } +/* Flags for version 2 context flags */ +#define KRB5_CTX_FLAG_INITIATOR 0x00000001 +#define KRB5_CTX_FLAG_CFX 0x00000002 +#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + +/* + * Prepare a new-style buffer, as defined in rfc4121 (a.k.a. cfx), + * to send to the kernel for newer encryption types -- or for DES3. + * + * The new format is: + * + * u32 flags; + * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 + * #define KRB5_CTX_FLAG_CFX 0x00000002 + * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + * s32 endtime; + * u64 seq_send; + * u32 enctype; ( encrption type of key ) + * raw key; ( raw key bytes (kernel will derive)) + * + */ static int -prepare_krb5_rfc_cfx_buffer(gss_krb5_lucid_context_v1_t *lctx, +prepare_krb5_rfc4121_buffer(gss_krb5_lucid_context_v1_t *lctx, gss_buffer_desc *buf, int32_t *endtime) { - printerr(0, "ERROR: prepare_krb5_rfc_cfx_buffer: not implemented\n"); + char *p, *end; + uint32_t v2_flags = 0; + uint32_t enctype; + uint32_t keysize; + + if (!(buf->value = calloc(1, MAX_CTX_LEN))) + goto out_err; + p = buf->value; + end = buf->value + MAX_CTX_LEN; + + /* Version 2 */ + if (lctx->initiate) + v2_flags |= KRB5_CTX_FLAG_INITIATOR; + if (lctx->protocol != 0) + v2_flags |= KRB5_CTX_FLAG_CFX; + if (lctx->protocol != 0 && lctx->cfx_kd.have_acceptor_subkey == 1) + v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; + + if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; + if (WRITE_BYTES(&p, end, lctx->endtime)) goto out_err; + if (endtime) + *endtime = lctx->endtime; + if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err; + + /* Protocol 0 here implies DES3 or RC4 */ + printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol); + if (lctx->protocol == 0) { + enctype = lctx->rfc1964_kd.ctx_key.type; + keysize = lctx->rfc1964_kd.ctx_key.length; + } else { + if (lctx->cfx_kd.have_acceptor_subkey) { + enctype = lctx->cfx_kd.acceptor_subkey.type; + keysize = lctx->cfx_kd.acceptor_subkey.length; + } else { + enctype = lctx->cfx_kd.ctx_key.type; + keysize = lctx->cfx_kd.ctx_key.length; + } + } + printerr(2, "%s: serializing key with enctype %d and size %d\n", + __FUNCTION__, enctype, keysize); + + if (WRITE_BYTES(&p, end, enctype)) goto out_err; + + if (lctx->protocol == 0) { + if (write_bytes(&p, end, lctx->rfc1964_kd.ctx_key.data, + lctx->rfc1964_kd.ctx_key.length)) + goto out_err; + } else { + if (lctx->cfx_kd.have_acceptor_subkey) { + if (write_bytes(&p, end, + lctx->cfx_kd.acceptor_subkey.data, + lctx->cfx_kd.acceptor_subkey.length)) + goto out_err; + } else { + if (write_bytes(&p, end, lctx->cfx_kd.ctx_key.data, + lctx->cfx_kd.ctx_key.length)) + goto out_err; + } + } + + buf->length = p - (char *)buf->value; + return 0; + +out_err: + printerr(0, "ERROR: %s: failed serializing krb5 context for kernel\n", + __FUNCTION__); + if (buf->value) { + free(buf->value); + buf->value = NULL; + } + buf->length = 0; return -1; } @@ -176,7 +266,7 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime) gss_krb5_lucid_context_v1_t *lctx = 0; int retcode = 0; - printerr(2, "DEBUG: serialize_krb5_ctx: lucid version!\n"); + printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__); maj_stat = gss_export_lucid_sec_context(&min_stat, &ctx, 1, &return_ctx); if (maj_stat != GSS_S_COMPLETE) { @@ -198,11 +288,20 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime) break; } - /* Now lctx points to a lucid context that we can send down to kernel */ - if (lctx->protocol == 0) + /* + * Now lctx points to a lucid context that we can send down to kernel + * + * Note: we send down different information to the kernel depending + * on the protocol version and the enctyption type. + * For protocol version 0 with all enctypes besides DES3, we use + * the original format. For protocol version != 0 or DES3, we + * send down the new style information. + */ + + if (lctx->protocol == 0 && lctx->rfc1964_kd.ctx_key.type <= 4) retcode = prepare_krb5_rfc1964_buffer(lctx, buf, endtime); else - retcode = prepare_krb5_rfc_cfx_buffer(lctx, buf, endtime); + retcode = prepare_krb5_rfc4121_buffer(lctx, buf, endtime); maj_stat = gss_free_lucid_sec_context(&min_stat, ctx, return_ctx); if (maj_stat != GSS_S_COMPLETE) { @@ -212,8 +311,8 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime) } if (retcode) { - printerr(1, "serialize_krb5_ctx: prepare_krb5_*_buffer " - "failed (retcode = %d)\n", retcode); + printerr(1, "%s: prepare_krb5_*_buffer failed (retcode = %d)\n", + __FUNCTION__, retcode); goto out_err; } @@ -223,4 +322,7 @@ out_err: printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); return -1; } + + + #endif /* HAVE_LUCID_CONTEXT_SUPPORT */ diff --git a/utils/gssd/context_mit.c b/utils/gssd/context_mit.c index 709a903..e6db9cb 100644 --- a/utils/gssd/context_mit.c +++ b/utils/gssd/context_mit.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2004 The Regents of the University of Michigan. + Copyright (c) 2004-2006 The Regents of the University of Michigan. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -52,8 +53,7 @@ /* XXX argggg, there's gotta be a better way than just duplicating this * whole struct. Unfortunately, this is in a "private" header file, * so this is our best choice at this point :-/ - * - * XXX Does this match the Heimdal definition? */ + */ typedef struct _krb5_gss_ctx_id_rec { unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */ @@ -156,50 +156,122 @@ serialize_krb5_ctx(gss_ctx_id_t ctx, gss_buffer_desc *buf, int32_t *endtime) { krb5_gss_ctx_id_t kctx = ((gss_union_ctx_id_t)ctx)->internal_ctx_id; char *p, *end; - static int constant_one = 1; static int constant_zero = 0; + static int constant_one = 1; + static int constant_two = 2; uint32_t word_seq_send; + u_int64_t seq_send_64bit; + uint32_t v2_flags = 0; if (!(buf->value = calloc(1, MAX_CTX_LEN))) goto out_err; p = buf->value; end = buf->value + MAX_CTX_LEN; - if (kctx->initiate) { - if (WRITE_BYTES(&p, end, constant_one)) goto out_err; - } - else { - if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; - } - if (kctx->seed_init) { - if (WRITE_BYTES(&p, end, constant_one)) goto out_err; - } - else { - if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; - } - if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) + switch (kctx->enc->enctype) { + case ENCTYPE_DES_CBC_CRC: + case ENCTYPE_DES_CBC_MD4: + case ENCTYPE_DES_CBC_MD5: + case ENCTYPE_DES_CBC_RAW: + /* Old format of context to the kernel */ + if (kctx->initiate) { + if (WRITE_BYTES(&p, end, constant_one)) goto out_err; + } + else { + if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; + } + if (kctx->seed_init) { + if (WRITE_BYTES(&p, end, constant_one)) goto out_err; + } + else { + if (WRITE_BYTES(&p, end, constant_zero)) goto out_err; + } + if (write_bytes(&p, end, &kctx->seed, sizeof(kctx->seed))) + goto out_err; + if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; + if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; + if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; + if (endtime) + *endtime = kctx->endtime; + word_seq_send = kctx->seq_send; + if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; + if (write_oid(&p, end, kctx->mech_used)) goto out_err; + + printerr(2, "serialize_krb5_ctx: serializing keys with " + "enctype %d and length %d\n", + kctx->enc->enctype, kctx->enc->length); + + if (write_keyblock(&p, end, kctx->enc)) goto out_err; + if (write_keyblock(&p, end, kctx->seq)) goto out_err; + break; + case ENCTYPE_DES3_CBC_RAW: + case ENCTYPE_DES3_CBC_SHA1: + case ENCTYPE_ARCFOUR_HMAC: + case ENCTYPE_ARCFOUR_HMAC_EXP: + case ENCTYPE_AES128_CTS_HMAC_SHA1_96: + case ENCTYPE_AES256_CTS_HMAC_SHA1_96: + /* New format of context to the kernel */ + /* u32 flags; + * #define KRB5_CTX_FLAG_INITIATOR 0x00000001 + * #define KRB5_CTX_FLAG_CFX 0x00000002 + * #define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004 + * s32 endtime; + * u64 seq_send; + * u32 enctype; + * rawkey data + */ + + if (kctx->initiate) + v2_flags |= KRB5_CTX_FLAG_INITIATOR; + if (kctx->proto == 1) + v2_flags |= KRB5_CTX_FLAG_CFX; + if (kctx->have_acceptor_subkey) + v2_flags |= KRB5_CTX_FLAG_ACCEPTOR_SUBKEY; + if (WRITE_BYTES(&p, end, v2_flags)) goto out_err; + if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; + + seq_send_64bit = kctx->seq_send; + if (WRITE_BYTES(&p, end, seq_send_64bit)) goto out_err; + + if (kctx->have_acceptor_subkey) { + if (WRITE_BYTES(&p, end, kctx->acceptor_subkey->enctype)) + goto out_err; + printerr(2, "serialize_krb5_ctx: serializing subkey " + "with enctype %d and size %d\n", + kctx->acceptor_subkey->enctype, + kctx->acceptor_subkey->length); + + if (write_bytes(&p, end, + kctx->acceptor_subkey->contents, + kctx->acceptor_subkey->length)) + goto out_err; + } else { + if (WRITE_BYTES(&p, end, kctx->enc->enctype)) + goto out_err; + printerr(2, "serialize_krb5_ctx: serializing key " + "with enctype %d and size %d\n", + kctx->enc->enctype, kctx->enc->length); + + if (write_bytes(&p, end, kctx->enc->contents, + kctx->enc->length)) + goto out_err; + } + break; + default: + printerr(0, "ERROR: serialize_krb5_ctx: unsupported encryption " + "algorithm %d\n", kctx->enc->enctype); goto out_err; - if (WRITE_BYTES(&p, end, kctx->signalg)) goto out_err; - if (WRITE_BYTES(&p, end, kctx->sealalg)) goto out_err; - if (WRITE_BYTES(&p, end, kctx->endtime)) goto out_err; - if (endtime) - *endtime = kctx->endtime; - word_seq_send = kctx->seq_send; - if (WRITE_BYTES(&p, end, word_seq_send)) goto out_err; - if (write_oid(&p, end, kctx->mech_used)) goto out_err; - - printerr(2, "serialize_krb5_ctx: serializing keys with " - "enctype %d and length %d\n", - kctx->enc->enctype, kctx->enc->length); - - if (write_keyblock(&p, end, kctx->enc)) goto out_err; - if (write_keyblock(&p, end, kctx->seq)) goto out_err; + } buf->length = p - (char *)buf->value; return 0; + out_err: printerr(0, "ERROR: failed serializing krb5 context for kernel\n"); - if (buf->value) free(buf->value); + if (buf->value) { + free(buf->value); + } + buf->value = NULL; buf->length = 0; return -1; } diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c index bd37a5f..ccadb07 100644 --- a/utils/gssd/gssd.c +++ b/utils/gssd/gssd.c @@ -78,7 +78,7 @@ void sig_hup(int signal) { /* don't exit on SIGHUP */ - printerr(1, "Received SIGHUP... Ignoring.\n"); + printerr(1, "Received SIGHUP(%d)... Ignoring.\n", signal); return; } diff --git a/utils/gssd/gssd_main_loop.c b/utils/gssd/gssd_main_loop.c index f1a68d3..b06c223 100644 --- a/utils/gssd/gssd_main_loop.c +++ b/utils/gssd/gssd_main_loop.c @@ -63,6 +63,8 @@ static volatile int dir_changed = 1; static void dir_notify_handler(int sig, siginfo_t *si, void *data) { + printerr(2, "dir_notify_handler: sig %d si %p data %p\n", sig, si, data); + dir_changed = 1; } diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c index be4fb11..c301d46 100644 --- a/utils/gssd/gssd_proc.c +++ b/utils/gssd/gssd_proc.c @@ -600,6 +600,67 @@ update_client_list(void) return retval; } +/* Encryption types supported by the kernel rpcsec_gss code */ +int num_krb5_enctypes = 0; +krb5_enctype *krb5_enctypes = NULL; + +/* + * Parse the supported encryption type information + */ +static int +parse_enctypes(char *enctypes) +{ + int n = 0; + char *curr, *comma; + int i; + static char *cached_types; + + if (cached_types && strcmp(cached_types, enctypes) == 0) + return 0; + free(cached_types); + + if (krb5_enctypes != NULL) { + free(krb5_enctypes); + krb5_enctypes = NULL; + num_krb5_enctypes = 0; + } + + /* count the number of commas */ + for (curr = enctypes; curr && *curr != '\0'; curr = ++comma) { + comma = strchr(curr, ','); + if (comma != NULL) + n++; + else + break; + } + /* If no more commas and we're not at the end, there's one more value */ + if (*curr != '\0') + n++; + + /* Empty string, return an error */ + if (n == 0) + return ENOENT; + + /* Allocate space for enctypes array */ + if ((krb5_enctypes = (int *) calloc(n, sizeof(int))) == NULL) { + return ENOMEM; + } + + /* Now parse each value into the array */ + for (curr = enctypes, i = 0; curr && *curr != '\0'; curr = ++comma) { + krb5_enctypes[i++] = atoi(curr); + comma = strchr(curr, ','); + if (comma == NULL) + break; + } + + num_krb5_enctypes = n; + if ((cached_types = malloc(strlen(enctypes)+1))) + strcpy(cached_types, enctypes); + + return 0; +} + static int do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, gss_buffer_desc *context_token) @@ -798,7 +859,7 @@ int create_auth_rpc_client(struct clnt_info *clp, * Do this before creating rpc connection since we won't need * rpc connection if it fails! */ - if (limit_krb5_enctypes(&sec, uid)) { + if (limit_krb5_enctypes(&sec)) { printerr(1, "WARNING: Failed while limiting krb5 " "encryption types for user with uid %d\n", uid); @@ -875,7 +936,7 @@ int create_auth_rpc_client(struct clnt_info *clp, if (sec.cred != GSS_C_NO_CREDENTIAL) gss_release_cred(&min_stat, &sec.cred); /* Restore euid to original value */ - if ((save_uid != -1) && (setfsuid(save_uid) != uid)) { + if (((int)save_uid != -1) && (setfsuid(save_uid) != (int)uid)) { printerr(0, "WARNING: Failed to restore fsuid" " to uid %d from %d\n", save_uid, uid); } @@ -1100,7 +1161,7 @@ handle_krb5_upcall(struct clnt_info *clp) { uid_t uid; - if (read(clp->krb5_fd, &uid, sizeof(uid)) < sizeof(uid)) { + if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) { printerr(0, "WARNING: failed reading uid from krb5 " "upcall pipe: %s\n", strerror(errno)); return; @@ -1114,7 +1175,7 @@ handle_spkm3_upcall(struct clnt_info *clp) { uid_t uid; - if (read(clp->spkm3_fd, &uid, sizeof(uid)) < sizeof(uid)) { + if (read(clp->spkm3_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) { printerr(0, "WARNING: failed reading uid from spkm3 " "upcall pipe: %s\n", strerror(errno)); return; @@ -1133,6 +1194,7 @@ handle_gssd_upcall(struct clnt_info *clp) char *mech = NULL; char *target = NULL; char *service = NULL; + char *enctypes = NULL; printerr(1, "handling gssd upcall (%s)\n", clp->dirname); @@ -1176,6 +1238,23 @@ handle_gssd_upcall(struct clnt_info *clp) goto out; } + /* read supported encryption types if supplied */ + if ((p = strstr(lbuf, "enctypes=")) != NULL) { + enctypes = malloc(lbuflen); + if (!enctypes) + goto out; + if (sscanf(p, "enctypes=%s", enctypes) != 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse target name " + "in upcall string '%s'\n", lbuf); + goto out; + } + if (parse_enctypes(enctypes) != 0) { + printerr(0, "WARNING: handle_gssd_upcall: " + "parsing encryption types failed: errno %d\n", errno); + } + } + /* read target name */ if ((p = strstr(lbuf, "target=")) != NULL) { target = malloc(lbuflen); @@ -1222,6 +1301,7 @@ handle_gssd_upcall(struct clnt_info *clp) out: free(lbuf); free(mech); + free(enctypes); free(target); free(service); return; diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c index 1295f57..f071600 100644 --- a/utils/gssd/krb5_util.c +++ b/utils/gssd/krb5_util.c @@ -224,6 +224,13 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) free(namelist[i]); continue; } + if (uid == 0 && !root_uses_machine_creds && + strstr(namelist[i]->d_name, "_machine_")) { + printerr(3, "CC file '%s' not available to root\n", + statname); + free(namelist[i]); + continue; + } if (!query_krb5_ccache(buf, &princname, &realm)) { printerr(3, "CC file '%s' is expired or corrupt\n", statname); @@ -292,61 +299,6 @@ gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, struct dirent **d) return err; } - -#ifdef HAVE_SET_ALLOWABLE_ENCTYPES -/* - * this routine obtains a credentials handle via gss_acquire_cred() - * then calls gss_krb5_set_allowable_enctypes() to limit the encryption - * types negotiated. - * - * XXX Should call some function to determine the enctypes supported - * by the kernel. (Only need to do that once!) - * - * Returns: - * 0 => all went well - * -1 => there was an error - */ - -int -limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid) -{ - u_int maj_stat, min_stat; - gss_cred_id_t credh; - gss_OID_set_desc desired_mechs; - krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, - ENCTYPE_DES_CBC_MD5, - ENCTYPE_DES_CBC_MD4 }; - int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); - - /* We only care about getting a krb5 cred */ - desired_mechs.count = 1; - desired_mechs.elements = &krb5oid; - - maj_stat = gss_acquire_cred(&min_stat, NULL, 0, - &desired_mechs, GSS_C_INITIATE, - &credh, NULL, NULL); - - if (maj_stat != GSS_S_COMPLETE) { - if (get_verbosity() > 0) - pgsserr("gss_acquire_cred", - maj_stat, min_stat, &krb5oid); - return -1; - } - - maj_stat = gss_set_allowable_enctypes(&min_stat, credh, &krb5oid, - num_enctypes, &enctypes); - if (maj_stat != GSS_S_COMPLETE) { - pgsserr("gss_set_allowable_enctypes", - maj_stat, min_stat, &krb5oid); - gss_release_cred(&min_stat, &credh); - return -1; - } - sec->cred = credh; - - return 0; -} -#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ - /* * Obtain credentials via a key in the keytab given * a keytab handle and a gssd_k5_kt_princ structure. @@ -661,24 +613,32 @@ out: * and has *any* instance (hostname), return 1. * Otherwise return 0, indicating no match. */ +#ifdef HAVE_KRB5 static int -realm_and_service_match(krb5_context context, krb5_principal p, - const char *realm, const char *service) +realm_and_service_match(krb5_principal p, const char *realm, const char *service) { -#ifdef HAVE_KRB5 /* Must have two components */ if (p->length != 2) return 0; + if ((strlen(realm) == p->realm.length) && (strncmp(realm, p->realm.data, p->realm.length) == 0) && (strlen(service) == p->data[0].length) && (strncmp(service, p->data[0].data, p->data[0].length) == 0)) return 1; + + return 0; +} #else +static int +realm_and_service_match(krb5_context context, krb5_principal p, + const char *realm, const char *service) +{ const char *name, *inst; if (p->name.name_string.len != 2) return 0; + name = krb5_principal_get_comp_string(context, p, 0); inst = krb5_principal_get_comp_string(context, p, 1); if (name == NULL || inst == NULL) @@ -686,9 +646,10 @@ realm_and_service_match(krb5_context context, krb5_principal p, if ((strcmp(realm, p->realm) == 0) && (strcmp(service, name) == 0)) return 1; -#endif + return 0; } +#endif /* * Search the given keytab file looking for an entry with the given @@ -710,7 +671,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, krb5_kt_cursor cursor; krb5_error_code code; struct gssd_k5_kt_princ *ple; - int retval = -1; + int retval = -1, status; char kt_name[BUFSIZ]; char *pname; char *k5err = NULL; @@ -753,8 +714,12 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, printerr(4, "Processing keytab entry for principal '%s'\n", pname); /* Use the first matching keytab entry found */ - if ((realm_and_service_match(context, kte->principal, realm, - service))) { +#ifdef HAVE_KRB5 + status = realm_and_service_match(kte->principal, realm, service); +#else + status = realm_and_service_match(context, kte->principal, realm, service); +#endif + if (status) { printerr(4, "We WILL use this entry (%s)\n", pname); ple = get_ple_by_princ(context, kte->principal); /* @@ -1304,3 +1269,68 @@ gssd_k5_get_default_realm(char **def_realm) krb5_free_context(context); } + +#ifdef HAVE_SET_ALLOWABLE_ENCTYPES +/* + * this routine obtains a credentials handle via gss_acquire_cred() + * then calls gss_krb5_set_allowable_enctypes() to limit the encryption + * types negotiated. + * + * XXX Should call some function to determine the enctypes supported + * by the kernel. (Only need to do that once!) + * + * Returns: + * 0 => all went well + * -1 => there was an error + */ + +int +limit_krb5_enctypes(struct rpc_gss_sec *sec) +{ + u_int maj_stat, min_stat; + gss_cred_id_t credh; + gss_OID_set_desc desired_mechs; + krb5_enctype enctypes[] = { ENCTYPE_DES_CBC_CRC, + ENCTYPE_DES_CBC_MD5, + ENCTYPE_DES_CBC_MD4 }; + int num_enctypes = sizeof(enctypes) / sizeof(enctypes[0]); + extern int num_krb5_enctypes; + extern krb5_enctype *krb5_enctypes; + + /* We only care about getting a krb5 cred */ + desired_mechs.count = 1; + desired_mechs.elements = &krb5oid; + + maj_stat = gss_acquire_cred(&min_stat, NULL, 0, + &desired_mechs, GSS_C_INITIATE, + &credh, NULL, NULL); + + if (maj_stat != GSS_S_COMPLETE) { + if (get_verbosity() > 0) + pgsserr("gss_acquire_cred", + maj_stat, min_stat, &krb5oid); + return -1; + } + + /* + * If we failed for any reason to produce global + * list of supported enctypes, use local default here. + */ + if (krb5_enctypes == NULL) + maj_stat = gss_set_allowable_enctypes(&min_stat, credh, + &krb5oid, num_enctypes, enctypes); + else + maj_stat = gss_set_allowable_enctypes(&min_stat, credh, + &krb5oid, num_krb5_enctypes, krb5_enctypes); + + if (maj_stat != GSS_S_COMPLETE) { + pgsserr("gss_set_allowable_enctypes", + maj_stat, min_stat, &krb5oid); + gss_release_cred(&min_stat, &credh); + return -1; + } + sec->cred = credh; + + return 0; +} +#endif /* HAVE_SET_ALLOWABLE_ENCTYPES */ diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h index 4602cc3..b42b91e 100644 --- a/utils/gssd/krb5_util.h +++ b/utils/gssd/krb5_util.h @@ -36,7 +36,7 @@ char *gssd_k5_err_msg(krb5_context context, krb5_error_code code); void gssd_k5_get_default_realm(char **def_realm); #ifdef HAVE_SET_ALLOWABLE_ENCTYPES -int limit_krb5_enctypes(struct rpc_gss_sec *sec, uid_t uid); +int limit_krb5_enctypes(struct rpc_gss_sec *sec); #endif /* diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c index 729b6a6..e7375a4 100644 --- a/utils/gssd/svcgssd.c +++ b/utils/gssd/svcgssd.c @@ -160,7 +160,7 @@ void sig_hup(int signal) { /* don't exit on SIGHUP */ - printerr(1, "Received SIGHUP... Ignoring.\n"); + printerr(1, "Received SIGHUP(%d)... Ignoring.\n", signal); return; } diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c index f1bfbef..3894078 100644 --- a/utils/gssd/svcgssd_proc.c +++ b/utils/gssd/svcgssd_proc.c @@ -132,7 +132,7 @@ struct gss_verifier { #define RPCSEC_GSS_SEQ_WIN 5 static int -send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token, +send_response(gss_buffer_desc *in_handle, gss_buffer_desc *in_token, u_int32_t maj_stat, u_int32_t min_stat, gss_buffer_desc *out_handle, gss_buffer_desc *out_token) { @@ -431,12 +431,6 @@ handle_nullreq(FILE *f) { print_hexl("in_tok", in_tok.value, in_tok.length); #endif - if (in_tok.length < 0) { - printerr(0, "WARNING: handle_nullreq: " - "failed parsing request\n"); - goto out_err; - } - if (in_handle.length != 0) { /* CONTINUE_INIT case */ if (in_handle.length != sizeof(ctx)) { printerr(0, "WARNING: handle_nullreq: " @@ -498,7 +492,7 @@ handle_nullreq(FILE *f) { do_svc_downcall(&out_handle, &cred, mech, &ctx_token, ctx_endtime, hostbased_name); continue_needed: - send_response(f, &in_handle, &in_tok, maj_stat, min_stat, + send_response(&in_handle, &in_tok, maj_stat, min_stat, &out_handle, &out_tok); out: if (ctx_token.value != NULL) @@ -514,7 +508,7 @@ out: out_err: if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context(&ignore_min_stat, &ctx, &ignore_out_tok); - send_response(f, &in_handle, &in_tok, maj_stat, min_stat, + send_response(&in_handle, &in_tok, maj_stat, min_stat, &null_token, &null_token); goto out; } diff --git a/utils/idmapd/atomicio.c b/utils/idmapd/atomicio.c index 05e7147..1fb1ff9 100644 --- a/utils/idmapd/atomicio.c +++ b/utils/idmapd/atomicio.c @@ -43,7 +43,8 @@ atomicio( size_t n) { char *s = _s; - ssize_t res, pos = 0; + ssize_t res; + size_t pos = 0; while (n > pos) { res = (f) (fd, s + pos, n - pos); diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c index 573abaa..9ecab66 100644 --- a/utils/idmapd/idmapd.c +++ b/utils/idmapd/idmapd.c @@ -117,8 +117,24 @@ struct idmap_client { TAILQ_ENTRY(idmap_client) ic_next; }; static struct idmap_client nfsd_ic[2] = { -{IC_IDNAME, "Server", "", IC_IDNAME_CHAN, -1, -1, 0}, -{IC_NAMEID, "Server", "", IC_NAMEID_CHAN, -1, -1, 0}, +{ + .ic_which = IC_IDNAME, + .ic_clid = "Server", + .ic_id = "", + .ic_path = IC_IDNAME_CHAN, + .ic_fd = -1, + .ic_dirfd = -1, + .ic_scanned = 0 +}, +{ + .ic_which = IC_NAMEID, + .ic_clid = "Server", + .ic_id = "", + .ic_path = IC_NAMEID_CHAN, + .ic_fd = -1, + .ic_dirfd = -1, + .ic_scanned = 0 +}, }; TAILQ_HEAD(idmap_clientq, idmap_client); @@ -170,7 +186,7 @@ flush_nfsd_cache(char *path, time_t now) fd = open(path, O_RDWR); if (fd == -1) return -1; - if (write(fd, stime, strlen(stime)) != strlen(stime)) { + if (write(fd, stime, strlen(stime)) != (ssize_t)strlen(stime)) { errx(1, "Flushing nfsd cache failed: errno %d (%s)", errno, strerror(errno)); } @@ -381,7 +397,7 @@ main(int argc, char **argv) } static void -dirscancb(int fd, short which, void *data) +dirscancb(int UNUSED(fd), short UNUSED(which), void *data) { int nent, i; struct dirent **ents; @@ -465,13 +481,13 @@ out: } static void -svrreopen(int fd, short which, void *data) +svrreopen(int UNUSED(fd), short UNUSED(which), void *UNUSED(data)) { nfsdreopen(); } static void -clntscancb(int fd, short which, void *data) +clntscancb(int UNUSED(fd), short UNUSED(which), void *data) { struct idmap_clientq *icq = data; struct idmap_client *ic; @@ -485,7 +501,7 @@ clntscancb(int fd, short which, void *data) } static void -nfsdcb(int fd, short which, void *data) +nfsdcb(int UNUSED(fd), short which, void *data) { struct idmap_client *ic = data; struct idmap_msg im; @@ -660,7 +676,7 @@ imconv(struct idmap_client *ic, struct idmap_msg *im) } static void -nfscb(int fd, short which, void *data) +nfscb(int UNUSED(fd), short which, void *data) { struct idmap_client *ic = data; struct idmap_msg im; @@ -845,7 +861,7 @@ nametoidres(struct idmap_msg *im) static int validateascii(char *string, u_int32_t len) { - int i; + u_int32_t i; for (i = 0; i < len; i++) { if (string[i] == '\0') @@ -901,7 +917,7 @@ static int getfield(char **bpp, char *fld, size_t fldsz) { char *bp; - u_int val, n; + int val, n; while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0') ; diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c index 5cff009..6f2ee75 100644 --- a/utils/mount/configfile.c +++ b/utils/mount/configfile.c @@ -192,7 +192,8 @@ void free_all(void) } } static char *versions[] = {"v2", "v3", "v4", "vers", "nfsvers", NULL}; -int inline check_vers(char *mopt, char *field) +static int +check_vers(char *mopt, char *field) { int i, found=0; @@ -229,7 +230,8 @@ extern sa_family_t config_default_family; * If so, set the appropriate global value which will * be used as the initial value in the server negation. */ -int inline default_value(char *mopt) +static int +default_value(char *mopt) { struct mount_options *options = NULL; int dftlen = strlen("default"); diff --git a/utils/mount/network.c b/utils/mount/network.c index 8dc183a..d6b5205 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -53,6 +53,7 @@ #include "parse_opt.h" #include "network.h" #include "conffile.h" +#include "nfslib.h" #define PMAP_TIMEOUT (10) #define CONNECT_TIMEOUT (20) @@ -857,7 +858,14 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen, return 0; } - client->cl_auth = authunix_create_default(); + client->cl_auth = nfs_authsys_create(); + if (client->cl_auth == NULL) { + if (verbose) + nfs_error(_("%s: Failed to create RPC auth handle"), + progname); + CLNT_DESTROY(client); + return 0; + } res = CLNT_CALL(client, MOUNTPROC_UMNT, (xdrproc_t)xdr_dirpath, (caddr_t)argp, @@ -957,8 +965,10 @@ CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock) } if (clnt) { /* try to mount hostname:dirname */ - clnt->cl_auth = authunix_create_default(); - return clnt; + clnt->cl_auth = nfs_authsys_create(); + if (clnt->cl_auth) + return clnt; + CLNT_DESTROY(clnt); } return NULL; } @@ -1203,6 +1213,8 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program) return 1; } case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'nfsprog=' option"), + progname); return 0; } @@ -1242,9 +1254,12 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) } return 0; case PO_NOT_FOUND: - nfs_error(_("%s: option parsing error\n"), + nfs_error(_("%s: parsing error on 'vers=' option\n"), progname); + return 0; case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'vers=' option"), + progname); return 0; } case 4: /* nfsvers */ @@ -1256,9 +1271,12 @@ nfs_nfs_version(struct mount_options *options, unsigned long *version) } return 0; case PO_NOT_FOUND: - nfs_error(_("%s: option parsing error\n"), + nfs_error(_("%s: parsing error on 'nfsvers=' option\n"), progname); + return 0; case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'nfsvers=' option"), + progname); return 0; } } @@ -1294,6 +1312,8 @@ nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol) if (option != NULL) { if (!nfs_get_proto(option, &family, protocol)) { errno = EPROTONOSUPPORT; + nfs_error(_("%s: Failed to find '%s' protocol"), + progname, option); return 0; } return 1; @@ -1327,6 +1347,8 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port) return 1; } case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'port=' option"), + progname); return 0; } @@ -1342,7 +1364,7 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port) sa_family_t config_default_family = AF_UNSPEC; static int -nfs_verify_family(sa_family_t family) +nfs_verify_family(sa_family_t UNUSED(family)) { return 1; } @@ -1380,8 +1402,13 @@ int nfs_nfs_proto_family(struct mount_options *options, case 2: /* proto */ option = po_get(options, "proto"); if (option != NULL && - !nfs_get_proto(option, &tmp_family, &protocol)) - goto out_err; + !nfs_get_proto(option, &tmp_family, &protocol)) { + + nfs_error(_("%s: Failed to find '%s' protocol"), + progname, option); + errno = EPROTONOSUPPORT; + return 0; + } } if (!nfs_verify_family(tmp_family)) @@ -1414,6 +1441,8 @@ nfs_mount_program(struct mount_options *options, unsigned long *program) return 1; } case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'mountprog=' option"), + progname); return 0; } @@ -1443,6 +1472,8 @@ nfs_mount_version(struct mount_options *options, unsigned long *version) return 1; } case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'mountvers=' option"), + progname); return 0; } @@ -1469,6 +1500,8 @@ nfs_mount_protocol(struct mount_options *options, unsigned long *protocol) if (option != NULL) { if (!nfs_get_proto(option, &family, protocol)) { errno = EPROTONOSUPPORT; + nfs_error(_("%s: Failed to find '%s' protocol"), + progname, option); return 0; } return 1; @@ -1501,6 +1534,8 @@ nfs_mount_port(struct mount_options *options, unsigned long *port) return 1; } case PO_BAD_VALUE: + nfs_error(_("%s: invalid value for 'mountport=' option"), + progname); return 0; } @@ -1526,8 +1561,12 @@ int nfs_mount_proto_family(struct mount_options *options, option = po_get(options, "mountproto"); if (option != NULL) { - if (!nfs_get_proto(option, &tmp_family, &protocol)) + if (!nfs_get_proto(option, &tmp_family, &protocol)) { + nfs_error(_("%s: Failed to find '%s' protocol"), + progname, option); + errno = EPROTONOSUPPORT; goto out_err; + } if (!nfs_verify_family(tmp_family)) goto out_err; *family = tmp_family; diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man index c64de5f..3806635 100644 --- a/utils/mount/nfs.man +++ b/utils/mount/nfs.man @@ -623,14 +623,9 @@ in such cases. .TP 1.5i .BI nfsvers= n The NFS protocol version number used to contact the server's NFS service. -The Linux client supports version 2 and version 3 of the NFS protocol -when using the file system type -.BR nfs . -If the server does not support the requested version, -the mount request fails. -If this option is not specified, the client attempts to use version 3, -but negotiates the NFS version with the server if version 3 support -is not available. +If the server does not support the requested version, the mount request fails. +If this option is not specified, the client negociate a suitable version with +the server, trying version 4 first, version 3 second, and version 2 last. .TP 1.5i .BI vers= n This option is an alternative to the diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c index 4a2fab7..028e7cd 100644 --- a/utils/mount/nfs4mount.c +++ b/utils/mount/nfs4mount.c @@ -146,7 +146,7 @@ static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr) progname, hostname); return -1; } - if (hp->h_length > sizeof(struct in_addr)) { + if (hp->h_length > (int)sizeof(struct in_addr)) { nfs_error(_("%s: got bad hp->h_length"), progname); hp->h_length = sizeof(struct in_addr); } diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c index 6b3356c..5b934b5 100644 --- a/utils/mount/nfsmount.c +++ b/utils/mount/nfsmount.c @@ -510,8 +510,12 @@ nfsmount(const char *spec, const char *node, int flags, int val; static int doonce = 0; - clnt_addr_t mnt_server = { &mounthost, }; - clnt_addr_t nfs_server = { &hostname, }; + clnt_addr_t mnt_server = { + .hostname = &mounthost + }; + clnt_addr_t nfs_server = { + .hostname = &hostname + }; struct sockaddr_in *nfs_saddr = &nfs_server.saddr; struct pmap *mnt_pmap = &mnt_server.pmap, *nfs_pmap = &nfs_server.pmap; diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c index 9d798a2..1514340 100644 --- a/utils/mount/nfsumount.c +++ b/utils/mount/nfsumount.c @@ -179,10 +179,8 @@ static int nfs_umount_do_umnt(struct mount_options *options, struct pmap nfs_pmap, mnt_pmap; sa_family_t family; - if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) { - nfs_error(_("%s: bad mount options"), progname); + if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) return EX_FAIL; - } /* Skip UMNT call for vers=4 mounts */ if (nfs_pmap.pm_vers == 4) diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c index 9b8c38f..0241400 100644 --- a/utils/mount/stropts.c +++ b/utils/mount/stropts.c @@ -538,7 +538,10 @@ nfs_rewrite_pmap_mount_options(struct mount_options *options) if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap, mnt_saddr, &mnt_pmap)) { - errno = EINVAL; + if (rpc_createerr.cf_stat == RPC_UNKNOWNPROTO) + errno = EPROTONOSUPPORT; + else + errno = EINVAL; return 0; } @@ -586,18 +589,21 @@ static int nfs_do_mount_v3v2(struct nfsmount_info *mi, errno = ENOMEM; return result; } - + errno = 0; if (!nfs_append_addr_option(sap, salen, options)) { - errno = EINVAL; + if (errno == 0) + errno = EINVAL; goto out_fail; } if (!nfs_fix_mounthost_option(options, mi->hostname)) { - errno = EINVAL; + if (errno == 0) + errno = EINVAL; goto out_fail; } if (!mi->fake && !nfs_verify_lock_option(options)) { - errno = EINVAL; + if (errno == 0) + errno = EINVAL; goto out_fail; } @@ -799,6 +805,7 @@ static int nfs_is_permanent_error(int error) case ESTALE: case ETIMEDOUT: case ECONNREFUSED: + case EHOSTUNREACH: return 0; /* temporary */ default: return 1; /* permanent */ diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c index 13eba70..ccc849a 100644 --- a/utils/mountd/auth.c +++ b/utils/mountd/auth.c @@ -15,6 +15,8 @@ #include #include #include + +#include "sockaddr.h" #include "misc.h" #include "nfslib.h" #include "exportfs.h" @@ -110,13 +112,16 @@ auth_reload() return counter; } -static char *get_client_hostname(struct sockaddr_in *caller, struct hostent *hp, enum auth_error *error) +static char * +get_client_hostname(const struct sockaddr *caller, struct addrinfo *ai, + enum auth_error *error) { + char buf[INET6_ADDRSTRLEN]; char *n; if (use_ipaddr) - return strdup(inet_ntoa(caller->sin_addr)); - n = client_compose(hp); + return strdup(host_ntop(caller, buf, sizeof(buf))); + n = client_compose(ai); *error = unknown_host; if (!n) return NULL; @@ -128,8 +133,8 @@ static char *get_client_hostname(struct sockaddr_in *caller, struct hostent *hp, /* return static nfs_export with details filled in */ static nfs_export * -auth_authenticate_newcache(char *what, struct sockaddr_in *caller, - char *path, struct hostent *hp, +auth_authenticate_newcache(const struct sockaddr *caller, + const char *path, struct addrinfo *ai, enum auth_error *error) { nfs_export *exp; @@ -137,12 +142,12 @@ auth_authenticate_newcache(char *what, struct sockaddr_in *caller, free(my_client.m_hostname); - my_client.m_hostname = get_client_hostname(caller, hp, error); + my_client.m_hostname = get_client_hostname(caller, ai, error); if (my_client.m_hostname == NULL) return NULL; my_client.m_naddr = 1; - my_client.m_addrlist[0] = caller->sin_addr; + set_addrlist(&my_client, 0, caller); my_exp.m_client = &my_client; exp = NULL; @@ -152,7 +157,7 @@ auth_authenticate_newcache(char *what, struct sockaddr_in *caller, continue; if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname)) continue; - if (use_ipaddr && !client_check(exp->m_client, hp)) + if (use_ipaddr && !client_check(exp->m_client, ai)) continue; break; } @@ -166,18 +171,18 @@ auth_authenticate_newcache(char *what, struct sockaddr_in *caller, } static nfs_export * -auth_authenticate_internal(char *what, struct sockaddr_in *caller, - char *path, struct hostent *hp, - enum auth_error *error) +auth_authenticate_internal(const struct sockaddr *caller, const char *path, + struct addrinfo *ai, enum auth_error *error) { nfs_export *exp; if (new_cache) { - exp = auth_authenticate_newcache(what, caller, path, hp, error); + exp = auth_authenticate_newcache(caller, path, ai, error); if (!exp) return NULL; } else { - if (!(exp = export_find(hp, path))) { + exp = export_find(ai, path); + if (exp == NULL) { *error = no_entry; return NULL; } @@ -187,7 +192,7 @@ auth_authenticate_internal(char *what, struct sockaddr_in *caller, return NULL; } if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) && - ntohs(caller->sin_port) >= IPPORT_RESERVED) { + nfs_get_port(caller) >= IPPORT_RESERVED) { *error = illegal_port; return NULL; } @@ -197,18 +202,19 @@ auth_authenticate_internal(char *what, struct sockaddr_in *caller, } nfs_export * -auth_authenticate(char *what, struct sockaddr_in *caller, char *path) +auth_authenticate(const char *what, const struct sockaddr *caller, + const char *path) { nfs_export *exp = NULL; char epath[MAXPATHLEN+1]; char *p = NULL; - struct hostent *hp = NULL; - struct in_addr addr = caller->sin_addr; + char buf[INET6_ADDRSTRLEN]; + struct addrinfo *ai = NULL; enum auth_error error = bad_path; - if (path [0] != '/') { - xlog(L_WARNING, "bad path in %s request from %s: \"%s\"", - what, inet_ntoa(addr), path); + if (path[0] != '/') { + xlog(L_WARNING, "Bad path in %s request from %s: \"%s\"", + what, host_ntop(caller, buf, sizeof(buf)), path); return exp; } @@ -216,14 +222,13 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path) epath[sizeof (epath) - 1] = '\0'; auth_fixpath(epath); /* strip duplicate '/' etc */ - hp = client_resolve(caller->sin_addr); - if (!hp) + ai = client_resolve(caller); + if (ai == NULL) return exp; /* Try the longest matching exported pathname. */ while (1) { - exp = auth_authenticate_internal(what, caller, epath, - hp, &error); + exp = auth_authenticate_internal(caller, epath, ai, &error); if (exp || (error != not_exported && error != no_entry)) break; /* We have to treat the root, "/", specially. */ @@ -236,41 +241,40 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path) switch (error) { case bad_path: xlog(L_WARNING, "bad path in %s request from %s: \"%s\"", - what, inet_ntoa(addr), path); + what, host_ntop(caller, buf, sizeof(buf)), path); break; case unknown_host: xlog(L_WARNING, "refused %s request from %s for %s (%s): unmatched host", - what, inet_ntoa(addr), path, epath); + what, host_ntop(caller, buf, sizeof(buf)), path, epath); break; case no_entry: xlog(L_WARNING, "refused %s request from %s for %s (%s): no export entry", - what, hp->h_name, path, epath); + what, ai->ai_canonname, path, epath); break; case not_exported: xlog(L_WARNING, "refused %s request from %s for %s (%s): not exported", - what, hp->h_name, path, epath); + what, ai->ai_canonname, path, epath); break; case illegal_port: - xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %d", - what, hp->h_name, path, epath, ntohs(caller->sin_port)); + xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %u", + what, ai->ai_canonname, path, epath, nfs_get_port(caller)); break; case success: - xlog(L_NOTICE, "authenticated %s request from %s:%d for %s (%s)", - what, hp->h_name, ntohs(caller->sin_port), path, epath); + xlog(L_NOTICE, "authenticated %s request from %s:%u for %s (%s)", + what, ai->ai_canonname, nfs_get_port(caller), path, epath); break; default: - xlog(L_NOTICE, "%s request from %s:%d for %s (%s) gave %d", - what, hp->h_name, ntohs(caller->sin_port), path, epath, error); + xlog(L_NOTICE, "%s request from %s:%u for %s (%s) gave %d", + what, ai->ai_canonname, nfs_get_port(caller), + path, epath, error); } - if (hp) - free (hp); - + freeaddrinfo(ai); return exp; } diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c index d63e10a..f70f4d6 100644 --- a/utils/mountd/cache.c +++ b/utils/mountd/cache.c @@ -37,6 +37,11 @@ #include "blkid/blkid.h" #endif +/* + * Invoked by RPC service loop + */ +void cache_set_fds(fd_set *fdset); +int cache_process_req(fd_set *readfds); enum nfsd_fsid { FSID_DEV = 0, @@ -57,14 +62,14 @@ enum nfsd_fsid { * Record is terminated with newline. * */ -int cache_export_ent(char *domain, struct exportent *exp, char *p); +static int cache_export_ent(char *domain, struct exportent *exp, char *p); char *lbuf = NULL; int lbuflen = 0; extern int use_ipaddr; -void auth_unix_ip(FILE *f) +static void auth_unix_ip(FILE *f) { /* requests are * class IP-ADDR @@ -75,10 +80,10 @@ void auth_unix_ip(FILE *f) */ char *cp; char class[20]; - char ipaddr[20]; + char ipaddr[INET6_ADDRSTRLEN]; char *client = NULL; - struct in_addr addr; - struct hostent *he = NULL; + struct addrinfo *tmp = NULL; + struct addrinfo *ai = NULL; if (readline(fileno(f), &lbuf, &lbuflen) != 1) return; @@ -90,20 +95,23 @@ void auth_unix_ip(FILE *f) strcmp(class, "nfsd") != 0) return; - if (qword_get(&cp, ipaddr, 20) <= 0) + if (qword_get(&cp, ipaddr, sizeof(ipaddr)) <= 0) return; - if (inet_aton(ipaddr, &addr)==0) + tmp = host_pton(ipaddr); + if (tmp == NULL) return; auth_reload(); /* addr is a valid, interesting address, find the domain name... */ if (!use_ipaddr) { - he = client_resolve(addr); - client = client_compose(he); + ai = client_resolve(tmp->ai_addr); + client = client_compose(ai); + freeaddrinfo(ai); } - + freeaddrinfo(tmp); + qword_print(f, "nfsd"); qword_print(f, ipaddr); qword_printint(f, time(0)+30*60); @@ -114,18 +122,17 @@ void auth_unix_ip(FILE *f) qword_eol(f); xlog(D_CALL, "auth_unix_ip: client %p '%s'", client, client?client: "DEFAULT"); - if (client) free(client); - free(he); + free(client); } -void auth_unix_gid(FILE *f) +static void auth_unix_gid(FILE *f) { /* Request are * uid * reply is * uid expiry count list of group ids */ - int uid; + uid_t uid; struct passwd *pw; gid_t glist[100], *groups = glist; int ngroups = 100; @@ -136,7 +143,7 @@ void auth_unix_gid(FILE *f) return; cp = lbuf; - if (qword_get_int(&cp, &uid) != 0) + if (qword_get_uint(&cp, &uid) != 0) return; pw = getpwuid(uid); @@ -153,14 +160,14 @@ void auth_unix_gid(FILE *f) groups, &ngroups); } } - qword_printint(f, uid); - qword_printint(f, time(0)+30*60); + qword_printuint(f, uid); + qword_printuint(f, time(0)+30*60); if (rv >= 0) { - qword_printint(f, ngroups); + qword_printuint(f, ngroups); for (i=0; imnt_dir; } -void nfsd_fh(FILE *f) +static void nfsd_fh(FILE *f) { /* request are: * domain fsidtype fsid @@ -294,8 +345,7 @@ void nfsd_fh(FILE *f) unsigned int fsidnum=0; char fsid[32]; struct exportent *found = NULL; - struct hostent *he = NULL; - struct in_addr addr; + struct addrinfo *ai = NULL; char *found_path = NULL; nfs_export *exp; int i; @@ -398,6 +448,7 @@ void nfsd_fh(FILE *f) struct stat stb; char u[16]; char *path; + int type; if (exp->m_export.e_flags & NFSEXP_CROSSMOUNT) { static nfs_export *prev = NULL; @@ -461,22 +512,29 @@ void nfsd_fh(FILE *f) continue; check_uuid: if (exp->m_export.e_uuid) - get_uuid(NULL, exp->m_export.e_uuid, + get_uuid(exp->m_export.e_uuid, uuidlen, u); - else if (get_uuid(path, NULL, uuidlen, u) == 0) - continue; + else + for (type = 0; + uuid_by_path(path, type, uuidlen, u); + type++) + if (memcmp(u, fhuuid, uuidlen) == 0) + break; if (memcmp(u, fhuuid, uuidlen) != 0) continue; break; } if (use_ipaddr) { - if (he == NULL) { - if (!inet_aton(dom, &addr)) + if (ai == NULL) { + struct addrinfo *tmp; + tmp = host_pton(dom); + if (tmp == NULL) goto out; - he = client_resolve(addr); + ai = client_resolve(tmp->ai_addr); + freeaddrinfo(tmp); } - if (!client_check(exp->m_client, he)) + if (!client_check(exp->m_client, ai)) continue; } /* It's a match !! */ @@ -534,21 +592,20 @@ void nfsd_fh(FILE *f) out: if (found_path) free(found_path); - if (he) - free(he); + freeaddrinfo(ai); free(dom); xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL); return; } -static void write_fsloc(FILE *f, struct exportent *ep, char *path) +static void write_fsloc(FILE *f, struct exportent *ep) { struct servers *servers; if (ep->e_fslocmethod == FSLOC_NONE) return; - servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata, path); + servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata); if (!servers) return; qword_print(f, "fsloc"); @@ -596,17 +653,17 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex qword_printint(f, exp->e_anonuid); qword_printint(f, exp->e_anongid); qword_printint(f, exp->e_fsid); - write_fsloc(f, exp, path); + write_fsloc(f, exp); write_secinfo(f, exp, flag_mask); if (exp->e_uuid == NULL || different_fs) { char u[16]; - if (get_uuid(path, NULL, 16, u)) { + if (uuid_by_path(path, 0, 16, u)) { qword_print(f, "uuid"); qword_printhex(f, u, 16); } } else { char u[16]; - get_uuid(NULL, exp->e_uuid, 16, u); + get_uuid(exp->e_uuid, 16, u); qword_print(f, "uuid"); qword_printhex(f, u, 16); } @@ -614,12 +671,12 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex return qword_eol(f); } -static int is_subdirectory(char *subpath, char *path) +static int is_subdirectory(char *child, char *parent) { - int l = strlen(path); + int l = strlen(parent); - return strcmp(subpath, path) == 0 - || (strncmp(subpath, path, l) == 0 && path[l] == '/'); + return strcmp(child, parent) == 0 + || (strncmp(child, parent, l) == 0 && child[l] == '/'); } static int path_matches(nfs_export *exp, char *path) @@ -629,19 +686,22 @@ static int path_matches(nfs_export *exp, char *path) return strcmp(path, exp->m_export.e_path) == 0; } -static int client_matches(nfs_export *exp, char *dom, struct hostent *he) +static int +client_matches(nfs_export *exp, char *dom, struct addrinfo *ai) { if (use_ipaddr) - return client_check(exp->m_client, he); + return client_check(exp->m_client, ai); return client_member(dom, exp->m_client->m_hostname); } -static int export_matches(nfs_export *exp, char *dom, char *path, struct hostent *he) +static int +export_matches(nfs_export *exp, char *dom, char *path, struct addrinfo *ai) { - return path_matches(exp, path) && client_matches(exp, dom, he); + return path_matches(exp, path) && client_matches(exp, dom, ai); } -static nfs_export *lookup_export(char *dom, char *path, struct hostent *he) +static nfs_export * +lookup_export(char *dom, char *path, struct addrinfo *ai) { nfs_export *exp; nfs_export *found = NULL; @@ -650,7 +710,7 @@ static nfs_export *lookup_export(char *dom, char *path, struct hostent *he) for (i=0 ; i < MCL_MAXTYPES; i++) { for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { - if (!export_matches(exp, dom, path, he)) + if (!export_matches(exp, dom, path, ai)) continue; if (!found) { found = exp; @@ -687,7 +747,7 @@ static nfs_export *lookup_export(char *dom, char *path, struct hostent *he) return found; } -void nfsd_export(FILE *f) +static void nfsd_export(FILE *f) { /* requests are: * domain path @@ -698,9 +758,7 @@ void nfsd_export(FILE *f) char *cp; char *dom, *path; nfs_export *found = NULL; - struct in_addr addr; - struct hostent *he = NULL; - + struct addrinfo *ai = NULL; if (readline(fileno(f), &lbuf, &lbuflen) != 1) return; @@ -722,12 +780,16 @@ void nfsd_export(FILE *f) auth_reload(); if (use_ipaddr) { - if (!inet_aton(dom, &addr)) + struct addrinfo *tmp; + tmp = host_pton(dom); + if (tmp == NULL) + goto out; + ai = client_resolve(tmp->ai_addr); + freeaddrinfo(tmp); goto out; - he = client_resolve(addr); } - found = lookup_export(dom, path, he); + found = lookup_export(dom, path, ai); if (found) { if (dump_to_cache(f, dom, path, &found->m_export) < 0) { @@ -743,7 +805,7 @@ void nfsd_export(FILE *f) xlog(D_CALL, "nfsd_export: found %p path %s", found, path ? path : NULL); if (dom) free(dom); if (path) free(path); - if (he) free(he); + freeaddrinfo(ai); } @@ -752,14 +814,19 @@ struct { void (*cache_handle)(FILE *f); FILE *f; } cachelist[] = { - { "auth.unix.ip", auth_unix_ip}, - { "auth.unix.gid", auth_unix_gid}, - { "nfsd.export", nfsd_export}, - { "nfsd.fh", nfsd_fh}, - { NULL, NULL } + { "auth.unix.ip", auth_unix_ip, NULL}, + { "auth.unix.gid", auth_unix_gid, NULL}, + { "nfsd.export", nfsd_export, NULL}, + { "nfsd.fh", nfsd_fh, NULL}, + { NULL, NULL, NULL } }; extern int manage_gids; + +/** + * cache_open - prepare communications channels with kernel RPC caches + * + */ void cache_open(void) { int i; @@ -772,6 +839,10 @@ void cache_open(void) } } +/** + * cache_set_fds - prepare cache file descriptors for one iteration of the service loop + * @fdset: pointer to fd_set to prepare + */ void cache_set_fds(fd_set *fdset) { int i; @@ -781,6 +852,10 @@ void cache_set_fds(fd_set *fdset) } } +/** + * cache_process_req - process any active cache file descriptors during service loop iteration + * @fdset: pointer to fd_set to examine for activity + */ int cache_process_req(fd_set *readfds) { int i; @@ -803,7 +878,7 @@ int cache_process_req(fd_set *readfds) * % echo $domain $path $[now+30*60] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel */ -int cache_export_ent(char *domain, struct exportent *exp, char *path) +static int cache_export_ent(char *domain, struct exportent *exp, char *path) { int err; FILE *f = fopen("/proc/net/rpc/nfsd.export/channel", "w"); @@ -824,8 +899,8 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path) * and export them with the same options */ struct stat stb; - int l = strlen(exp->e_path); - int dev; + size_t l = strlen(exp->e_path); + __dev_t dev; if (strlen(path) <= l || path[l] != '/' || strncmp(exp->e_path, path, l) != 0) @@ -861,8 +936,14 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path) return err; } +/** + * cache_export - Inform kernel of a new nfs_export + * @exp: target nfs_export + * @path: NUL-terminated C string containing export path + */ int cache_export(nfs_export *exp, char *path) { + char buf[INET6_ADDRSTRLEN]; int err; FILE *f; @@ -870,8 +951,10 @@ int cache_export(nfs_export *exp, char *path) if (!f) return -1; + qword_print(f, "nfsd"); - qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0])); + qword_print(f, + host_ntop(get_addrlist(exp->m_client, 0), buf, sizeof(buf))); qword_printint(f, time(0)+30*60); qword_print(f, exp->m_client->m_hostname); err = qword_eol(f); @@ -883,7 +966,14 @@ int cache_export(nfs_export *exp, char *path) return err; } -/* Get a filehandle. +/** + * cache_get_filehandle - given an nfs_export, get its root filehandle + * @exp: target nfs_export + * @len: length of requested file handle + * @p: NUL-terminated C string containing export path + * + * Returns pointer to NFS file handle of root directory of export + * * { * echo $domain $path $length * read filehandle <&0 @@ -917,4 +1007,3 @@ cache_get_filehandle(nfs_export *exp, int len, char *p) fh.fh_size = qword_get(&bp, (char *)fh.fh_handle, NFS3_FHSIZE); return &fh; } - diff --git a/utils/mountd/fsloc.c b/utils/mountd/fsloc.c index 5b094b0..e2add2d 100644 --- a/utils/mountd/fsloc.c +++ b/utils/mountd/fsloc.c @@ -146,7 +146,7 @@ static struct servers *method_list(char *data) } /* Returns appropriately filled struct servers, or NULL if had a problem */ -struct servers *replicas_lookup(int method, char *data, char *key) +struct servers *replicas_lookup(int method, char *data) { struct servers *sp=NULL; switch(method) { diff --git a/utils/mountd/fsloc.h b/utils/mountd/fsloc.h index 8296d1c..1605df4 100644 --- a/utils/mountd/fsloc.h +++ b/utils/mountd/fsloc.h @@ -44,7 +44,7 @@ struct servers { int h_referral; /* 0=replica, 1=referral */ }; -struct servers *replicas_lookup(int method, char *data, char *key); +struct servers *replicas_lookup(int method, char *data); void release_replicas(struct servers *server); #endif /* FSLOC_H */ diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c index a0a1f2d..d309950 100644 --- a/utils/mountd/mountd.c +++ b/utils/mountd/mountd.c @@ -28,10 +28,6 @@ #include "rpcmisc.h" #include "pseudoflavors.h" -extern void cache_open(void); -extern struct nfs_fh_len *cache_get_filehandle(nfs_export *exp, int len, char *p); -extern int cache_export(nfs_export *exp, char *path); - extern void my_svc_run(void); static void usage(const char *, int exitcode); @@ -75,17 +71,40 @@ static struct option longopts[] = { NULL, 0, 0, 0 } }; -static int nfs_version = -1; +#define NFSVERSBIT(vers) (0x1 << (vers - 1)) +#define NFSVERSBIT_ALL (NFSVERSBIT(2) | NFSVERSBIT(3) | NFSVERSBIT(4)) + +static int nfs_version = NFSVERSBIT_ALL; + +static int version2(void) +{ + return nfs_version & NFSVERSBIT(2); +} + +static int version3(void) +{ + return nfs_version & NFSVERSBIT(3); +} + +static int version23(void) +{ + return nfs_version & (NFSVERSBIT(2) | NFSVERSBIT(3)); +} + +static int version_any(void) +{ + return nfs_version & NFSVERSBIT_ALL; +} static void unregister_services (void) { - if (nfs_version & 0x1) - pmap_unset (MOUNTPROG, MOUNTVERS); - if (nfs_version & (0x1 << 1)) - pmap_unset (MOUNTPROG, MOUNTVERS_POSIX); - if (nfs_version & (0x1 << 2)) - pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3); + if (version2()) { + nfs_svc_unregister(MOUNTPROG, MOUNTVERS); + nfs_svc_unregister(MOUNTPROG, MOUNTVERS_POSIX); + } + if (version3()) + nfs_svc_unregister(MOUNTPROG, MOUNTVERS_NFSV3); } static void @@ -192,17 +211,28 @@ sig_hup (int sig) } bool_t -mount_null_1_svc(struct svc_req *rqstp, void *argp, void *resp) +mount_null_1_svc(struct svc_req *rqstp, void *UNUSED(argp), + void *UNUSED(resp)) { + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + + xlog(D_CALL, "Received NULL request from %s", + host_ntop(sap, buf, sizeof(buf))); + return 1; } bool_t mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res) { + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; struct nfs_fh_len *fh; - xlog(D_CALL, "MNT1(%s) called", *path); + xlog(D_CALL, "Received MNT1(%s) request from %s", *path, + host_ntop(sap, buf, sizeof(buf))); + fh = get_rootfh(rqstp, path, NULL, &res->fhs_status, 0); if (fh) memcpy(&res->fhstatus_u.fhs_fhandle, fh->fh_handle, 32); @@ -210,23 +240,27 @@ mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res) } bool_t -mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res) +mount_dump_1_svc(struct svc_req *rqstp, void *UNUSED(argp), mountlist *res) { - struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt); + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + + xlog(D_CALL, "Received DUMP request from %s", + host_ntop(sap, buf, sizeof(buf))); - xlog(D_CALL, "dump request from %s.", inet_ntoa(addr->sin_addr)); *res = mountlist_list(); return 1; } bool_t -mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp) +mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *UNUSED(resp)) { - struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); nfs_export *exp; char *p = *argp; char rpath[MAXPATHLEN+1]; + char buf[INET6_ADDRSTRLEN]; if (*p == '\0') p = "/"; @@ -236,41 +270,57 @@ mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp) p = rpath; } - if (!(exp = auth_authenticate("unmount", sin, p))) { + xlog(D_CALL, "Received UMNT(%s) request from %s", p, + host_ntop(sap, buf, sizeof(buf))); + + exp = auth_authenticate("unmount", sap, p); + if (exp == NULL) return 1; - } - mountlist_del(inet_ntoa(sin->sin_addr), p); + mountlist_del(host_ntop(sap, buf, sizeof(buf)), p); return 1; } bool_t -mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp) +mount_umntall_1_svc(struct svc_req *rqstp, void *UNUSED(argp), + void *UNUSED(resp)) { + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + + xlog(D_CALL, "Received UMNTALL request from %s", + host_ntop(sap, buf, sizeof(buf))); + /* Reload /etc/xtab if necessary */ auth_reload(); - mountlist_del_all(nfs_getrpccaller_in(rqstp->rq_xprt)); + mountlist_del_all(nfs_getrpccaller(rqstp->rq_xprt)); return 1; } bool_t -mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp) +mount_export_1_svc(struct svc_req *rqstp, void *UNUSED(argp), exports *resp) { - struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt); + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + + xlog(D_CALL, "Received EXPORT request from %s.", + host_ntop(sap, buf, sizeof(buf))); - xlog(D_CALL, "export request from %s.", inet_ntoa(addr->sin_addr)); *resp = get_exportlist(); return 1; } bool_t -mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp) +mount_exportall_1_svc(struct svc_req *rqstp, void *UNUSED(argp), exports *resp) { - struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt); + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + + xlog(D_CALL, "Received EXPORTALL request from %s.", + host_ntop(sap, buf, sizeof(buf))); - xlog(D_CALL, "exportall request from %s.", inet_ntoa(addr->sin_addr)); *resp = get_exportlist(); return 1; @@ -290,11 +340,12 @@ mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp) bool_t mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res) { - struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); struct stat stb; nfs_export *exp; char rpath[MAXPATHLEN+1]; char *p = *path; + char buf[INET6_ADDRSTRLEN]; memset(res, 0, sizeof(*res)); @@ -310,11 +361,14 @@ mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res) p = rpath; } + xlog(D_CALL, "Received PATHCONF(%s) request from %s", p, + host_ntop(sap, buf, sizeof(buf))); + /* Now authenticate the intruder... */ - exp = auth_authenticate("pathconf", sin, p); - if (!exp) { + exp = auth_authenticate("pathconf", sap, p); + if (exp == NULL) return 1; - } else if (stat(p, &stb) < 0) { + else if (stat(p, &stb) < 0) { xlog(L_WARNING, "can't stat exported dir %s: %s", p, strerror(errno)); return 1; @@ -374,11 +428,15 @@ static void set_authflavors(struct mountres3_ok *ok, nfs_export *exp) bool_t mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res) { + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); struct mountres3_ok *ok = &res->mountres3_u.mountinfo; + char buf[INET6_ADDRSTRLEN]; nfs_export *exp; struct nfs_fh_len *fh; - xlog(D_CALL, "MNT3(%s) called", *path); + xlog(D_CALL, "Received MNT3(%s) request from %s", *path, + host_ntop(sap, buf, sizeof(buf))); + fh = get_rootfh(rqstp, path, &exp, &res->fhs_status, 1); if (!fh) return 1; @@ -393,12 +451,13 @@ static struct nfs_fh_len * get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, mountstat3 *error, int v3) { - struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt); + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); struct stat stb, estb; nfs_export *exp; struct nfs_fh_len *fh; char rpath[MAXPATHLEN+1]; char *p = *path; + char buf[INET6_ADDRSTRLEN]; if (*p == '\0') p = "/"; @@ -413,29 +472,29 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, } /* Now authenticate the intruder... */ - exp = auth_authenticate("mount", sin, p); - if (!exp) { - *error = NFSERR_ACCES; + exp = auth_authenticate("mount", sap, p); + if (exp == NULL) { + *error = MNT3ERR_ACCES; return NULL; } if (stat(p, &stb) < 0) { xlog(L_WARNING, "can't stat exported dir %s: %s", p, strerror(errno)); if (errno == ENOENT) - *error = NFSERR_NOENT; + *error = MNT3ERR_NOENT; else - *error = NFSERR_ACCES; + *error = MNT3ERR_ACCES; return NULL; } if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { xlog(L_WARNING, "%s is not a directory or regular file", p); - *error = NFSERR_NOTDIR; + *error = MNT3ERR_NOTDIR; return NULL; } if (stat(exp->m_export.e_path, &estb) < 0) { xlog(L_WARNING, "can't stat export point %s: %s", p, strerror(errno)); - *error = NFSERR_NOENT; + *error = MNT3ERR_NOENT; return NULL; } if (estb.st_dev != stb.st_dev @@ -443,7 +502,7 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, || !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT))) { xlog(L_WARNING, "request to export directory %s below nearest filesystem %s", p, exp->m_export.e_path); - *error = NFSERR_ACCES; + *error = MNT3ERR_ACCES; return NULL; } if (exp->m_export.e_mountpoint && @@ -452,7 +511,7 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, exp->m_export.e_path)) { xlog(L_WARNING, "request to export an unmounted filesystem: %s", p); - *error = NFSERR_NOENT; + *error = MNT3ERR_NOENT; return NULL; } @@ -463,12 +522,12 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, */ if (cache_export(exp, p)) { - *error = NFSERR_ACCES; + *error = MNT3ERR_ACCES; return NULL; } fh = cache_get_filehandle(exp, v3?64:32, p); if (fh == NULL) { - *error = NFSERR_ACCES; + *error = MNT3ERR_ACCES; return NULL; } } else { @@ -482,13 +541,13 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, xtab_append(exp); if (v3) - fh = getfh_size ((struct sockaddr *) sin, p, 64); + fh = getfh_size((struct sockaddr_in *)sap, p, 64); if (!v3 || (fh == NULL && errno == EINVAL)) { /* We first try the new nfs syscall. */ - fh = getfh ((struct sockaddr *) sin, p); + fh = getfh((struct sockaddr_in *)sap, p); if (fh == NULL && errno == EINVAL) /* Let's try the old one. */ - fh = getfh_old ((struct sockaddr *) sin, + fh = getfh_old((struct sockaddr_in *)sap, stb.st_dev, stb.st_ino); } if (fh == NULL && !did_export) { @@ -498,12 +557,12 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, if (fh == NULL) { xlog(L_WARNING, "getfh failed: %s", strerror(errno)); - *error = NFSERR_ACCES; + *error = MNT3ERR_ACCES; return NULL; } } - *error = NFS_OK; - mountlist_add(inet_ntoa(sin->sin_addr), p); + *error = MNT_OK; + mountlist_add(host_ntop(sap, buf, sizeof(buf)), p); if (expret) *expret = exp; return fh; @@ -536,22 +595,21 @@ static void free_exportlist(exports *elist) static void prune_clients(nfs_export *exp, struct exportnode *e) { - struct hostent *hp; + struct addrinfo *ai = NULL; struct groupnode *c, **cp; cp = &e->ex_groups; while ((c = *cp) != NULL) { if (client_gettype(c->gr_name) == MCL_FQDN - && (hp = gethostbyname(c->gr_name))) { - hp = hostent_dup(hp); - if (client_check(exp->m_client, hp)) { + && (ai = host_addrinfo(c->gr_name))) { + if (client_check(exp->m_client, ai)) { *cp = c->gr_next; xfree(c->gr_name); xfree(c); - xfree (hp); + freeaddrinfo(ai); continue; } - xfree (hp); + freeaddrinfo(ai); } cp = &(c->gr_next); } @@ -634,13 +692,22 @@ main(int argc, char **argv) { char *export_file = _PATH_EXPORTS; char *state_dir = NFS_STATEDIR; + char *progname; + unsigned int listeners = 0; int foreground = 0; int port = 0; int descriptors = 0; int c; + int vers; struct sigaction sa; struct rlimit rlim; + /* Set the basename */ + if ((progname = strrchr(argv[0], '/')) != NULL) + progname++; + else + progname = argv[0]; + /* Parse the command line options and arguments. */ opterr = 0; while ((c = getopt_long(argc, argv, "o:nFd:f:p:P:hH:N:V:vrs:t:g", longopts, NULL)) != EOF) @@ -652,8 +719,8 @@ main(int argc, char **argv) descriptors = atoi(optarg); if (descriptors <= 0) { fprintf(stderr, "%s: bad descriptors: %s\n", - argv [0], optarg); - usage(argv [0], 1); + progname, optarg); + usage(progname, 1); } break; case 'F': @@ -669,19 +736,25 @@ main(int argc, char **argv) ha_callout_prog = optarg; break; case 'h': - usage(argv [0], 0); + usage(progname, 0); break; case 'P': /* XXX for nfs-server compatibility */ case 'p': port = atoi(optarg); if (port <= 0 || port > 65535) { fprintf(stderr, "%s: bad port number: %s\n", - argv [0], optarg); - usage(argv [0], 1); + progname, optarg); + usage(progname, 1); } break; case 'N': - nfs_version &= ~(1 << (atoi (optarg) - 1)); + vers = atoi(optarg); + if (vers < 2 || vers > 4) { + fprintf(stderr, "%s: bad version number: %s\n", + argv[0], optarg); + usage(argv[0], 1); + } + nfs_version &= ~NFSVERSBIT(vers); break; case 'n': _rpcfdtype = SOCK_DGRAM; @@ -692,7 +765,7 @@ main(int argc, char **argv) case 's': if ((state_dir = xstrdup(optarg)) == NULL) { fprintf(stderr, "%s: xstrdup(%s) failed!\n", - argv[0], optarg); + progname, optarg); exit(1); } break; @@ -700,31 +773,37 @@ main(int argc, char **argv) num_threads = atoi (optarg); break; case 'V': - nfs_version |= 1 << (atoi (optarg) - 1); + vers = atoi(optarg); + if (vers < 2 || vers > 4) { + fprintf(stderr, "%s: bad version number: %s\n", + argv[0], optarg); + usage(argv[0], 1); + } + nfs_version |= NFSVERSBIT(vers); break; case 'v': - printf("kmountd %s\n", VERSION); + printf("%s version " VERSION "\n", progname); exit(0); case 0: break; case '?': default: - usage(argv [0], 1); + usage(progname, 1); } /* No more arguments allowed. */ - if (optind != argc || !(nfs_version & 0x7)) - usage(argv [0], 1); + if (optind != argc || !version_any()) + usage(progname, 1); if (chdir(state_dir)) { fprintf(stderr, "%s: chdir(%s) failed: %s\n", - argv [0], state_dir, strerror(errno)); + progname, state_dir, strerror(errno)); exit(1); } if (getrlimit (RLIMIT_NOFILE, &rlim) != 0) fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n", - argv [0], strerror(errno)); + progname, strerror(errno)); else { /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */ if ((descriptors == 0 && rlim.rlim_cur > FD_SETSIZE) || @@ -734,14 +813,14 @@ main(int argc, char **argv) rlim.rlim_cur = descriptors; if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) { fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n", - argv [0], strerror(errno)); + progname, strerror(errno)); exit(1); } } } /* Initialize logging. */ if (!foreground) xlog_stderr(0); - xlog_open("mountd"); + xlog_open(progname); sa.sa_handler = SIG_IGN; sa.sa_flags = 0; @@ -761,15 +840,17 @@ main(int argc, char **argv) if (new_cache) cache_open(); - if (nfs_version & 0x1) - rpc_init("mountd", MOUNTPROG, MOUNTVERS, - mount_dispatch, port); - if (nfs_version & (0x1 << 1)) - rpc_init("mountd", MOUNTPROG, MOUNTVERS_POSIX, - mount_dispatch, port); - if (nfs_version & (0x1 << 2)) - rpc_init("mountd", MOUNTPROG, MOUNTVERS_NFSV3, - mount_dispatch, port); + if (version2()) { + listeners += nfs_svc_create("mountd", MOUNTPROG, + MOUNTVERS, mount_dispatch, port); + listeners += nfs_svc_create("mountd", MOUNTPROG, + MOUNTVERS_POSIX, mount_dispatch, port); + } + if (version3()) + listeners += nfs_svc_create("mountd", MOUNTPROG, + MOUNTVERS_NFSV3, mount_dispatch, port); + if (version23() && listeners == 0) + xlog(L_FATAL, "mountd: could not create listeners\n"); sa.sa_handler = killer; sigaction(SIGINT, &sa, NULL); @@ -810,9 +891,11 @@ main(int argc, char **argv) if (num_threads > 1) fork_workers(); + xlog(L_NOTICE, "Version " VERSION " starting"); my_svc_run(); - xlog(L_ERROR, "Ack! Gack! svc_run returned!\n"); + xlog(L_ERROR, "RPC service loop terminated unexpectedly. Exiting...\n"); + unregister_services(); exit(1); } diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h index 31bacb5..4c184d2 100644 --- a/utils/mountd/mountd.h +++ b/utils/mountd/mountd.h @@ -41,14 +41,19 @@ bool_t mount_mnt_3_svc(struct svc_req *, dirpath *, mountres3 *); void mount_dispatch(struct svc_req *, SVCXPRT *); void auth_init(char *export_file); unsigned int auth_reload(void); -nfs_export * auth_authenticate(char *what, struct sockaddr_in *sin, - char *path); +nfs_export * auth_authenticate(const char *what, + const struct sockaddr *caller, + const char *path); void auth_export(nfs_export *exp); void mountlist_add(char *host, const char *path); void mountlist_del(char *host, const char *path); -void mountlist_del_all(struct sockaddr_in *sin); +void mountlist_del_all(const struct sockaddr *sap); mountlist mountlist_list(void); +void cache_open(void); +struct nfs_fh_len * + cache_get_filehandle(nfs_export *exp, int len, char *p); +int cache_export(nfs_export *exp, char *path); #endif /* MOUNTD_H */ diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man index bfa06e0..4bb96e8 100644 --- a/utils/mountd/mountd.man +++ b/utils/mountd/mountd.man @@ -1,9 +1,9 @@ -.\" -.\" mountd(8) +.\"@(#)rpc.mountd.8" .\" .\" Copyright (C) 1999 Olaf Kirch .\" Modified by Paul Clements, 2004. -.TH rpc.mountd 8 "31 Aug 2004" +.\" +.TH rpc.mountd 8 "31 Dec 2009" .SH NAME rpc.mountd \- NFS mount daemon .SH SYNOPSIS @@ -11,48 +11,73 @@ rpc.mountd \- NFS mount daemon .SH DESCRIPTION The .B rpc.mountd -program implements the NFS mount protocol. When receiving a MOUNT -request from an NFS client, it checks the request against the list of -currently exported file systems. If the client is permitted to mount -the file system, -.B rpc.mountd -obtains a file handle for requested directory and returns it to -the client. -.SS Exporting NFS File Systems -Making file systems available to NFS clients is called -.IR exporting . -.P -Usually, a file system and the hosts it should be made available to -are listed in the -.B /etc/exports -file, and invoking -.B exportfs -a -whenever the system is booted. The +daemon implements the server side of the NFS MOUNT protocol, +an NFS side protocol used by NFS version 2 [RFC1094] and NFS version 3 [RFC1813]. +.PP +An NFS server maintains a table of local physical file systems +that are accessible to NFS clients. +Each file system in this table is referred to as an +.IR "exported file system" , +or +.IR export , +for short. +.PP +Each file system in the export table has an access control list. +.B rpc.mountd +uses these access control lists to determine +whether an NFS client is permitted to access a given file system. +For details on how to manage your NFS server's export table, see the +.BR exports (5) +and .BR exportfs (8) -command makes export information available to both the kernel NFS -server module and the -.B rpc.mountd -daemon. -.P -Alternatively, you can export individual directories temporarily -using -.BR exportfs 's -.IB host : /directory -syntax. +man pages. +.SS Mounting exported NFS File Systems +The NFS MOUNT protocol has several procedures. +The most important of these are +MNT (mount an export) and +UMNT (unmount an export). +.PP +A MNT request has two arguments: an explicit argument that +contains the pathname of the root directory of the export to be mounted, +and an implicit argument that is the sender's IP address. +.PP +When receiving a MNT request from an NFS client, +.B rpc.mountd +checks both the pathname and the sender's IP address against its export table. +If the sender is permitted to access the requested export, +.B rpc.mountd +returns an NFS file handle for the export's root directory to the client. +The client can then use the root file handle and NFS LOOKUP requests +to navigate the directory structure of the export. .SS The rmtab File -For every mount request received from an NFS client, -.B rpc.mountd -adds an entry to the -.B /var/lib/nfs/rmtab -file. When receiving an unmount request, that entry is removed. -.P -However, this file is mostly ornamental. One, the client can continue -to use the file handle even after calling -.B rpc.mountd 's -UMOUNT procedure. And two, if a client reboots without notifying -.B rpc.mountd , -a stale entry will remain in -.BR rmtab . +The +.B rpc.mountd +daemon registers every successful MNT request by adding an entry to the +.I /var/lib/nfs/rmtab +file. +When receivng a UMNT request from an NFS client, +.B rpc.mountd +simply removes the matching entry from +.IR /var/lib/nfs/rmtab , +as long as the access control list for that export allows that sender +to access the export. +.PP +Clients can discover the list of file systems an NFS server is +currently exporting, or the list of other clients that have mounted +its exports, by using the +.BR showmount (8) +command. +.BR showmount (8) +uses other procedures in the NFS MOUNT protocol to report information +about the server's exported file systems. +.PP +Note, however, that there is little to guarantee that the contents of +.I /var/lib/nfs/rmtab +are accurate. +A client may continue accessing an export even after invoking UMNT. +If the client reboots without sending a UMNT request, stale entries +remain for that client in +.IR /var/lib/nfs/rmtab . .SH OPTIONS .TP .B \-d kind " or " \-\-debug kind @@ -94,22 +119,25 @@ Don't advertise TCP for mount. Ignored (compatibility with unfsd??). .TP .B \-p " or " \-\-port num -Force +Specifies the port number used for RPC listener sockets. +If this option is not specified, .B rpc.mountd -to bind to the specified port num, instead of using the random port -number assigned by the portmapper. +chooses a random ephemeral port for each listener socket. +.IP +This option can be used to fix the port value of +.BR rpc.mountd 's +listeners when NFS MOUNT requests must traverse a firewall +between clients and servers. .TP .B \-H " or " \-\-ha-callout prog -Specify a high availability callout program, which will receive callouts -for all client mount and unmount requests. This allows -.B rpc.mountd -to be used in a High Availability NFS (HA-NFS) environment. This callout is not -needed (and should not be used) with 2.6 and later kernels (instead, -mount the nfsd filesystem on -.B /proc/fs/nfsd -). -The program will be called with 4 arguments. -The first will be +Specify a high availability callout program. +This program receives callouts for all MOUNT and UNMOUNT requests. +This allows +.B rpc.mountd +to be used in a High Availability NFS (HA-NFS) environment. +.IP +The callout program is run with 4 arguments. +The first is .B mount or .B unmount @@ -118,19 +146,30 @@ The second will be the name of the client performing the mount. The third will be the path that the client is mounting. The last is the number of concurrent mounts that we believe the client has of that path. +.IP +This callout is not needed with 2.6 and later kernels. +Instead, mount the nfsd filesystem on +.IR /proc/fs/nfsd . .TP .BI "\-s," "" " \-\-state\-directory\-path " directory -specify a directory in which to place statd state information. +Specify a directory in which to place statd state information. If this option is not specified the default of -.BR /var/lib/nfs +.I /var/lib/nfs is used. .TP .BI "\-r," "" " \-\-reverse\-lookup" -mountd tracks IP addresses in the rmtab, and when a DUMP request is made (by -someone running showmount -a, for instance), it returns IP addresses instead -of hostnames by default. This option causes mountd to do a reverse -lookup on each IP address and return that hostname instead. Enabling this can -have a substantial negative effect on performance in some situations. +.B rpc.mountd +tracks IP addresses in the +.I rmtab +file. When a DUMP request is made (by +someone running +.BR "showmount -a" , +for instance), it returns IP addresses instead +of hostnames by default. This option causes +.B rpc.mountd +to perform a reverse lookup on each IP address and return that hostname instead. +Enabling this can have a substantial negative effect on performance +in some situations. .TP .BR "\-t N" " or " "\-\-num\-threads=N" This option specifies the number of worker threads that rpc.mountd @@ -162,41 +201,70 @@ If you use the flag, then the list of group ids received from the client will be replaced by a list of group ids determined by an appropriate lookup on the server. Note that the 'primary' group id is not affected so a -.I newgroup +.B newgroup command on the client will still be effective. This function requires a Linux Kernel with version at least 2.6.21. - .SH TCP_WRAPPERS SUPPORT -This +You can protect your .B rpc.mountd -version is protected by the +listeners using the +.B tcp_wrapper +library or +.BR iptables (8). +.PP +Note that the .B tcp_wrapper -library. You have to give the clients access to -.B rpc.mountd -if they should be allowed to use it. To allow connects from clients of -the .bar.com domain you could use the following line in /etc/hosts.allow: - -mountd: .bar.com - -You have to use the daemon name +library supports only IPv4 networking. +.PP +Add the hostnames of NFS peers that are allowed to access +.B rpc.mountd +to +.IR /etc/hosts.allow . +Use the daemon name .B mountd -for the daemon name (even if the binary has a different name). -.B Note: -hostnames used in either access file will be ignored when +even if the +.B rpc.mountd +binary has a different name. +.PP +Hostnames used in either access file will be ignored when they can not be resolved into IP addresses. - -For further information please have a look at the +For further information see the .BR tcpd (8) and .BR hosts_access (5) -manual pages. +man pages. +.SS IPv6 and TI-RPC support +TI-RPC is a pre-requisite for supporting NFS on IPv6. +If TI-RPC support is built into +.BR rpc.mountd , +it attempts to start listeners on network transports marked 'visible' in +.IR /etc/netconfig . +As long as at least one network transport listener starts successfully, +.B rpc.mountd +will operate. +.SH FILES +.TP 2.5i +.I /etc/exports +input file for +.BR exportfs , +listing exports, export options, and access control lists +.TP 2.5i +.I /var/lib/nfs/rmtab +table of clients accessing server's exports .SH SEE ALSO -.BR rpc.nfsd (8), .BR exportfs (8), .BR exports (5), -.BR rpc.rquotad (8). -.SH FILES -.BR /etc/exports , -.BR /var/lib/nfs/xtab . +.BR showmount (8), +.BR rpc.nfsd (8), +.BR rpc.rquotad (8), +.BR nfs (5), +.BR tcpd (8), +.BR hosts_access (5), +.BR iptables (8), +.BR netconfig (5) +.sp +RFC 1094 - "NFS: Network File System Protocol Specification" +.br +RFC 1813 - "NFS Version 3 Protocol Specification" .SH AUTHOR Olaf Kirch, H. J. Lu, G. Allan Morris III, and a host of others. diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c index 19b22ee..d339296 100644 --- a/utils/mountd/rmtab.c +++ b/utils/mountd/rmtab.c @@ -16,7 +16,7 @@ #include #include #include -#include "xmalloc.h" + #include "misc.h" #include "exportfs.h" #include "xio.h" @@ -131,22 +131,22 @@ mountlist_del(char *hname, const char *path) } void -mountlist_del_all(struct sockaddr_in *sin) +mountlist_del_all(const struct sockaddr *sap) { - struct in_addr addr = sin->sin_addr; - struct hostent *hp; + char *hostname; struct rmtabent *rep; - nfs_export *exp; FILE *fp; int lockid; if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0) return; - if (!(hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) { - xlog(L_ERROR, "can't get hostname of %s", inet_ntoa(addr)); + hostname = host_canonname(sap); + if (hostname == NULL) { + char buf[INET6_ADDRSTRLEN]; + xlog(L_ERROR, "can't get hostname of %s", + host_ntop(sap, buf, sizeof(buf))); goto out_unlock; } - hp = hostent_dup (hp); if (!setrmtabent("r")) goto out_free; @@ -155,8 +155,8 @@ mountlist_del_all(struct sockaddr_in *sin) goto out_close; while ((rep = getrmtabent(1, NULL)) != NULL) { - if (strcmp(rep->r_client, hp->h_name) == 0 && - (exp = auth_authenticate("umountall", sin, rep->r_path))) + if (strcmp(rep->r_client, hostname) == 0 && + auth_authenticate("umountall", sap, rep->r_path) != NULL) continue; fputrmtabent(fp, rep, NULL); } @@ -168,11 +168,23 @@ mountlist_del_all(struct sockaddr_in *sin) out_close: endrmtabent(); /* close & unlink */ out_free: - free (hp); + free(hostname); out_unlock: xfunlock(lockid); } +static void +mountlist_freeall(mountlist list) +{ + while (list != NULL) { + mountlist m = list; + list = m->ml_next; + free(m->ml_hostname); + free(m->ml_directory); + free(m); + } +} + mountlist mountlist_list(void) { @@ -182,8 +194,6 @@ mountlist_list(void) struct rmtabent *rep; struct stat stb; int lockid; - struct in_addr addr; - struct hostent *he; if ((lockid = xflock(_PATH_RMTABLCK, "r")) < 0) return NULL; @@ -194,26 +204,41 @@ mountlist_list(void) return NULL; } if (stb.st_mtime != last_mtime) { - while (mlist) { - mlist = (m = mlist)->ml_next; - xfree(m->ml_hostname); - xfree(m->ml_directory); - xfree(m); - } + mountlist_freeall(mlist); last_mtime = stb.st_mtime; setrmtabent("r"); while ((rep = getrmtabent(1, NULL)) != NULL) { - m = (mountlist) xmalloc(sizeof(*m)); - - if (reverse_resolve && - inet_aton((const char *) rep->r_client, &addr) && - (he = gethostbyaddr(&addr, sizeof(addr), AF_INET))) - m->ml_hostname = xstrdup(he->h_name); - else - m->ml_hostname = xstrdup(rep->r_client); + m = calloc(1, sizeof(*m)); + if (m == NULL) { + mountlist_freeall(mlist); + mlist = NULL; + xlog(L_ERROR, "%s: memory allocation failed", + __func__); + break; + } + + if (reverse_resolve) { + struct addrinfo *ai; + ai = host_pton(rep->r_client); + if (ai != NULL) { + m->ml_hostname = host_canonname(ai->ai_addr); + freeaddrinfo(ai); + } + } + if (m->ml_hostname == NULL) + m->ml_hostname = strdup(rep->r_client); + + m->ml_directory = strdup(rep->r_path); + + if (m->ml_hostname == NULL || m->ml_directory == NULL) { + mountlist_freeall(mlist); + mlist = NULL; + xlog(L_ERROR, "%s: memory allocation failed", + __func__); + break; + } - m->ml_directory = xstrdup(rep->r_path); m->ml_next = mlist; mlist = m; } diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c index 1cda1e5..650c593 100644 --- a/utils/nfsd/nfsd.c +++ b/utils/nfsd/nfsd.c @@ -27,15 +27,6 @@ #include "nfssvc.h" #include "xlog.h" -/* - * IPv6 support for nfsd was finished before some of the other daemons (mountd - * and statd in particular). That could be a problem in the future if someone - * were to boot a kernel that supports IPv6 serving with an older nfs-utils. For - * now, hardcode the IPv6 switch into the off position until the other daemons - * are functional. - */ -#undef IPV6_SUPPORTED - static void usage(const char *); static struct option longopts[] = diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c index 60232b8..25cd459 100644 --- a/utils/nfsd/nfssvc.c +++ b/utils/nfsd/nfssvc.c @@ -22,15 +22,6 @@ #include "nfslib.h" #include "xlog.h" -/* - * IPv6 support for nfsd was finished before some of the other daemons (mountd - * and statd in particular). That could be a problem in the future if someone - * were to boot a kernel that supports IPv6 serving with an older nfs-utils. For - * now, hardcode the IPv6 switch into the off position until the other daemons - * are functional. - */ -#undef IPV6_SUPPORTED - #define NFSD_PORTS_FILE "/proc/fs/nfsd/portlist" #define NFSD_VERS_FILE "/proc/fs/nfsd/versions" #define NFSD_THREAD_FILE "/proc/fs/nfsd/threads" @@ -181,7 +172,7 @@ nfssvc_setfds(const struct addrinfo *hints, const char *node, const char *port) } snprintf(buf, sizeof(buf), "%d\n", sockfd); - if (write(fd, buf, strlen(buf)) != strlen(buf)) { + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) { /* * this error may be common on older kernels that don't * support IPv6, so turn into a debug message. @@ -251,7 +242,7 @@ nfssvc_setvers(unsigned int ctlbits, int minorvers4) } xlog(D_GENERAL, "Writing version string to kernel: %s", buf); snprintf(ptr+off, sizeof(buf) - off, "\n"); - if (write(fd, buf, strlen(buf)) != strlen(buf)) + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) xlog(L_ERROR, "Setting version failed: errno %d (%m)", errno); close(fd); @@ -277,7 +268,7 @@ nfssvc_threads(unsigned short port, const int nrservs) snprintf(buf, sizeof(buf), "%d\n", nrservs); n = write(fd, buf, strlen(buf)); close(fd); - if (n != strlen(buf)) + if (n != (ssize_t)strlen(buf)) return -1; else return 0; diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c index 99d77c9..740a803 100644 --- a/utils/nfsstat/nfsstat.c +++ b/utils/nfsstat/nfsstat.c @@ -791,7 +791,7 @@ print_callstats(const char *hdr, const char **names, { unsigned long long total; unsigned long long pct; - int i, j; + unsigned int i, j; fputs(hdr, stdout); for (i = 0, total = 0; i < nr; i++) @@ -816,7 +816,7 @@ print_callstats_list(const char *hdr, const char **names, unsigned int *callinfo, unsigned int nr) { unsigned long long calltotal; - int i; + unsigned int i; for (i = 0, calltotal = 0; i < nr; i++) { calltotal += callinfo[i]; @@ -1118,7 +1118,7 @@ unpause(int sig) time_diff = difftime(endtime, starttime); minutes = time_diff / 60; seconds = (int)time_diff % 60; - printf("Signal received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", minutes, seconds); + printf("Signal %d received; displaying (only) statistics gathered over the last %d minutes, %d seconds:\n\n", sig, minutes, seconds); } static void diff --git a/utils/showmount/showmount.c b/utils/showmount/showmount.c index f567093..394f528 100644 --- a/utils/showmount/showmount.c +++ b/utils/showmount/showmount.c @@ -194,7 +194,13 @@ int main(int argc, char **argv) } mclient = nfs_get_mount_client(hostname, mount_vers_tbl[vers]); - mclient->cl_auth = authunix_create_default(); + mclient->cl_auth = nfs_authsys_create(); + if (mclient->cl_auth == NULL) { + fprintf(stderr, "%s: unable to create RPC auth handle.\n", + program_name); + clnt_destroy(mclient); + exit(1); + } total_timeout.tv_sec = TOTAL_TIMEOUT; total_timeout.tv_usec = 0; diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c index 7d704cc..38f2265 100644 --- a/utils/statd/hostname.c +++ b/utils/statd/hostname.c @@ -212,7 +212,9 @@ statd_canonical_name(const char *hostname) buf, (socklen_t)sizeof(buf)); freeaddrinfo(ai); if (!result) - return NULL; + /* OK to use presentation address, + * if no reverse map exists */ + return strdup(hostname); return strdup(buf); } diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c index 3259a3e..437e37a 100644 --- a/utils/statd/sm-notify.c +++ b/utils/statd/sm-notify.c @@ -54,7 +54,7 @@ struct nsm_host { uint32_t xid; }; -static char nsm_hostname[256]; +static char nsm_hostname[SM_MAXSTRLEN + 1]; static int nsm_state; static int nsm_family = AF_INET; static int opt_debug = 0; @@ -412,12 +412,33 @@ usage: fprintf(stderr, } } - if (opt_srcaddr) { - strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)-1); - } else - if (gethostname(nsm_hostname, sizeof(nsm_hostname)) < 0) { - xlog(L_ERROR, "Failed to obtain name of local host: %m"); - exit(1); + if (opt_srcaddr != NULL) { + struct addrinfo *ai = NULL; + struct addrinfo hint = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_NUMERICHOST, + }; + + if (getaddrinfo(opt_srcaddr, NULL, &hint, &ai)) + /* not a presentation address - use it */ + strncpy(nsm_hostname, opt_srcaddr, sizeof(nsm_hostname)); + else { + /* was a presentation address - look it up in + * /etc/hosts, so it can be used for my_name */ + int error; + + freeaddrinfo(ai); + hint.ai_flags = AI_CANONNAME; + error = getaddrinfo(opt_srcaddr, NULL, &hint, &ai); + if (error != 0) { + xlog(L_ERROR, "Bind address %s is unusable: %s", + opt_srcaddr, gai_strerror(error)); + exit(1); + } + strncpy(nsm_hostname, ai->ai_canonname, + sizeof(nsm_hostname)); + freeaddrinfo(ai); + } } (void)nsm_retire_monitored_hosts(); @@ -535,6 +556,8 @@ notify(const int sock) static int notify_host(int sock, struct nsm_host *host) { + const char *my_name = (opt_srcaddr != NULL ? + nsm_hostname : host->my_name); struct sockaddr *sap; socklen_t salen; @@ -580,8 +603,8 @@ notify_host(int sock, struct nsm_host *host) host->xid = nsm_xmit_rpcbind(sock, sap, SM_PROG, SM_VERS); else host->xid = nsm_xmit_notify(sock, sap, salen, - SM_PROG, nsm_hostname, nsm_state); - + SM_PROG, my_name, nsm_state); + return 0; } @@ -611,15 +634,28 @@ recv_rpcbind_reply(struct sockaddr *sap, struct nsm_host *host, XDR *xdr) } /* - * Successful NOTIFY call. Server returns void, so nothing - * we need to do here. + * Successful NOTIFY call. Server returns void. + * + * Try sending another SM_NOTIFY with an unqualified "my_name" + * argument. Reuse the port number. If "my_name" is already + * unqualified, we're done. */ static void recv_notify_reply(struct nsm_host *host) { - xlog(D_GENERAL, "Host %s notified successfully", host->name); + char *dot = strchr(host->my_name, '.'); - smn_forget_host(host); + if (dot != NULL) { + *dot = '\0'; + host->send_next = time(NULL); + host->xid = 0; + if (host->timeout >= NSM_MAX_TIMEOUT / 4) + host->timeout = NSM_MAX_TIMEOUT / 4; + insert_host(host); + } else { + xlog(D_GENERAL, "Host %s notified successfully", host->name); + smn_forget_host(host); + } } /* diff --git a/utils/statd/sm-notify.man b/utils/statd/sm-notify.man index 163713e..7a1cbfa 100644 --- a/utils/statd/sm-notify.man +++ b/utils/statd/sm-notify.man @@ -97,11 +97,9 @@ It uses the string as the destination. To identify which host has rebooted, the .B sm-notify -command normally sends the results of -.BR gethostname (3) -as the +command normally sends .I my_name -string. +string recorded when that remote was monitored. The remote .B rpc.statd matches incoming SM_NOTIFY requests using this string, @@ -202,15 +200,22 @@ argument to use when sending SM_NOTIFY requests. If this option is not specified, .B sm-notify uses a wildcard address as the transport bind address, -and uses the results of -.BR gethostname (3) -as the +and uses the +.I my_name +recorded when the remote was monitored as the .I mon_name -argument. +argument when sending SM_NOTIFY requests. .IP The .I ipaddr form can be expressed as either an IPv4 or an IPv6 presentation address. +If the +.I ipaddr +form is used, the +.B sm-notify +command converts this address to a hostname for use as the +.I mon_name +argument when sending SM_NOTIFY requests. .IP This option can be useful in multi-homed configurations where the remote requires notification from a specific network address. @@ -252,13 +257,6 @@ consistent The hostname the client uses to mount the server should match the server's .I mon_name in SM_NOTIFY requests it sends -.IP -The use of network addresses as a -.I mon_name -or a -.I my_name -string should be avoided when -interoperating with non-Linux NFS implementations. .PP Unmounting an NFS file system does not necessarily stop either the NFS client or server from monitoring each other. diff --git a/utils/statd/statd.man b/utils/statd/statd.man index ffc5e95..ca00e24 100644 --- a/utils/statd/statd.man +++ b/utils/statd/statd.man @@ -100,11 +100,9 @@ It uses the string as the destination. To identify which host has rebooted, the .B sm-notify -command normally sends the results of -.BR gethostname (3) -as the +command sends the .I my_name -string. +string recorded when that remote was monitored. The remote .B rpc.statd matches incoming SM_NOTIFY requests using this string, @@ -292,7 +290,6 @@ man pages. .SH ADDITIONAL NOTES Lock recovery after a reboot is critical to maintaining data integrity and preventing unnecessary application hangs. -.PP To help .B rpc.statd match SM_NOTIFY requests to NLM requests, a number of best practices @@ -309,13 +306,6 @@ consistent The hostname the client uses to mount the server should match the server's .I mon_name in SM_NOTIFY requests it sends -.IP -The use of network addresses as a -.I mon_name -or a -.I my_name -string should be avoided when -interoperating with non-Linux NFS implementations. .PP Unmounting an NFS file system does not necessarily stop either the NFS client or server from monitoring each other.