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 /xlators/nfs | |
| 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>
Diffstat (limited to 'xlators/nfs')
| -rw-r--r-- | xlators/nfs/server/src/mount3.c | 193 | ||||
| -rw-r--r-- | xlators/nfs/server/src/mount3.h | 2 | 
2 files changed, 105 insertions, 90 deletions
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 */  };  | 
