diff options
Diffstat (limited to 'xlators/protocol/auth/addr/src/addr.c')
| -rw-r--r-- | xlators/protocol/auth/addr/src/addr.c | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/xlators/protocol/auth/addr/src/addr.c b/xlators/protocol/auth/addr/src/addr.c new file mode 100644 index 00000000000..bf12c455d7c --- /dev/null +++ b/xlators/protocol/auth/addr/src/addr.c @@ -0,0 +1,341 @@ +/* + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include <fnmatch.h> +#include <sys/socket.h> +#include <netdb.h> +#include "authenticate.h" +#include <glusterfs/dict.h> +#include "rpc-transport.h" + +#define ENTRY_DELIMITER "," +#define ADDR_DELIMITER "|" +#define PRIVILEGED_PORT_CEILING 1024 + +#ifndef AF_INET_SDP +#define AF_INET_SDP 27 +#endif + +/* An option for subdir validation be like below */ + +/* 1. '*' + 2. '192.168.*' + 3. ' + 4. '!10.10.1*' (Today as per the code, if negate is set on one entry, its + never reset) + 5. '192.168.1.*, 10.1.10.*';168.168.2.* =/dir;* =/another-dir' + +*/ + +int +compare_addr_and_update(char *option_str, char *peer_addr, char *subvol, + char *delimiter, auth_result_t *result, + auth_result_t status) +{ + char *addr_str = NULL; + char *tmp = NULL; + char negate = 0; + char match = 0; + int length = 0; + int ret = 0; + + addr_str = strtok_r(option_str, delimiter, &tmp); + + while (addr_str) { + gf_log(subvol, GF_LOG_INFO, "%s = \"%s\", received addr = \"%s\"", + (status == AUTH_ACCEPT) ? "allowed" : "rejected", addr_str, + peer_addr); + if (addr_str[0] == '!') { + negate = 1; + addr_str++; + } + + length = strlen(addr_str); + if ((addr_str[0] != '*') && valid_host_name(addr_str, length)) { + match = gf_is_same_address(addr_str, peer_addr); + if (match) { + *result = status; + goto out; + } + } else { + if (strstr(addr_str, "/")) { + match = gf_is_ip_in_net(addr_str, peer_addr); + if (negate ? !match : match) { + *result = status; + goto out; + } + } else { + match = fnmatch(addr_str, peer_addr, 0); + if (negate ? match : !match) { + *result = status; + goto out; + } + } + } + + addr_str = strtok_r(NULL, delimiter, &tmp); + } + + ret = -1; +out: + return ret; +} + +void +parse_entries_and_compare(char *option_str, char *peer_addr, char *subvol, + char *subdir, auth_result_t *result, + auth_result_t status) +{ + char *entry = NULL; + char *entry_cpy = NULL; + char *directory = NULL; + char *entries = NULL; + char *addr_str = NULL; + char *addr = NULL; + char *tmp = NULL; + char *tmpdir = NULL; + int ret = 0; + + if (!subdir) { + gf_log(subvol, GF_LOG_WARNING, + "subdir entry not present, not performing any operation."); + goto out; + } + + entries = gf_strdup(option_str); + if (!entries) + goto out; + + if (entries[0] != '/' && !strchr(entries, '(')) { + /* Backward compatible option */ + ret = compare_addr_and_update(entries, peer_addr, subvol, ",", result, + status); + goto out; + } + + entry = strtok_r(entries, ENTRY_DELIMITER, &tmp); + while (entry) { + entry_cpy = gf_strdup(entry); + if (!entry_cpy) { + goto out; + } + + directory = strtok_r(entry_cpy, "(", &tmpdir); + if (directory[0] != '/') + goto out; + + /* send second portion, after ' =' if directory matches */ + if (strcmp(subdir, directory)) + goto next_entry; + + addr_str = strtok_r(NULL, ")", &tmpdir); + if (!addr_str) + goto out; + + addr = gf_strdup(addr_str); + if (!addr) + goto out; + + gf_log(subvol, GF_LOG_INFO, + "Found an entry for dir %s (%s)," + " performing validation", + subdir, addr); + + ret = compare_addr_and_update(addr, peer_addr, subvol, ADDR_DELIMITER, + result, status); + if (ret == 0) { + break; + } + + GF_FREE(addr); + addr = NULL; + + next_entry: + entry = strtok_r(NULL, ENTRY_DELIMITER, &tmp); + GF_FREE(entry_cpy); + entry_cpy = NULL; + } + +out: + GF_FREE(entries); + GF_FREE(entry_cpy); + GF_FREE(addr); +} + +auth_result_t +gf_auth(dict_t *input_params, dict_t *config_params) +{ + auth_result_t result = AUTH_DONT_CARE; + int ret = 0; + char *name = NULL; + char *searchstr = NULL; + peer_info_t *peer_info = NULL; + data_t *peer_info_data = NULL; + data_t *allow_addr = NULL; + data_t *reject_addr = NULL; + char *service = NULL; + uint16_t peer_port = 0; + char peer_addr[UNIX_PATH_MAX] = { + 0, + }; + char *type = NULL; + gf_boolean_t allow_insecure = _gf_false; + char *subdir = NULL; + + name = data_to_str(dict_get(input_params, "remote-subvolume")); + if (!name) { + gf_log("authenticate/addr", GF_LOG_DEBUG, + "remote-subvolume not specified"); + goto out; + } + + ret = gf_asprintf(&searchstr, "auth.addr.%s.allow", name); + if (-1 == ret) { + gf_log("auth/addr", GF_LOG_DEBUG, + "asprintf failed while setting search string"); + goto out; + } + + allow_addr = dict_get(config_params, searchstr); + GF_FREE(searchstr); + + ret = gf_asprintf(&searchstr, "auth.addr.%s.reject", name); + if (-1 == ret) { + gf_log("auth/addr", GF_LOG_ERROR, + "asprintf failed while setting search string"); + goto out; + } + reject_addr = dict_get(config_params, searchstr); + GF_FREE(searchstr); + + if (!allow_addr) { + /* TODO: backward compatibility */ + ret = gf_asprintf(&searchstr, "auth.ip.%s.allow", name); + if (-1 == ret) { + gf_log("auth/addr", GF_LOG_ERROR, + "asprintf failed while setting search string"); + goto out; + } + allow_addr = dict_get(config_params, searchstr); + GF_FREE(searchstr); + } + + if (!(allow_addr || reject_addr)) { + gf_log("auth/addr", GF_LOG_DEBUG, + "none of the options auth.addr.%s.allow or " + "auth.addr.%s.reject specified, returning auth_dont_care", + name, name); + goto out; + } + + peer_info_data = dict_get(input_params, "peer-info"); + if (!peer_info_data) { + gf_log("auth/addr", GF_LOG_ERROR, "peer-info not present"); + goto out; + } + + ret = dict_get_str(input_params, "subdir-mount", &subdir); + if (ret) { + subdir = "/"; + } + + peer_info = data_to_ptr(peer_info_data); + + switch (((struct sockaddr *)&peer_info->sockaddr)->sa_family) { + case AF_INET_SDP: + case AF_INET: + case AF_INET6: + strcpy(peer_addr, peer_info->identifier); + service = strrchr(peer_addr, ':'); + *service = '\0'; + service++; + + ret = dict_get_str(config_params, "rpc-auth-allow-insecure", &type); + if (ret == 0) { + ret = gf_string2boolean(type, &allow_insecure); + if (ret < 0) { + gf_log("auth/addr", GF_LOG_WARNING, + "rpc-auth-allow-insecure option %s " + "is not a valid bool option", + type); + goto out; + } + } + + peer_port = atoi(service); + if (peer_port >= PRIVILEGED_PORT_CEILING && !allow_insecure) { + gf_log("auth/addr", GF_LOG_ERROR, + "client is bound to port %d which is not privileged", + peer_port); + result = AUTH_REJECT; + goto out; + } + break; + + case AF_UNIX: + strcpy(peer_addr, peer_info->identifier); + break; + + default: + gf_log("authenticate/addr", GF_LOG_ERROR, + "unknown address family %d", + ((struct sockaddr *)&peer_info->sockaddr)->sa_family); + goto out; + } + + if (reject_addr) { + parse_entries_and_compare(reject_addr->data, peer_addr, name, subdir, + &result, AUTH_REJECT); + if (result == AUTH_REJECT) + goto out; + } + + if (allow_addr) { + parse_entries_and_compare(allow_addr->data, peer_addr, name, subdir, + &result, AUTH_ACCEPT); + } + +out: + return result; +} + +struct volume_options options[] = { + { + .key = {"auth.addr.*.allow"}, + .type = GF_OPTION_TYPE_INTERNET_ADDRESS_LIST, + .default_value = "*", + .description = "List of addresses to be allowed to access volume", + .op_version = {1}, + .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC, + .tags = {}, + /* option_validation_fn validate_fn; */ + }, + { + .key = {"auth.addr.*.reject"}, + .type = GF_OPTION_TYPE_INTERNET_ADDRESS_LIST, + .default_value = "*", + .description = "List of addresses to be rejected to access volume", + .op_version = {1}, + .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC, + .tags = {}, + /* option_validation_fn validate_fn; */ + }, + /* Backward compatibility */ + { + .key = {"auth.ip.*.allow"}, + .type = GF_OPTION_TYPE_INTERNET_ADDRESS_LIST, + .default_value = "*", + .description = "List of addresses to be allowed to access volume", + .op_version = {1}, + .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC, + .tags = {}, + /* option_validation_fn validate_fn; */ + }, + {.key = {NULL}}}; |
