summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libglusterfs/src/common-utils.c123
-rw-r--r--libglusterfs/src/common-utils.h5
-rw-r--r--libglusterfs/src/options.c40
-rw-r--r--libglusterfs/src/options.h1
-rw-r--r--rpc/rpc-lib/src/rpcsvc.c91
-rwxr-xr-xtests/bugs/bug-822830.t24
-rw-r--r--xlators/nfs/server/src/nfs.c8
7 files changed, 281 insertions, 11 deletions
diff --git a/libglusterfs/src/common-utils.c b/libglusterfs/src/common-utils.c
index 80d9d29..b7d06d9 100644
--- a/libglusterfs/src/common-utils.c
+++ b/libglusterfs/src/common-utils.c
@@ -1858,6 +1858,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 > 32)) {
+ 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)
{
@@ -1939,6 +2003,65 @@ out:
}
/**
+ * valid_mount_auth_address - Validate the rpc-auth.addr.allow/reject pattern
+ *
+ * @param address - Pattern to be validated
+ *
+ * @return _gf_true if "address" is "*" (anonymous) 'OR'
+ * if "address" is valid FQDN or valid IPv4/6 address 'OR'
+ * if "address" contains wildcard chars e.g. "'*' or '?' or '['"
+ * if "address" is valid ipv4 subnet pattern (xx.xx.xx.xx/n)
+ * _gf_false otherwise
+ *
+ *
+ * NB: If the user/admin set for wildcard pattern, then it does not have
+ * to be validated. Make it similar to the way exportfs (kNFS) works.
+ */
+gf_boolean_t
+valid_mount_auth_address (char *address)
+{
+ int length = 0;
+ char *cp = NULL;
+
+ /* 1. Check for "NULL and empty string */
+ if ((address == NULL) || (address[0] == '\0')){
+ gf_log_callingfn (THIS->name,
+ GF_LOG_WARNING, "argument invalid");
+ return _gf_false;
+ }
+
+ /* 2. Check for Anonymous */
+ if (strcmp(address, "*") == 0)
+ return _gf_true;
+
+ for (cp = address; *cp; cp++) {
+ /* 3. Check for wildcard pattern */
+ if (*cp == '*' || *cp == '?' || *cp == '[') {
+ return _gf_true;
+ }
+
+ /*
+ * 4. check for IPv4 subnetwork i.e. xx.xx.xx.xx/n
+ * TODO: check for IPv6 subnetwork
+ * NB: Wildcard must not be mixed with subnetwork.
+ */
+ if (*cp == '/') {
+ return valid_ipv4_subnetwork (address);
+ }
+ }
+
+ /* 5. Check for v4/v6 IP addr and FQDN/hostname */
+ length = strlen (address);
+ if ((valid_ipv4_address (address, length, _gf_false)) ||
+ (valid_ipv6_address (address, length, _gf_false)) ||
+ (valid_host_name (address, length))) {
+ return _gf_true;
+ }
+
+ return _gf_false;
+}
+
+/**
* gf_sock_union_equal_addr - check if two given gf_sock_unions have same addr
*
* @param a - first sock union
diff --git a/libglusterfs/src/common-utils.h b/libglusterfs/src/common-utils.h
index e17029d..3a58a93 100644
--- a/libglusterfs/src/common-utils.h
+++ b/libglusterfs/src/common-utils.h
@@ -580,9 +580,8 @@ 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);
char valid_internet_address (char *address, gf_boolean_t wildcard_acc);
-char valid_ipv4_wildcard_check (char *address);
-char valid_ipv6_wildcard_check (char *address);
-char valid_wildcard_internet_address (char *address);
+gf_boolean_t valid_mount_auth_address (char *address);
+gf_boolean_t valid_ipv4_subnetwork (const char *address);
gf_boolean_t gf_sock_union_equal_addr (union gf_sock_union *a,
union gf_sock_union *b);
diff --git a/libglusterfs/src/options.c b/libglusterfs/src/options.c
index 31e5a68..a0881b4 100644
--- a/libglusterfs/src/options.c
+++ b/libglusterfs/src/options.c
@@ -574,6 +574,45 @@ out:
return ret;
}
+static int
+xlator_option_validate_mntauth (xlator_t *xl, const char *key,
+ const char *value, volume_option_t *opt,
+ char **op_errstr)
+{
+ int ret = -1;
+ char *dup_val = NULL;
+ char *addr_tok = NULL;
+ char *save_ptr = NULL;
+ char errstr[4096] = {0,};
+
+ dup_val = gf_strdup (value);
+ if (!dup_val)
+ goto out;
+
+ addr_tok = strtok_r (dup_val, ",", &save_ptr);
+ if (addr_tok == NULL)
+ goto out;
+ while (addr_tok) {
+ if (!valid_mount_auth_address (addr_tok))
+ goto out;
+
+ addr_tok = strtok_r (NULL, ",", &save_ptr);
+ }
+ ret = 0;
+
+out:
+ if (ret) {
+ snprintf (errstr, sizeof (errstr), "option %s %s: '%s' is not "
+ "a valid mount-auth-address", key, value, value);
+ gf_log (xl->name, GF_LOG_ERROR, "%s", errstr);
+ if (op_errstr)
+ *op_errstr = gf_strdup (errstr);
+ }
+ GF_FREE (dup_val);
+
+ return ret;
+}
+
/*XXX: the rules to validate are as per block-size required for stripe xlator */
static int
gf_validate_size (const char *sizestr, volume_option_t *opt)
@@ -744,6 +783,7 @@ xlator_option_validate (xlator_t *xl, char *key, char *value,
xlator_option_validate_priority_list,
[GF_OPTION_TYPE_SIZE_LIST] = xlator_option_validate_size_list,
[GF_OPTION_TYPE_ANY] = xlator_option_validate_any,
+ [GF_OPTION_TYPE_CLIENT_AUTH_ADDR] = xlator_option_validate_mntauth,
[GF_OPTION_TYPE_MAX] = NULL,
};
diff --git a/libglusterfs/src/options.h b/libglusterfs/src/options.h
index 62f4ee9..134cc36 100644
--- a/libglusterfs/src/options.h
+++ b/libglusterfs/src/options.h
@@ -38,6 +38,7 @@ typedef enum {
GF_OPTION_TYPE_INTERNET_ADDRESS_LIST,
GF_OPTION_TYPE_PRIORITY_LIST,
GF_OPTION_TYPE_SIZE_LIST,
+ GF_OPTION_TYPE_CLIENT_AUTH_ADDR,
GF_OPTION_TYPE_MAX,
} volume_option_type_t;
diff --git a/rpc/rpc-lib/src/rpcsvc.c b/rpc/rpc-lib/src/rpcsvc.c
index 8be64c1..34ee6f2 100644
--- a/rpc/rpc-lib/src/rpcsvc.c
+++ b/rpc/rpc-lib/src/rpcsvc.c
@@ -59,6 +59,9 @@ int
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_notify_wrapper_t *
rpcsvc_notify_wrapper_alloc (void)
{
@@ -2181,6 +2184,13 @@ rpcsvc_transport_peer_check_search (dict_t *options, char *pattern,
goto err;
}
+ /* Compare IPv4 subnetwork */
+ if (strchr (addrtok, '/')) {
+ ret = match_subnet_v4 (addrtok, ip);
+ if (ret == 0)
+ goto err;
+ }
+
addrtok = strtok_r (NULL, ",", &svptr);
}
@@ -2327,8 +2337,20 @@ rpcsvc_auth_check (rpcsvc_t *svc, char *volname,
ret = dict_get_str (options, srchstr, &reject_str);
GF_FREE (srchstr);
- if (reject_str == NULL && !strcmp ("*", allow_str))
- return RPCSVC_AUTH_ACCEPT;
+
+ /*
+ * If "reject_str" is being set as '*' (anonymous), then NFS-server
+ * would reject everything. If the "reject_str" is not set and
+ * "allow_str" is set as '*' (anonymous), then NFS-server would
+ * accept mount requests from all clients.
+ */
+ if (reject_str != NULL) {
+ if (!strcmp ("*", reject_str))
+ return RPCSVC_AUTH_REJECT;
+ } else {
+ if (!strcmp ("*", allow_str))
+ return RPCSVC_AUTH_ACCEPT;
+ }
/* Non-default rule, authenticate */
if (!get_host_name (client_ip, &ip))
@@ -2461,6 +2483,71 @@ out:
return addrstr;
}
+/*
+ * 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
+match_subnet_v4 (const char *addrtok, const char *ipaddr)
+{
+ char *slash = NULL;
+ char *netaddr = NULL;
+ long prefixlen = -1;
+ int ret = -1;
+ 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 network mask in network byte order.
+ * NB: 32 : Max len of IPv4 address.
+ */
+ prefixlen = atoi (slash + 1);
+ shift = 32 - (uint32_t)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 */
+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/tests/bugs/bug-822830.t b/tests/bugs/bug-822830.t
index 000d99f..b7a5704 100755
--- a/tests/bugs/bug-822830.t
+++ b/tests/bugs/bug-822830.t
@@ -18,18 +18,38 @@ EXPECT 'Created' volinfo_field $V0 'Status';
TEST $CLI volume start $V0;
EXPECT 'Started' volinfo_field $V0 'Status';
-## Setting nfs.rpc-auth-reject as 192.*..*
-TEST ! $CLI volume set $V0 nfs.rpc-auth-reject 192.*..*
+## Setting nfs.rpc-auth-reject as 192.{}.1.2
+TEST ! $CLI volume set $V0 nfs.rpc-auth-reject 192.{}.1.2
EXPECT '' volinfo_field $V0 'nfs.rpc-auth-reject';
# Setting nfs.rpc-auth-allow as a.a.
TEST ! $CLI volume set $V0 nfs.rpc-auth-allow a.a.
EXPECT '' volinfo_field $V0 'nfs.rpc-auth-allow';
+## Setting nfs.rpc-auth-reject as 192.*..*
+TEST $CLI volume set $V0 nfs.rpc-auth-reject 192.*..*
+EXPECT '192.*..*' volinfo_field $V0 'nfs.rpc-auth-reject';
+
# Setting nfs.rpc-auth-allow as a.a
TEST $CLI volume set $V0 nfs.rpc-auth-allow a.a
EXPECT 'a.a' volinfo_field $V0 'nfs.rpc-auth-allow';
+# Setting nfs.rpc-auth-allow as *.redhat.com
+TEST $CLI volume set $V0 nfs.rpc-auth-allow *.redhat.com
+EXPECT '\*.redhat.com' volinfo_field $V0 'nfs.rpc-auth-allow';
+
+# Setting nfs.rpc-auth-allow as 192.168.10.[1-5]
+TEST $CLI volume set $V0 nfs.rpc-auth-allow 192.168.10.[1-5]
+EXPECT '192.168.10.\[1-5]' volinfo_field $V0 'nfs.rpc-auth-allow';
+
+# Setting nfs.rpc-auth-allow as 192.168.70.?
+TEST $CLI volume set $V0 nfs.rpc-auth-allow 192.168.70.?
+EXPECT '192.168.70.?' volinfo_field $V0 'nfs.rpc-auth-allow';
+
+# Setting nfs.rpc-auth-reject as 192.168.10.5/16
+TEST $CLI volume set $V0 nfs.rpc-auth-reject 192.168.10.5/16
+EXPECT '192.168.10.5/16' volinfo_field $V0 'nfs.rpc-auth-reject';
+
## Setting nfs.rpc-auth-reject as 192.*.*
TEST $CLI volume set $V0 nfs.rpc-auth-reject 192.*.*
EXPECT '192.*.*' volinfo_field $V0 'nfs.rpc-auth-reject';
diff --git a/xlators/nfs/server/src/nfs.c b/xlators/nfs/server/src/nfs.c
index 04cf030..d962663 100644
--- a/xlators/nfs/server/src/nfs.c
+++ b/xlators/nfs/server/src/nfs.c
@@ -1665,7 +1665,7 @@ struct volume_options options[] = {
"unrecognized option warnings."
},
{ .key = {"rpc-auth.addr.allow"},
- .type = GF_OPTION_TYPE_INTERNET_ADDRESS_LIST,
+ .type = GF_OPTION_TYPE_CLIENT_AUTH_ADDR,
.default_value = "all",
.description = "Allow a comma separated list of addresses and/or"
" hostnames to connect to the server. By default, all"
@@ -1673,7 +1673,7 @@ struct volume_options options[] = {
"define a general rule for all exported volumes."
},
{ .key = {"rpc-auth.addr.reject"},
- .type = GF_OPTION_TYPE_INTERNET_ADDRESS_LIST,
+ .type = GF_OPTION_TYPE_CLIENT_AUTH_ADDR,
.default_value = "none",
.description = "Reject a comma separated list of addresses and/or"
" hostnames from connecting to the server. By default,"
@@ -1681,7 +1681,7 @@ struct volume_options options[] = {
"define a general rule for all exported volumes."
},
{ .key = {"rpc-auth.addr.*.allow"},
- .type = GF_OPTION_TYPE_INTERNET_ADDRESS_LIST,
+ .type = GF_OPTION_TYPE_CLIENT_AUTH_ADDR,
.default_value = "all",
.description = "Allow a comma separated list of addresses and/or"
" hostnames to connect to the server. By default, all"
@@ -1689,7 +1689,7 @@ struct volume_options options[] = {
"define a rule for a specific exported volume."
},
{ .key = {"rpc-auth.addr.*.reject"},
- .type = GF_OPTION_TYPE_INTERNET_ADDRESS_LIST,
+ .type = GF_OPTION_TYPE_CLIENT_AUTH_ADDR,
.default_value = "none",
.description = "Reject a comma separated list of addresses and/or"
" hostnames from connecting to the server. By default,"