From e433c91119b40b6066092b8165505332518f4918 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Mon, 6 Jun 2011 22:19:08 -0400 Subject: Add helper functions for looking up HBAC rule components --- Makefile.am | 6 + src/providers/ipa/ipa_hbac_common.c | 871 ++++++++++++++++++++++++++++++++++ src/providers/ipa/ipa_hbac_hosts.c | 524 ++++++++++++++++++++ src/providers/ipa/ipa_hbac_private.h | 194 ++++++++ src/providers/ipa/ipa_hbac_rules.c | 231 +++++++++ src/providers/ipa/ipa_hbac_services.c | 451 ++++++++++++++++++ src/providers/ipa/ipa_hbac_users.c | 345 ++++++++++++++ 7 files changed, 2622 insertions(+) create mode 100644 src/providers/ipa/ipa_hbac_common.c create mode 100644 src/providers/ipa/ipa_hbac_hosts.c create mode 100644 src/providers/ipa/ipa_hbac_private.h create mode 100644 src/providers/ipa/ipa_hbac_rules.c create mode 100644 src/providers/ipa/ipa_hbac_services.c create mode 100644 src/providers/ipa/ipa_hbac_users.c diff --git a/Makefile.am b/Makefile.am index 8002bbd0b..cf41889d7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -864,6 +864,12 @@ libsss_ipa_la_SOURCES = \ src/providers/ipa/ipa_auth.c \ src/providers/ipa/ipa_access.c \ src/providers/ipa/ipa_dyndns.c \ + src/providers/ipa/ipa_hbac_hosts.c \ + src/providers/ipa/ipa_hbac_private.h \ + src/providers/ipa/ipa_hbac_rules.c \ + src/providers/ipa/ipa_hbac_services.c \ + src/providers/ipa/ipa_hbac_users.c \ + src/providers/ipa/ipa_hbac_common.c \ src/providers/ldap/ldap_id.c \ src/providers/ldap/ldap_id_enum.c \ src/providers/ldap/ldap_id_cleanup.c \ diff --git a/src/providers/ipa/ipa_hbac_common.c b/src/providers/ipa/ipa_hbac_common.c new file mode 100644 index 000000000..f05c3e2e7 --- /dev/null +++ b/src/providers/ipa/ipa_hbac_common.c @@ -0,0 +1,871 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2011 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "providers/ipa/ipa_hbac_private.h" +#include "providers/ipa/ipa_hbac.h" +#include "providers/ipa/ipa_common.h" + +errno_t +ipa_hbac_save_list(struct sysdb_ctx *sysdb, bool delete_subdir, + const char *subdir, struct sss_domain_info *domain, + const char *naming_attribute, size_t count, + struct sysdb_attrs **list) +{ + int ret; + size_t c; + struct ldb_dn *base_dn; + const char *object_name; + struct ldb_message_element *el; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + DEBUG(1, ("talloc_new failed.\n")); + return ENOMEM; + } + + if (delete_subdir) { + base_dn = sysdb_custom_subtree_dn(sysdb, tmp_ctx, domain->name, subdir); + if (base_dn == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_delete_recursive(tmp_ctx, sysdb, base_dn, true); + if (ret != EOK) { + DEBUG(1, ("sysdb_delete_recursive failed.\n")); + goto done; + } + } + + for (c = 0; c < count; c++) { + ret = sysdb_attrs_get_el(list[c], naming_attribute, &el); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto done; + } + if (el->num_values == 0) { + DEBUG(1, ("[%s] not found.\n", naming_attribute)); + ret = EINVAL; + goto done; + } + object_name = talloc_strndup(tmp_ctx, (const char *)el->values[0].data, + el->values[0].length); + if (object_name == NULL) { + DEBUG(1, ("talloc_strndup failed.\n")); + ret = ENOMEM; + goto done; + } + DEBUG(9, ("Object name: [%s].\n", object_name)); + + ret = sysdb_store_custom(tmp_ctx, sysdb, domain, object_name, subdir, + list[c]); + if (ret != EOK) { + DEBUG(1, ("sysdb_store_custom failed.\n")); + goto done; + } + } + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +errno_t +ipa_hbac_sysdb_save(struct sysdb_ctx *sysdb, struct sss_domain_info *domain, + const char *primary_subdir, const char *attr_name, + size_t primary_count, struct sysdb_attrs **primary, + const char *group_subdir, const char *groupattr_name, + size_t group_count, struct sysdb_attrs **groups) +{ + int lret; + errno_t ret, sret; + bool in_transaction = false; + const char **orig_member_dns; + size_t i, j, member_count; + struct ldb_message **members; + TALLOC_CTX *tmp_ctx = NULL; + const char *member_dn; + const char *group_id; + struct ldb_message *msg; + char *member_filter; + + if ((primary_count == 0 || primary == NULL) + || (group_count > 0 && groups == NULL)) { + /* There always has to be at least one + * primary entry. + */ + return EINVAL; + } + + /* Save the entries and groups to the cache */ + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) return ret; + in_transaction = true; + + /* First, save the specific entries */ + ret = ipa_hbac_save_list(sysdb, true, + primary_subdir, + domain, + attr_name, + primary_count, + primary); + if (ret != EOK) { + DEBUG(1, ("Could not save %s. [%d][%s]\n", + primary_subdir, ret, strerror(ret))); + goto done; + } + + /* Second, save the groups */ + if (group_count > 0) { + ret = ipa_hbac_save_list(sysdb, true, + group_subdir, + domain, + groupattr_name, + group_count, + groups); + if (ret != EOK) { + DEBUG(1, ("Could not save %s. [%d][%s]\n", + group_subdir, ret, strerror(ret))); + goto done; + } + + /* Third, save the memberships */ + for (i = 0; i < group_count; i++) { + if (!groups[i]) { + ret = EINVAL; + goto done; + } + + talloc_free(tmp_ctx); + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_attrs_get_string(groups[i], + groupattr_name, + &group_id); + if (ret != EOK) { + DEBUG(1, ("Could not determine group attribute name\n")); + goto done; + } + + msg = ldb_msg_new(tmp_ctx); + if (msg == NULL) { + ret = ENOMEM; + goto done; + } + + msg->dn = sysdb_custom_dn(sysdb, msg, domain->name, + group_id, group_subdir); + if (msg->dn == NULL) { + ret = ENOMEM; + goto done; + } + + lret = ldb_msg_add_empty(msg, SYSDB_MEMBER, LDB_FLAG_MOD_ADD, NULL); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + + ret = sysdb_attrs_get_string_array(groups[i], + SYSDB_ORIG_MEMBER, + tmp_ctx, + &orig_member_dns); + if (ret != EOK) { + DEBUG(1, ("Could not determine original members\n")); + goto done; + } + + for (j = 0; orig_member_dns[j]; j++) { + member_filter = talloc_asprintf(tmp_ctx, "%s=%s", + SYSDB_ORIG_DN, + orig_member_dns[j]); + if (member_filter == NULL) { + ret = ENOMEM; + goto done; + } + + ret = sysdb_search_custom(tmp_ctx, sysdb, domain, + member_filter, primary_subdir, + NULL, &member_count, &members); + talloc_zfree(member_filter); + if (ret != EOK && ret != ENOENT) { + goto done; + } else if (ret == ENOENT || member_count == 0) { + /* No member exists with this orig_dn. Skip it */ + DEBUG(6, ("[%s] does not exist\n", orig_member_dns[j])); + continue; + } else if (member_count > 1) { + /* This probably means corruption in the cache, but + * we'll try to proceed anyway. + */ + DEBUG(1, ("More than one result for DN [%s], skipping\n")); + continue; + } + + member_dn = ldb_dn_get_linearized(members[0]->dn); + if (!member_dn) { + ret = ENOMEM; + goto done; + } + lret = ldb_msg_add_fmt(msg, SYSDB_MEMBER, "%s", member_dn); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + } + + lret = ldb_modify(sysdb_ctx_get_ldb(sysdb), msg); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + } + talloc_zfree(tmp_ctx); + } + + ret = sysdb_transaction_commit(sysdb); + if (ret != EOK) goto done; + in_transaction = false; + +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(0, ("Could not cancel sysdb transaction\n")); + } + } + + if (ret != EOK) { + DEBUG(3, ("Error [%d][%s]\n", ret, strerror(ret))); + } + return ret; +} + +errno_t +replace_attribute_name(const char *old_name, + const char *new_name, const size_t count, + struct sysdb_attrs **list) +{ + int ret; + int i; + + for (i = 0; i < count; i++) { + ret = sysdb_attrs_replace_name(list[i], old_name, new_name); + if (ret != EOK) { + DEBUG(1, ("sysdb_attrs_replace_name failed.\n")); + return ret; + } + } + + return EOK; +} + + +/******************************************** + * Functions for handling conversion to the * + * HBAC evaluator format * + ********************************************/ + +static errno_t +hbac_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct hbac_ctx *hbac_ctx, + size_t index, + struct hbac_rule **rule); + +static errno_t +hbac_ctx_to_eval_request(TALLOC_CTX *mem_ctx, + struct hbac_ctx *hbac_ctx, + struct hbac_eval_req **request); + +errno_t +hbac_ctx_to_rules(TALLOC_CTX *mem_ctx, + struct hbac_ctx *hbac_ctx, + struct hbac_rule ***rules, + struct hbac_eval_req **request) +{ + errno_t ret; + struct hbac_rule **new_rules; + struct hbac_eval_req *new_request; + size_t i; + TALLOC_CTX *tmp_ctx = NULL; + + if (!rules || !request) return EINVAL; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) return ENOMEM; + + /* First create an array of rules */ + new_rules = talloc_array(tmp_ctx, struct hbac_rule *, + hbac_ctx->rule_count + 1); + if (new_rules == NULL) { + ret = ENOMEM; + goto done; + } + + /* Create each rule one at a time */ + for (i = 0; i < hbac_ctx->rule_count ; i++) { + ret = hbac_attrs_to_rule(new_rules, hbac_ctx, i, &(new_rules[i])); + if (ret == EPERM) { + goto done; + } else if (ret != EOK) { + DEBUG(1, ("Could not construct rules\n")) + goto done; + } + } + new_rules[i] = NULL; + + /* Create the eval request */ + ret = hbac_ctx_to_eval_request(tmp_ctx, hbac_ctx, &new_request); + if (ret != EOK) { + DEBUG(1, ("Could not construct eval request\n")); + goto done; + } + + *rules = talloc_steal(mem_ctx, new_rules); + *request = talloc_steal(mem_ctx, new_request); + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +hbac_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct hbac_ctx *hbac_ctx, + size_t idx, + struct hbac_rule **rule) +{ + errno_t ret; + struct hbac_rule *new_rule; + struct ldb_message_element *el; + const char *rule_type; + + new_rule = talloc_zero(mem_ctx, struct hbac_rule); + if (new_rule == NULL) return ENOMEM; + + ret = sysdb_attrs_get_el(hbac_ctx->rules[idx], + IPA_CN, &el); + if (ret != EOK || el->num_values == 0) { + DEBUG(4, ("rule has no name, assuming '(none)'.\n")); + new_rule->name = talloc_strdup(new_rule, "(none)"); + } else { + new_rule->name = talloc_strndup(new_rule, + (const char*) el->values[0].data, + el->values[0].length); + } + + DEBUG(7, ("Processing rule [%s]\n", new_rule->name)); + + ret = sysdb_attrs_get_bool(hbac_ctx->rules[idx], IPA_ENABLED_FLAG, + &new_rule->enabled); + if (ret != EOK) goto done; + + if (!new_rule->enabled) { + ret = EOK; + goto done; + } + + ret = sysdb_attrs_get_string(hbac_ctx->rules[idx], + IPA_ACCESS_RULE_TYPE, + &rule_type); + if (ret != EOK) goto done; + + if (strcasecmp(rule_type, IPA_HBAC_ALLOW) != 0) { + DEBUG(7, ("Rule [%s] is not an ALLOW rule\n", new_rule->name)); + ret = EPERM; + goto done; + } + + /* Get the users */ + ret = hbac_user_attrs_to_rule(new_rule, + hbac_ctx_sysdb(hbac_ctx), + hbac_ctx_be(hbac_ctx)->domain, + new_rule->name, + hbac_ctx->rules[idx], + &new_rule->users); + if (ret != EOK) { + DEBUG(1, ("Could not parse users for rule [%s]\n", + new_rule->name)); + goto done; + } + + /* Get the services */ + ret = hbac_service_attrs_to_rule(new_rule, + hbac_ctx_sysdb(hbac_ctx), + hbac_ctx_be(hbac_ctx)->domain, + new_rule->name, + hbac_ctx->rules[idx], + &new_rule->services); + if (ret != EOK) { + DEBUG(1, ("Could not parse services for rule [%s]\n", + new_rule->name)); + goto done; + } + + /* Get the target hosts */ + ret = hbac_thost_attrs_to_rule(new_rule, + hbac_ctx_sysdb(hbac_ctx), + hbac_ctx_be(hbac_ctx)->domain, + new_rule->name, + hbac_ctx->rules[idx], + &new_rule->targethosts); + if (ret != EOK) { + DEBUG(1, ("Could not parse target hosts for rule [%s]\n", + new_rule->name)); + goto done; + } + + /* Get the source hosts */ + ret = hbac_shost_attrs_to_rule(new_rule, + hbac_ctx_sysdb(hbac_ctx), + hbac_ctx_be(hbac_ctx)->domain, + new_rule->name, + hbac_ctx->rules[idx], + &new_rule->srchosts); + if (ret != EOK) { + DEBUG(1, ("Could not parse source hosts for rule [%s]\n", + new_rule->name)); + goto done; + } + + *rule = new_rule; + ret = EOK; + +done: + if (ret != EOK) talloc_free(new_rule); + return ret; +} + +errno_t +hbac_get_category(struct sysdb_attrs *attrs, + const char *category_attr, + uint32_t *_categories) +{ + errno_t ret; + size_t i; + uint32_t cats = HBAC_CATEGORY_NULL; + const char **categories; + + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) return ENOMEM; + + ret = sysdb_attrs_get_string_array(attrs, category_attr, + tmp_ctx, &categories); + if (ret != EOK && ret != ENOENT) goto done; + + if (ret != ENOENT) { + for (i = 0; categories[i]; i++) { + if (strcasecmp("all", categories[i]) == 0) { + DEBUG(5, ("Category is set to 'all'.\n")); + cats |= HBAC_CATEGORY_ALL; + continue; + } + DEBUG(9, ("Unsupported user category [%s].\n", + categories[i])); + } + } + + *_categories = cats; + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +hbac_eval_user_element(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *username, + struct hbac_request_element **user_element); + +static errno_t +hbac_eval_service_element(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *hostname, + struct hbac_request_element **svc_element); + +static errno_t +hbac_eval_host_element(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *hostname, + struct hbac_request_element **host_element); + +static errno_t +hbac_ctx_to_eval_request(TALLOC_CTX *mem_ctx, + struct hbac_ctx *hbac_ctx, + struct hbac_eval_req **request) +{ + errno_t ret; + struct pam_data *pd = hbac_ctx->pd; + TALLOC_CTX *tmp_ctx; + struct hbac_eval_req *eval_req; + struct sysdb_ctx *sysdb = hbac_ctx_sysdb(hbac_ctx); + struct sss_domain_info *domain = hbac_ctx_be(hbac_ctx)->domain; + const char *rhost; + const char *thost; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) return ENOMEM; + + eval_req = talloc_zero(tmp_ctx, struct hbac_eval_req); + if (tmp_ctx == NULL) { + ret = ENOMEM; + goto done; + } + + eval_req->request_time = time(NULL); + + /* Get user the user name and groups */ + ret = hbac_eval_user_element(eval_req, sysdb, domain, + pd->user, &eval_req->user); + if (ret != EOK) goto done; + + /* Get the PAM service and service groups */ + ret = hbac_eval_service_element(eval_req, sysdb, domain, + pd->service, &eval_req->service); + if (ret != EOK) goto done; + + /* Get the source host */ + if (pd->rhost == NULL || pd->rhost[0] == '\0') { + /* If we haven't been passed an rhost, we + * have to assume it's coming from the + * target host + */ + rhost = dp_opt_get_cstring(hbac_ctx->ipa_options, IPA_HOSTNAME); + } else { + rhost = pd->rhost; + } + if (rhost == NULL) { + ret = EINVAL; + goto done; + } + + ret = hbac_eval_host_element(eval_req, sysdb, domain, + rhost, &eval_req->srchost); + if (ret != EOK) goto done; + + /* The target host is always the current machine */ + thost = dp_opt_get_cstring(hbac_ctx->ipa_options, IPA_HOSTNAME); + if (thost == NULL) { + DEBUG(1, ("Missing ipa_hostname, this should never happen.\n")); + ret = EINVAL; + goto done; + } + + ret = hbac_eval_host_element(eval_req, sysdb, domain, + thost, &eval_req->targethost); + if (ret != EOK) goto done; + + *request = talloc_steal(mem_ctx, eval_req); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +hbac_eval_user_element(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *username, + struct hbac_request_element **user_element) +{ + errno_t ret; + unsigned int i; + unsigned int num_groups = 0; + TALLOC_CTX *tmp_ctx; + const char *member_dn; + struct hbac_request_element *users; + struct ldb_message *msg; + struct ldb_message_element *el; + const char *attrs[] = { SYSDB_ORIG_MEMBEROF, NULL }; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) return ENOMEM; + + users = talloc_zero(tmp_ctx, struct hbac_request_element); + if (users == NULL) { + ret = ENOMEM; + goto done; + } + + users->name = username; + + /* Read the originalMemberOf attribute + * This will give us the list of both POSIX and + * non-POSIX groups that this user belongs to. + */ + ret = sysdb_search_user_by_name(tmp_ctx, sysdb, domain, + users->name, attrs, &msg); + if (ret != EOK) { + DEBUG(1, ("Could not determine user memberships for [%s]\n", + users->name)); + goto done; + } + + el = ldb_msg_find_element(msg, SYSDB_ORIG_MEMBEROF); + if (el == NULL || el->num_values == 0) { + DEBUG(7, ("No groups for [%s]\n", users->name)); + users->groups = talloc_array(users, const char *, 1); + if (users->groups == NULL) { + ret = ENOMEM; + goto done; + } + users->groups[0] = NULL; + goto done; + } + DEBUG(7, ("[%d] groups for [%s]\n", el->num_values, users->name)); + + users->groups = talloc_array(users, const char *, el->num_values + 1); + if (users->groups == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < el->num_values; i++) { + member_dn = (const char *)el->values[i].data; + + ret = get_ipa_groupname(users->groups, sysdb, member_dn, + &users->groups[num_groups]); + if (ret != EOK && ret != ENOENT) { + DEBUG(3, ("Parse error on [%s]\n", member_dn)); + goto done; + } else if (ret == EOK) { + DEBUG(7, ("Added group [%s] for user [%s]\n", + users->groups[num_groups], users->name)); + num_groups++; + continue; + } + /* Skip entries that are not groups */ + DEBUG(8, ("Skipping non-group memberOf [%s]\n", member_dn)); + } + users->groups[num_groups] = NULL; + + if (num_groups < el->num_values) { + /* Shrink the array memory */ + users->groups = talloc_realloc(users, users->groups, const char *, + num_groups+1); + if (users->groups == NULL) { + ret = ENOMEM; + goto done; + } + } + + ret = EOK; +done: + if (ret == EOK) { + *user_element = talloc_steal(mem_ctx, users); + } + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +hbac_eval_service_element(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *hostname, + struct hbac_request_element **svc_element) +{ + errno_t ret; + size_t i, count; + TALLOC_CTX *tmp_ctx; + struct hbac_request_element *svc; + struct ldb_message **msgs; + const char *group_name; + struct ldb_dn *svc_dn; + const char *attrs[] = { IPA_CN, NULL }; + const char *service_filter; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) return ENOMEM; + + svc = talloc_zero(tmp_ctx, struct hbac_request_element); + if (svc == NULL) { + ret = ENOMEM; + goto done; + } + + svc->name = hostname; + + service_filter = talloc_asprintf(tmp_ctx, + "(objectClass=%s)", + IPA_HBAC_SERVICE_GROUP); + if (service_filter == NULL) { + ret = ENOMEM; + goto done; + } + + svc_dn = sysdb_custom_dn(sysdb, tmp_ctx, domain->name, + svc->name, HBAC_SERVICES_SUBDIR); + if (svc_dn == NULL) { + ret = ENOMEM; + goto done; + } + + /* Find the service groups */ + ret = sysdb_asq_search(tmp_ctx, sysdb, domain, svc_dn, + service_filter, SYSDB_MEMBEROF, + attrs, &count, &msgs); + if (ret != EOK && ret != ENOENT) { + DEBUG(1, ("Could not look up servicegroups\n")); + goto done; + } else if (ret == ENOENT) { + count = 0; + } + + svc->groups = talloc_array(svc, const char *, count + 1); + if (svc->groups == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < count; i++) { + group_name = ldb_msg_find_attr_as_string(msgs[i], IPA_CN, NULL); + if (group_name == NULL) { + DEBUG(1, ("Group with no name?\n")); + ret = EINVAL; + goto done; + } + svc->groups[i] = talloc_strdup(svc->groups, + group_name); + if (svc->groups[i] == NULL) { + ret = ENOMEM; + goto done; + } + + DEBUG(6, ("Added service group [%s] to the eval request\n", + svc->groups[i])); + } + svc->groups[i] = NULL; + + *svc_element = talloc_steal(mem_ctx, svc); + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +hbac_eval_host_element(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *hostname, + struct hbac_request_element **host_element) +{ + errno_t ret; + size_t i, count; + TALLOC_CTX *tmp_ctx; + struct hbac_request_element *host; + struct ldb_message **msgs; + const char *group_name; + struct ldb_dn *host_dn; + const char *attrs[] = { IPA_HOST_FQDN, NULL }; + const char *host_filter; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) return ENOMEM; + + host = talloc_zero(tmp_ctx, struct hbac_request_element); + if (host == NULL) { + ret = ENOMEM; + goto done; + } + + host->name = hostname; + + + host_filter = talloc_asprintf(tmp_ctx, + "(objectClass=%s)", + IPA_HOSTGROUP); + if (host_filter == NULL) { + ret = ENOMEM; + goto done; + } + + host_dn = sysdb_custom_dn(sysdb, tmp_ctx, domain->name, + host->name, HBAC_SERVICES_SUBDIR); + if (host_dn == NULL) { + ret = ENOMEM; + goto done; + } + + /* Find the host groups */ + ret = sysdb_asq_search(tmp_ctx, sysdb, domain, host_dn, + host_filter, SYSDB_MEMBEROF, + attrs, &count, &msgs); + if (ret != EOK && ret != ENOENT) { + DEBUG(1, ("Could not look up host groups\n")); + goto done; + } else if (ret == ENOENT) { + count = 0; + } + + host->groups = talloc_array(host, const char *, count + 1); + if (host->groups == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < count; i++) { + group_name = ldb_msg_find_attr_as_string(msgs[i], + IPA_HOST_FQDN, + NULL); + if (group_name == NULL) { + DEBUG(1, ("Group with no name?\n")); + ret = EINVAL; + goto done; + } + host->groups[i] = talloc_strdup(host->groups, + group_name); + if (host->groups[i] == NULL) { + ret = ENOMEM; + goto done; + } + + DEBUG(6, ("Added host group [%s] to the eval request\n", + host->groups[i])); + } + host->groups[i] = NULL; + + *host_element = talloc_steal(mem_ctx, host); + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/providers/ipa/ipa_hbac_hosts.c b/src/providers/ipa/ipa_hbac_hosts.c new file mode 100644 index 000000000..4e753f374 --- /dev/null +++ b/src/providers/ipa/ipa_hbac_hosts.c @@ -0,0 +1,524 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2011 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "db/sysdb.h" +#include "providers/ipa/ipa_hbac_private.h" +#include "providers/ldap/sdap_async.h" + +struct ipa_hbac_host_state { + struct tevent_context *ev; + struct sysdb_ctx *sysdb; + struct sss_domain_info *dom; + struct sdap_handle *sh; + struct sdap_options *opts; + const char *search_base; + const char **attrs; + + /* Return values */ + size_t host_count; + struct sysdb_attrs **hosts; + + size_t hostgroup_count; + struct sysdb_attrs **hostgroups; +}; + +static void +ipa_hbac_host_info_done(struct tevent_req *subreq); + +static void +ipa_hbac_hostgroup_info_done(struct tevent_req *subreq); + +struct tevent_req * +ipa_hbac_host_info_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *dom, + struct sdap_handle *sh, + struct sdap_options *opts, + const char *search_base) +{ + errno_t ret; + struct ipa_hbac_host_state *state; + struct tevent_req *req; + struct tevent_req *subreq; + char *host_filter; + + req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_host_state); + if (req == NULL) { + return NULL; + } + + state->ev = ev; + state->sysdb = sysdb; + state->dom = dom; + state->sh = sh; + state->opts = opts; + state->search_base = search_base; + + host_filter = talloc_asprintf(state, "(objectClass=%s)", IPA_HOST); + if (host_filter == NULL) { + ret = ENOMEM; + goto immediate; + } + + state->attrs = talloc_array(state, const char *, 8); + if (state->attrs == NULL) { + DEBUG(1, ("Failed to allocate host attribute list.\n")); + ret = ENOMEM; + goto immediate; + } + state->attrs[0] = "objectClass"; + state->attrs[1] = IPA_HOST_SERVERHOSTNAME; + state->attrs[2] = IPA_HOST_FQDN; + state->attrs[3] = IPA_UNIQUE_ID; + state->attrs[4] = IPA_MEMBER; + state->attrs[5] = IPA_MEMBEROF; + state->attrs[6] = IPA_CN; + state->attrs[7] = NULL; + + subreq = sdap_get_generic_send(state, ev, opts, sh, search_base, + LDAP_SCOPE_SUB, host_filter, + state->attrs, NULL, 0, + dp_opt_get_int(opts->basic, + SDAP_ENUM_SEARCH_TIMEOUT)); + if (subreq == NULL) { + DEBUG(1, ("Error requesting host info\n")); + ret = EIO; + goto immediate; + } + tevent_req_set_callback(subreq, ipa_hbac_host_info_done, req); + + return req; + +immediate: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + return req; +} + +static void +ipa_hbac_host_info_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct ipa_hbac_host_state *state = + tevent_req_data(req, struct ipa_hbac_host_state); + char *hostgroup_filter; + + ret = sdap_get_generic_recv(subreq, state, + &state->host_count, + &state->hosts); + talloc_zfree(subreq); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + if (state->host_count == 0) { + tevent_req_error(req, ENOENT); + return; + } + + ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF, + state->host_count, + state->hosts); + if (ret != EOK) { + DEBUG(1, ("Could not replace attribute names\n")); + tevent_req_error(req, ret); + return; + } + + hostgroup_filter = talloc_asprintf(state, "(objectClass=%s)", + IPA_HOSTGROUP); + if (hostgroup_filter == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + /* Look up host groups */ + subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, + state->search_base, LDAP_SCOPE_SUB, + hostgroup_filter, state->attrs, NULL, 0, + dp_opt_get_int(state->opts->basic, + SDAP_ENUM_SEARCH_TIMEOUT)); + if (subreq == NULL) { + DEBUG(1, ("Error requesting host info\n")); + tevent_req_error(req, EIO); + return; + } + tevent_req_set_callback(subreq, ipa_hbac_hostgroup_info_done, req); +} + +static void +ipa_hbac_hostgroup_info_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct ipa_hbac_host_state *state = + tevent_req_data(req, struct ipa_hbac_host_state); + + ret = sdap_get_generic_recv(subreq, state, + &state->hostgroup_count, + &state->hostgroups); + talloc_zfree(subreq); + if (ret != EOK) goto done; + + ret = replace_attribute_name(IPA_MEMBER, SYSDB_ORIG_MEMBER, + state->hostgroup_count, + state->hostgroups); + if (ret != EOK) { + DEBUG(1, ("Could not replace attribute names\n")); + goto done; + } + + ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF, + state->hostgroup_count, + state->hostgroups); + if (ret != EOK) { + DEBUG(1, ("Could not replace attribute names\n")); + goto done; + } + +done: + if (ret == EOK) { + tevent_req_done(req); + } else { + DEBUG(3, ("Error [%d][%s]\n", ret, strerror(ret))); + tevent_req_error(req, ret); + } +} + +errno_t +ipa_hbac_host_info_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *host_count, + struct sysdb_attrs ***hosts, + size_t *hostgroup_count, + struct sysdb_attrs ***hostgroups) +{ + size_t c; + struct ipa_hbac_host_state *state = + tevent_req_data(req, struct ipa_hbac_host_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *host_count = state->host_count; + *hosts = talloc_steal(mem_ctx, state->hosts); + for (c = 0; c < state->host_count; c++) { + /* Guarantee the memory heirarchy of the list */ + talloc_steal(state->hosts, state->hosts[c]); + } + + *hostgroup_count = state->hostgroup_count; + *hostgroups = talloc_steal(mem_ctx, state->hostgroups); + + return EOK; +} + +/* + * Functions to convert sysdb_attrs to the hbac_rule format + */ +static errno_t hbac_host_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *rule_name, + struct sysdb_attrs *rule_attrs, + const char *category_attr, + const char *member_attr, + size_t *host_count, + struct hbac_rule_element **hosts) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx; + struct hbac_rule_element *new_hosts; + const char *attrs[] = { IPA_HOST_FQDN, NULL }; + struct ldb_message_element *el; + size_t num_hosts = 0; + size_t num_hostgroups = 0; + size_t i; + char *member_dn; + char *filter; + size_t count; + struct ldb_message **msgs; + const char *name; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) return ENOMEM; + + new_hosts = talloc_zero(tmp_ctx, struct hbac_rule_element); + if (new_hosts == NULL) { + ret = ENOMEM; + goto done; + } + + /* First check for host category */ + ret = hbac_get_category(rule_attrs, category_attr, &new_hosts->category); + if (ret != EOK) { + DEBUG(1, ("Could not identify host categories\n")); + goto done; + } + if (new_hosts->category & HBAC_CATEGORY_ALL) { + /* Short-cut to the exit */ + ret = EOK; + goto done; + } + + /* Get the list of DNs from the member_attr */ + ret = sysdb_attrs_get_el(rule_attrs, member_attr, &el); + if (ret != EOK && ret != ENOENT) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto done; + } + if (ret == ENOENT || el->num_values == 0) { + el->num_values = 0; + DEBUG(4, ("No host specified, rule will never apply.\n")); + } + + /* Assume maximum size; We'll trim it later */ + new_hosts->names = talloc_array(new_hosts, + const char *, + el->num_values +1); + if (new_hosts->names == NULL) { + ret = ENOMEM; + goto done; + } + + new_hosts->groups = talloc_array(new_hosts, + const char *, + el->num_values + 1); + if (new_hosts->groups == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < el->num_values; i++) { + ret = sss_filter_sanitize(tmp_ctx, + (const char *)el->values[i].data, + &member_dn); + if (ret != EOK) goto done; + + filter = talloc_asprintf(member_dn, "(%s=%s)", + SYSDB_ORIG_DN, member_dn); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + + /* First check if this is a specific host */ + ret = sysdb_search_custom(tmp_ctx, sysdb, domain, filter, + HBAC_HOSTS_SUBDIR, attrs, + &count, &msgs); + if (ret != EOK && ret != ENOENT) goto done; + if (ret == EOK && count == 0) { + ret = ENOENT; + } + + if (ret == EOK) { + if (count > 1) { + DEBUG(1, ("Original DN matched multiple hosts. Skipping \n")); + talloc_zfree(member_dn); + continue; + } + + /* Original DN matched a single host. Get the hostname */ + name = ldb_msg_find_attr_as_string(msgs[0], + IPA_HOST_FQDN, + NULL); + if (name == NULL) { + DEBUG(1, ("Attribute is missing!\n")); + ret = EFAULT; + goto done; + } + + new_hosts->names[num_hosts] = talloc_strdup(new_hosts->names, + name); + if (new_hosts->names[num_hosts] == NULL) { + ret = ENOMEM; + goto done; + } + DEBUG(8, ("Added host [%s] to rule [%s]\n", + name, rule_name)); + num_hosts++; + } else { /* ret == ENOENT */ + /* Check if this is a hostgroup */ + ret = sysdb_search_custom(tmp_ctx, sysdb, domain, filter, + HBAC_HOSTGROUPS_SUBDIR, attrs, + &count, &msgs); + if (ret != EOK && ret != ENOENT) goto done; + if (ret == EOK && count == 0) { + ret = ENOENT; + } + + if (ret == EOK) { + if (count > 1) { + DEBUG(1, ("Original DN matched multiple hostgroups. " + "Skipping\n")); + talloc_zfree(member_dn); + continue; + } + + /* Original DN matched a single group. Get the groupname */ + name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL); + if (name == NULL) { + DEBUG(1, ("Attribute is missing!\n")); + ret = EFAULT; + goto done; + } + + new_hosts->groups[num_hostgroups] = + talloc_strdup(new_hosts->groups, name); + if (new_hosts->groups[num_hostgroups] == NULL) { + ret = ENOMEM; + goto done; + } + + DEBUG(8, ("Added hostgroup [%s] to rule [%s]\n", + name, rule_name)); + num_hostgroups++; + } else { /* ret == ENOENT */ + /* Neither a host nor a hostgroup? Skip it */ + DEBUG(1, ("[%s] does not map to either a host or hostgroup. " + "Skipping\n", member_dn)); + } + } + talloc_zfree(member_dn); + } + new_hosts->names[num_hosts] = NULL; + new_hosts->groups[num_hostgroups] = NULL; + + /* Shrink the arrays down to their real sizes */ + new_hosts->names = talloc_realloc(new_hosts, new_hosts->names, + const char *, num_hosts + 1); + if (new_hosts->names == NULL) { + ret = ENOMEM; + goto done; + } + + new_hosts->groups = talloc_realloc(new_hosts, new_hosts->groups, + const char *, num_hostgroups + 1); + if (new_hosts->groups == NULL) { + ret = ENOMEM; + goto done; + } + + ret = EOK; + +done: + if (ret == EOK) { + *hosts = talloc_steal(mem_ctx, new_hosts); + if (host_count) *host_count = num_hosts; + } + talloc_free(tmp_ctx); + return ret; +} + +errno_t +hbac_thost_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *rule_name, + struct sysdb_attrs *rule_attrs, + struct hbac_rule_element **thosts) +{ + DEBUG(7, ("Processing target hosts for rule [%s]\n", rule_name)); + + return hbac_host_attrs_to_rule(mem_ctx, sysdb, domain, + rule_name, rule_attrs, + IPA_HOST_CATEGORY, IPA_MEMBER_HOST, + NULL, thosts); +} + +errno_t +hbac_shost_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *rule_name, + struct sysdb_attrs *rule_attrs, + struct hbac_rule_element **source_hosts) +{ + errno_t ret; + size_t host_count; + TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); + size_t idx; + struct ldb_message_element *el; + struct hbac_rule_element *shosts; + + DEBUG(7, ("Processing source hosts for rule [%s]\n", rule_name)); + + ret = hbac_host_attrs_to_rule(tmp_ctx, sysdb, domain, + rule_name, rule_attrs, + IPA_SOURCE_HOST_CATEGORY, IPA_SOURCE_HOST, + &host_count, &shosts); + if (ret != EOK) { + goto done; + } + + if (shosts->category & HBAC_CATEGORY_ALL) { + /* All hosts (including external) are + * allowed. + */ + goto done; + } + + /* Include external (non-IPA-managed) source hosts */ + ret = sysdb_attrs_get_el(rule_attrs, IPA_EXTERNAL_HOST, &el); + if (ret != EOK && ret != ENOENT) goto done; + if (ret == EOK && el->num_values == 0) ret = ENOENT; + + if (ret != ENOENT) { + shosts->names = talloc_realloc(shosts, shosts->names, const char *, + host_count + el->num_values + 1); + if (shosts->names == NULL) { + ret = ENOMEM; + goto done; + } + + for (idx = host_count; idx <= host_count + el->num_values; idx++) { + shosts->names[idx] = + talloc_strdup(shosts->names, + (const char *)el->values[idx].data); + if (shosts->names[idx] == NULL) { + ret = ENOMEM; + goto done; + } + DEBUG(8, ("Added external source host [%s] to rule [%s]\n", + shosts->names[idx], rule_name)); + } + shosts->names[idx] = NULL; + } + + ret = EOK; + +done: + if (ret == EOK) { + *source_hosts = talloc_steal(mem_ctx, shosts); + } + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/providers/ipa/ipa_hbac_private.h b/src/providers/ipa/ipa_hbac_private.h new file mode 100644 index 000000000..7289a0422 --- /dev/null +++ b/src/providers/ipa/ipa_hbac_private.h @@ -0,0 +1,194 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2011 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef IPA_HBAC_PRIVATE_H_ +#define IPA_HBAC_PRIVATE_H_ + +#include "providers/ipa/ipa_access.h" +#include "providers/ipa/ipa_hbac.h" + +#define IPA_HBAC_RULE "ipaHBACRule" + +#define IPA_HOST "ipaHost" +#define IPA_HOSTGROUP "ipaHostGroup" + +#define IPA_HBAC_SERVICE "ipaHBACService" +#define IPA_HBAC_SERVICE_GROUP "ipaHBACServiceGroup" + +#define IPA_HOST_SERVERHOSTNAME "serverHostName" +#define IPA_HOST_FQDN "fqdn" +#define IPA_UNIQUE_ID "ipauniqueid" + +#define IPA_MEMBER "member" +#define SYSDB_ORIG_MEMBER "orig_member" +#define HBAC_HOSTS_SUBDIR "hbac_hosts" +#define HBAC_HOSTGROUPS_SUBDIR "hbac_hostgroups" + +#define OBJECTCLASS "objectclass" +#define IPA_MEMBEROF "memberOf" +#define IPA_ACCESS_RULE_TYPE "accessRuleType" +#define IPA_HBAC_ALLOW "allow" +#define IPA_MEMBER_USER "memberUser" +#define IPA_USER_CATEGORY "userCategory" +#define IPA_SERVICE_NAME "serviceName" +#define IPA_SOURCE_HOST "sourceHost" +#define IPA_SOURCE_HOST_CATEGORY "sourceHostCategory" +#define IPA_EXTERNAL_HOST "externalHost" +#define IPA_ENABLED_FLAG "ipaenabledflag" +#define IPA_MEMBER_HOST "memberHost" +#define IPA_HOST_CATEGORY "hostCategory" +#define IPA_CN "cn" +#define IPA_MEMBER_SERVICE "memberService" +#define IPA_SERVICE_CATEGORY "serviceCategory" +#define IPA_TRUE_VALUE "TRUE" + +#define IPA_HOST_BASE_TMPL "cn=computers,cn=accounts,%s" +#define IPA_HBAC_BASE_TMPL "cn=hbac,%s" +#define IPA_SERVICES_BASE_TMPL "cn=hbacservices,cn=accounts,%s" + +#define SYSDB_HBAC_BASE_TMPL "cn=hbac,"SYSDB_TMPL_CUSTOM_BASE + +#define HBAC_RULES_SUBDIR "hbac_rules" +#define HBAC_SERVICES_SUBDIR "hbac_services" +#define HBAC_SERVICEGROUPS_SUBDIR "hbac_servicegroups" + +/* From ipa_hbac_common.c */ +errno_t ipa_hbac_save_list(struct sysdb_ctx *sysdb, bool delete_subdir, + const char *subdir, struct sss_domain_info *domain, + const char *naming_attribute, size_t count, + struct sysdb_attrs **list); +errno_t +ipa_hbac_sysdb_save(struct sysdb_ctx *sysdb, struct sss_domain_info *domain, + const char *primary_subdir, const char *attr_name, + size_t primary_count, struct sysdb_attrs **primary, + const char *group_subdir, const char *groupattr_name, + size_t group_count, struct sysdb_attrs **groups); + +errno_t +replace_attribute_name(const char *old_name, + const char *new_name, const size_t count, + struct sysdb_attrs **list); + +errno_t hbac_ctx_to_rules(TALLOC_CTX *mem_ctx, + struct hbac_ctx *hbac_ctx, + struct hbac_rule ***rules, + struct hbac_eval_req **request); + +errno_t +hbac_get_category(struct sysdb_attrs *attrs, + const char *category_attr, + uint32_t *_categories); + +/* From ipa_hbac_hosts.c */ +struct tevent_req * +ipa_hbac_host_info_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *dom, + struct sdap_handle *sh, + struct sdap_options *opts, + const char *search_base); + +errno_t +ipa_hbac_host_info_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *host_count, + struct sysdb_attrs ***hosts, + size_t *hostgroup_count, + struct sysdb_attrs ***hostgroups); + +errno_t +hbac_thost_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *rule_name, + struct sysdb_attrs *rule_attrs, + struct hbac_rule_element **thosts); + +errno_t +hbac_shost_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *rule_name, + struct sysdb_attrs *rule_attrs, + struct hbac_rule_element **source_hosts); + +/* From ipa_hbac_services.c */ +struct tevent_req * +ipa_hbac_service_info_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *dom, + struct sdap_handle *sh, + struct sdap_options *opts, + const char *search_base); + +errno_t +ipa_hbac_service_info_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *service_count, + struct sysdb_attrs ***services, + size_t *servicegroup_count, + struct sysdb_attrs ***servicegroups); + +errno_t +hbac_service_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *rule_name, + struct sysdb_attrs *rule_attrs, + struct hbac_rule_element **services); + +/* From ipa_hbac_rules.c */ +struct tevent_req * +ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx, + bool get_deny_rules, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *dom, + struct sdap_handle *sh, + struct sdap_options *opts, + const char *search_base, + struct sysdb_attrs *ipa_host); + +errno_t +ipa_hbac_rule_info_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *rule_count, + struct sysdb_attrs ***rules); + +/* From ipa_hbac_users.c */ +errno_t +hbac_user_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *rule_name, + struct sysdb_attrs *rule_attrs, + struct hbac_rule_element **users); + +errno_t +get_ipa_groupname(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *group_dn, + const char **groupname); + +#endif /* IPA_HBAC_PRIVATE_H_ */ diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c new file mode 100644 index 000000000..43e1e4263 --- /dev/null +++ b/src/providers/ipa/ipa_hbac_rules.c @@ -0,0 +1,231 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2011 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "providers/ipa/ipa_hbac_private.h" +#include "providers/ldap/sdap_async.h" + +struct ipa_hbac_rule_state { + struct sdap_options *opts; + + size_t rule_count; + struct sysdb_attrs **rules; +}; + +static void +ipa_hbac_rule_info_done(struct tevent_req *subreq); + +struct tevent_req * +ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx, + bool get_deny_rules, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *dom, + struct sdap_handle *sh, + struct sdap_options *opts, + const char *search_base, + struct sysdb_attrs *ipa_host) +{ + errno_t ret; + size_t i; + struct tevent_req *req = NULL; + struct tevent_req *subreq; + struct ipa_hbac_rule_state *state; + TALLOC_CTX *tmp_ctx; + const char *host_dn; + char *host_dn_clean; + char *host_group_clean; + char *rule_filter; + const char **memberof_list; + const char *rule_attrs[] = { OBJECTCLASS, + IPA_CN, + IPA_UNIQUE_ID, + IPA_ENABLED_FLAG, + IPA_ACCESS_RULE_TYPE, + IPA_MEMBER_USER, + IPA_USER_CATEGORY, + IPA_MEMBER_SERVICE, + IPA_SERVICE_CATEGORY, + IPA_SOURCE_HOST, + IPA_SOURCE_HOST_CATEGORY, + IPA_EXTERNAL_HOST, + IPA_MEMBER_HOST, + IPA_HOST_CATEGORY, + NULL }; + + if (ipa_host == NULL) { + DEBUG(1, ("Missing host\n")); + return NULL; + } + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) return NULL; + + ret = sysdb_attrs_get_string(ipa_host, SYSDB_ORIG_DN, &host_dn); + if (ret != EOK) { + DEBUG(1, ("Could not identify IPA hostname\n")); + goto error; + } + + ret = sss_filter_sanitize(tmp_ctx, host_dn, &host_dn_clean); + if (ret != EOK) goto error; + + req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_rule_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->opts = opts; + + if (get_deny_rules) { + rule_filter = talloc_asprintf(tmp_ctx, + "(&(objectclass=%s)" + "(%s=%s)(|(%s=%s)(%s=%s)", + IPA_HBAC_RULE, + IPA_ENABLED_FLAG, IPA_TRUE_VALUE, + IPA_HOST_CATEGORY, "all", + IPA_MEMBER_HOST, host_dn_clean); + } else { + rule_filter = talloc_asprintf(tmp_ctx, + "(&(objectclass=%s)" + "(%s=%s)(%s=%s)" + "(|(%s=%s)(%s=%s)", + IPA_HBAC_RULE, + IPA_ENABLED_FLAG, IPA_TRUE_VALUE, + IPA_ACCESS_RULE_TYPE, IPA_HBAC_ALLOW, + IPA_HOST_CATEGORY, "all", + IPA_MEMBER_HOST, host_dn_clean); + } + if (rule_filter == NULL) { + ret = ENOMEM; + goto immediate; + } + + /* Add all parent groups of ipa_hostname to the filter */ + ret = sysdb_attrs_get_string_array(ipa_host, SYSDB_ORIG_MEMBEROF, + tmp_ctx, &memberof_list); + if (ret != EOK && ret != ENOENT) { + DEBUG(1, ("Could not identify ")) + } if (ret == ENOENT) { + /* This host is not a member of any hostgroups */ + memberof_list = talloc_array(tmp_ctx, const char *, 1); + if (memberof_list == NULL) { + ret = ENOMEM; + goto immediate; + } + memberof_list[0] = NULL; + } + + for (i = 0; memberof_list[i]; i++) { + ret = sss_filter_sanitize(tmp_ctx, + memberof_list[i], + &host_group_clean); + if (ret != EOK) goto immediate; + + rule_filter = talloc_asprintf_append(rule_filter, "(%s=%s)", + IPA_MEMBER_HOST, + host_group_clean); + if (rule_filter == NULL) { + ret = ENOMEM; + goto immediate; + } + } + + rule_filter = talloc_asprintf_append(rule_filter, "))"); + if (rule_filter == NULL) { + ret = ENOMEM; + goto immediate; + } + talloc_steal(state, rule_filter); + + subreq = sdap_get_generic_send(state, ev, opts, sh, search_base, + LDAP_SCOPE_SUB, rule_filter, rule_attrs, + NULL, 0, + dp_opt_get_int(state->opts->basic, + SDAP_ENUM_SEARCH_TIMEOUT)); + if (subreq == NULL) { + DEBUG(1, ("sdap_get_generic_send failed.\n")); + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, ipa_hbac_rule_info_done, req); + + talloc_free(tmp_ctx); + return req; + +immediate: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + talloc_free(tmp_ctx); + return req; + +error: + talloc_free(tmp_ctx); + return NULL; +} + +static void +ipa_hbac_rule_info_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct ipa_hbac_rule_state *state = + tevent_req_data(req, struct ipa_hbac_rule_state); + + ret = sdap_get_generic_recv(subreq, state, + &state->rule_count, + &state->rules); + if (ret != EOK) { + DEBUG(3, ("Could not retrieve HBAC rules\n")); + tevent_req_error(req, ret); + return; + } else if (state->rule_count == 0) { + DEBUG(3, ("No rules apply to this host\n")); + tevent_req_error(req, ENOENT); + return; + } + + tevent_req_done(req); +} + +errno_t +ipa_hbac_rule_info_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *rule_count, + struct sysdb_attrs ***rules) +{ + struct ipa_hbac_rule_state *state = + tevent_req_data(req, struct ipa_hbac_rule_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *rule_count = state->rule_count; + *rules = talloc_steal(mem_ctx, state->rules); + + return EOK; +} diff --git a/src/providers/ipa/ipa_hbac_services.c b/src/providers/ipa/ipa_hbac_services.c new file mode 100644 index 000000000..df276b86c --- /dev/null +++ b/src/providers/ipa/ipa_hbac_services.c @@ -0,0 +1,451 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2011 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "providers/ipa/ipa_hbac_private.h" +#include "providers/ldap/sdap_async.h" + +struct ipa_hbac_service_state { + struct tevent_context *ev; + struct sysdb_ctx *sysdb; + struct sss_domain_info *dom; + struct sdap_handle *sh; + struct sdap_options *opts; + const char *search_base; + const char **attrs; + + /* Return values */ + size_t service_count; + struct sysdb_attrs **services; + + size_t servicegroup_count; + struct sysdb_attrs **servicegroups; +}; + +static void +ipa_hbac_service_info_done(struct tevent_req *subreq); +static void +ipa_hbac_servicegroup_info_done(struct tevent_req *subreq); + +struct tevent_req * +ipa_hbac_service_info_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct sysdb_ctx *sysdb, + struct sss_domain_info *dom, + struct sdap_handle *sh, + struct sdap_options *opts, + const char *search_base) +{ + errno_t ret; + struct ipa_hbac_service_state *state; + struct tevent_req *req; + struct tevent_req *subreq; + char *service_filter; + + req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_service_state); + if (req == NULL) { + DEBUG(1, ("tevent_req_create failed.\n")); + return NULL; + } + + state->ev = ev; + state->sysdb = sysdb; + state->dom = dom; + state->sh = sh; + state->opts = opts; + state->search_base = search_base; + + service_filter = talloc_asprintf(state, "(objectClass=%s)", + IPA_HBAC_SERVICE); + if (service_filter == NULL) { + ret = ENOMEM; + goto immediate; + } + + state->attrs = talloc_array(state, const char *, 6); + if (state->attrs == NULL) { + DEBUG(1, ("Failed to allocate service attribute list.\n")); + ret = ENOMEM; + goto immediate; + } + state->attrs[0] = OBJECTCLASS; + state->attrs[1] = IPA_CN; + state->attrs[2] = IPA_UNIQUE_ID; + state->attrs[3] = IPA_MEMBER; + state->attrs[4] = IPA_MEMBEROF; + state->attrs[5] = NULL; + + subreq = sdap_get_generic_send(state, ev, opts, sh, search_base, + LDAP_SCOPE_SUB, service_filter, + state->attrs, NULL, 0, + dp_opt_get_int(opts->basic, + SDAP_ENUM_SEARCH_TIMEOUT)); + if (subreq == NULL) { + DEBUG(1, ("Error requesting service info\n")); + ret = EIO; + goto immediate; + } + tevent_req_set_callback(subreq, ipa_hbac_service_info_done, req); + + return req; + +immediate: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + return req; +} + +static void +ipa_hbac_service_info_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct ipa_hbac_service_state *state = + tevent_req_data(req, struct ipa_hbac_service_state); + char *servicegroup_filter; + + ret = sdap_get_generic_recv(subreq, state, + &state->service_count, + &state->services); + talloc_zfree(subreq); + if (ret != EOK && ret != ENOENT) { + goto done; + } + + if (ret == ENOENT || state->service_count == 0) { + /* If there are no services, we'll shortcut out + * This is still valid, as rules can apply to + * all services + * + * There's no reason to try to process groups + */ + + state->service_count = 0; + state->services = NULL; + ret = EOK; + goto done; + } + + ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF, + state->service_count, + state->services); + if (ret != EOK) { + DEBUG(1, ("Could not replace attribute names\n")); + goto done; + } + + servicegroup_filter = talloc_asprintf(state, "(objectClass=%s)", + IPA_HBAC_SERVICE_GROUP); + if (servicegroup_filter == NULL) { + ret = ENOMEM; + goto done; + } + + /* Look up service groups */ + subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, + state->search_base, LDAP_SCOPE_SUB, + servicegroup_filter, state->attrs, NULL, 0, + dp_opt_get_int(state->opts->basic, + SDAP_ENUM_SEARCH_TIMEOUT)); + if (subreq == NULL) { + DEBUG(1, ("Error requesting host info\n")); + ret = EIO; + goto done; + } + tevent_req_set_callback(subreq, ipa_hbac_servicegroup_info_done, req); + + return; + +done: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } +} + +static void +ipa_hbac_servicegroup_info_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct ipa_hbac_service_state *state = + tevent_req_data(req, struct ipa_hbac_service_state); + + ret = sdap_get_generic_recv(subreq, state, + &state->servicegroup_count, + &state->servicegroups); + talloc_zfree(subreq); + if (ret != EOK) { + goto done; + } + + ret = replace_attribute_name(IPA_MEMBER, SYSDB_ORIG_MEMBER, + state->servicegroup_count, + state->servicegroups); + if (ret != EOK) { + DEBUG(1, ("Could not replace attribute names\n")); + goto done; + } + + ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF, + state->servicegroup_count, + state->servicegroups); + if (ret != EOK) { + DEBUG(1, ("Could not replace attribute names\n")); + goto done; + } + +done: + if (ret == EOK) { + tevent_req_done(req); + } else { + DEBUG(3, ("Error [%d][%s]\n", ret, strerror(ret))); + tevent_req_error(req, ret); + } +} + +errno_t +ipa_hbac_service_info_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *service_count, + struct sysdb_attrs ***services, + size_t *servicegroup_count, + struct sysdb_attrs ***servicegroups) +{ + size_t c; + struct ipa_hbac_service_state *state = + tevent_req_data(req, struct ipa_hbac_service_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + *service_count = state->service_count; + *services = talloc_steal(mem_ctx, state->services); + for (c = 0; c < state->service_count; c++) { + /* Guarantee the memory heirarchy of the list */ + talloc_steal(state->services, state->services[c]); + } + + *servicegroup_count = state->servicegroup_count; + *servicegroups = talloc_steal(mem_ctx, state->servicegroups); + + return EOK; +} + +errno_t +hbac_service_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *rule_name, + struct sysdb_attrs *rule_attrs, + struct hbac_rule_element **services) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx; + struct hbac_rule_element *new_services; + const char *attrs[] = { IPA_CN, NULL }; + struct ldb_message_element *el; + size_t num_services = 0; + size_t num_servicegroups = 0; + size_t i; + char *member_dn; + char *filter; + size_t count; + struct ldb_message **msgs; + const char *name; + + DEBUG(7, ("Processing PAM services for rule [%s]\n", rule_name)); + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) return ENOMEM; + + new_services = talloc_zero(tmp_ctx, struct hbac_rule_element); + if (new_services == NULL) { + ret = ENOMEM; + goto done; + } + + /* First check for service category */ + ret = hbac_get_category(rule_attrs, IPA_SERVICE_CATEGORY, + &new_services->category); + if (ret != EOK) { + DEBUG(1, ("Could not identify service categories\n")); + goto done; + } + if (new_services->category & HBAC_CATEGORY_ALL) { + /* Short-cut to the exit */ + ret = EOK; + goto done; + } + + /* Get the list of DNs from the member attr */ + ret = sysdb_attrs_get_el(rule_attrs, IPA_MEMBER_SERVICE, &el); + if (ret != EOK && ret != ENOENT) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto done; + } + if (ret == ENOENT || el->num_values == 0) { + el->num_values = 0; + DEBUG(4, ("No services specified, rule will never apply.\n")); + } + + /* Assume maximum size; We'll trim it later */ + new_services->names = talloc_array(new_services, + const char *, + el->num_values +1); + if (new_services->names == NULL) { + ret = ENOMEM; + goto done; + } + + new_services->groups = talloc_array(new_services, + const char *, + el->num_values + 1); + if (new_services->groups == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < el->num_values; i++) { + ret = sss_filter_sanitize(tmp_ctx, + (const char *)el->values[i].data, + &member_dn); + if (ret != EOK) goto done; + + filter = talloc_asprintf(member_dn, "(%s=%s)", + SYSDB_ORIG_DN, member_dn); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + + /* First check if this is a specific service */ + ret = sysdb_search_custom(tmp_ctx, sysdb, domain, filter, + HBAC_SERVICES_SUBDIR, attrs, + &count, &msgs); + if (ret != EOK && ret != ENOENT) goto done; + if (ret == EOK && count == 0) { + ret = ENOENT; + } + + if (ret == EOK) { + if (count > 1) { + DEBUG(1, ("Original DN matched multiple services. " + "Skipping \n")); + talloc_zfree(member_dn); + continue; + } + + /* Original DN matched a single service. Get the service name */ + name = ldb_msg_find_attr_as_string(msgs[0], IPA_CN, NULL); + if (name == NULL) { + DEBUG(1, ("Attribute is missing!\n")); + ret = EFAULT; + goto done; + } + + new_services->names[num_services] = + talloc_strdup(new_services->names, name); + if (new_services->names[num_services] == NULL) { + ret = ENOMEM; + goto done; + } + DEBUG(8, ("Added service [%s] to rule [%s]\n", + name, rule_name)); + num_services++; + } else { /* ret == ENOENT */ + /* Check if this is a service group */ + ret = sysdb_search_custom(tmp_ctx, sysdb, domain, filter, + HBAC_SERVICEGROUPS_SUBDIR, attrs, + &count, &msgs); + if (ret != EOK && ret != ENOENT) goto done; + if (ret == EOK && count == 0) { + ret = ENOENT; + } + + if (ret == EOK) { + if (count > 1) { + DEBUG(1, ("Original DN matched multiple service groups. " + "Skipping\n")); + talloc_zfree(member_dn); + continue; + } + + /* Original DN matched a single group. Get the groupname */ + name = ldb_msg_find_attr_as_string(msgs[0], IPA_CN, NULL); + if (name == NULL) { + DEBUG(1, ("Attribute is missing!\n")); + ret = EFAULT; + goto done; + } + + new_services->groups[num_servicegroups] = + talloc_strdup(new_services->groups, name); + if (new_services->groups[num_servicegroups] == NULL) { + ret = ENOMEM; + goto done; + } + + DEBUG(8, ("Added service group [%s] to rule [%s]\n", + name, rule_name)); + num_servicegroups++; + } else { /* ret == ENOENT */ + /* Neither a service nor a service group? Skip it */ + DEBUG(1, ("[%s] does not map to either a service or " + "service group. Skipping\n", member_dn)); + } + } + talloc_zfree(member_dn); + } + new_services->names[num_services] = NULL; + new_services->groups[num_servicegroups] = NULL; + + /* Shrink the arrays down to their real sizes */ + new_services->names = talloc_realloc(new_services, new_services->names, + const char *, num_services + 1); + if (new_services->names == NULL) { + ret = ENOMEM; + goto done; + } + + new_services->groups = talloc_realloc(new_services, new_services->groups, + const char *, num_servicegroups + 1); + if (new_services->groups == NULL) { + ret = ENOMEM; + goto done; + } + + ret = EOK; + +done: + if (ret == EOK) { + *services = talloc_steal(mem_ctx, new_services); + } + talloc_free(tmp_ctx); + return ret; +} diff --git a/src/providers/ipa/ipa_hbac_users.c b/src/providers/ipa/ipa_hbac_users.c new file mode 100644 index 000000000..9b7cadb2e --- /dev/null +++ b/src/providers/ipa/ipa_hbac_users.c @@ -0,0 +1,345 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2011 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "util/util.h" +#include "providers/ipa/ipa_hbac_private.h" +#include "providers/ldap/sdap_async.h" + +struct hbac_update_groups_state { + struct hbac_ctx *hbac_ctx; + struct sysdb_ctx *sysdb; + struct sss_domain_info *domain; +}; + + +/* Returns EOK and populates groupname if + * the group_dn is actually a group. + * Returns ENOENT if group_dn does not point + * at a a group. + * Returns EINVAL if there is a parsing error. + * Returns ENOMEM as appropriate + */ +errno_t +get_ipa_groupname(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *group_dn, + const char **groupname) +{ + errno_t ret; + struct ldb_dn *dn; + const char *rdn_name; + const char *group_comp_name; + const char *account_comp_name; + const struct ldb_val *rdn_val; + const struct ldb_val *group_comp_val; + const struct ldb_val *account_comp_val; + + /* This is an IPA-specific hack. It may not + * work for non-IPA servers and will need to + * be changed if SSSD ever supports HBAC on + * a non-IPA server. + */ + *groupname = NULL; + + dn = ldb_dn_new(mem_ctx, sysdb_ctx_get_ldb(sysdb), group_dn); + if (dn == NULL) { + ret = ENOMEM; + goto done; + } + + if (!ldb_dn_validate(dn)) { + ret = EINVAL; + goto done; + } + + if (ldb_dn_get_comp_num(dn) < 4) { + /* RDN, groups, accounts, and at least one DC= */ + ret = EINVAL; + goto done; + } + + /* If the RDN name is 'cn' */ + rdn_name = ldb_dn_get_rdn_name(dn); + if (rdn_name == NULL) { + /* Shouldn't happen if ldb_dn_validate() + * passed, but we'll be careful. + */ + ret = EINVAL; + goto done; + } + + if (strcasecmp("cn", rdn_name) != 0) { + /* RDN has the wrong attribute name. + * It's not a group. + */ + ret = ENOENT; + goto done; + } + + /* and the second component is "cn=groups" */ + group_comp_name = ldb_dn_get_component_name(dn, 1); + if (strcasecmp("cn", group_comp_name) != 0) { + /* The second component name is not "cn" */ + ret = ENOENT; + goto done; + } + + group_comp_val = ldb_dn_get_component_val(dn, 1); + if (strncasecmp("groups", + (const char *) group_comp_val->data, + group_comp_val->length) != 0) { + /* The second component value is not "groups" */ + ret = ENOENT; + goto done; + } + + /* and the third component is "accounts" */ + account_comp_name = ldb_dn_get_component_name(dn, 2); + if (strcasecmp("cn", account_comp_name) != 0) { + /* The third component name is not "cn" */ + ret = ENOENT; + goto done; + } + + account_comp_val = ldb_dn_get_component_val(dn, 2); + if (strncasecmp("accounts", + (const char *) account_comp_val->data, + account_comp_val->length) != 0) { + /* The third component value is not "accounts" */ + ret = ENOENT; + goto done; + } + + /* Then the value of the RDN is the group name */ + rdn_val = ldb_dn_get_rdn_val(dn); + *groupname = talloc_strndup(mem_ctx, + (const char *)rdn_val->data, + rdn_val->length); + if (*groupname == NULL) { + ret = ENOMEM; + goto done; + } + + ret = EOK; + +done: + talloc_free(dn); + return ret; +} + +errno_t +hbac_user_attrs_to_rule(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + struct sss_domain_info *domain, + const char *rule_name, + struct sysdb_attrs *rule_attrs, + struct hbac_rule_element **users) +{ + errno_t ret; + TALLOC_CTX *tmp_ctx = NULL; + struct hbac_rule_element *new_users = NULL; + struct ldb_message_element *el = NULL; + struct ldb_message **msgs = NULL; + char *filter; + char *member_dn; + const char *member_user; + const char *attrs[] = { SYSDB_NAME, NULL }; + size_t num_users = 0; + size_t num_groups = 0; + const char *name; + + size_t count; + size_t i; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) return ENOMEM; + + new_users = talloc_zero(tmp_ctx, struct hbac_rule_element); + if (new_users == NULL) { + ret = ENOMEM; + goto done; + } + + DEBUG(7, ("Processing users for rule [%s]\n", rule_name)); + + ret = hbac_get_category(rule_attrs, IPA_USER_CATEGORY, + &new_users->category); + if (ret != EOK) { + DEBUG(1, ("Could not identify user categories\n")); + goto done; + } + if (new_users->category & HBAC_CATEGORY_ALL) { + /* Short-cut to the exit */ + ret = EOK; + goto done; + } + + ret = sysdb_attrs_get_el(rule_attrs, IPA_MEMBER_USER, &el); + if (ret != EOK && ret != ENOENT) { + DEBUG(1, ("sysdb_attrs_get_el failed.\n")); + goto done; + } + if (ret == ENOENT || el->num_values == 0) { + el->num_values = 0; + DEBUG(4, ("No user specified, rule will never apply.\n")); + } + + new_users->names = talloc_array(new_users, + const char *, + el->num_values + 1); + if (new_users->names == NULL) { + ret = ENOMEM; + goto done; + } + + new_users->groups = talloc_array(new_users, + const char *, + el->num_values + 1); + if (new_users->groups == NULL) { + ret = ENOMEM; + goto done; + } + + for (i = 0; i < el->num_values; i++) { + member_user = (const char *)el->values[i].data; + ret = sss_filter_sanitize(tmp_ctx, member_user, &member_dn); + if (ret != EOK) goto done; + + filter = talloc_asprintf(member_dn, "(%s=%s)", + SYSDB_ORIG_DN, member_dn); + if (filter == NULL) { + ret = ENOMEM; + goto done; + } + + /* First check if this is a user */ + ret = sysdb_search_users(tmp_ctx, sysdb, domain, + filter, attrs, &count, &msgs); + if (ret != EOK && ret != ENOENT) goto done; + if (ret == EOK && count == 0) { + ret = ENOENT; + } + + if (ret == EOK) { + if (count > 1) { + DEBUG(1, ("Original DN matched multiple users. Skipping \n")); + talloc_zfree(member_dn); + continue; + } + + /* Original DN matched a single user. Get the username */ + name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL); + if (name == NULL) { + DEBUG(1, ("Attribute is missing!\n")); + ret = EFAULT; + goto done; + } + + new_users->names[num_users] = talloc_strdup(new_users->names, + name); + if (new_users->names[num_users] == NULL) { + ret = ENOMEM; + goto done; + } + DEBUG(8, ("Added user [%s] to rule [%s]\n", + name, rule_name)); + num_users++; + } else { + /* Check if it is a group instead */ + ret = sysdb_search_groups(tmp_ctx, sysdb, domain, + filter, attrs, &count, &msgs); + if (ret != EOK && ret != ENOENT) goto done; + if (ret == EOK && count == 0) { + ret = ENOENT; + } + + if (ret == EOK) { + if (count > 1) { + DEBUG(1, ("Original DN matched multiple groups. " + "Skipping\n")); + talloc_zfree(member_dn); + continue; + } + + /* Original DN matched a single group. Get the groupname */ + name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL); + if (name == NULL) { + DEBUG(1, ("Attribute is missing!\n")); + ret = EFAULT; + goto done; + } + + new_users->groups[num_groups] = + talloc_strdup(new_users->groups, name); + if (new_users->groups[num_groups] == NULL) { + ret = ENOMEM; + goto done; + } + DEBUG(8, ("Added POSIX group [%s] to rule [%s]\n", + name, rule_name)); + num_groups++; + } else { + /* If the group still matches the group pattern, + * we can assume it is a non-POSIX group. + */ + ret = get_ipa_groupname(new_users->groups, sysdb, member_user, + &new_users->groups[num_groups]); + if (ret == EOK) { + DEBUG(8, ("Added non-POSIX group [%s] to rule [%s]\n", + new_users->groups[num_groups], rule_name)); + num_groups++; + } else { + /* Not a group, so we don't care about it */ + DEBUG(1, ("[%s] does not map to either a user or group. " + "Skipping\n", member_dn)); + } + } + } + talloc_zfree(member_dn); + } + new_users->names[num_users] = NULL; + new_users->groups[num_groups] = NULL; + + /* Shrink the arrays down to their real sizes */ + new_users->names = talloc_realloc(new_users, new_users->names, + const char *, num_users + 1); + if (new_users->names == NULL) { + ret = ENOMEM; + goto done; + } + + new_users->groups = talloc_realloc(new_users, new_users->groups, + const char *, num_groups + 1); + if (new_users->groups == NULL) { + ret = ENOMEM; + goto done; + } + + ret = EOK; +done: + if (ret == EOK) { + *users = talloc_steal(mem_ctx, new_users); + } + talloc_free(tmp_ctx); + + return ret; +} -- cgit