diff -up nfs-utils-1.2.2/aclocal/libcap.m4.orig nfs-utils-1.2.2/aclocal/libcap.m4 --- nfs-utils-1.2.2/aclocal/libcap.m4.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/aclocal/libcap.m4 2010-05-06 07:16:37.851057000 -0400 @@ -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 -up nfs-utils-1.2.2/configure.ac.orig nfs-utils-1.2.2/configure.ac --- nfs-utils-1.2.2/configure.ac.orig 2010-05-06 07:15:49.881176000 -0400 +++ nfs-utils-1.2.2/configure.ac 2010-05-06 07:16:37.856060000 -0400 @@ -89,7 +89,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"]) @@ -436,6 +436,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 -up nfs-utils-1.2.2/support/export/client.c.orig nfs-utils-1.2.2/support/export/client.c --- nfs-utils-1.2.2/support/export/client.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/support/export/client.c 2010-05-06 07:16:37.862057000 -0400 @@ -17,7 +17,7 @@ #include #include #include -#include "xmalloc.h" + #include "misc.h" #include "nfslib.h" #include "exportfs.h" @@ -28,13 +28,116 @@ #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, }; +static void +init_addrlist(nfs_client *clp, const struct hostent *hp) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + }; + char **ap; + int i; + + if (hp == NULL) + return; + + ap = hp->h_addr_list; + for (i = 0; *ap != NULL && i < NFSCLNT_ADDRMAX; i++, ap++) { + sin.sin_addr = *(struct in_addr *)*ap; + set_addrlist_in(clp, i, &sin); + } + 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 hostent *hp) +{ + 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, hp); + 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; +} + /* 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 @@ -87,23 +190,23 @@ 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 (htype == MCL_FQDN && clp->m_naddr == 0) + init_addrlist(clp, hp); +out: if (hp) free (hp); @@ -115,78 +218,21 @@ client_dup(nfs_client *clp, struct hoste { 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, hp->h_name, hp)) { + 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; -} - void client_release(nfs_client *clp) { @@ -205,34 +251,11 @@ client_freeall(void) head = clientlist + i; while (*head) { *head = (clp = *head)->m_next; - xfree(clp->m_hostname); - xfree(clp); + client_free(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 - } - } - return NULL; -} - struct hostent * client_resolve(struct in_addr addr) { @@ -246,14 +269,18 @@ client_resolve(struct in_addr addr) return he; } -/* - * 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 + * @he: pointer to hostent 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) { @@ -271,13 +298,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 +323,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 +337,9 @@ name_cmp(char *a, char *b) } static char * -add_name(char *old, char *add) +add_name(char *old, const char *add) { - int len = strlen(add)+2; + size_t len = strlen(add) + 2; char *new; char *cp; if (old) len += strlen(old); @@ -340,101 +372,164 @@ add_name(char *old, char *add) } /* - * Match a host (given its hostent record) to a client record. This - * is usually called from mountd. + * Check each address listed in @hp 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 hostent *hp) { - char *hname = (char *) hp->h_name; - char *cname = clp->m_hostname; - char **ap; + const struct sockaddr_in *sin; + struct in_addr addr; + 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 (ap = hp->h_addr_list; *ap; ap++) { + addr = *(struct in_addr *)*ap; + + for (i = 0; i < clp->m_naddr; i++) { + sin = get_addrlist_in(clp, i); + if (sin->sin_addr.s_addr == addr.s_addr) return 1; } - return 0; - case MCL_WILDCARD: - if (wildmat(hname, cname)) - 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; - } +/* + * Check each address listed in @hp 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 hostent *hp) +{ + const struct sockaddr_in *address, *mask; + struct in_addr addr; + char **ap; + + for (ap = hp->h_addr_list; *ap; ap++) { + address = get_addrlist_in(clp, 0); + mask = get_addrlist_in(clp, 1); + addr = *(struct in_addr *)*ap; - /* 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; - } + if (!((address->sin_addr.s_addr ^ addr.s_addr) & + mask->sin_addr.s_addr)) + return 1; + } + return 0; +} - /* 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 = '.'; +/* + * 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 hostent *hp) +{ + char *cname = clp->m_hostname; + char *hname = hp->h_name; + char **ap; - return match; - } -#else - return 0; -#endif - case MCL_ANONYMOUS: + if (wildmat(hname, cname)) return 1; - case MCL_GSS: - return 0; - default: - xlog(L_FATAL, "internal: bad client type %d", clp->m_type); + + /* See if hname aliases listed in /etc/hosts or nis[+] + * match the requested wildcard */ + for (ap = hp->h_aliases; *ap; ap++) { + if (wildmat(*ap, cname)) + return 1; } return 0; } +/* + * Check if @hp's hostname or aliases fall in a given netgroup. + * Return 1 if @hp represents a host in the netgroup, otherwise zero. + */ +#ifdef HAVE_INNETGR +static int +check_netgroup(const nfs_client *clp, const struct hostent *hp) +{ + const char *netgroup = clp->m_hostname + 1; + const char *hname = hp->h_name; + struct hostent *nhp = NULL; + struct sockaddr_in addr; + int match, i; + char *dot; + + /* First, try to match the hostname without + * splitting off the domain */ + if (innetgr(netgroup, hname, NULL, NULL)) + return 1; + + /* See if hname aliases listed in /etc/hosts or nis[+] + * match the requested netgroup */ + 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 */ + if (inet_aton(hname, &addr.sin_addr) && + (nhp = gethostbyaddr((const char *)&(addr.sin_addr), + sizeof(addr.sin_addr), AF_INET))) { + hname = nhp->h_name; + 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 -client_checkaddr(nfs_client *clp, struct in_addr addr) +check_netgroup(__attribute__((unused)) const nfs_client *clp, + __attribute__((unused)) const struct hostent *hp) { - int i; + return 0; +} +#endif /* !HAVE_INNETGR */ +/** + * client_check - check if IP address information matches a cached nfs_client + * @clp: pointer to a cached nfs_client record + * @hp: pointer to hostent containing host IP information + * + * Returns 1 if the address information matches the cached nfs_client, + * otherwise zero. + */ +int +client_check(nfs_client *clp, struct hostent *hp) +{ switch (clp->m_type) { case MCL_FQDN: - for (i = 0; i < clp->m_naddr; i++) { - if (clp->m_addrlist[i].s_addr == addr.s_addr) - return 1; - } - return 0; + return check_fqdn(clp, hp); case MCL_SUBNETWORK: - return !((clp->m_addrlist[0].s_addr ^ addr.s_addr) - & clp->m_addrlist[1].s_addr); + return check_subnetwork(clp, hp); + case MCL_WILDCARD: + return check_wildcard(clp, hp); + case MCL_NETGROUP: + return check_netgroup(clp, hp); + 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; } diff -up nfs-utils-1.2.2/support/export/export.c.orig nfs-utils-1.2.2/support/export/export.c --- nfs-utils-1.2.2/support/export/export.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/support/export/export.c 2010-05-06 07:16:37.867057000 -0400 @@ -28,6 +28,18 @@ static int export_check(nfs_export *, st static nfs_export * export_allowed_internal(struct hostent *hp, 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) { if (exp->m_export.e_flags != eep->e_flags) { @@ -117,6 +129,10 @@ export_dup(nfs_export *exp, struct hoste if (exp->m_export.e_hostname) new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname); clp = client_dup(exp->m_client, hp); + if (clp == NULL) { + export_free(new); + return NULL; + } clp->m_count++; new->m_client = clp; new->m_mayexport = exp->m_mayexport; @@ -128,6 +144,7 @@ export_dup(nfs_export *exp, struct hoste return new; } + /* * Add export entry to hash table */ @@ -259,6 +276,10 @@ export_check(nfs_export *exp, struct hos return client_check(exp->m_client, hp); } +/** + * export_freeall - deallocate all nfs_export records + * + */ void export_freeall(void) { @@ -269,22 +290,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 -up nfs-utils-1.2.2/support/export/nfsctl.c.orig nfs-utils-1.2.2/support/export/nfsctl.c --- nfs-utils-1.2.2/support/export/nfsctl.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/support/export/nfsctl.c 2010-05-06 07:16:37.872057000 -0400 @@ -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, n 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++) { + 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 -up nfs-utils-1.2.2/support/include/exportfs.h.orig nfs-utils-1.2.2/support/include/exportfs.h --- nfs-utils-1.2.2/support/include/exportfs.h.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/support/include/exportfs.h 2010-05-06 07:16:37.877057000 -0400 @@ -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,17 +116,15 @@ extern exp_hash_table exportlist[MCL_MAX 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 *); int client_gettype(char *hname); int client_check(nfs_client *, struct hostent *); -int client_match(nfs_client *, char *hname); 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); +int client_member(const char *client, + const char *name); int export_read(char *fname); void export_add(nfs_export *); diff -up nfs-utils-1.2.2/support/include/nfslib.h.orig nfs-utils-1.2.2/support/include/nfslib.h --- nfs-utils-1.2.2/support/include/nfslib.h.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/support/include/nfslib.h 2010-05-06 07:16:37.882059000 -0400 @@ -152,6 +152,8 @@ void qword_addhex(char **bpp, int *lp, c 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 -up nfs-utils-1.2.2/support/include/nfsrpc.h.orig nfs-utils-1.2.2/support/include/nfsrpc.h --- nfs-utils-1.2.2/support/include/nfsrpc.h.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/support/include/nfsrpc.h 2010-05-06 07:16:37.888057000 -0400 @@ -160,4 +160,7 @@ extern int nfs_rpc_ping(const struct so 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 -up nfs-utils-1.2.2/support/nfs/cacheio.c.orig nfs-utils-1.2.2/support/nfs/cacheio.c --- nfs-utils-1.2.2/support/nfs/cacheio.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/support/nfs/cacheio.c 2010-05-06 07:16:37.893057000 -0400 @@ -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 -up nfs-utils-1.2.2/support/nfs/rpc_socket.c.orig nfs-utils-1.2.2/support/nfs/rpc_socket.c --- nfs-utils-1.2.2/support/nfs/rpc_socket.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/support/nfs/rpc_socket.c 2010-05-06 07:16:37.898057000 -0400 @@ -557,3 +557,24 @@ rpcprog_t nfs_getrpcbyname(const rpcprog 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 -up nfs-utils-1.2.2/support/nsm/file.c.orig nfs-utils-1.2.2/support/nsm/file.c --- nfs-utils-1.2.2/support/nsm/file.c.orig 2010-05-06 07:15:49.888166000 -0400 +++ nfs-utils-1.2.2/support/nsm/file.c 2010-05-06 07:16:37.917057000 -0400 @@ -67,7 +67,9 @@ #endif #include +#ifdef HAVE_SYS_CAPABILITY_H #include +#endif #include #include @@ -348,6 +350,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"); @@ -363,6 +366,7 @@ nsm_clear_capabilities(void) } (void)cap_free(caps); +#endif return true; } diff -up nfs-utils-1.2.2/tools/Makefile.am.orig nfs-utils-1.2.2/tools/Makefile.am --- nfs-utils-1.2.2/tools/Makefile.am.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/tools/Makefile.am 2010-05-06 07:16:37.923062000 -0400 @@ -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 -up nfs-utils-1.2.2/tools/mountstats/Makefile.am.orig nfs-utils-1.2.2/tools/mountstats/Makefile.am --- nfs-utils-1.2.2/tools/mountstats/Makefile.am.orig 2010-05-06 07:16:37.939040000 -0400 +++ nfs-utils-1.2.2/tools/mountstats/Makefile.am 2010-05-06 07:16:37.941038000 -0400 @@ -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 -up nfs-utils-1.2.2/tools/mountstats/mountstats.man.orig nfs-utils-1.2.2/tools/mountstats/mountstats.man --- nfs-utils-1.2.2/tools/mountstats/mountstats.man.orig 2010-05-06 07:16:37.944036000 -0400 +++ nfs-utils-1.2.2/tools/mountstats/mountstats.man 2010-05-06 07:16:37.945043000 -0400 @@ -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 -up nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am.orig nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am --- nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am.orig 2010-05-06 07:16:37.958021000 -0400 +++ nfs-utils-1.2.2/tools/nfs-iostat/Makefile.am 2010-05-06 07:16:37.960024000 -0400 @@ -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 -up nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man.orig nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man --- nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man.orig 2010-05-06 07:16:37.963021000 -0400 +++ nfs-utils-1.2.2/tools/nfs-iostat/nfsiostat.man 2010-05-06 07:16:37.965018000 -0400 @@ -0,0 +1,70 @@ +.\" +.\" 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 +.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 -up nfs-utils-1.2.2/utils/gssd/context.h.orig nfs-utils-1.2.2/utils/gssd/context.h --- nfs-utils-1.2.2/utils/gssd/context.h.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/gssd/context.h 2010-05-06 07:16:37.970009000 -0400 @@ -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 -up nfs-utils-1.2.2/utils/gssd/context_lucid.c.orig nfs-utils-1.2.2/utils/gssd/context_lucid.c --- nfs-utils-1.2.2/utils/gssd/context_lucid.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/gssd/context_lucid.c 2010-05-06 07:16:37.975007000 -0400 @@ -42,6 +42,7 @@ #include #include #include +#include #include @@ -119,15 +120,13 @@ prepare_krb5_rfc1964_buffer(gss_krb5_luc * 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 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 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 } 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 -up nfs-utils-1.2.2/utils/gssd/context_mit.c.orig nfs-utils-1.2.2/utils/gssd/context_mit.c --- nfs-utils-1.2.2/utils/gssd/context_mit.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/gssd/context_mit.c 2010-05-06 07:16:37.980998000 -0400 @@ -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 { 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 -up nfs-utils-1.2.2/utils/gssd/gssd_proc.c.orig nfs-utils-1.2.2/utils/gssd/gssd_proc.c --- nfs-utils-1.2.2/utils/gssd/gssd_proc.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/gssd/gssd_proc.c 2010-05-06 07:16:37.986992000 -0400 @@ -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 -up nfs-utils-1.2.2/utils/gssd/krb5_util.c.orig nfs-utils-1.2.2/utils/gssd/krb5_util.c --- nfs-utils-1.2.2/utils/gssd/krb5_util.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/gssd/krb5_util.c 2010-05-06 07:16:37.992992000 -0400 @@ -292,61 +292,6 @@ gssd_find_existing_krb5_ccache(uid_t uid 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_rea 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 -up nfs-utils-1.2.2/utils/mountd/auth.c.orig nfs-utils-1.2.2/utils/mountd/auth.c --- nfs-utils-1.2.2/utils/mountd/auth.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/mountd/auth.c 2010-05-06 07:16:38.009992000 -0400 @@ -142,7 +142,7 @@ auth_authenticate_newcache(char *what, s 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; diff -up nfs-utils-1.2.2/utils/mountd/cache.c.orig nfs-utils-1.2.2/utils/mountd/cache.c --- nfs-utils-1.2.2/utils/mountd/cache.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/mountd/cache.c 2010-05-06 07:16:38.014995000 -0400 @@ -125,7 +125,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 +136,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 +153,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_client, 0); int err; FILE *f; @@ -871,7 +872,7 @@ int cache_export(nfs_export *exp, char * return -1; qword_print(f, "nfsd"); - qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0])); + qword_print(f, inet_ntoa(sin->sin_addr)); qword_printint(f, time(0)+30*60); qword_print(f, exp->m_client->m_hostname); err = qword_eol(f); diff -up nfs-utils-1.2.2/utils/mount/network.c.orig nfs-utils-1.2.2/utils/mount/network.c --- nfs-utils-1.2.2/utils/mount/network.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/mount/network.c 2010-05-06 07:16:37.998992000 -0400 @@ -857,7 +857,14 @@ int nfs_advise_umount(const struct socka 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_se } 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; } diff -up nfs-utils-1.2.2/utils/mount/stropts.c.orig nfs-utils-1.2.2/utils/mount/stropts.c --- nfs-utils-1.2.2/utils/mount/stropts.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/mount/stropts.c 2010-05-06 07:16:38.004992000 -0400 @@ -799,6 +799,7 @@ static int nfs_is_permanent_error(int er case ESTALE: case ETIMEDOUT: case ECONNREFUSED: + case EHOSTUNREACH: return 0; /* temporary */ default: return 1; /* permanent */ diff -up nfs-utils-1.2.2/utils/showmount/showmount.c.orig nfs-utils-1.2.2/utils/showmount/showmount.c --- nfs-utils-1.2.2/utils/showmount/showmount.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/showmount/showmount.c 2010-05-06 07:16:38.019995000 -0400 @@ -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 -up nfs-utils-1.2.2/utils/statd/sm-notify.c.orig nfs-utils-1.2.2/utils/statd/sm-notify.c --- nfs-utils-1.2.2/utils/statd/sm-notify.c.orig 2010-02-18 07:35:00.000000000 -0500 +++ nfs-utils-1.2.2/utils/statd/sm-notify.c 2010-05-06 07:16:38.025992000 -0400 @@ -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 *h 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, } /* - * 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 -up nfs-utils-1.2.2/utils/statd/sm-notify.man.orig nfs-utils-1.2.2/utils/statd/sm-notify.man --- nfs-utils-1.2.2/utils/statd/sm-notify.man.orig 2010-05-06 07:15:49.867180000 -0400 +++ nfs-utils-1.2.2/utils/statd/sm-notify.man 2010-05-06 07:16:38.030995000 -0400 @@ -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 r 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 -up nfs-utils-1.2.2/utils/statd/statd.man.orig nfs-utils-1.2.2/utils/statd/statd.man --- nfs-utils-1.2.2/utils/statd/statd.man.orig 2010-05-06 07:15:49.874177000 -0400 +++ nfs-utils-1.2.2/utils/statd/statd.man 2010-05-06 07:16:38.036992000 -0400 @@ -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.