diff --git a/include/crm/msg_xml.h b/include/crm/msg_xml.h index f762815204..46e01455e6 100644 --- a/include/crm/msg_xml.h +++ b/include/crm/msg_xml.h @@ -1,297 +1,297 @@ /* * Copyright (C) 2004 Andrew Beekhof * * 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 2 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef XML_TAGS__H #define XML_TAGS__H #define CIB_OPTIONS_FIRST "cib-bootstrap-options" #define F_CRM_DATA "crm_xml" #define F_CRM_TASK "crm_task" #define F_CRM_HOST_TO "crm_host_to" #define F_CRM_MSG_TYPE F_SUBTYPE #define F_CRM_SYS_TO "crm_sys_to" #define F_CRM_SYS_FROM "crm_sys_from" #define F_CRM_HOST_FROM F_ORIG #define F_CRM_REFERENCE XML_ATTR_REFERENCE #define F_CRM_VERSION XML_ATTR_VERSION #define F_CRM_ORIGIN "origin" #define F_CRM_JOIN_ID "join_id" #define F_CRM_ELECTION_ID "election-id" #define F_CRM_ELECTION_AGE_S "election-age-sec" #define F_CRM_ELECTION_AGE_US "election-age-nano-sec" #define F_CRM_ELECTION_OWNER "election-owner" #define F_CRM_TGRAPH "crm-tgraph" #define F_CRM_TGRAPH_INPUT "crm-tgraph-in" /*---- Common tags/attrs */ #define XML_DIFF_MARKER "__crm_diff_marker__" #define XML_ATTR_TAGNAME F_XML_TAGNAME #define XML_TAG_CIB "cib" #define XML_TAG_FAILED "failed" #define XML_ATTR_CRM_VERSION "crm_feature_set" #define XML_ATTR_DIGEST "digest" #define XML_ATTR_VALIDATION "validate-with" #define XML_ATTR_QUORUM_PANIC "no-quorum-panic" #define XML_ATTR_HAVE_QUORUM "have-quorum" #define XML_ATTR_EXPECTED_VOTES "expected-quorum-votes" #define XML_ATTR_GENERATION "epoch" #define XML_ATTR_GENERATION_ADMIN "admin_epoch" #define XML_ATTR_NUMUPDATES "num_updates" #define XML_ATTR_TIMEOUT "timeout" #define XML_ATTR_ORIGIN "crm-debug-origin" #define XML_ATTR_TSTAMP "crm-timestamp" #define XML_CIB_ATTR_WRITTEN "cib-last-written" #define XML_ATTR_VERSION "version" #define XML_ATTR_DESC "description" #define XML_ATTR_ID "id" #define XML_ATTR_IDREF "id-ref" #define XML_ATTR_ID_LONG "long-id" #define XML_ATTR_TYPE "type" #define XML_ATTR_FILTER_TYPE "type-filter" #define XML_ATTR_FILTER_ID "id-filter" #define XML_ATTR_FILTER_PRIORITY "priority-filter" #define XML_ATTR_VERBOSE "verbose" #define XML_ATTR_OP "op" #define XML_ATTR_DC "is_dc" #define XML_ATTR_DC_UUID "dc-uuid" #define XML_BOOLEAN_TRUE "true" #define XML_BOOLEAN_FALSE "false" #define XML_BOOLEAN_YES XML_BOOLEAN_TRUE #define XML_BOOLEAN_NO XML_BOOLEAN_FALSE #define XML_TAG_OPTIONS "options" /*---- top level tags/attrs */ #define XML_MSG_TAG "crm_message" #define XML_MSG_TAG_DATA "msg_data" #define XML_ATTR_REQUEST "request" #define XML_ATTR_RESPONSE "response" #define XML_ATTR_UNAME "uname" #define XML_ATTR_UUID "id" #define XML_ATTR_REFERENCE "reference" #define XML_FAIL_TAG_RESOURCE "failed_resource" #define XML_FAILRES_ATTR_RESID "resource_id" #define XML_FAILRES_ATTR_REASON "reason" #define XML_FAILRES_ATTR_RESSTATUS "resource_status" #define XML_CRM_TAG_PING "ping_response" #define XML_PING_ATTR_STATUS "result" #define XML_PING_ATTR_SYSFROM "crm_subsystem" #define XML_TAG_FRAGMENT "cib_fragment" #define XML_ATTR_RESULT "result" #define XML_ATTR_SECTION "section" #define XML_FAIL_TAG_CIB "failed_update" #define XML_FAILCIB_ATTR_ID "id" #define XML_FAILCIB_ATTR_OBJTYPE "object_type" #define XML_FAILCIB_ATTR_OP "operation" #define XML_FAILCIB_ATTR_REASON "reason" /*---- CIB specific tags/attrs */ #define XML_CIB_TAG_SECTION_ALL "all" #define XML_CIB_TAG_CONFIGURATION "configuration" #define XML_CIB_TAG_STATUS "status" #define XML_CIB_TAG_RESOURCES "resources" #define XML_CIB_TAG_NODES "nodes" #define XML_CIB_TAG_DOMAINS "domains" #define XML_CIB_TAG_CONSTRAINTS "constraints" #define XML_CIB_TAG_CRMCONFIG "crm_config" #define XML_CIB_TAG_OPCONFIG "op_defaults" #define XML_CIB_TAG_RSCCONFIG "rsc_defaults" #define XML_CIB_TAG_ACLS "acls" #define XML_CIB_TAG_STATE "node_state" #define XML_CIB_TAG_NODE "node" #define XML_CIB_TAG_DOMAIN "domain" #define XML_CIB_TAG_CONSTRAINT "constraint" #define XML_CIB_TAG_NVPAIR "nvpair" #define XML_CIB_TAG_PROPSET "cluster_property_set" #define XML_TAG_ATTR_SETS "instance_attributes" #define XML_TAG_META_SETS "meta_attributes" #define XML_TAG_ATTRS "attributes" #define XML_TAG_PARAMS "parameters" #define XML_TAG_PARAM "param" #define XML_TAG_UTILIZATION "utilization" #define XML_TAG_RESOURCE_REF "resource_ref" #define XML_CIB_TAG_RESOURCE "primitive" #define XML_CIB_TAG_GROUP "group" #define XML_CIB_TAG_INCARNATION "clone" #define XML_CIB_TAG_MASTER "master" #define XML_RSC_ATTR_RESTART "restart-type" #define XML_RSC_ATTR_ORDERED "ordered" #define XML_RSC_ATTR_INTERLEAVE "interleave" #define XML_RSC_ATTR_INCARNATION "clone" #define XML_RSC_ATTR_INCARNATION_MAX "clone-max" #define XML_RSC_ATTR_INCARNATION_NODEMAX "clone-node-max" #define XML_RSC_ATTR_MASTER_MAX "master-max" #define XML_RSC_ATTR_MASTER_NODEMAX "master-node-max" #define XML_RSC_ATTR_STATE "clone-state" #define XML_RSC_ATTR_MANAGED "is-managed" #define XML_RSC_ATTR_TARGET_ROLE "target-role" #define XML_RSC_ATTR_UNIQUE "globally-unique" #define XML_RSC_ATTR_NOTIFY "notify" #define XML_RSC_ATTR_STICKINESS "resource-stickiness" #define XML_RSC_ATTR_FAIL_STICKINESS "migration-threshold" #define XML_RSC_ATTR_FAIL_TIMEOUT "failure-timeout" #define XML_RSC_ATTR_MULTIPLE "multiple-active" #define XML_RSC_ATTR_PRIORITY "priority" #define XML_OP_ATTR_ON_FAIL "on-fail" #define XML_OP_ATTR_START_DELAY "start-delay" #define XML_OP_ATTR_ALLOW_MIGRATE "allow-migrate" #define XML_OP_ATTR_ORIGIN "interval-origin" #define XML_OP_ATTR_PENDING "record-pending" #define XML_CIB_TAG_LRM "lrm" #define XML_LRM_TAG_RESOURCES "lrm_resources" #define XML_LRM_TAG_RESOURCE "lrm_resource" #define XML_LRM_TAG_AGENTS "lrm_agents" #define XML_LRM_TAG_AGENT "lrm_agent" #define XML_LRM_TAG_RSC_OP "lrm_rsc_op" #define XML_AGENT_ATTR_CLASS "class" #define XML_AGENT_ATTR_PROVIDER "provider" #define XML_LRM_TAG_ATTRIBUTES "attributes" #define XML_CIB_ATTR_REPLACE "replace" #define XML_CIB_ATTR_SOURCE "source" #define XML_CIB_ATTR_HEALTH "health" #define XML_CIB_ATTR_WEIGHT "weight" #define XML_CIB_ATTR_PRIORITY "priority" #define XML_CIB_ATTR_CLEAR "clear_on" #define XML_CIB_ATTR_SOURCE "source" #define XML_CIB_ATTR_JOINSTATE "join" #define XML_CIB_ATTR_EXPSTATE "expected" #define XML_CIB_ATTR_INCCM "in_ccm" #define XML_CIB_ATTR_CRMDSTATE "crmd" #define XML_CIB_ATTR_HASTATE "ha" #define XML_CIB_ATTR_SHUTDOWN "shutdown" #define XML_CIB_ATTR_STONITH "stonith" #define XML_LRM_ATTR_INTERVAL "interval" #define XML_LRM_ATTR_TASK "operation" #define XML_LRM_ATTR_TASK_KEY "operation_key" #define XML_LRM_ATTR_TARGET "on_node" #define XML_LRM_ATTR_TARGET_UUID "on_node_uuid" #define XML_LRM_ATTR_RSCID "rsc-id" #define XML_LRM_ATTR_OPSTATUS "op-status" #define XML_LRM_ATTR_RC "rc-code" #define XML_LRM_ATTR_CALLID "call-id" #define XML_LRM_ATTR_OP_DIGEST "op-digest" #define XML_LRM_ATTR_OP_RESTART "op-force-restart" #define XML_LRM_ATTR_RESTART_DIGEST "op-restart-digest" #define XML_LRM_ATTR_MIGRATE_SOURCE "migrate_source" #define XML_LRM_ATTR_MIGRATE_TARGET "migrate_target" #define XML_TAG_GRAPH "transition_graph" #define XML_GRAPH_TAG_RSC_OP "rsc_op" #define XML_GRAPH_TAG_PSEUDO_EVENT "pseudo_event" #define XML_GRAPH_TAG_CRM_EVENT "crm_event" #define XML_TAG_RULE "rule" #define XML_RULE_ATTR_SCORE "score" #define XML_RULE_ATTR_SCORE_ATTRIBUTE "score-attribute" #define XML_RULE_ATTR_SCORE_MANGLED "score-attribute-mangled" #define XML_RULE_ATTR_ROLE "role" #define XML_RULE_ATTR_RESULT "result" #define XML_RULE_ATTR_BOOLEAN_OP "boolean-op" #define XML_TAG_EXPRESSION "expression" #define XML_EXPR_ATTR_ATTRIBUTE "attribute" #define XML_EXPR_ATTR_OPERATION "operation" #define XML_EXPR_ATTR_VALUE "value" #define XML_EXPR_ATTR_TYPE "type" #define XML_CONS_TAG_RSC_DEPEND "rsc_colocation" #define XML_CONS_TAG_RSC_ORDER "rsc_order" #define XML_CONS_TAG_RSC_LOCATION "rsc_location" #define XML_CONS_TAG_RSC_SET "resource_set" #define XML_CONS_ATTR_SYMMETRICAL "symmetrical" #define XML_COLOC_ATTR_SOURCE "rsc" #define XML_COLOC_ATTR_SOURCE_ROLE "rsc-role" #define XML_COLOC_ATTR_TARGET "with-rsc" #define XML_COLOC_ATTR_TARGET_ROLE "with-rsc-role" #define XML_COLOC_ATTR_NODE_ATTR "node-attribute" #define XML_COLOC_ATTR_SOURCE_INSTANCE "rsc-instance" #define XML_COLOC_ATTR_TARGET_INSTANCE "with-rsc-instance" #define XML_ORDER_ATTR_FIRST "first" #define XML_ORDER_ATTR_THEN "then" #define XML_ORDER_ATTR_FIRST_ACTION "first-action" #define XML_ORDER_ATTR_THEN_ACTION "then-action" #define XML_ORDER_ATTR_FIRST_INSTANCE "first-instance" #define XML_ORDER_ATTR_THEN_INSTANCE "then-instance" #define XML_ORDER_ATTR_KIND "kind" #define XML_NVPAIR_ATTR_NAME "name" #define XML_NVPAIR_ATTR_VALUE "value" #define XML_NODE_ATTR_STATE "state" #define XML_CONFIG_ATTR_DC_DEADTIME "dc-deadtime" #define XML_CONFIG_ATTR_ELECTION_FAIL "election-timeout" #define XML_CONFIG_ATTR_FORCE_QUIT "shutdown-escalation" #define XML_CONFIG_ATTR_RECHECK "cluster-recheck-interval" #define XML_CIB_TAG_GENERATION_TUPPLE "generation_tuple" #define XML_ATTR_TRANSITION_MAGIC "transition-magic" #define XML_ATTR_TRANSITION_KEY "transition-key" #define XML_ATTR_TE_NOWAIT "op_no_wait" #define XML_ATTR_TE_TARGET_RC "op_target_rc" #define XML_ATTR_TE_ALLOWFAIL "op_allow_fail" #define XML_ATTR_LRM_PROBE "lrm-is-probe" #define XML_TAG_TRANSIENT_NODEATTRS "transient_attributes" #define XML_TAG_DIFF_ADDED "diff-added" #define XML_TAG_DIFF_REMOVED "diff-removed" #define XML_ACL_TAG_USER "acl_user" #define XML_ACL_TAG_ROLE "acl_role" -#define XML_ACL_ATTR_ROLE_REF "role-ref" +#define XML_ACL_TAG_ROLE_REF "role_ref" #define XML_ACL_TAG_READ "read" #define XML_ACL_TAG_WRITE "write" #define XML_ACL_TAG_DENY "deny" #define XML_ACL_ATTR_REF "ref" #define XML_ACL_ATTR_TAG "tag" #define XML_ACL_ATTR_XPATH "xpath" #define XML_ACL_ATTR_ATTRIBUTE "attribute" #include #define ID(x) crm_element_value(x, XML_ATTR_ID) #define INSTANCE(x) crm_element_value(x, XML_CIB_ATTR_INSTANCE) #define TSTAMP(x) crm_element_value(x, XML_ATTR_TSTAMP) #define TYPE(x) crm_element_name(x) #endif diff --git a/lib/cib/cib_acl.c b/lib/cib/cib_acl.c index 75f8805423..d2a584c15f 100644 --- a/lib/cib/cib_acl.c +++ b/lib/cib/cib_acl.c @@ -1,794 +1,793 @@ /* * Copyright (C) 2009 Yan Gao * * 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 2.1 of the License, or (at your option) any later version. * * This software 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include typedef struct acl_obj_s { const char *mode; const char *tag; const char *ref; const char *xpath; const char *attribute; } acl_obj_t; typedef struct xml_perm_s { const char *mode; GHashTable *attribute_perms; } xml_perm_t; static gboolean req_by_superuser(xmlNode *request); static xmlNode *diff_xml_object_orig(xmlNode *old, xmlNode *new, gboolean suppress, xmlNode *new_diff); static gboolean unpack_user_acl(xmlNode *xml_acls, const char *user, GListPtr *user_acl); static gboolean user_match(const char *user, const char *uid); static gboolean unpack_acl(xmlNode *xml_acls, xmlNode *xml_acl, GListPtr *acl); static gboolean unpack_role_acl(xmlNode *xml_acls, const char *role, GListPtr *acl); static gboolean acl_append(xmlNode *acl_child, GListPtr *acl); static void free_acl(GListPtr acl); static gboolean parse_acl_xpath(xmlNode *xml, GListPtr acl, GListPtr *parsed_acl); static gboolean gen_xml_perms(xmlNode *xml, GListPtr acl, GHashTable **xml_perms); static int search_xml_children(GListPtr *children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches); static int search_xpath_objects(GListPtr *objects, xmlNode *xml_obj, const char *xpath); static gboolean update_xml_perms(xmlNode *xml, acl_obj_t *acl_obj, GHashTable *xml_perms); static gboolean update_xml_children_perms(xmlNode *xml, const char *mode, GHashTable *xml_perms); static void free_xml_perm(gpointer xml_perm); static gboolean acl_filter_xml(xmlNode *xml, GHashTable *xml_perms); static gboolean acl_check_diff_xml(xmlNode *xml, GHashTable *xml_perms); gboolean acl_enabled(GHashTable *config_hash) { const char *value = NULL; gboolean rc = FALSE; value = cib_pref(config_hash, "enable-acl"); rc = crm_is_true(value); crm_debug("CIB ACL is %s", rc?"enabled":"disabled"); return rc; } /* rc = TRUE if orig_cib has been filtered*/ /* That means *filtered_cib rather than orig_cib should be exploited afterwards*/ gboolean acl_filter_cib(xmlNode *request, xmlNode *current_cib, xmlNode *orig_cib, xmlNode **filtered_cib) { const char *user = NULL; xmlNode *xml_acls = NULL; xmlNode *tmp_cib = NULL; GListPtr user_acl = NULL; GHashTable *xml_perms = NULL; *filtered_cib = NULL; if (req_by_superuser(request)) { return FALSE; } if (orig_cib == NULL) { return FALSE; } if (current_cib == NULL) { return TRUE; } xml_acls = get_object_root(XML_CIB_TAG_ACLS, current_cib); if (xml_acls == NULL) { crm_warn("Ordinary users cannot access the CIB without any defined ACLs: '%s'", user); return TRUE; } user = crm_element_value(request, F_CIB_USER); unpack_user_acl(xml_acls, user, &user_acl); tmp_cib = copy_xml(orig_cib); gen_xml_perms(tmp_cib, user_acl, &xml_perms); if (acl_filter_xml(tmp_cib, xml_perms)) { crm_warn("User '%s' doesn't have the permission for the whole CIB", user); tmp_cib = NULL; } g_hash_table_destroy(xml_perms); free_acl(user_acl); *filtered_cib = tmp_cib; return TRUE; } /* rc = TRUE if the request passes the ACL check */ /* rc = FALSE if the permission is denied */ gboolean acl_check_diff(xmlNode *request, xmlNode *current_cib, xmlNode *result_cib, xmlNode *diff) { const char *user = NULL; xmlNode *xml_acls = NULL; GListPtr user_acl = NULL; xmlNode *orig_diff = NULL; int rc = FALSE; if (req_by_superuser(request)) { return TRUE; } if (diff == NULL) { return TRUE; } if (current_cib == NULL) { return FALSE; } xml_acls = get_object_root(XML_CIB_TAG_ACLS, current_cib); if (xml_acls == NULL) { crm_warn("Ordinary users cannot access the CIB without any defined ACLs: '%s'", user); return FALSE; } user = crm_element_value(request, F_CIB_USER); unpack_user_acl(xml_acls, user, &user_acl); orig_diff = diff_xml_object_orig(current_cib, result_cib, FALSE, diff); xml_child_iter( orig_diff, diff_child, const char *tag = crm_element_name(diff_child); GListPtr parsed_acl = NULL; crm_debug("Preparing ACL checking on '%s'", tag); if (crm_str_eq(tag, XML_TAG_DIFF_REMOVED, TRUE)) { crm_debug("Parsing any xpaths under the ACL according to the current CIB"); parse_acl_xpath(current_cib, user_acl, &parsed_acl); } else if (crm_str_eq(tag, XML_TAG_DIFF_ADDED, TRUE)) { crm_debug("Parsing any xpaths under the ACL according to the result CIB"); parse_acl_xpath(result_cib, user_acl, &parsed_acl); } else { continue; } xml_child_iter( diff_child, diff_cib, GHashTable *xml_perms = NULL; gen_xml_perms(diff_cib, parsed_acl, &xml_perms); rc = acl_check_diff_xml(diff_cib, xml_perms); g_hash_table_destroy(xml_perms); if (rc == FALSE) { crm_warn("User '%s' doesn't have enough permission to modify the CIB objects", user); goto done; } ); free_acl(parsed_acl); ); done: free_xml(orig_diff); free_acl(user_acl); return rc; } static gboolean req_by_superuser(xmlNode *request) { const char *user = crm_element_value(request, F_CIB_USER); if (user == NULL) { crm_debug("Request without an explicit client user: op=%s, origin=%s, client=%s", crm_element_value(request, F_CIB_OPERATION), crm_element_value(request, F_ORIG)?crm_element_value(request, F_ORIG):"local", crm_element_value(request, F_CIB_CLIENTNAME)); return TRUE; } if (crm_str_eq(user, CRM_DAEMON_USER, TRUE) || crm_str_eq(user, "root", TRUE)) { return TRUE; } return FALSE; } /* Borrowed from lib/common/xml.c: diff_xml_object() */ /* But if a new format of diff ("new_diff") exists, we could reuse its "diff-removed" part */ /* So it would be more time-saving than generating the diff from start */ static xmlNode * diff_xml_object_orig(xmlNode *old, xmlNode *new, gboolean suppress, xmlNode *new_diff) { xmlNode *tmp1 = NULL; xmlNode *diff = create_xml_node(NULL, "diff"); xmlNode *removed = NULL; xmlNode *added = NULL; crm_xml_add(diff, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); if (new_diff && (tmp1 = find_xml_node(new_diff, "diff-removed", FALSE))) { removed = add_node_copy(diff, tmp1); } else { removed = create_xml_node(diff, "diff-removed"); tmp1 = subtract_xml_object(removed, old, new, FALSE, "removed:top"); if(suppress && tmp1 != NULL && can_prune_leaf(tmp1)) { free_xml_from_parent(removed, tmp1); } } added = create_xml_node(diff, "diff-added"); tmp1 = subtract_xml_object(added, new, old, FALSE, "added:top"); if(suppress && tmp1 != NULL && can_prune_leaf(tmp1)) { free_xml_from_parent(added, tmp1); } if(added->children == NULL && removed->children == NULL) { free_xml(diff); diff = NULL; } return diff; } static gboolean unpack_user_acl(xmlNode *xml_acls, const char *user, GListPtr *user_acl) { if (xml_acls == NULL) { return FALSE; } xml_child_iter( xml_acls, xml_acl, const char *tag = crm_element_name(xml_acl); const char *id = crm_element_value(xml_acl, XML_ATTR_ID); if (crm_str_eq(tag, XML_ACL_TAG_USER, TRUE)) { if (user_match(user, id)) { crm_debug("Unpacking ACL of user: '%s'", id); unpack_acl(xml_acls, xml_acl, user_acl); return TRUE; } } ); return FALSE; } static gboolean user_match(const char *user, const char *uid) { struct passwd *pwent = NULL; int user_uid_num = -1; int uid_num = -1; if (crm_str_eq(user, uid, TRUE)) { return TRUE; } pwent = getpwnam(user); if (pwent == NULL) { crm_warn("No user named '%s' exists!", user); return FALSE; } user_uid_num = pwent->pw_uid; uid_num = crm_int_helper(uid, NULL); if(errno == 0 && uid_num == user_uid_num) { return TRUE; } return FALSE; } static gboolean unpack_acl(xmlNode *xml_acls, xmlNode *xml_acl, GListPtr *acl) { - const char *role = crm_element_value(xml_acl, XML_ACL_ATTR_ROLE_REF); - - if (role) { - unpack_role_acl(xml_acls, role, acl); - } - xml_child_iter( xml_acl, acl_child, const char *tag = crm_element_name(acl_child); - if (crm_str_eq(XML_ACL_TAG_READ, tag, TRUE) + if (crm_str_eq(XML_ACL_TAG_ROLE_REF, tag, TRUE)) { + const char *ref_role = crm_element_value(acl_child, XML_ATTR_ID); + if (ref_role) { + unpack_role_acl(xml_acls, ref_role, acl); + } + } else if (crm_str_eq(XML_ACL_TAG_READ, tag, TRUE) || crm_str_eq(XML_ACL_TAG_WRITE, tag, TRUE) || crm_str_eq(XML_ACL_TAG_DENY, tag, TRUE)) acl_append(acl_child, acl); ); return TRUE; } static gboolean unpack_role_acl(xmlNode *xml_acls, const char *role, GListPtr *acl) { xml_child_iter_filter( xml_acls, xml_acl, XML_ACL_TAG_ROLE, const char *role_id = crm_element_value(xml_acl, XML_ATTR_ID); if (role_id && crm_str_eq(role, role_id, TRUE)) { crm_debug("Unpacking ACL of the referenced role: '%s'", role); unpack_acl(xml_acls, xml_acl, acl); return TRUE; } ); return FALSE; } static gboolean acl_append(xmlNode *acl_child, GListPtr *acl) { acl_obj_t *acl_obj = NULL; const char *tag = crm_element_value(acl_child, XML_ACL_ATTR_TAG); const char *ref = crm_element_value(acl_child, XML_ACL_ATTR_REF); const char *xpath = crm_element_value(acl_child, XML_ACL_ATTR_XPATH); if (tag == NULL && ref == NULL && xpath == NULL) { return FALSE; } crm_malloc0(acl_obj, sizeof(acl_obj_t)); if (acl_obj == NULL) { return FALSE; } acl_obj->mode = crm_element_name(acl_child); acl_obj->tag = tag; acl_obj->ref = ref; acl_obj->xpath = xpath; acl_obj->attribute = crm_element_value(acl_child, XML_ACL_ATTR_ATTRIBUTE); *acl = g_list_append(*acl, acl_obj); crm_debug_3("ACL object appended: mode=%s, tag=%s, ref=%s, xpath=%s, attribute=%s", acl_obj->mode, acl_obj->tag, acl_obj->ref, acl_obj->xpath, acl_obj->attribute); return TRUE; } static void free_acl(GListPtr acl) { GListPtr iterator = acl; while(iterator != NULL) { crm_free(iterator->data); iterator = iterator->next; } if(acl != NULL) { g_list_free(acl); } } static gboolean parse_acl_xpath(xmlNode *xml, GListPtr acl, GListPtr *parsed_acl) { GListPtr acl_iterator = acl; acl_obj_t *new_acl_obj = NULL; *parsed_acl = NULL; while (acl_iterator != NULL) { acl_obj_t *acl_obj = acl_iterator->data; if (acl_obj->tag || acl_obj->ref) { crm_malloc0(new_acl_obj, sizeof(acl_obj_t)); if (new_acl_obj == NULL) { return FALSE; } memcpy(new_acl_obj, acl_obj, sizeof(acl_obj_t)); *parsed_acl = g_list_append(*parsed_acl, new_acl_obj); crm_debug_3("Copied ACL object: mode=%s, tag=%s, ref=%s, xpath=%s, attribute=%s", new_acl_obj->mode, new_acl_obj->tag, new_acl_obj->ref, new_acl_obj->xpath, new_acl_obj->attribute); } else if (acl_obj->xpath) { GListPtr children = NULL; GListPtr children_iterator = NULL; search_xpath_objects(&children, xml, acl_obj->xpath); children_iterator = children; while (children_iterator != NULL) { crm_malloc0(new_acl_obj, sizeof(acl_obj_t)); if (new_acl_obj == NULL) { return FALSE; } new_acl_obj->mode = acl_obj->mode; new_acl_obj->tag = crm_element_name((xmlNode*)children_iterator->data); new_acl_obj->ref = crm_element_value(children_iterator->data, XML_ATTR_ID); new_acl_obj->attribute = acl_obj->attribute; *parsed_acl = g_list_append(*parsed_acl, new_acl_obj); crm_debug_3("Parsed the ACL object with xpath '%s' to: mode=%s, tag=%s, ref=%s, xpath=%s, attribute=%s", acl_obj->xpath, new_acl_obj->mode, new_acl_obj->tag, new_acl_obj->ref, new_acl_obj->xpath, new_acl_obj->attribute); children_iterator = children_iterator->next; } g_list_free(children); } acl_iterator = acl_iterator->next; } return TRUE; } static gboolean gen_xml_perms(xmlNode *xml, GListPtr acl, GHashTable **xml_perms) { GListPtr acl_iterator = acl; if (*xml_perms == NULL) { *xml_perms = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_xml_perm); } while (acl_iterator != NULL) { acl_obj_t *acl_obj = acl_iterator->data; GListPtr children = NULL; GListPtr children_iterator = NULL; crm_debug("Generating permissions with ACL: mode=%s, tag=%s, ref=%s, xpath=%s, attribute=%s", acl_obj->mode, acl_obj->tag, acl_obj->ref, acl_obj->xpath, acl_obj->attribute); if (acl_obj->tag || acl_obj->ref) { search_xml_children(&children, xml, acl_obj->tag, XML_ATTR_ID, acl_obj->ref, TRUE); } else if (acl_obj->xpath) { /* Never be here for a modification operation */ /* Already parse_acl_xpath() previously */ search_xpath_objects(&children, xml, acl_obj->xpath); } children_iterator = children; while (children_iterator != NULL) { update_xml_perms(children_iterator->data, acl_obj, *xml_perms); children_iterator = children_iterator->next; } g_list_free(children); acl_iterator = acl_iterator->next; } return TRUE; } /* Borrowed from lib/common/xml.c: find_xml_children() */ /* But adding the original xmlNode pointers into a GList */ static int search_xml_children(GListPtr *children, xmlNode *root, const char *tag, const char *field, const char *value, gboolean search_matches) { int match_found = 0; CRM_CHECK(root != NULL, return FALSE); CRM_CHECK(children != NULL, return FALSE); if(tag != NULL && safe_str_neq(tag, crm_element_name(root))) { } else if(value != NULL && safe_str_neq(value, crm_element_value(root, field))) { } else { *children = g_list_append(*children, root); match_found = 1; } if(search_matches || match_found == 0) { xml_child_iter( root, child, match_found += search_xml_children( children, child, tag, field, value, search_matches); ); } return match_found; } static int search_xpath_objects(GListPtr *objects, xmlNode *xml_obj, const char *xpath) { int match_found = 0; xmlXPathObjectPtr xpathObj = NULL; if(xpath == NULL) { return 0; } xpathObj = xpath_search(xml_obj, xpath); if(xpathObj == NULL || xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr < 1) { crm_debug("No match for %s in %s", xpath, xmlGetNodePath(xml_obj)); } else if(xpathObj->nodesetval->nodeNr > 0) { int lpc = 0, max = xpathObj->nodesetval->nodeNr; for(lpc = 0; lpc < max; lpc++) { xmlNode *match = getXpathResult(xpathObj, lpc); if (match == NULL) { continue; } *objects = g_list_append(*objects, match); match_found++; } } if(xpathObj) { xmlXPathFreeObject(xpathObj); } return match_found; } static gboolean update_xml_perms(xmlNode *xml, acl_obj_t *acl_obj, GHashTable *xml_perms) { xml_perm_t *perm = NULL; if (g_hash_table_lookup_extended(xml_perms, xml, NULL, (gpointer)&perm)) { if (perm->mode != NULL) { return FALSE; } } else { crm_malloc0(perm, sizeof(xml_perm_t)); if (perm == NULL) { return FALSE; } g_hash_table_insert(xml_perms, xml, perm); } if (acl_obj->attribute == NULL) { perm->mode = acl_obj->mode; crm_debug_3("Permission for element: element_mode=%s, tag=%s, id=%s", perm->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID)); xml_child_iter( xml, child, update_xml_children_perms(child, perm->mode, xml_perms); ); } else { if (perm->attribute_perms == NULL || (g_hash_table_lookup_extended(perm->attribute_perms, acl_obj->attribute, NULL, NULL) == FALSE)) { if (perm->attribute_perms == NULL) { perm->attribute_perms = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); } g_hash_table_insert(perm->attribute_perms, crm_strdup(acl_obj->attribute), crm_strdup(acl_obj->mode)); crm_debug_3("Permission for attribute: attribute_mode=%s, tag=%s, id=%s attribute=%s", acl_obj->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), acl_obj->attribute); } } return TRUE; } static gboolean update_xml_children_perms(xmlNode *xml, const char *mode, GHashTable *xml_perms) { xml_perm_t *perm = NULL; if (g_hash_table_lookup_extended(xml_perms, xml, NULL, (gpointer)&perm)) { if (perm->mode != NULL) { return FALSE; } } else { crm_malloc0(perm, sizeof(xml_perm_t)); if (perm == NULL) { return FALSE; } g_hash_table_insert(xml_perms, xml, perm); } perm->mode = mode; crm_debug_4("Permission for child element: element_mode=%s, tag=%s, id=%s", mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID)); xml_child_iter( xml, child, update_xml_children_perms(child, mode, xml_perms); ); return TRUE; } static void free_xml_perm(gpointer xml_perm) { xml_perm_t *perm = xml_perm; if (perm == NULL) { return; } if (perm->attribute_perms != NULL) { g_hash_table_destroy(perm->attribute_perms); } crm_free(perm); } #define can_read(mode) (crm_str_eq(mode, XML_ACL_TAG_READ, TRUE) \ || crm_str_eq(mode, XML_ACL_TAG_WRITE, TRUE)) #define can_write(mode) crm_str_eq(mode, XML_ACL_TAG_WRITE, TRUE) /* rc = TRUE if the xml is filtered out*/ static gboolean acl_filter_xml(xmlNode *xml, GHashTable *xml_perms) { int children_counter = 0; xml_perm_t *perm = NULL; int allow_counter = 0; xml_child_iter( xml, child, if (acl_filter_xml(child, xml_perms) == FALSE) { children_counter++; } ); g_hash_table_lookup_extended(xml_perms, xml, NULL, (gpointer)&perm); if (perm == NULL) { crm_debug_4("No ACL defined to read the element: tag=%s, id=%s", crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID)); goto end_filter; } if (perm->attribute_perms == NULL) { if (can_read(perm->mode)) { return FALSE; } else { crm_debug_4("No enough permission to read the element: element_mode=%s, tag=%s, id=%s", perm->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID)); goto end_filter; } } xml_prop_iter(xml, prop_name, prop_value, gpointer mode = NULL; if (g_hash_table_lookup_extended(perm->attribute_perms, prop_name, NULL, &mode)) { if (can_read(mode)) { allow_counter++; } else { xml_remove_prop(xml, prop_name); crm_debug_4("Filtered out the attribute: attribute_mode=%s, tag=%s, id=%s, attribute=%s", (char *)mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name); } } else { if (can_read(perm->mode)) { allow_counter++; } else if (crm_str_eq(prop_name, XML_ATTR_ID, TRUE) == FALSE) { xml_remove_prop(xml, prop_name); crm_debug_4("Filtered out the attribute: element_mode=%s, tag=%s, id=%s, attribute=%s", perm->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name); } } ); if (allow_counter) { return FALSE; } if (can_read(perm->mode)) { return FALSE; } end_filter: if (children_counter) { crm_debug_4("Don't filter out the element (tag=%s, id=%s) because user can read its children", crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID)); return FALSE; } free_xml_from_parent(NULL, xml); crm_debug_4("Filtered out the element: tag=%s, id=%s", crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID)); return TRUE; } static gboolean acl_check_diff_xml(xmlNode *xml, GHashTable *xml_perms) { xml_perm_t *perm = NULL; xml_child_iter( xml, child, if (acl_check_diff_xml(child, xml_perms) == FALSE) { return FALSE; } ); g_hash_table_lookup_extended(xml_perms, xml, NULL, (gpointer)&perm); xml_prop_iter(xml, prop_name, prop_value, gpointer mode = NULL; if (crm_str_eq(crm_element_name(xml), XML_TAG_CIB, TRUE)) { if (crm_str_eq(prop_name, XML_ATTR_GENERATION, TRUE) || crm_str_eq(prop_name, XML_ATTR_NUMUPDATES, TRUE) || crm_str_eq(prop_name, XML_ATTR_GENERATION_ADMIN, TRUE)) { continue; } } if (crm_str_eq(prop_name, XML_ATTR_ID, TRUE)) { continue; } if (perm == NULL) { crm_warn("No ACL defined to modify the element: tag=%s, id=%s, attribute=%s", crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name); return FALSE; } if (perm->attribute_perms == NULL) { if (can_write(perm->mode)) { return TRUE; } else { crm_warn("No enough permission to modify the element: element_mode=%s, tag=%s, id=%s, attribute=%s", perm->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name); return FALSE; } } if (g_hash_table_lookup_extended(perm->attribute_perms, prop_name, NULL, &mode)) { if (can_write(mode) == FALSE) { crm_warn("No enough permission to modify the attribute: attribute_mode=%s, tag=%s, id=%s, attribute=%s", (char *)mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name); return FALSE; } } else if (can_write(perm->mode) == FALSE) { crm_warn("No enough permission to modify the element and the attribute: element_mode=%s, tag=%s, id=%s, attribute=%s", perm->mode, crm_element_name(xml), crm_element_value(xml, XML_ATTR_ID), prop_name); return FALSE; } ); return TRUE; } diff --git a/xml/pacemaker-1.1.rng b/xml/pacemaker-1.1.rng index 5ea2efd6c1..53a63f7d0c 100644 --- a/xml/pacemaker-1.1.rng +++ b/xml/pacemaker-1.1.rng @@ -1,201 +1,205 @@ normal member ping - + + + + +