diff options
| author | Santosh Kumar Pradhan <spradhan@redhat.com> | 2014-06-06 12:22:04 +0530 | 
|---|---|---|
| committer | Niels de Vos <ndevos@redhat.com> | 2014-06-25 00:41:52 -0700 | 
| commit | d3f0de90d0c5166e63f5764d2f21703fd29ce976 (patch) | |
| tree | bdfc1b91f4d34ce7a29a8ad9774ad513d24a8730 | |
| parent | 5740fd404827a70e6318b6fff5773a9d9234d704 (diff) | |
gNFS: Fix multi-homed m/c issue in NFS subdir auth
NFS subdir authentication doesn't correctly handle multi-homed
(host with multiple NIC having multiple IP addr) OR multi-protocol
(IPv4 and IPv6) network addresses.
When user/admin sets HOSTNAME in gluster CLI for NFS subdir auth,
mnt3_verify_auth() routine does not iterate over all the resolved
n/w addrs returned by getaddrinfo() n/w API. Instead, it just tests
with the one returned first.
1. Iterate over all the n/w addrs (linked list) returned by getaddrinfo().
2. Move the n/w mask calculation part to mnt3_export_fill_hostspec()
   instead of doing it in mnt3_verify_auth() i.e. calculating for each
   mount request. It does not change for MOUNT req.
3. Integrate "subnet support code rpc-auth.addr.<volname>.allow"
   and "NFS subdir auth code" to remove code duplication.
Change-Id: I26b0def52c22cda35ca11766afca3df5fd4360bf
BUG: 1102293
Signed-off-by: Santosh Kumar Pradhan <spradhan@redhat.com>
Reviewed-on: http://review.gluster.org/8048
Reviewed-by: Rajesh Joseph <rjoseph@redhat.com>
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Niels de Vos <ndevos@redhat.com>
| -rw-r--r-- | libglusterfs/src/common-utils.c | 19 | ||||
| -rw-r--r-- | libglusterfs/src/common-utils.h | 3 | ||||
| -rw-r--r-- | rpc/rpc-lib/src/rpcsvc.c | 45 | ||||
| -rw-r--r-- | xlators/nfs/server/src/mount3.c | 193 | ||||
| -rw-r--r-- | xlators/nfs/server/src/mount3.h | 2 | 
5 files changed, 144 insertions, 118 deletions
diff --git a/libglusterfs/src/common-utils.c b/libglusterfs/src/common-utils.c index 9fe1b6a4463..6ba5223da0b 100644 --- a/libglusterfs/src/common-utils.c +++ b/libglusterfs/src/common-utils.c @@ -1936,7 +1936,7 @@ valid_ipv4_subnetwork (const char *address)          prefixlen = strtol (slash + 1, &endptr, 10);          if ((errno != 0) || (*endptr != '\0') || -            (prefixlen < 0) || (prefixlen > 32)) { +            (prefixlen < 0) || (prefixlen > IPv4_ADDR_SIZE)) {                  gf_log_callingfn (THIS->name, GF_LOG_WARNING,                                    "Invalid IPv4 subnetwork mask");                  retv = _gf_false; @@ -2132,6 +2132,23 @@ gf_sock_union_equal_addr (union gf_sock_union *a,          return _gf_false;  } +/* + * Check if both have same network address. + * Extract the network address from the sockaddr(s) addr by applying the + * network mask. If they match, return boolean _gf_true, _gf_false otherwise. + * + * (x == y) <=> (x ^ y == 0) + * (x & y) ^ (x & z) <=> x & (y ^ z) + * + * ((ip1 & mask) == (ip2 & mask)) <=> ((mask & (ip1 ^ ip2)) == 0) + */ +gf_boolean_t +mask_match(const uint32_t a, const uint32_t b, const uint32_t m) +{ +        return (((a ^ b) & m) == 0); +} + +  /*Thread safe conversion function*/  char *  uuid_utoa (uuid_t uuid) diff --git a/libglusterfs/src/common-utils.h b/libglusterfs/src/common-utils.h index 61da39767a2..86848df452d 100644 --- a/libglusterfs/src/common-utils.h +++ b/libglusterfs/src/common-utils.h @@ -50,6 +50,8 @@ void trap (void);  #define roof(a,b) ((((a)+(b)-1)/((b)?(b):1))*(b))  #define floor(a,b) (((a)/((b)?(b):1))*(b)) +#define IPv4_ADDR_SIZE 32 +  #define GF_UNIT_KB    1024ULL  #define GF_UNIT_MB    1048576ULL @@ -582,6 +584,7 @@ void skip_word (char **str);  /* returns a new string with nth word of given string. n>=1 */  char *get_nth_word (const char *str, int n); +gf_boolean_t mask_match (const uint32_t a, const uint32_t b, const uint32_t m);  char valid_host_name (char *address, int length);  char valid_ipv4_address (char *address, int length, gf_boolean_t wildcard_acc);  char valid_ipv6_address (char *address, int length, gf_boolean_t wildcard_acc); diff --git a/rpc/rpc-lib/src/rpcsvc.c b/rpc/rpc-lib/src/rpcsvc.c index 5241a265ce8..814af05f7b6 100644 --- a/rpc/rpc-lib/src/rpcsvc.c +++ b/rpc/rpc-lib/src/rpcsvc.c @@ -61,7 +61,7 @@ rpcsvc_notify (rpc_transport_t *trans, void *mydata,                 rpc_transport_event_t event, void *data, ...);  static int -match_subnet_v4 (const char *addrtok, const char *ipaddr); +rpcsvc_match_subnet_v4 (const char *addrtok, const char *ipaddr);  rpcsvc_notify_wrapper_t *  rpcsvc_notify_wrapper_alloc (void) @@ -2265,9 +2265,9 @@ rpcsvc_transport_peer_check_search (dict_t *options, char *pattern,                                  goto err;                  } -                /* Compare IPv4 subnetwork */ +                /* Compare IPv4 subnetwork, TODO: IPv6 subnet support */                  if (strchr (addrtok, '/')) { -                        ret = match_subnet_v4 (addrtok, ip); +                        ret = rpcsvc_match_subnet_v4 (addrtok, ip);                          if (ret == 0)                                  goto err;                  } @@ -2565,9 +2565,9 @@ out:  }  /* - * match_subnet_v4() takes subnetwork address pattern and checks - * if the target IPv4 address has the same network address with - * the help of network mask. + * rpcsvc_match_subnet_v4() takes subnetwork address pattern and checks + * if the target IPv4 address has the same network address with the help + * of network mask.   *   * Returns 0 for SUCCESS and -1 otherwise.   * @@ -2575,12 +2575,12 @@ out:   *     as it's already being done at the time of CLI SET.   */  static int -match_subnet_v4 (const char *addrtok, const char *ipaddr) +rpcsvc_match_subnet_v4 (const char *addrtok, const char *ipaddr)  {          char                 *slash     = NULL;          char                 *netaddr   = NULL; -        long                  prefixlen = -1;          int                   ret       = -1; +        uint32_t              prefixlen = 0;          uint32_t              shift     = 0;          struct sockaddr_in    sin1      = {0, };          struct sockaddr_in    sin2      = {0, }; @@ -2602,28 +2602,19 @@ match_subnet_v4 (const char *addrtok, const char *ipaddr)                  goto out;          /* -         * Find the network mask in network byte order. -         * NB: 32 : Max len of IPv4 address. +         * Find the IPv4 network mask in network byte order. +         * IMP: String slash+1 is already validated, it cant have value +         * more than IPv4_ADDR_SIZE (32).           */ -        prefixlen = atoi (slash + 1); -        shift = 32 - (uint32_t)prefixlen; +        prefixlen = (uint32_t) atoi (slash + 1); +        shift = IPv4_ADDR_SIZE - prefixlen;          mask.sin_addr.s_addr = htonl ((uint32_t)~0 << shift); -        /* -         * Check if both have same network address. -         * Extract the network address from the IP addr by applying the -         * network mask. If they match, return SUCCESS. i.e. -         * -         * (x == y) <=> (x ^ y == 0) -         * (x & y) ^ (x & z) <=> x & (y ^ z) -         * -         * ((ip1 & mask) == (ip2 & mask)) <=> ((mask & (ip1 ^ ip2)) == 0) -         */ -        if (((mask.sin_addr.s_addr) & -             (sin1.sin_addr.s_addr ^ sin2.sin_addr.s_addr)) != 0) -                goto out; - -        ret = 0; /* SUCCESS */ +        if (mask_match (sin1.sin_addr.s_addr, +                        sin2.sin_addr.s_addr, +                        mask.sin_addr.s_addr)) { +                ret = 0; /* SUCCESS */ +        }  out:          GF_FREE (netaddr);          return ret; diff --git a/xlators/nfs/server/src/mount3.c b/xlators/nfs/server/src/mount3.c index e4cfcebefc5..d40cb37a864 100644 --- a/xlators/nfs/server/src/mount3.c +++ b/xlators/nfs/server/src/mount3.c @@ -37,22 +37,6 @@  #include <sys/uio.h> -#define IPv4_ADDR_SIZE  32 - -/* Macro to typecast the parameter to struct sockaddr_in - */ -#define SA(addr) ((struct sockaddr_in*)(addr)) - -/* Macro will mask the ip address with netmask. - */ -#define MASKED_IP(ipv4addr, netmask)                    \ -                (ntohl(SA(ipv4addr)->sin_addr.s_addr) & (netmask)) - -/* Macro will compare two IP address after applying the mask - */ -#define COMPARE_IPv4_ADDRS(ip1, ip2, netmask)           \ -                ((MASKED_IP(ip1, netmask)) == (MASKED_IP(ip2, netmask))) -  /* This macro will assist in freeing up entire link list   * of host_auth_spec structure.   */ @@ -1015,6 +999,23 @@ err:  } +static gf_boolean_t +mnt3_match_subnet_v4 (struct addrinfo *ai, uint32_t saddr, uint32_t mask) +{ +        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 (saddr, sin->sin_addr.s_addr, mask)) +                        return _gf_true; +        } + +        return _gf_false; +} + +  /**   * This function will verify if the client is allowed to mount   * the directory or not. Client's IP address will be compared with @@ -1024,20 +1025,25 @@ err:   * @param export - mnt3_export structure. Contains allowed IP list/range.   *   * @return 0 - on Success and -EACCES on failure. + * + * TODO: Support IPv6 subnetwork   */  int  mnt3_verify_auth (rpcsvc_request_t *req, struct mnt3_export *export)  {          int                     retvalue = -EACCES;          int                     ret = 0; -        int                     shiftbits = 0; -        uint32_t                ipv4netmask = 0; -        uint32_t                routingprefix = 0;          struct host_auth_spec   *host = NULL;          struct sockaddr_in      *client_addr = NULL;          struct sockaddr_in      *allowed_addr = NULL;          struct addrinfo         *allowed_addrinfo = NULL; +        struct addrinfo         hint = { +                .ai_family      = AF_INET, +                .ai_protocol    = (int)IPPROTO_TCP, +                .ai_flags       = AI_CANONNAME, +        }; +          /* Sanity check */          if ((NULL == req) ||              (NULL == req->trans) || @@ -1049,10 +1055,19 @@ mnt3_verify_auth (rpcsvc_request_t *req, struct mnt3_export *export)          host = export->hostspec; -          /* Client's IP address. */          client_addr = (struct sockaddr_in *)(&(req->trans->peerinfo.sockaddr)); +        /* +         * Currently IPv4 subnetwork is supported i.e. AF_INET. +         * TODO: IPv6 subnetwork i.e. AF_INET6. +         */ +        if (client_addr->sin_family != AF_INET) { +                gf_log (GF_MNT, GF_LOG_ERROR, +                        "Only IPv4 is supported for subdir-auth"); +                return retvalue; +        } +          /* Try to see if the client IP matches the allowed IP list.*/          while (NULL != host){                  GF_ASSERT (host->host_addr); @@ -1063,77 +1078,38 @@ mnt3_verify_auth (rpcsvc_request_t *req, struct mnt3_export *export)                  }                  /* Get the addrinfo for the allowed host (host_addr). */ -                ret = getaddrinfo (host->host_addr, -                                NULL, -                                NULL, -                                &allowed_addrinfo); +                ret = getaddrinfo (host->host_addr, NULL, +                                   &hint, &allowed_addrinfo);                  if (0 != ret){ -                        gf_log (GF_MNT, GF_LOG_ERROR, "getaddrinfo: %s\n", -                                gai_strerror (ret)); -                        host = host->next; - -                        /* Failed to get IP addrinfo. Continue to check other -                         * allowed IPs in the list. +                        /* +                         * getaddrinfo() FAILED for the host IP addr. Continue +                         * to search other allowed hosts in the  hostspec list.                           */ +                        gf_log (GF_MNT, GF_LOG_DEBUG, +                                "getaddrinfo: %s\n", gai_strerror (ret)); +                        host = host->next;                          continue;                  }                  allowed_addr = (struct sockaddr_in *)(allowed_addrinfo->ai_addr); -                  if (NULL == allowed_addr) {                          gf_log (GF_MNT, GF_LOG_ERROR, "Invalid structure");                          break;                  } -                if (AF_INET == allowed_addr->sin_family){ -                        if (IPv4_ADDR_SIZE < host->routeprefix) { -                                gf_log (GF_MNT, GF_LOG_ERROR, "invalid IP " -                                        "configured for export-dir AUTH"); -                                host = host->next; -                                continue; -                        } - -                        /* -1 means no route prefix is provided. In this case -                         * the IP should be an exact match. Which is same as -                         * providing a route prefix of IPv4_ADDR_SIZE. -                         */ -                        if (-1 == host->routeprefix) { -                                routingprefix = IPv4_ADDR_SIZE; -                        } else { -                                routingprefix = host->routeprefix; -                        } - -                        /* Create a mask from the routing prefix. User provided -                         * CIDR address is split into IP address (host_addr) and -                         * routing prefix (routeprefix). This CIDR address may -                         * denote a single, distinct interface address or the -                         * beginning address of an entire network. -                         * -                         * e.g. the IPv4 block 192.168.100.0/24 represents the -                         * 256 IPv4 addresses from 192.168.100.0 to -                         * 192.168.100.255. -                         * Therefore to check if an IP matches 192.168.100.0/24 -                         * we should mask the IP with FFFFFF00 and compare it -                         * with host address part of CIDR. -                         */ -                        shiftbits = IPv4_ADDR_SIZE - routingprefix; -                        ipv4netmask = 0xFFFFFFFFUL << shiftbits; - -                        /* Mask both the IPs and then check if they match -                         * or not. */ -                        if (COMPARE_IPv4_ADDRS (allowed_addr, -                                                client_addr, -                                                ipv4netmask)){ -                                retvalue = 0; -                                break; -                        } +                /* Check if the network addr of both IPv4 socket match */ +                if (mnt3_match_subnet_v4 (allowed_addrinfo, +                                          client_addr->sin_addr.s_addr, +                                          host->netmask)) { +                        retvalue = 0; +                        break;                  } -                /* Client IP didn't match the allowed IP. -                 * Check with the next allowed IP.*/ +                /* No match yet, continue the search */                 host = host->next;          } +        /* FREE the dynamic memory allocated by getaddrinfo() */          if (NULL != allowed_addrinfo) {                 freeaddrinfo (allowed_addrinfo);          } @@ -2040,15 +2016,21 @@ mount3udp_delete_mountlist (char *hostname, dirpath *expname)   * @param hostip   - IP address, IP range (CIDR format) or hostname   *   * @return 0 - on success and -1 on failure + * + * NB: This does not support IPv6 currently.   */  int  mnt3_export_fill_hostspec (struct host_auth_spec* hostspec, const char* hostip)  { -        char *ipdupstr = NULL; -        char *savptr = NULL; -        char *ip = NULL; -        char *token = NULL; -        int  ret = -1; +        char     *ipdupstr = NULL; +        char     *savptr = NULL; +        char     *endptr = NULL; +        char     *ip = NULL; +        char     *token = NULL; +        int      ret = -1; +        long     prefixlen = IPv4_ADDR_SIZE; /* default */ +        uint32_t shiftbits = 0; +        size_t   length = 0;          /* Create copy of the string so that the source won't change           */ @@ -2059,25 +2041,58 @@ mnt3_export_fill_hostspec (struct host_auth_spec* hostspec, const char* hostip)          }          ip = strtok_r (ipdupstr, "/", &savptr); +        /* Validate the Hostname or IPv4 address +         * TODO: IPv6 support for subdir auth. +         */ +        length = strlen (ip); +        if ((!valid_ipv4_address (ip, (int)length, _gf_false)) && +            (!valid_host_name (ip, (int)length))) { +                gf_log (GF_MNT, GF_LOG_ERROR, +                        "Invalid hostname or IPv4 address: %s", ip); +                goto err; +        } +          hostspec->host_addr = gf_strdup (ip);          if (NULL == hostspec->host_addr) {                  gf_log (GF_MNT, GF_LOG_ERROR, "Memory allocation failed");                  goto err;          } -        /* Check if the IP is in <IP address> / <Range> format. -         * If yes, then strip the range and store it separately. +        /** +         * User provided CIDR address (xx.xx.xx.xx/n format) is split +         * into HOST (IP addr or hostname) and network prefix(n) from +         * which netmask would be calculated. This CIDR address may +         * denote a single, distinct interface address or the beginning +         * address of an entire network. +         * +         * e.g. the IPv4 block 192.168.100.0/24 represents the 256 +         * IPv4 addresses from 192.168.100.0 to 192.168.100.255. +         * Therefore to check if an IP matches 192.168.100.0/24 +         * we should mask the IP with FFFFFF00 and compare it with +         * host address part of CIDR. +         * +         * Refer: mask_match() in common-utils.c.           */          token = strtok_r (NULL, "/", &savptr); - -        if (NULL == token) { -              hostspec->routeprefix = -1; -        } else { -              hostspec->routeprefix = atoi (token); +        if (token != NULL) { +              prefixlen = strtol (token, &endptr, 10); +              if ((errno != 0) || (*endptr != '\0') || +                  (prefixlen < 0) || (prefixlen > IPv4_ADDR_SIZE)) { +                      gf_log (THIS->name, GF_LOG_WARNING, +                              "Invalid IPv4 subnetwork mask"); +                      goto err; +              }          } -        // success -        ret = 0; +        /* +         * 1. Calculate the network mask address. +         * 2. Convert it into Big-Endian format. +         * 3. Store it in hostspec netmask. +         */ +        shiftbits = IPv4_ADDR_SIZE - prefixlen; +        hostspec->netmask = htonl ((uint32_t)~0 << shiftbits); + +        ret = 0; /* SUCCESS */  err:          if (NULL != ipdupstr) {                  GF_FREE (ipdupstr); diff --git a/xlators/nfs/server/src/mount3.h b/xlators/nfs/server/src/mount3.h index 7fc16ed5790..8474244f191 100644 --- a/xlators/nfs/server/src/mount3.h +++ b/xlators/nfs/server/src/mount3.h @@ -68,7 +68,7 @@ struct mountentry {  /* Structure to hold export-dir AUTH parameter */  struct host_auth_spec {          char                    *host_addr;    /* Allowed IP or host name */ -        int                     routeprefix;   /* Routing prefix */ +        uint32_t                netmask;       /* Network mask (Big-Endian) */          struct host_auth_spec   *next;         /* Pointer to next AUTH struct */  };  | 
