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 */ }; |