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
21 #include <crm/crm.h>
22 #include <crm/common/xml.h>
23 #include <crm/common/xml_internal.h>
24 #include "crmcommon_private.h"
25
26 typedef struct xml_acl_s {
27 enum xml_private_flags mode;
28 gchar *xpath;
29 } xml_acl_t;
30
31 static void
32 free_acl(void *data)
33 {
34 if (data) {
35 xml_acl_t *acl = data;
36
37 g_free(acl->xpath);
38 free(acl);
39 }
40 }
41
42 void
43 pcmk__free_acls(GList *acls)
44 {
45 g_list_free_full(acls, free_acl);
46 }
47
48 static GList *
49 create_acl(const xmlNode *xml, GList *acls, enum xml_private_flags mode)
50 {
51 xml_acl_t *acl = NULL;
52
53 const char *tag = crm_element_value(xml, PCMK_XA_OBJECT_TYPE);
54 const char *ref = crm_element_value(xml, PCMK_XA_REFERENCE);
55 const char *xpath = crm_element_value(xml, PCMK_XA_XPATH);
56 const char *attr = crm_element_value(xml, PCMK_XA_ATTRIBUTE);
57
58 if ((tag == NULL) && (ref == NULL) && (xpath == NULL)) {
59
60 crm_trace("Ignoring ACL <%s> element without selection criteria",
61 xml->name);
62 return NULL;
63 }
64
65 acl = pcmk__assert_alloc(1, sizeof (xml_acl_t));
66
67 acl->mode = mode;
68 if (xpath) {
69 acl->xpath = g_strdup(xpath);
70 crm_trace("Unpacked ACL <%s> element using xpath: %s",
71 xml->name, acl->xpath);
72
73 } else {
74 GString *buf = g_string_sized_new(128);
75
76 if ((ref != NULL) && (attr != NULL)) {
77
78 pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@" PCMK_XA_ID "='",
79 ref, "' and @", attr, "]", NULL);
80
81 } else if (ref != NULL) {
82 pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@" PCMK_XA_ID "='",
83 ref, "']", NULL);
84
85 } else if (attr != NULL) {
86 pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@", attr, "]", NULL);
87
88 } else {
89 pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), NULL);
90 }
91
92 acl->xpath = buf->str;
93
94 g_string_free(buf, FALSE);
95 crm_trace("Unpacked ACL <%s> element as xpath: %s",
96 xml->name, acl->xpath);
97 }
98
99 return g_list_append(acls, acl);
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113
114 static GList *
115 parse_acl_entry(const xmlNode *acl_top, const xmlNode *acl_entry, GList *acls)
116 {
117 for (const xmlNode *child = pcmk__xe_first_child(acl_entry, NULL, NULL,
118 NULL);
119 child != NULL; child = pcmk__xe_next(child, NULL)) {
120
121 if (pcmk__xe_is(child, PCMK_XE_ACL_PERMISSION)) {
122 const char *kind = crm_element_value(child, PCMK_XA_KIND);
123
124 pcmk__assert(kind != NULL);
125 crm_trace("Unpacking <" PCMK_XE_ACL_PERMISSION "> element of "
126 "kind '%s'",
127 kind);
128
129 if (pcmk__str_eq(kind, PCMK_VALUE_READ, pcmk__str_none)) {
130 acls = create_acl(child, acls, pcmk__xf_acl_read);
131
132 } else if (pcmk__str_eq(kind, PCMK_VALUE_WRITE, pcmk__str_none)) {
133 acls = create_acl(child, acls, pcmk__xf_acl_write);
134
135 } else if (pcmk__str_eq(kind, PCMK_VALUE_DENY, pcmk__str_none)) {
136 acls = create_acl(child, acls, pcmk__xf_acl_deny);
137
138 } else {
139 crm_warn("Ignoring unknown ACL kind '%s'", kind);
140 }
141
142 } else if (pcmk__xe_is(child, PCMK_XE_ROLE)) {
143 const char *ref_role = crm_element_value(child, PCMK_XA_ID);
144
145 crm_trace("Unpacking <" PCMK_XE_ROLE "> element");
146
147 if (ref_role == NULL) {
148 continue;
149 }
150
151 for (xmlNode *role = pcmk__xe_first_child(acl_top, NULL, NULL,
152 NULL);
153 role != NULL; role = pcmk__xe_next(role, NULL)) {
154
155 const char *role_id = NULL;
156
157 if (!pcmk__xe_is(role, PCMK_XE_ACL_ROLE)) {
158 continue;
159 }
160
161 role_id = crm_element_value(role, PCMK_XA_ID);
162
163 if (pcmk__str_eq(ref_role, role_id, pcmk__str_none)) {
164 crm_trace("Unpacking referenced role '%s' in <%s> element",
165 role_id, acl_entry->name);
166 acls = parse_acl_entry(acl_top, role, acls);
167 break;
168 }
169 }
170 }
171 }
172
173 return acls;
174 }
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198 static const char *
199 acl_to_text(enum xml_private_flags flags)
200 {
201 if (pcmk_is_set(flags, pcmk__xf_acl_deny)) {
202 return "deny";
203
204 } else if (pcmk_any_flags_set(flags, pcmk__xf_acl_write|pcmk__xf_acl_create)) {
205 return "read/write";
206
207 } else if (pcmk_is_set(flags, pcmk__xf_acl_read)) {
208 return "read";
209 }
210 return "none";
211 }
212
213 void
214 pcmk__apply_acl(xmlNode *xml)
215 {
216 GList *aIter = NULL;
217 xml_doc_private_t *docpriv = xml->doc->_private;
218 xml_node_private_t *nodepriv;
219 xmlXPathObjectPtr xpathObj = NULL;
220
221 if (!xml_acl_enabled(xml)) {
222 crm_trace("Skipping ACLs for user '%s' because not enabled for this XML",
223 docpriv->user);
224 return;
225 }
226
227 for (aIter = docpriv->acls; aIter != NULL; aIter = aIter->next) {
228 int max = 0, lpc = 0;
229 xml_acl_t *acl = aIter->data;
230
231 xpathObj = xpath_search(xml, acl->xpath);
232 max = numXpathResults(xpathObj);
233
234 for (lpc = 0; lpc < max; lpc++) {
235 xmlNode *match = getXpathResult(xpathObj, lpc);
236
237 nodepriv = match->_private;
238 pcmk__set_xml_flags(nodepriv, acl->mode);
239
240
241 pcmk__if_tracing(
242 {
243 GString *path = pcmk__element_xpath(match);
244 crm_trace("Applying %s ACL to %s matched by %s",
245 acl_to_text(acl->mode), path->str, acl->xpath);
246 g_string_free(path, TRUE);
247 },
248 {}
249 );
250 }
251 crm_trace("Applied %s ACL %s (%d match%s)",
252 acl_to_text(acl->mode), acl->xpath, max,
253 ((max == 1)? "" : "es"));
254 freeXpathObject(xpathObj);
255 }
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271 void
272 pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
273 {
274 xml_doc_private_t *docpriv = NULL;
275
276 if ((target == NULL) || (target->doc == NULL)
277 || (target->doc->_private == NULL)) {
278 return;
279 }
280
281 docpriv = target->doc->_private;
282 if (!pcmk_acl_required(user)) {
283 crm_trace("Not unpacking ACLs because not required for user '%s'",
284 user);
285
286 } else if (docpriv->acls == NULL) {
287 xmlNode *acls = get_xpath_object("//" PCMK_XE_ACLS, source, LOG_NEVER);
288
289 pcmk__str_update(&docpriv->user, user);
290
291 if (acls) {
292 xmlNode *child = NULL;
293
294 for (child = pcmk__xe_first_child(acls, NULL, NULL, NULL);
295 child != NULL; child = pcmk__xe_next(child, NULL)) {
296
297 if (pcmk__xe_is(child, PCMK_XE_ACL_TARGET)) {
298 const char *id = crm_element_value(child, PCMK_XA_NAME);
299
300 if (id == NULL) {
301 id = crm_element_value(child, PCMK_XA_ID);
302 }
303
304 if (id && strcmp(id, user) == 0) {
305 crm_debug("Unpacking ACLs for user '%s'", id);
306 docpriv->acls = parse_acl_entry(acls, child, docpriv->acls);
307 }
308 } else if (pcmk__xe_is(child, PCMK_XE_ACL_GROUP)) {
309 const char *id = crm_element_value(child, PCMK_XA_NAME);
310
311 if (id == NULL) {
312 id = crm_element_value(child, PCMK_XA_ID);
313 }
314
315 if (id && pcmk__is_user_in_group(user,id)) {
316 crm_debug("Unpacking ACLs for group '%s'", id);
317 docpriv->acls = parse_acl_entry(acls, child, docpriv->acls);
318 }
319 }
320 }
321 }
322 }
323 }
324
325
326
327
328
329
330
331
332
333 void
334 pcmk__enable_acl(xmlNode *acl_source, xmlNode *target, const char *user)
335 {
336 pcmk__unpack_acl(acl_source, target, user);
337 pcmk__set_xml_doc_flag(target, pcmk__xf_acl_enabled);
338 pcmk__apply_acl(target);
339 }
340
341 static inline bool
342 test_acl_mode(enum xml_private_flags allowed, enum xml_private_flags requested)
343 {
344 if (pcmk_is_set(allowed, pcmk__xf_acl_deny)) {
345 return false;
346
347 } else if (pcmk_all_flags_set(allowed, requested)) {
348 return true;
349
350 } else if (pcmk_is_set(requested, pcmk__xf_acl_read)
351 && pcmk_is_set(allowed, pcmk__xf_acl_write)) {
352 return true;
353
354 } else if (pcmk_is_set(requested, pcmk__xf_acl_create)
355 && pcmk_any_flags_set(allowed, pcmk__xf_acl_write|pcmk__xf_created)) {
356 return true;
357 }
358 return false;
359 }
360
361
362
363
364
365
366
367
368
369
370
371
372 static bool
373 purge_xml_attributes(xmlNode *xml)
374 {
375 xmlNode *child = NULL;
376 xmlAttr *xIter = NULL;
377 bool readable_children = false;
378 xml_node_private_t *nodepriv = xml->_private;
379
380 if (test_acl_mode(nodepriv->flags, pcmk__xf_acl_read)) {
381 crm_trace("%s[@" PCMK_XA_ID "=%s] is readable",
382 xml->name, pcmk__xe_id(xml));
383 return true;
384 }
385
386 xIter = xml->properties;
387 while (xIter != NULL) {
388 xmlAttr *tmp = xIter;
389 const char *prop_name = (const char *)xIter->name;
390
391 xIter = xIter->next;
392 if (strcmp(prop_name, PCMK_XA_ID) == 0) {
393 continue;
394 }
395
396 pcmk__xa_remove(tmp, true);
397 }
398
399 child = pcmk__xml_first_child(xml);
400 while ( child != NULL ) {
401 xmlNode *tmp = child;
402
403 child = pcmk__xml_next(child);
404 readable_children |= purge_xml_attributes(tmp);
405 }
406
407 if (!readable_children) {
408
409 pcmk__xml_free(xml);
410 }
411 return readable_children;
412 }
413
414
415
416
417
418
419
420
421
422
423
424
425 bool
426 xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml,
427 xmlNode **result)
428 {
429 GList *aIter = NULL;
430 xmlNode *target = NULL;
431 xml_doc_private_t *docpriv = NULL;
432
433 *result = NULL;
434 if ((xml == NULL) || !pcmk_acl_required(user)) {
435 crm_trace("Not filtering XML because ACLs not required for user '%s'",
436 user);
437 return false;
438 }
439
440 crm_trace("Filtering XML copy using user '%s' ACLs", user);
441 target = pcmk__xml_copy(NULL, xml);
442 if (target == NULL) {
443 return true;
444 }
445
446 pcmk__enable_acl(acl_source, target, user);
447
448 docpriv = target->doc->_private;
449 for(aIter = docpriv->acls; aIter != NULL && target; aIter = aIter->next) {
450 int max = 0;
451 xml_acl_t *acl = aIter->data;
452
453 if (acl->mode != pcmk__xf_acl_deny) {
454
455
456 } else if (acl->xpath) {
457 int lpc = 0;
458 xmlXPathObjectPtr xpathObj = xpath_search(target, acl->xpath);
459
460 max = numXpathResults(xpathObj);
461 for(lpc = 0; lpc < max; lpc++) {
462 xmlNode *match = getXpathResult(xpathObj, lpc);
463
464 if (!purge_xml_attributes(match) && (match == target)) {
465 crm_trace("ACLs deny user '%s' access to entire XML document",
466 user);
467 freeXpathObject(xpathObj);
468 return true;
469 }
470 }
471 crm_trace("ACLs deny user '%s' access to %s (%d %s)",
472 user, acl->xpath, max,
473 pcmk__plural_alt(max, "match", "matches"));
474 freeXpathObject(xpathObj);
475 }
476 }
477
478 if (!purge_xml_attributes(target)) {
479 crm_trace("ACLs deny user '%s' access to entire XML document", user);
480 return true;
481 }
482
483 if (docpriv->acls) {
484 g_list_free_full(docpriv->acls, free_acl);
485 docpriv->acls = NULL;
486
487 } else {
488 crm_trace("User '%s' without ACLs denied access to entire XML document",
489 user);
490 pcmk__xml_free(target);
491 target = NULL;
492 }
493
494 if (target) {
495 *result = target;
496 }
497
498 return true;
499 }
500
501
502
503
504
505
506
507
508
509
510
511
512
513 static bool
514 implicitly_allowed(const xmlNode *xml)
515 {
516 GString *path = NULL;
517
518 for (xmlAttr *prop = xml->properties; prop != NULL; prop = prop->next) {
519 if (strcmp((const char *) prop->name, PCMK_XA_ID) != 0) {
520 return false;
521 }
522 }
523
524 path = pcmk__element_xpath(xml);
525 pcmk__assert(path != NULL);
526
527 if (strstr((const char *) path->str, "/" PCMK_XE_ACLS "/") != NULL) {
528 g_string_free(path, TRUE);
529 return false;
530 }
531
532 g_string_free(path, TRUE);
533 return true;
534 }
535
536 #define display_id(xml) pcmk__s(pcmk__xe_id(xml), "<unset>")
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553 void
554 pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
555 {
556 xml_node_private_t *nodepriv = xml->_private;
557
558 if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
559 if (implicitly_allowed(xml)) {
560 crm_trace("Creation of <%s> scaffolding with " PCMK_XA_ID "=\"%s\""
561 " is implicitly allowed",
562 xml->name, display_id(xml));
563
564 } else if (pcmk__check_acl(xml, NULL, pcmk__xf_acl_write)) {
565 crm_trace("ACLs allow creation of <%s> with " PCMK_XA_ID "=\"%s\"",
566 xml->name, display_id(xml));
567
568 } else if (check_top) {
569
570
571
572 bool is_root = (xmlDocGetRootElement(xml->doc) == xml);
573 xml_doc_private_t *docpriv = xml->doc->_private;
574
575 crm_trace("ACLs disallow creation of %s<%s> with "
576 PCMK_XA_ID "=\"%s\"",
577 (is_root? "root element " : ""), xml->name,
578 display_id(xml));
579
580
581 pcmk__clear_xml_flags(docpriv, pcmk__xf_acl_enabled);
582 pcmk__xml_free(xml);
583
584 if (!is_root) {
585
586 pcmk__set_xml_flags(docpriv, pcmk__xf_acl_enabled);
587 }
588 return;
589
590 } else {
591 crm_notice("ACLs would disallow creation of %s<%s> with "
592 PCMK_XA_ID "=\"%s\"",
593 ((xml == xmlDocGetRootElement(xml->doc))? "root element " : ""),
594 xml->name, display_id(xml));
595 }
596 }
597
598 for (xmlNode *cIter = pcmk__xml_first_child(xml); cIter != NULL; ) {
599 xmlNode *child = cIter;
600 cIter = pcmk__xml_next(cIter);
601 pcmk__apply_creation_acl(child, true);
602 }
603 }
604
605
606
607
608
609
610
611
612 bool
613 xml_acl_denied(const xmlNode *xml)
614 {
615 if (xml && xml->doc && xml->doc->_private){
616 xml_doc_private_t *docpriv = xml->doc->_private;
617
618 return pcmk_is_set(docpriv->flags, pcmk__xf_acl_denied);
619 }
620 return false;
621 }
622
623 void
624 xml_acl_disable(xmlNode *xml)
625 {
626 if (xml_acl_enabled(xml)) {
627 xml_doc_private_t *docpriv = xml->doc->_private;
628
629
630 pcmk__apply_acl(xml);
631 pcmk__apply_creation_acl(xml, false);
632 pcmk__clear_xml_flags(docpriv, pcmk__xf_acl_enabled);
633 }
634 }
635
636
637
638
639
640
641
642
643 bool
644 xml_acl_enabled(const xmlNode *xml)
645 {
646 if (xml && xml->doc && xml->doc->_private){
647 xml_doc_private_t *docpriv = xml->doc->_private;
648
649 return pcmk_is_set(docpriv->flags, pcmk__xf_acl_enabled);
650 }
651 return false;
652 }
653
654 bool
655 pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
656 {
657 pcmk__assert((xml != NULL) && (xml->doc != NULL)
658 && (xml->doc->_private != NULL));
659
660 if (pcmk__tracking_xml_changes(xml, false) && xml_acl_enabled(xml)) {
661 xmlNode *parent = xml;
662 xml_doc_private_t *docpriv = xml->doc->_private;
663 GString *xpath = NULL;
664
665 if (docpriv->acls == NULL) {
666 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
667
668 pcmk__if_tracing({}, return false);
669 xpath = pcmk__element_xpath(xml);
670 if (name != NULL) {
671 pcmk__g_strcat(xpath, "[@", name, "]", NULL);
672 }
673
674 qb_log_from_external_source(__func__, __FILE__,
675 "User '%s' without ACLs denied %s "
676 "access to %s", LOG_TRACE, __LINE__, 0,
677 docpriv->user, acl_to_text(mode),
678 (const char *) xpath->str);
679 g_string_free(xpath, TRUE);
680 return false;
681 }
682
683
684
685
686
687
688 if (name) {
689 xmlAttr *attr = xmlHasProp(xml, (pcmkXmlStr) name);
690
691 if (attr && mode == pcmk__xf_acl_create) {
692 mode = pcmk__xf_acl_write;
693 }
694 }
695
696 while (parent && parent->_private) {
697 xml_node_private_t *nodepriv = parent->_private;
698 if (test_acl_mode(nodepriv->flags, mode)) {
699 return true;
700
701 } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_acl_deny)) {
702 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
703
704 pcmk__if_tracing({}, return false);
705 xpath = pcmk__element_xpath(xml);
706 if (name != NULL) {
707 pcmk__g_strcat(xpath, "[@", name, "]", NULL);
708 }
709
710 qb_log_from_external_source(__func__, __FILE__,
711 "%sACL denies user '%s' %s access "
712 "to %s", LOG_TRACE, __LINE__, 0,
713 (parent != xml)? "Parent ": "",
714 docpriv->user, acl_to_text(mode),
715 (const char *) xpath->str);
716 g_string_free(xpath, TRUE);
717 return false;
718 }
719 parent = parent->parent;
720 }
721
722 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
723
724 pcmk__if_tracing({}, return false);
725 xpath = pcmk__element_xpath(xml);
726 if (name != NULL) {
727 pcmk__g_strcat(xpath, "[@", name, "]", NULL);
728 }
729
730 qb_log_from_external_source(__func__, __FILE__,
731 "Default ACL denies user '%s' %s access to "
732 "%s", LOG_TRACE, __LINE__, 0,
733 docpriv->user, acl_to_text(mode),
734 (const char *) xpath->str);
735 g_string_free(xpath, TRUE);
736 return false;
737 }
738
739 return true;
740 }
741
742
743
744
745
746
747
748
749 bool
750 pcmk_acl_required(const char *user)
751 {
752 if (pcmk__str_empty(user)) {
753 crm_trace("ACLs not required because no user set");
754 return false;
755
756 } else if (!strcmp(user, CRM_DAEMON_USER) || !strcmp(user, "root")) {
757 crm_trace("ACLs not required for privileged user %s", user);
758 return false;
759 }
760 crm_trace("ACLs required for %s", user);
761 return true;
762 }
763
764 char *
765 pcmk__uid2username(uid_t uid)
766 {
767 struct passwd *pwent = getpwuid(uid);
768
769 if (pwent == NULL) {
770 crm_perror(LOG_INFO, "Cannot get user details for user ID %d", uid);
771 return NULL;
772 }
773 return pcmk__str_copy(pwent->pw_name);
774 }
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794 const char *
795 pcmk__update_acl_user(xmlNode *request, const char *field,
796 const char *peer_user)
797 {
798 static const char *effective_user = NULL;
799 const char *requested_user = NULL;
800 const char *user = NULL;
801
802 if (effective_user == NULL) {
803 effective_user = pcmk__uid2username(geteuid());
804 if (effective_user == NULL) {
805 effective_user = pcmk__str_copy("#unprivileged");
806 crm_err("Unable to determine effective user, assuming unprivileged for ACLs");
807 }
808 }
809
810 requested_user = crm_element_value(request, PCMK__XA_ACL_TARGET);
811 if (requested_user == NULL) {
812
813
814
815
816
817
818
819
820 requested_user = crm_element_value(request, field);
821 }
822
823 if (!pcmk__is_privileged(effective_user)) {
824
825
826
827 user = effective_user;
828
829 } else if (peer_user == NULL && requested_user == NULL) {
830
831
832
833 user = effective_user;
834
835 } else if (peer_user == NULL) {
836
837 user = requested_user;
838
839 } else if (!pcmk__is_privileged(peer_user)) {
840
841
842
843 user = peer_user;
844
845 } else if (requested_user == NULL) {
846
847 user = peer_user;
848
849 } else {
850
851 user = requested_user;
852 }
853
854
855 if (user != crm_element_value(request, PCMK__XA_ACL_TARGET)) {
856 crm_xml_add(request, PCMK__XA_ACL_TARGET, user);
857 }
858
859 if (field != NULL && user != crm_element_value(request, field)) {
860 crm_xml_add(request, field, user);
861 }
862
863 return requested_user;
864 }