This source file includes following definitions.
- free_acl
- pcmk__free_acls
- create_acl
- parse_acl_entry
- acl_to_text
- pcmk__apply_acl
- pcmk__unpack_acl
- pcmk__enable_acl
- test_acl_mode
- purge_xml_attributes
- xml_acl_filtered_copy
- implicitly_allowed
- pcmk__apply_creation_acl
- xml_acl_denied
- xml_acl_disable
- xml_acl_enabled
- pcmk__check_acl
- pcmk_acl_required
- pcmk__uid2username
- pcmk__update_acl_user
1
2
3
4
5
6
7
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>
20 #include <libxml/xmlstring.h>
21 #include <libxml/xpath.h>
22
23 #include <crm/crm.h>
24 #include <crm/common/xml.h>
25 #include <crm/common/xml_internal.h>
26 #include "crmcommon_private.h"
27
28 typedef struct xml_acl_s {
29 enum pcmk__xml_flags mode;
30 gchar *xpath;
31 } xml_acl_t;
32
33 static void
34 free_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
44 void
45 pcmk__free_acls(GList *acls)
46 {
47 g_list_free_full(acls, free_acl);
48 }
49
50 static GList *
51 create_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
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
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
104
105
106
107
108
109
110
111
112
113
114
115
116 static GList *
117 parse_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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200 static const char *
201 acl_to_text(enum pcmk__xml_flags flags)
202 {
203 if (pcmk_is_set(flags, pcmk__xf_acl_deny)) {
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
215 void
216 pcmk__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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
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
284 pcmk__if_tracing(
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
301
302
303
304
305
306
307
308
309
310
311
312
313
314 void
315 pcmk__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
369
370
371
372
373
374
375
376
377 void
378 pcmk__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);
384 pcmk__xml_doc_set_flags(target->doc, pcmk__xf_acl_enabled);
385 pcmk__apply_acl(target);
386 }
387
388 static inline bool
389 test_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
408
409
410
411
412
413
414
415
416
417
418
419 static bool
420 purge_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
456 pcmk__xml_free(xml);
457 }
458 return readable_children;
459 }
460
461
462
463
464
465
466
467
468
469
470
471
472 bool
473 xml_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
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
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);
548 pcmk__xml_free(target);
549 target = NULL;
550 }
551
552 if (target) {
553 *result = target;
554 }
555
556 return true;
557 }
558
559
560
561
562
563
564
565
566
567
568
569
570
571 static bool
572 implicitly_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
582 path = pcmk__element_xpath(xml);
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
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611 void
612 pcmk__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
628
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
639 pcmk__clear_xml_flags(docpriv, pcmk__xf_acl_enabled);
640 pcmk__xml_free(xml);
641
642 if (!is_root) {
643
644 pcmk__set_xml_flags(docpriv, pcmk__xf_acl_enabled);
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);
659 pcmk__apply_creation_acl(child, true);
660 }
661 }
662
663
664
665
666
667
668
669
670 bool
671 xml_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
681 void
682 xml_acl_disable(xmlNode *xml)
683 {
684 if (xml_acl_enabled(xml)) {
685 xml_doc_private_t *docpriv = xml->doc->_private;
686
687
688 pcmk__apply_acl(xml);
689 pcmk__apply_creation_acl(xml, false);
690 pcmk__clear_xml_flags(docpriv, pcmk__xf_acl_enabled);
691 }
692 }
693
694
695
696
697
698
699
700
701 bool
702 xml_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
712
713
714
715
716
717
718
719
720
721
722
723
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
747 bool
748 pcmk__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
754 if (!pcmk__xml_doc_all_flags_set(xml->doc, pcmk__xf_tracking)
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
766
767
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
800
801
802
803
804
805
806
807 bool
808 pcmk_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
822 char *
823 pcmk__uid2username(uid_t uid)
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
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852 const char *
853 pcmk__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
871
872
873
874
875
876
877
878 requested_user = crm_element_value(request, field);
879 }
880
881 if (!pcmk__is_privileged(effective_user)) {
882
883
884
885 user = effective_user;
886
887 } else if (peer_user == NULL && requested_user == NULL) {
888
889
890
891 user = effective_user;
892
893 } else if (peer_user == NULL) {
894
895 user = requested_user;
896
897 } else if (!pcmk__is_privileged(peer_user)) {
898
899
900
901 user = peer_user;
902
903 } else if (requested_user == NULL) {
904
905 user = peer_user;
906
907 } else {
908
909 user = requested_user;
910 }
911
912
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 }