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..d90a88f 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"]) @@ -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..dc01067 100644 --- a/support/export/client.c +++ b/support/export/client.c @@ -17,7 +17,7 @@ #include #include #include -#include "xmalloc.h" + #include "misc.h" #include "nfslib.h" #include "exportfs.h" @@ -28,58 +28,140 @@ #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) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + }; + + if (strchr(slash + 1, '.') != NULL) + sin.sin_addr.s_addr = inet_addr(slash + 1); + else { + int prefixlen = atoi(slash + 1); + if (0 < prefixlen && prefixlen <= 32) + sin.sin_addr.s_addr = + htonl((uint32_t)~0 << (32 - prefixlen)); + else + goto out_badprefix; + } + + set_addrlist_in(clp, 1, &sin); + return 1; + +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 sockaddr_in sin = { + .sin_family = AF_INET, + }; + static char slash32[] = "/32"; + char *cp; + + cp = strchr(clp->m_hostname, '/'); + if (cp == NULL) + cp = slash32; + + *cp = '\0'; + sin.sin_addr.s_addr = inet_addr(clp->m_hostname); + set_addrlist_in(clp, 0, &sin); + *cp = '/'; + + return init_netmask(clp, cp); +} + +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 +169,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 +231,10 @@ client_release(nfs_client *clp) clp->m_count--; } +/** + * client_freeall - deallocate all nfs_client records + * + */ void client_freeall(void) { @@ -205,57 +245,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 +291,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 +299,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 +324,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 +338,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); @@ -339,105 +372,211 @@ add_name(char *old, char *add) return new; } +static _Bool +addrs_match4(const struct sockaddr *sa1, const struct sockaddr *sa2) +{ + const struct sockaddr_in *si1 = (const struct sockaddr_in *)sa1; + const struct sockaddr_in *si2 = (const struct sockaddr_in *)sa2; + + return si1->sin_addr.s_addr == si2->sin_addr.s_addr; +} + +static _Bool +addrs_match(const struct sockaddr *sa1, const struct sockaddr *sa2) +{ + if (sa1->sa_family == sa2->sa_family) + switch (sa1->sa_family) { + case AF_INET: + return addrs_match4(sa1, sa2); + } + + return false; +} + /* - * 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 (addrs_match(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; - } - - /* 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; - } - - /* Okay, strip off the domain (if we have one) */ - if ((dot = strchr(hname, '.')) == NULL) - return 0; - - *dot = '\0'; - match = innetgr(cname+1, hname, NULL, NULL); - *dot = '.'; - - return match; - } -#else - return 0; -#endif - case MCL_ANONYMOUS: +/* + * 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); + } + + return 0; +} + +/* + * 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; + + 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 -client_checkaddr(nfs_client *clp, struct in_addr addr) +check_netgroup(const nfs_client *clp, const struct addrinfo *ai) { - int i; + const char *netgroup = clp->m_hostname + 1; + const char *hname = ai->ai_canonname; + struct addrinfo *tmp = NULL; + struct hostent *hp; + int i, match; + char *dot; + + /* First, try to match the hostname without + * splitting off the domain */ + if (innetgr(netgroup, hname, NULL, NULL)) + return 1; - 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) + /* 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)) return 1; - } + } + + /* If hname is ip address convert to FQDN */ + tmp = host_pton(hname); + if (tmp != NULL) { + freeaddrinfo(tmp); + if (innetgr(netgroup, hname, NULL, NULL)) + return 1; + } + + /* Okay, strip off the domain (if we have one) */ + dot = strchr(hname, '.'); + if (dot == NULL) return 0; + + *dot = '\0'; + match = innetgr(netgroup, hname, NULL, NULL); + *dot = '.'; + + return match; +} +#else /* !HAVE_INNETGR */ +static int +check_netgroup(__attribute__((unused)) const nfs_client *clp, + __attribute__((unused)) const struct addrinfo *ai) +{ + 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: + 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) { diff --git a/support/export/export.c b/support/export/export.c index 2943466..f528603 100644 --- a/support/export/export.c +++ b/support/export/export.c @@ -24,9 +24,24 @@ 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); + + xfree(exp->m_export.e_hostname); + xfree(exp); +} static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) { @@ -44,7 +59,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 +79,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 +129,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 +140,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 +156,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 +185,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 +213,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 +221,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 +230,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 +252,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 +265,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 +299,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 +321,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..232e040 100644 --- a/support/export/hostname.c +++ b/support/export/hostname.c @@ -1,315 +1,377 @@ /* - * 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 + +#include "sockaddr.h" +#include "exportfs.h" -#define ALIGNMENT sizeof (char *) +#ifndef HAVE_DECL_AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif -static int -align (int len, int al) +#ifdef HAVE_GETNAMEINFO +static socklen_t +sockaddr_size(const struct sockaddr *sap) { - int i; - i = len % al; - if (i) - len += al - i; - return len; + if (sap->sa_family != AF_INET) + return 0; + return (socklen_t)sizeof(struct sockaddr_in); } +#endif /* HAVE_GETNAMEINFO */ + +/** + * 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) +{ + socklen_t salen = sockaddr_size(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; + + /* + * 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. + */ + if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0) + return NULL; + + error = getaddrinfo(paddr, NULL, &hint, &ai); + switch (error) { + case 0: + 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 = { + .ai_family = AF_INET, + /* 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 = sockaddr_size(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 hostent *reverse; - struct hostent *forward; - char **sp; + struct addrinfo *ai; + char *hostname; - 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 = sockaddr_size(sap); + char buf[INET_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..3b9876a 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; } 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..e44cf8f 100644 --- a/support/include/nfslib.h +++ b/support/include/nfslib.h @@ -152,6 +152,8 @@ 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); 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/nfs/cacheio.c b/support/nfs/cacheio.c index bdf5d84..55fa45d 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) 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/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..edc1625 100644 --- a/utils/exportfs/exportfs.c +++ b/utils/exportfs/exportfs.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ static void dump(int verbose); static void error(nfs_export *exp, int err); static void usage(void); static void validate_export(nfs_export *exp); +static int matchhostname(const char *hostname1, const char *hostname2); int main(int argc, char **argv) @@ -232,7 +234,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; @@ -245,32 +247,21 @@ exportfs(char *arg, char *options, int verbose) 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 +271,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; @@ -302,10 +295,9 @@ unexportfs(char *arg, int verbose) } 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 +333,7 @@ unexportfs(char *arg, int verbose) exp->m_mayexport = 0; } - if (hp) free (hp); + freeaddrinfo(ai); } static int can_test(void) @@ -431,6 +423,82 @@ validate_export(nfs_export *exp) } } +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 _Bool +compare_sockaddrs4(const struct sockaddr *sa1, const struct sockaddr *sa2) +{ + const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1; + const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2; + return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; +} + +static _Bool +compare_sockaddrs(const struct sockaddr *sa1, const struct sockaddr *sa2) +{ + if (sa1->sa_family == sa2->sa_family) + switch (sa1->sa_family) { + case AF_INET: + return compare_sockaddrs4(sa1, sa2); + } + + return false; +} + +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 (compare_sockaddrs(ai1->ai_addr, ai2->ai_addr)) { + result = 1; + break; + } + +out: + freeaddrinfo(results1); + freeaddrinfo(results2); + return result; +} static char dumpopt(char c, char *fmt, ...) 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..b87bf76 100644 --- a/utils/gssd/context_lucid.c +++ b/utils/gssd/context_lucid.c @@ -42,6 +42,7 @@ #include #include #include +#include #include @@ -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,100 @@ 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 (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 +264,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 +286,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 +309,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 +320,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..f9cbb02 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,120 @@ 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; + 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_proc.c b/utils/gssd/gssd_proc.c index be4fb11..a55418b 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) @@ -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..dccbeb6 100644 --- a/utils/gssd/krb5_util.c +++ b/utils/gssd/krb5_util.c @@ -292,61 +292,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. @@ -1304,3 +1249,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, 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]); + 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/mount/network.c b/utils/mount/network.c index 8dc183a..ffb18ab 100644 --- a/utils/mount/network.c +++ b/utils/mount/network.c @@ -857,7 +857,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 +964,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 +1212,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 +1253,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 +1270,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 +1311,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 +1346,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; } @@ -1380,8 +1401,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 +1440,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 +1471,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 +1499,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 +1533,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 +1560,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/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..04487e5 100644 --- a/utils/mountd/auth.c +++ b/utils/mountd/auth.c @@ -110,13 +110,15 @@ 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(struct sockaddr_in *caller, struct addrinfo *ai, + enum auth_error *error) { char *n; if (use_ipaddr) return strdup(inet_ntoa(caller->sin_addr)); - n = client_compose(hp); + n = client_compose(ai); *error = unknown_host; if (!n) return NULL; @@ -128,8 +130,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(struct sockaddr_in *caller, + char *path, struct addrinfo *ai, enum auth_error *error) { nfs_export *exp; @@ -137,12 +139,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_in(&my_client, 0, caller); my_exp.m_client = &my_client; exp = NULL; @@ -152,7 +154,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 +168,19 @@ 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, +auth_authenticate_internal(struct sockaddr_in *caller, + 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; } @@ -202,7 +205,7 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path) nfs_export *exp = NULL; char epath[MAXPATHLEN+1]; char *p = NULL; - struct hostent *hp = NULL; + struct addrinfo *ai = NULL; struct in_addr addr = caller->sin_addr; enum auth_error error = bad_path; @@ -216,14 +219,14 @@ 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((struct sockaddr *)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. */ @@ -246,31 +249,30 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path) 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)); + what, ai->ai_canonname, path, epath, ntohs(caller->sin_port)); 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); + what, ai->ai_canonname, ntohs(caller->sin_port), 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); + what, ai->ai_canonname, ntohs(caller->sin_port), + 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..9e1b164 100644 --- a/utils/mountd/cache.c +++ b/utils/mountd/cache.c @@ -77,8 +77,8 @@ void auth_unix_ip(FILE *f) char class[20]; char ipaddr[20]; 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; @@ -93,17 +93,20 @@ void auth_unix_ip(FILE *f) if (qword_get(&cp, ipaddr, 20) <= 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,8 +117,7 @@ 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) @@ -125,7 +127,7 @@ void auth_unix_gid(FILE *f) * 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 +138,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 +155,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; im_export.e_flags & NFSEXP_CROSSMOUNT) { static nfs_export *prev = NULL; @@ -461,22 +507,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,8 +587,7 @@ 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; @@ -600,13 +652,13 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex 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 +666,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 +681,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 +705,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; @@ -698,9 +753,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 +775,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 +800,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); } @@ -863,6 +920,7 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path) int cache_export(nfs_export *exp, char *path) { + char buf[INET_ADDRSTRLEN]; int err; FILE *f; @@ -870,8 +928,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); diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c index a0a1f2d..6571454 100644 --- a/utils/mountd/mountd.c +++ b/utils/mountd/mountd.c @@ -80,10 +80,10 @@ static int nfs_version = -1; static void unregister_services (void) { - if (nfs_version & 0x1) + if (nfs_version & (0x1 << 1)) { pmap_unset (MOUNTPROG, MOUNTVERS); - if (nfs_version & (0x1 << 1)) pmap_unset (MOUNTPROG, MOUNTVERS_POSIX); + } if (nfs_version & (0x1 << 2)) pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3); } @@ -536,22 +536,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); } @@ -712,8 +711,10 @@ main(int argc, char **argv) usage(argv [0], 1); } - /* No more arguments allowed. */ - if (optind != argc || !(nfs_version & 0x7)) + /* No more arguments allowed. + * Require at least one valid version (2, 3, or 4) + */ + if (optind != argc || !(nfs_version & 0xE)) usage(argv [0], 1); if (chdir(state_dir)) { @@ -761,12 +762,12 @@ main(int argc, char **argv) if (new_cache) cache_open(); - if (nfs_version & 0x1) + if (nfs_version & (0x1 << 1)) { 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); diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c index 19b22ee..ba0fcf6 100644 --- a/utils/mountd/rmtab.c +++ b/utils/mountd/rmtab.c @@ -133,8 +133,7 @@ mountlist_del(char *hname, const char *path) void mountlist_del_all(struct sockaddr_in *sin) { - struct in_addr addr = sin->sin_addr; - struct hostent *hp; + char *hostname; struct rmtabent *rep; nfs_export *exp; FILE *fp; @@ -142,11 +141,13 @@ mountlist_del_all(struct sockaddr_in *sin) 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((struct sockaddr *)sin); + if (hostname == NULL) { + char buf[INET_ADDRSTRLEN]; + xlog(L_ERROR, "can't get hostname of %s", + host_ntop((struct sockaddr *)sin, buf, sizeof(buf))); goto out_unlock; } - hp = hostent_dup (hp); if (!setrmtabent("r")) goto out_free; @@ -155,7 +156,7 @@ 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 && + if (strcmp(rep->r_client, hostname) == 0 && (exp = auth_authenticate("umountall", sin, rep->r_path))) continue; fputrmtabent(fp, rep, NULL); @@ -168,7 +169,7 @@ mountlist_del_all(struct sockaddr_in *sin) out_close: endrmtabent(); /* close & unlink */ out_free: - free (hp); + free(hostname); out_unlock: xfunlock(lockid); } 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/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.