pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
acl.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2025 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11
12#include <stdio.h>
13#include <sys/types.h>
14#include <pwd.h>
15#include <string.h>
16#include <stdlib.h>
17#include <stdarg.h>
18
19#include <libxml/tree.h> // xmlNode, etc.
20#include <libxml/xmlstring.h> // xmlChar
21#include <libxml/xpath.h> // xmlXPathObject, etc.
22
23#include <crm/crm.h>
24#include <crm/common/xml.h>
26#include "crmcommon_private.h"
27
28typedef struct xml_acl_s {
29 enum pcmk__xml_flags mode;
30 gchar *xpath;
32
33static void
34free_acl(void *data)
35{
36 if (data) {
37 xml_acl_t *acl = data;
38
39 g_free(acl->xpath);
40 free(acl);
41 }
42}
43
44void
45pcmk__free_acls(GList *acls)
46{
47 g_list_free_full(acls, free_acl);
48}
49
50static GList *
51create_acl(const xmlNode *xml, GList *acls, enum pcmk__xml_flags mode)
52{
53 xml_acl_t *acl = NULL;
54
55 const char *tag = crm_element_value(xml, PCMK_XA_OBJECT_TYPE);
56 const char *ref = crm_element_value(xml, PCMK_XA_REFERENCE);
57 const char *xpath = crm_element_value(xml, PCMK_XA_XPATH);
58 const char *attr = crm_element_value(xml, PCMK_XA_ATTRIBUTE);
59
60 if ((tag == NULL) && (ref == NULL) && (xpath == NULL)) {
61 // Schema should prevent this, but to be safe ...
62 crm_trace("Ignoring ACL <%s> element without selection criteria",
63 xml->name);
64 return NULL;
65 }
66
67 acl = pcmk__assert_alloc(1, sizeof (xml_acl_t));
68
69 acl->mode = mode;
70 if (xpath) {
71 acl->xpath = g_strdup(xpath);
72 crm_trace("Unpacked ACL <%s> element using xpath: %s",
73 xml->name, acl->xpath);
74
75 } else {
76 GString *buf = g_string_sized_new(128);
77
78 if ((ref != NULL) && (attr != NULL)) {
79 // NOTE: schema currently does not allow this
80 pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@" PCMK_XA_ID "='",
81 ref, "' and @", attr, "]", NULL);
82
83 } else if (ref != NULL) {
84 pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@" PCMK_XA_ID "='",
85 ref, "']", NULL);
86
87 } else if (attr != NULL) {
88 pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@", attr, "]", NULL);
89
90 } else {
91 pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), NULL);
92 }
93
94 acl->xpath = buf->str;
95
96 g_string_free(buf, FALSE);
97 crm_trace("Unpacked ACL <%s> element as xpath: %s",
98 xml->name, acl->xpath);
99 }
100
101 return g_list_append(acls, acl);
102}
103
116static GList *
117parse_acl_entry(const xmlNode *acl_top, const xmlNode *acl_entry, GList *acls)
118{
119 for (const xmlNode *child = pcmk__xe_first_child(acl_entry, NULL, NULL,
120 NULL);
121 child != NULL; child = pcmk__xe_next(child, NULL)) {
122
123 if (pcmk__xe_is(child, PCMK_XE_ACL_PERMISSION)) {
124 const char *kind = crm_element_value(child, PCMK_XA_KIND);
125
126 pcmk__assert(kind != NULL);
127 crm_trace("Unpacking <" PCMK_XE_ACL_PERMISSION "> element of "
128 "kind '%s'",
129 kind);
130
131 if (pcmk__str_eq(kind, PCMK_VALUE_READ, pcmk__str_none)) {
132 acls = create_acl(child, acls, pcmk__xf_acl_read);
133
134 } else if (pcmk__str_eq(kind, PCMK_VALUE_WRITE, pcmk__str_none)) {
135 acls = create_acl(child, acls, pcmk__xf_acl_write);
136
137 } else if (pcmk__str_eq(kind, PCMK_VALUE_DENY, pcmk__str_none)) {
138 acls = create_acl(child, acls, pcmk__xf_acl_deny);
139
140 } else {
141 crm_warn("Ignoring unknown ACL kind '%s'", kind);
142 }
143
144 } else if (pcmk__xe_is(child, PCMK_XE_ROLE)) {
145 const char *ref_role = crm_element_value(child, PCMK_XA_ID);
146
147 crm_trace("Unpacking <" PCMK_XE_ROLE "> element");
148
149 if (ref_role == NULL) {
150 continue;
151 }
152
153 for (xmlNode *role = pcmk__xe_first_child(acl_top, NULL, NULL,
154 NULL);
155 role != NULL; role = pcmk__xe_next(role, NULL)) {
156
157 const char *role_id = NULL;
158
159 if (!pcmk__xe_is(role, PCMK_XE_ACL_ROLE)) {
160 continue;
161 }
162
163 role_id = crm_element_value(role, PCMK_XA_ID);
164
165 if (pcmk__str_eq(ref_role, role_id, pcmk__str_none)) {
166 crm_trace("Unpacking referenced role '%s' in <%s> element",
167 role_id, acl_entry->name);
168 acls = parse_acl_entry(acl_top, role, acls);
169 break;
170 }
171 }
172 }
173 }
174
175 return acls;
176}
177
178/*
179 <acls>
180 <acl_target id="l33t-haxor"><role id="auto-l33t-haxor"/></acl_target>
181 <acl_role id="auto-l33t-haxor">
182 <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
183 </acl_role>
184 <acl_target id="niceguy">
185 <role id="observer"/>
186 </acl_target>
187 <acl_role id="observer">
188 <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
189 <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
190 <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
191 </acl_role>
192 <acl_target id="badidea"><role id="auto-badidea"/></acl_target>
193 <acl_role id="auto-badidea">
194 <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
195 <acl_permission id="badidea-resources-2" kind="deny" reference="dummy-meta_attributes"/>
196 </acl_role>
197 </acls>
198*/
199
200static const char *
201acl_to_text(enum pcmk__xml_flags flags)
202{
204 return "deny";
205
206 } else if (pcmk_any_flags_set(flags, pcmk__xf_acl_write|pcmk__xf_acl_create)) {
207 return "read/write";
208
209 } else if (pcmk_is_set(flags, pcmk__xf_acl_read)) {
210 return "read";
211 }
212 return "none";
213}
214
215void
216pcmk__apply_acl(xmlNode *xml)
217{
218 GList *aIter = NULL;
219 xml_doc_private_t *docpriv = xml->doc->_private;
220 xml_node_private_t *nodepriv;
221 xmlXPathObject *xpathObj = NULL;
222
223 if (!xml_acl_enabled(xml)) {
224 crm_trace("Skipping ACLs for user '%s' because not enabled for this XML",
225 docpriv->acl_user);
226 return;
227 }
228
229 for (aIter = docpriv->acls; aIter != NULL; aIter = aIter->next) {
230 int max = 0, lpc = 0;
231 xml_acl_t *acl = aIter->data;
232
233 xpathObj = pcmk__xpath_search(xml->doc, acl->xpath);
234 max = pcmk__xpath_num_results(xpathObj);
235
236 for (lpc = 0; lpc < max; lpc++) {
237 xmlNode *match = pcmk__xpath_result(xpathObj, lpc);
238
239 if (match == NULL) {
240 continue;
241 }
242
243 /* @COMPAT If the ACL's XPath matches a node that is neither an
244 * element nor a document, we apply the ACL to the parent element
245 * rather than to the matched node. For example, if the XPath
246 * matches a "score" attribute, then it applies to every element
247 * that contains a "score" attribute. That is, the XPath expression
248 * "//@score" matches all attributes named "score", but we apply the
249 * ACL to all elements containing such an attribute.
250 *
251 * This behavior is incorrect from an XPath standpoint and is thus
252 * confusing and counterintuitive. The correct way to match all
253 * elements containing a "score" attribute is to use an XPath
254 * predicate: "// *[@score]". (Space inserted after slashes so that
255 * GCC doesn't throw an error about nested comments.)
256 *
257 * Additionally, if an XPath expression matches the entire document
258 * (for example, "/"), then the ACL applies to the document's root
259 * element if it exists.
260 *
261 * These behaviors should be changed so that the ACL applies to the
262 * nodes matched by the XPath expression, or so that it doesn't
263 * apply at all if applying an ACL to an attribute doesn't make
264 * sense.
265 *
266 * Unfortunately, we document in Pacemaker Explained that matching
267 * attributes is a valid way to match elements: "Attributes may be
268 * specified in the XPath to select particular elements, but the
269 * permissions apply to the entire element."
270 *
271 * So we have to keep this behavior at least until a compatibility
272 * break. Even then, it's not feasible in the general case to
273 * transform such XPath expressions using XSLT.
274 */
275 match = pcmk__xpath_match_element(match);
276 if (match == NULL) {
277 continue;
278 }
279
280 nodepriv = match->_private;
281 pcmk__set_xml_flags(nodepriv, acl->mode);
282
283 // Build a GString only if tracing is enabled
285 {
286 GString *path = pcmk__element_xpath(match);
287 crm_trace("Applying %s ACL to %s matched by %s",
288 acl_to_text(acl->mode), path->str, acl->xpath);
289 g_string_free(path, TRUE);
290 },
291 {}
292 );
293 }
294 crm_trace("Applied %s ACL %s (%d match%s)",
295 acl_to_text(acl->mode), acl->xpath, max,
296 ((max == 1)? "" : "es"));
297 xmlXPathFreeObject(xpathObj);
298 }
299}
300
314void
315pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
316{
317 xml_doc_private_t *docpriv = NULL;
318
319 if ((target == NULL) || (target->doc == NULL)
320 || (target->doc->_private == NULL)) {
321 return;
322 }
323
324 docpriv = target->doc->_private;
325 if (!pcmk_acl_required(user)) {
326 crm_trace("Not unpacking ACLs because not required for user '%s'",
327 user);
328
329 } else if (docpriv->acls == NULL) {
330 xmlNode *acls = pcmk__xpath_find_one(source->doc, "//" PCMK_XE_ACLS,
331 LOG_NEVER);
332
333 pcmk__str_update(&(docpriv->acl_user), user);
334
335 if (acls) {
336 xmlNode *child = NULL;
337
338 for (child = pcmk__xe_first_child(acls, NULL, NULL, NULL);
339 child != NULL; child = pcmk__xe_next(child, NULL)) {
340
341 if (pcmk__xe_is(child, PCMK_XE_ACL_TARGET)) {
342 const char *id = crm_element_value(child, PCMK_XA_NAME);
343
344 if (id == NULL) {
345 id = crm_element_value(child, PCMK_XA_ID);
346 }
347
348 if (id && strcmp(id, user) == 0) {
349 crm_debug("Unpacking ACLs for user '%s'", id);
350 docpriv->acls = parse_acl_entry(acls, child, docpriv->acls);
351 }
352 } else if (pcmk__xe_is(child, PCMK_XE_ACL_GROUP)) {
353 const char *id = crm_element_value(child, PCMK_XA_NAME);
354
355 if (id == NULL) {
356 id = crm_element_value(child, PCMK_XA_ID);
357 }
358
359 if (id && pcmk__is_user_in_group(user,id)) {
360 crm_debug("Unpacking ACLs for group '%s'", id);
361 docpriv->acls = parse_acl_entry(acls, child, docpriv->acls);
362 }
363 }
364 }
365 }
366 }
367}
368
377void
378pcmk__enable_acl(xmlNode *acl_source, xmlNode *target, const char *user)
379{
380 if (target == NULL) {
381 return;
382 }
383 pcmk__unpack_acl(acl_source, target, user);
386}
387
388static inline bool
389test_acl_mode(enum pcmk__xml_flags allowed, enum pcmk__xml_flags requested)
390{
391 if (pcmk_is_set(allowed, pcmk__xf_acl_deny)) {
392 return false;
393
394 } else if (pcmk_all_flags_set(allowed, requested)) {
395 return true;
396
397 } else if (pcmk_is_set(requested, pcmk__xf_acl_read)
398 && pcmk_is_set(allowed, pcmk__xf_acl_write)) {
399 return true;
400
401 } else if (pcmk_is_set(requested, pcmk__xf_acl_create)
402 && pcmk_any_flags_set(allowed, pcmk__xf_acl_write|pcmk__xf_created)) {
403 return true;
404 }
405 return false;
406}
407
419static bool
420purge_xml_attributes(xmlNode *xml)
421{
422 xmlNode *child = NULL;
423 xmlAttr *xIter = NULL;
424 bool readable_children = false;
425 xml_node_private_t *nodepriv = xml->_private;
426
427 if (test_acl_mode(nodepriv->flags, pcmk__xf_acl_read)) {
428 crm_trace("%s[@" PCMK_XA_ID "=%s] is readable",
429 xml->name, pcmk__xe_id(xml));
430 return true;
431 }
432
433 xIter = xml->properties;
434 while (xIter != NULL) {
435 xmlAttr *tmp = xIter;
436 const char *prop_name = (const char *)xIter->name;
437
438 xIter = xIter->next;
439 if (strcmp(prop_name, PCMK_XA_ID) == 0) {
440 continue;
441 }
442
443 pcmk__xa_remove(tmp, true);
444 }
445
446 child = pcmk__xml_first_child(xml);
447 while ( child != NULL ) {
448 xmlNode *tmp = child;
449
450 child = pcmk__xml_next(child);
451 readable_children |= purge_xml_attributes(tmp);
452 }
453
454 if (!readable_children) {
455 // Nothing readable under here, so purge completely
456 pcmk__xml_free(xml);
457 }
458 return readable_children;
459}
460
472bool
473xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml,
474 xmlNode **result)
475{
476 GList *aIter = NULL;
477 xmlNode *target = NULL;
478 xml_doc_private_t *docpriv = NULL;
479
480 *result = NULL;
481 if ((xml == NULL) || !pcmk_acl_required(user)) {
482 crm_trace("Not filtering XML because ACLs not required for user '%s'",
483 user);
484 return false;
485 }
486
487 crm_trace("Filtering XML copy using user '%s' ACLs", user);
488 target = pcmk__xml_copy(NULL, xml);
489 if (target == NULL) {
490 return true;
491 }
492
493 pcmk__enable_acl(acl_source, target, user);
494
495 docpriv = target->doc->_private;
496 for(aIter = docpriv->acls; aIter != NULL && target; aIter = aIter->next) {
497 int max = 0;
498 xml_acl_t *acl = aIter->data;
499
500 if (acl->mode != pcmk__xf_acl_deny) {
501 /* Nothing to do */
502
503 } else if (acl->xpath) {
504 int lpc = 0;
505 xmlXPathObject *xpathObj = pcmk__xpath_search(target->doc,
506 acl->xpath);
507
508 max = pcmk__xpath_num_results(xpathObj);
509 for(lpc = 0; lpc < max; lpc++) {
510 xmlNode *match = pcmk__xpath_result(xpathObj, lpc);
511
512 if (match == NULL) {
513 continue;
514 }
515
516 // @COMPAT See COMPAT comment in pcmk__apply_acl()
517 match = pcmk__xpath_match_element(match);
518 if (match == NULL) {
519 continue;
520 }
521
522 if (!purge_xml_attributes(match) && (match == target)) {
523 crm_trace("ACLs deny user '%s' access to entire XML document",
524 user);
525 xmlXPathFreeObject(xpathObj);
526 return true;
527 }
528 }
529 crm_trace("ACLs deny user '%s' access to %s (%d %s)",
530 user, acl->xpath, max,
531 pcmk__plural_alt(max, "match", "matches"));
532 xmlXPathFreeObject(xpathObj);
533 }
534 }
535
536 if (!purge_xml_attributes(target)) {
537 crm_trace("ACLs deny user '%s' access to entire XML document", user);
538 return true;
539 }
540
541 if (docpriv->acls) {
542 g_list_free_full(docpriv->acls, free_acl);
543 docpriv->acls = NULL;
544
545 } else {
546 crm_trace("User '%s' without ACLs denied access to entire XML document",
547 user);
549 target = NULL;
550 }
551
552 if (target) {
553 *result = target;
554 }
555
556 return true;
557}
558
571static bool
572implicitly_allowed(const xmlNode *xml)
573{
574 GString *path = NULL;
575
576 for (xmlAttr *prop = xml->properties; prop != NULL; prop = prop->next) {
577 if (strcmp((const char *) prop->name, PCMK_XA_ID) != 0) {
578 return false;
579 }
580 }
581
583 pcmk__assert(path != NULL);
584
585 if (strstr((const char *) path->str, "/" PCMK_XE_ACLS "/") != NULL) {
586 g_string_free(path, TRUE);
587 return false;
588 }
589
590 g_string_free(path, TRUE);
591 return true;
592}
593
594#define display_id(xml) pcmk__s(pcmk__xe_id(xml), "<unset>")
595
611void
612pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
613{
614 xml_node_private_t *nodepriv = xml->_private;
615
616 if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
617 if (implicitly_allowed(xml)) {
618 crm_trace("Creation of <%s> scaffolding with " PCMK_XA_ID "=\"%s\""
619 " is implicitly allowed",
620 xml->name, display_id(xml));
621
622 } else if (pcmk__check_acl(xml, NULL, pcmk__xf_acl_write)) {
623 crm_trace("ACLs allow creation of <%s> with " PCMK_XA_ID "=\"%s\"",
624 xml->name, display_id(xml));
625
626 } else if (check_top) {
627 /* is_root=true should be impossible with check_top=true, but check
628 * for sanity
629 */
630 bool is_root = (xmlDocGetRootElement(xml->doc) == xml);
631 xml_doc_private_t *docpriv = xml->doc->_private;
632
633 crm_trace("ACLs disallow creation of %s<%s> with "
634 PCMK_XA_ID "=\"%s\"",
635 (is_root? "root element " : ""), xml->name,
636 display_id(xml));
637
638 // pcmk__xml_free() checks ACLs if enabled, which would fail
640 pcmk__xml_free(xml);
641
642 if (!is_root) {
643 // If root, the document was freed. Otherwise re-enable ACLs.
645 }
646 return;
647
648 } else {
649 crm_notice("ACLs would disallow creation of %s<%s> with "
650 PCMK_XA_ID "=\"%s\"",
651 ((xml == xmlDocGetRootElement(xml->doc))? "root element " : ""),
652 xml->name, display_id(xml));
653 }
654 }
655
656 for (xmlNode *cIter = pcmk__xml_first_child(xml); cIter != NULL; ) {
657 xmlNode *child = cIter;
658 cIter = pcmk__xml_next(cIter); /* In case it is free'd */
659 pcmk__apply_creation_acl(child, true);
660 }
661}
662
670bool
671xml_acl_denied(const xmlNode *xml)
672{
673 if (xml && xml->doc && xml->doc->_private){
674 xml_doc_private_t *docpriv = xml->doc->_private;
675
676 return pcmk_is_set(docpriv->flags, pcmk__xf_acl_denied);
677 }
678 return false;
679}
680
681void
682xml_acl_disable(xmlNode *xml)
683{
684 if (xml_acl_enabled(xml)) {
685 xml_doc_private_t *docpriv = xml->doc->_private;
686
687 /* Catch anything that was created but shouldn't have been */
688 pcmk__apply_acl(xml);
689 pcmk__apply_creation_acl(xml, false);
691 }
692}
693
701bool
702xml_acl_enabled(const xmlNode *xml)
703{
704 if (xml && xml->doc && xml->doc->_private){
705 xml_doc_private_t *docpriv = xml->doc->_private;
706
707 return pcmk_is_set(docpriv->flags, pcmk__xf_acl_enabled);
708 }
709 return false;
710}
711
724#define check_acl_deny(xml, attr_name, prefix, user, mode) do { \
725 xmlNode *tree = xml; \
726 \
727 pcmk__xml_doc_set_flags(tree->doc, pcmk__xf_acl_denied); \
728 pcmk__if_tracing( \
729 { \
730 GString *xpath = pcmk__element_xpath(tree); \
731 \
732 if ((attr_name) != NULL) { \
733 pcmk__g_strcat(xpath, "[@", attr_name, "]", NULL); \
734 } \
735 qb_log_from_external_source(__func__, __FILE__, \
736 "%sACL denies user '%s' %s " \
737 "access to %s", \
738 LOG_TRACE, __LINE__, 0 , \
739 prefix, user, \
740 acl_to_text(mode), xpath->str); \
741 g_string_free(xpath, TRUE); \
742 }, \
743 {} \
744 ); \
745 } while (false);
746
747bool
748pcmk__check_acl(xmlNode *xml, const char *attr_name, enum pcmk__xml_flags mode)
749{
750 xml_doc_private_t *docpriv = NULL;
751
752 pcmk__assert((xml != NULL) && (xml->doc->_private != NULL));
753
755 || !xml_acl_enabled(xml)) {
756 return true;
757 }
758
759 docpriv = xml->doc->_private;
760 if (docpriv->acls == NULL) {
761 check_acl_deny(xml, attr_name, "Lack of ", docpriv->acl_user, mode);
762 return false;
763 }
764
765 /* Walk the tree upwards looking for xml_acl_* flags
766 * - Creating an attribute requires write permissions for the node
767 * - Creating a child requires write permissions for the parent
768 */
769
770 if (attr_name != NULL) {
771 xmlAttr *attr = xmlHasProp(xml, (const xmlChar *) attr_name);
772
773 if ((attr != NULL) && (mode == pcmk__xf_acl_create)) {
774 mode = pcmk__xf_acl_write;
775 }
776 }
777
778 for (const xmlNode *parent = xml;
779 (parent != NULL) && (parent->_private != NULL);
780 parent = parent->parent) {
781
782 const xml_node_private_t *nodepriv = parent->_private;
783
784 if (test_acl_mode(nodepriv->flags, mode)) {
785 return true;
786 }
787
788 if (pcmk_is_set(nodepriv->flags, pcmk__xf_acl_deny)) {
789 const char *pfx = (parent != xml)? "Parent " : "";
790
791 check_acl_deny(xml, attr_name, pfx, docpriv->acl_user, mode);
792 return false;
793 }
794 }
795
796 check_acl_deny(xml, attr_name, "Default ", docpriv->acl_user, mode);
797 return false;
798}
799
807bool
808pcmk_acl_required(const char *user)
809{
810 if (pcmk__str_empty(user)) {
811 crm_trace("ACLs not required because no user set");
812 return false;
813
814 } else if (!strcmp(user, CRM_DAEMON_USER) || !strcmp(user, "root")) {
815 crm_trace("ACLs not required for privileged user %s", user);
816 return false;
817 }
818 crm_trace("ACLs required for %s", user);
819 return true;
820}
821
822char *
824{
825 struct passwd *pwent = getpwuid(uid);
826
827 if (pwent == NULL) {
828 crm_perror(LOG_INFO, "Cannot get user details for user ID %d", uid);
829 return NULL;
830 }
831 return pcmk__str_copy(pwent->pw_name);
832}
833
852const char *
853pcmk__update_acl_user(xmlNode *request, const char *field,
854 const char *peer_user)
855{
856 static const char *effective_user = NULL;
857 const char *requested_user = NULL;
858 const char *user = NULL;
859
860 if (effective_user == NULL) {
861 effective_user = pcmk__uid2username(geteuid());
862 if (effective_user == NULL) {
863 effective_user = pcmk__str_copy("#unprivileged");
864 crm_err("Unable to determine effective user, assuming unprivileged for ACLs");
865 }
866 }
867
868 requested_user = crm_element_value(request, PCMK__XA_ACL_TARGET);
869 if (requested_user == NULL) {
870 /* Currently, different XML attribute names are used for the ACL user in
871 * different contexts (PCMK__XA_ATTR_USER, PCMK__XA_CIB_USER, etc.).
872 * The caller may specify that name as the field argument.
873 *
874 * @TODO Standardize on PCMK__XA_ACL_TARGET and eventually drop the
875 * others once rolling upgrades from versions older than that are no
876 * longer supported.
877 */
878 requested_user = crm_element_value(request, field);
879 }
880
881 if (!pcmk__is_privileged(effective_user)) {
882 /* We're not running as a privileged user, set or overwrite any existing
883 * value for PCMK__XA_ACL_TARGET
884 */
885 user = effective_user;
886
887 } else if (peer_user == NULL && requested_user == NULL) {
888 /* No user known or requested, use 'effective_user' and make sure one is
889 * set for the request
890 */
891 user = effective_user;
892
893 } else if (peer_user == NULL) {
894 /* No user known, trusting 'requested_user' */
895 user = requested_user;
896
897 } else if (!pcmk__is_privileged(peer_user)) {
898 /* The peer is not a privileged user, set or overwrite any existing
899 * value for PCMK__XA_ACL_TARGET
900 */
901 user = peer_user;
902
903 } else if (requested_user == NULL) {
904 /* Even if we're privileged, make sure there is always a value set */
905 user = peer_user;
906
907 } else {
908 /* Legal delegation to 'requested_user' */
909 user = requested_user;
910 }
911
912 // This requires pointer comparison, not string comparison
913 if (user != crm_element_value(request, PCMK__XA_ACL_TARGET)) {
914 crm_xml_add(request, PCMK__XA_ACL_TARGET, user);
915 }
916
917 if (field != NULL && user != crm_element_value(request, field)) {
918 crm_xml_add(request, field, user);
919 }
920
921 return requested_user;
922}
bool xml_acl_denied(const xmlNode *xml)
Check whether or not an XML node is ACL-denied.
Definition acl.c:671
#define display_id(xml)
Definition acl.c:594
void pcmk__free_acls(GList *acls)
Definition acl.c:45
bool pcmk_acl_required(const char *user)
Check whether ACLs are required for a given user.
Definition acl.c:808
char * pcmk__uid2username(uid_t uid)
Definition acl.c:823
bool pcmk__check_acl(xmlNode *xml, const char *attr_name, enum pcmk__xml_flags mode)
Definition acl.c:748
struct xml_acl_s xml_acl_t
void xml_acl_disable(xmlNode *xml)
Definition acl.c:682
#define check_acl_deny(xml, attr_name, prefix, user, mode)
Definition acl.c:724
void pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
Definition acl.c:315
const char * pcmk__update_acl_user(xmlNode *request, const char *field, const char *peer_user)
Definition acl.c:853
void pcmk__enable_acl(xmlNode *acl_source, xmlNode *target, const char *user)
Definition acl.c:378
bool xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml, xmlNode **result)
Copy ACL-allowed portions of specified XML.
Definition acl.c:473
bool xml_acl_enabled(const xmlNode *xml)
Check whether or not an XML node is ACL-enabled.
Definition acl.c:702
void pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
Definition acl.c:612
void pcmk__apply_acl(xmlNode *xml)
Definition acl.c:216
const char * parent
Definition cib.c:27
const char * path
Definition cib.c:28
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:246
uint64_t flags
Definition remote.c:3
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
#define CRM_DAEMON_USER
Definition config.h:27
char data[0]
Definition cpg.c:10
A dumping ground.
G_GNUC_INTERNAL int pcmk__xa_remove(xmlAttr *attr, bool force)
Definition xml_attr.c:45
#define pcmk__set_xml_flags(xml_priv, flags_to_set)
#define pcmk__clear_xml_flags(xml_priv, flags_to_clear)
G_GNUC_INTERNAL bool pcmk__is_user_in_group(const char *user, const char *group)
Definition utils.c:71
#define crm_warn(fmt, args...)
Definition logging.h:360
#define crm_notice(fmt, args...)
Definition logging.h:363
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
Definition logging.h:299
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
#define LOG_NEVER
Definition logging.h:48
#define crm_trace(fmt, args...)
Definition logging.h:370
#define pcmk__if_tracing(if_action, else_action)
#define PCMK_VALUE_WRITE
Definition options.h:224
#define PCMK_VALUE_READ
Definition options.h:199
#define PCMK_VALUE_DENY
Definition options.h:148
pcmk__action_result_t result
Definition pcmk_fence.c:37
const char * target
Definition pcmk_fence.c:31
#define pcmk__assert(expr)
#define pcmk__plural_alt(i, s1, s2)
void pcmk__str_update(char **str, const char *value)
Definition strings.c:1280
@ pcmk__str_none
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1299
#define pcmk__str_copy(str)
char * acl_user
User affected by acls (for logging)
GList * acls
ACLs to check requested changes against (list of xml_acl_t)
uint32_t flags
Group of enum pcmk__xml_flags
uint32_t flags
Group of enum pcmk__xml_flags
Wrappers for and extensions to libxml2.
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml_element.c:43
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:832
bool pcmk__xml_doc_all_flags_set(const xmlDoc *xml, uint32_t flags)
Definition xml.c:147
void pcmk__xml_doc_set_flags(xmlDoc *doc, uint32_t flags)
Definition xml.c:128
pcmk__xml_flags
@ pcmk__xf_acl_create
@ pcmk__xf_acl_enabled
ACLs are enabled (set for document only)
@ pcmk__xf_created
Node was created.
@ pcmk__xf_acl_denied
ACLs deny the user access (set for document only)
@ pcmk__xf_acl_deny
ACL deny permission (that is, no permission)
@ pcmk__xf_acl_write
ACL write permission (implies read permission in most or all contexts)
@ pcmk__xf_tracking
Tracking is enabled (set for document only)
@ pcmk__xf_acl_read
ACL read permission.
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816
#define PCMK_XE_ACL_GROUP
Definition xml_names.h:55
#define PCMK_XE_ACL_ROLE
Definition xml_names.h:57
#define PCMK_XE_ACL_TARGET
Definition xml_names.h:58
#define PCMK_XA_OBJECT_TYPE
Definition xml_names.h:344
#define PCMK_XA_XPATH
Definition xml_names.h:454
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XE_ACLS
Definition xml_names.h:59
#define PCMK_XE_ACL_PERMISSION
Definition xml_names.h:56
#define PCMK_XA_KIND
Definition xml_names.h:312
#define PCMK_XA_ATTRIBUTE
Definition xml_names.h:236
#define PCMK_XA_NAME
Definition xml_names.h:330
#define PCMK_XE_ROLE
Definition xml_names.h:183
#define PCMK_XA_REFERENCE
Definition xml_names.h:372
#define PCMK__XA_ACL_TARGET
xmlXPathObject * pcmk__xpath_search(xmlDoc *doc, const char *path)
Definition xpath.c:137
xmlNode * pcmk__xpath_result(xmlXPathObject *xpath_obj, int index)
Definition xpath.c:65
GString * pcmk__element_xpath(const xmlNode *xml)
Definition xpath.c:281
xmlNode * pcmk__xpath_match_element(xmlNode *match)
Definition xpath.c:102
xmlNode * pcmk__xpath_find_one(xmlDoc *doc, const char *path, uint8_t level)
Definition xpath.c:206