summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libglusterfs/src/common-utils.c81
-rw-r--r--libglusterfs/src/common-utils.h3
-rw-r--r--rpc/rpc-lib/src/rpcsvc.c66
-rw-r--r--xlators/nfs/server/src/mount3.c193
-rw-r--r--xlators/nfs/server/src/mount3.h2
5 files changed, 255 insertions, 90 deletions
diff --git a/libglusterfs/src/common-utils.c b/libglusterfs/src/common-utils.c
index 1dfb418e4a8..96319624361 100644
--- a/libglusterfs/src/common-utils.c
+++ b/libglusterfs/src/common-utils.c
@@ -1921,6 +1921,70 @@ out:
return ret;
}
+/**
+ * valid_ipv4_subnetwork() takes the pattern and checks if it contains
+ * a valid ipv4 subnetwork pattern i.e. xx.xx.xx.xx/n. IPv4 address
+ * part (xx.xx.xx.xx) and mask bits lengh part (n). The mask bits lengh
+ * must be in 0-32 range (ipv4 addr is 32 bit). The pattern must be
+ * in this format.
+ *
+ * Returns _gf_true if both IP addr and mask bits len are valid
+ * _gf_false otherwise.
+ */
+gf_boolean_t
+valid_ipv4_subnetwork (const char *address)
+{
+ char *slash = NULL;
+ char *paddr = NULL;
+ char *endptr = NULL;
+ long prefixlen = -1;
+ gf_boolean_t retv = _gf_true;
+
+ if (address == NULL) {
+ gf_log_callingfn (THIS->name, GF_LOG_WARNING,
+ "argument invalid");
+ return _gf_false;
+ }
+
+ paddr = gf_strdup (address);
+ if (paddr == NULL) /* ENOMEM */
+ return _gf_false;
+
+ /*
+ * INVALID: If '/' is not present OR
+ * Nothing specified after '/'
+ */
+ slash = strchr(paddr, '/');
+ if ((slash == NULL) || (slash[1] == '\0')) {
+ gf_log_callingfn (THIS->name, GF_LOG_WARNING,
+ "Invalid IPv4 subnetwork format");
+ retv = _gf_false;
+ goto out;
+ }
+
+ *slash = '\0';
+ retv = valid_ipv4_address (paddr, strlen(paddr), _gf_false);
+ if (retv == _gf_false) {
+ gf_log_callingfn (THIS->name, GF_LOG_WARNING,
+ "Invalid IPv4 subnetwork address");
+ goto out;
+ }
+
+ prefixlen = strtol (slash + 1, &endptr, 10);
+ if ((errno != 0) || (*endptr != '\0') ||
+ (prefixlen < 0) || (prefixlen > IPv4_ADDR_SIZE)) {
+ gf_log_callingfn (THIS->name, GF_LOG_WARNING,
+ "Invalid IPv4 subnetwork mask");
+ retv = _gf_false;
+ goto out;
+ }
+
+ retv = _gf_true;
+out:
+ GF_FREE (paddr);
+ return retv;
+}
+
char
valid_ipv6_address (char *address, int length, gf_boolean_t wildcard_acc)
{
@@ -2045,6 +2109,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 6f8436fcba0..a0c0db170de 100644
--- a/libglusterfs/src/common-utils.h
+++ b/libglusterfs/src/common-utils.h
@@ -49,6 +49,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
@@ -572,6 +574,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 9374ee7328f..a751330a7c9 100644
--- a/rpc/rpc-lib/src/rpcsvc.c
+++ b/rpc/rpc-lib/src/rpcsvc.c
@@ -60,6 +60,9 @@ int
rpcsvc_notify (rpc_transport_t *trans, void *mydata,
rpc_transport_event_t event, void *data, ...);
+static int
+rpcsvc_match_subnet_v4 (const char *addrtok, const char *ipaddr);
+
rpcsvc_notify_wrapper_t *
rpcsvc_notify_wrapper_alloc (void)
{
@@ -2236,6 +2239,13 @@ rpcsvc_transport_peer_check_search (dict_t *options, char *pattern,
goto err;
}
+ /* Compare IPv4 subnetwork, TODO: IPv6 subnet support */
+ if (strchr (addrtok, '/')) {
+ ret = rpcsvc_match_subnet_v4 (addrtok, ip);
+ if (ret == 0)
+ goto err;
+ }
+
addrtok = strtok_r (NULL, ",", &svptr);
}
@@ -2516,6 +2526,62 @@ out:
return addrstr;
}
+/*
+ * 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.
+ *
+ * NB: Validation of subnetwork address pattern is not required
+ * as it's already being done at the time of CLI SET.
+ */
+static int
+rpcsvc_match_subnet_v4 (const char *addrtok, const char *ipaddr)
+{
+ char *slash = NULL;
+ char *netaddr = NULL;
+ int ret = -1;
+ uint32_t prefixlen = 0;
+ uint32_t shift = 0;
+ struct sockaddr_in sin1 = {0, };
+ struct sockaddr_in sin2 = {0, };
+ struct sockaddr_in mask = {0, };
+
+ /* Copy the input */
+ netaddr = gf_strdup (addrtok);
+ if (netaddr == NULL) /* ENOMEM */
+ goto out;
+
+ /* Find the network socket addr of target */
+ if (inet_pton (AF_INET, ipaddr, &sin1.sin_addr) == 0)
+ goto out;
+
+ /* Find the network socket addr of subnet pattern */
+ slash = strchr (netaddr, '/');
+ *slash = '\0';
+ if (inet_pton (AF_INET, netaddr, &sin2.sin_addr) == 0)
+ goto out;
+
+ /*
+ * 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 = (uint32_t) atoi (slash + 1);
+ shift = IPv4_ADDR_SIZE - prefixlen;
+ mask.sin_addr.s_addr = htonl ((uint32_t)~0 << shift);
+
+ 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;
+}
+
rpcsvc_actor_t gluster_dump_actors[] = {
[GF_DUMP_NULL] = {"NULL", GF_DUMP_NULL, NULL, NULL, 0, DRC_NA},
diff --git a/xlators/nfs/server/src/mount3.c b/xlators/nfs/server/src/mount3.c
index b0824bf1029..dd66c44af77 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.
*/
@@ -1017,6 +1001,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
@@ -1026,20 +1027,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) ||
@@ -1051,10 +1057,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);
@@ -1065,77 +1080,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);
}
@@ -2020,15 +1996,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
*/
@@ -2039,25 +2021,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 */
};