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