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/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, pcmk__xf_acl_read);
189
190 } else if (strcmp(XML_ACL_TAG_WRITE, tag) == 0) {
191 acls = create_acl(child, acls, pcmk__xf_acl_write);
192
193 } else if (strcmp(XML_ACL_TAG_DENY, tag) == 0) {
194 acls = create_acl(child, acls, pcmk__xf_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, pcmk__xf_acl_deny)) {
231 return "deny";
232
233 } else if (pcmk_any_flags_set(flags, pcmk__xf_acl_write|pcmk__xf_acl_create)) {
234 return "read/write";
235
236 } else if (pcmk_is_set(flags, pcmk__xf_acl_read)) {
237 return "read";
238 }
239 return "none";
240 }
241
242 void
243 pcmk__apply_acl(xmlNode *xml)
244 {
245 GList *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 pcmk__set_xml_flags(p, acl->mode);
270 free(path);
271 }
272 crm_trace("Applied %s ACL %s (%d match%s)",
273 acl_to_text(acl->mode), acl->xpath, max,
274 ((max == 1)? "" : "es"));
275 freeXpathObject(xpathObj);
276 }
277 }
278
279
280
281
282
283
284
285
286
287 void
288 pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
289 {
290 xml_private_t *p = NULL;
291
292 if ((target == NULL) || (target->doc == NULL)
293 || (target->doc->_private == NULL)) {
294 return;
295 }
296
297 p = target->doc->_private;
298 if (!pcmk_acl_required(user)) {
299 crm_trace("Not unpacking ACLs because not required for user '%s'",
300 user);
301
302 } else if (p->acls == NULL) {
303 xmlNode *acls = get_xpath_object("//" XML_CIB_TAG_ACLS,
304 source, LOG_NEVER);
305
306 pcmk__str_update(&p->user, user);
307
308 if (acls) {
309 xmlNode *child = NULL;
310
311 for (child = pcmk__xe_first_child(acls); child;
312 child = pcmk__xe_next(child)) {
313 const char *tag = crm_element_name(child);
314
315 if (!strcmp(tag, XML_ACL_TAG_USER)
316 || !strcmp(tag, XML_ACL_TAG_USERv1)) {
317 const char *id = crm_element_value(child, XML_ATTR_ID);
318
319 if (id && strcmp(id, user) == 0) {
320 crm_debug("Unpacking ACLs for user '%s'", id);
321 p->acls = parse_acl_entry(acls, child, p->acls);
322 }
323 }
324 }
325 }
326 }
327 }
328
329
330
331
332
333
334
335
336
337 void
338 pcmk__enable_acl(xmlNode *acl_source, xmlNode *target, const char *user)
339 {
340 pcmk__unpack_acl(acl_source, target, user);
341 pcmk__set_xml_doc_flag(target, pcmk__xf_acl_enabled);
342 pcmk__apply_acl(target);
343 }
344
345 static inline bool
346 test_acl_mode(enum xml_private_flags allowed, enum xml_private_flags requested)
347 {
348 if (pcmk_is_set(allowed, pcmk__xf_acl_deny)) {
349 return false;
350
351 } else if (pcmk_all_flags_set(allowed, requested)) {
352 return true;
353
354 } else if (pcmk_is_set(requested, pcmk__xf_acl_read)
355 && pcmk_is_set(allowed, pcmk__xf_acl_write)) {
356 return true;
357
358 } else if (pcmk_is_set(requested, pcmk__xf_acl_create)
359 && pcmk_any_flags_set(allowed, pcmk__xf_acl_write|pcmk__xf_created)) {
360 return true;
361 }
362 return false;
363 }
364
365 static bool
366 purge_xml_attributes(xmlNode *xml)
367 {
368 xmlNode *child = NULL;
369 xmlAttr *xIter = NULL;
370 bool readable_children = false;
371 xml_private_t *p = xml->_private;
372
373 if (test_acl_mode(p->flags, pcmk__xf_acl_read)) {
374 crm_trace("%s[@id=%s] is readable", crm_element_name(xml), ID(xml));
375 return true;
376 }
377
378 xIter = xml->properties;
379 while (xIter != NULL) {
380 xmlAttr *tmp = xIter;
381 const char *prop_name = (const char *)xIter->name;
382
383 xIter = xIter->next;
384 if (strcmp(prop_name, XML_ATTR_ID) == 0) {
385 continue;
386 }
387
388 xmlUnsetProp(xml, tmp->name);
389 }
390
391 child = pcmk__xml_first_child(xml);
392 while ( child != NULL ) {
393 xmlNode *tmp = child;
394
395 child = pcmk__xml_next(child);
396 readable_children |= purge_xml_attributes(tmp);
397 }
398
399 if (!readable_children) {
400 free_xml(xml);
401 }
402 return readable_children;
403 }
404
405
406
407
408
409
410
411
412
413
414
415
416 bool
417 xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml,
418 xmlNode **result)
419 {
420 GList *aIter = NULL;
421 xmlNode *target = NULL;
422 xml_private_t *doc = NULL;
423
424 *result = NULL;
425 if ((xml == NULL) || !pcmk_acl_required(user)) {
426 crm_trace("Not filtering XML because ACLs not required for user '%s'",
427 user);
428 return false;
429 }
430
431 crm_trace("Filtering XML copy using user '%s' ACLs", user);
432 target = copy_xml(xml);
433 if (target == NULL) {
434 return true;
435 }
436
437 pcmk__enable_acl(acl_source, target, user);
438
439 doc = target->doc->_private;
440 for(aIter = doc->acls; aIter != NULL && target; aIter = aIter->next) {
441 int max = 0;
442 xml_acl_t *acl = aIter->data;
443
444 if (acl->mode != pcmk__xf_acl_deny) {
445
446
447 } else if (acl->xpath) {
448 int lpc = 0;
449 xmlXPathObjectPtr xpathObj = xpath_search(target, acl->xpath);
450
451 max = numXpathResults(xpathObj);
452 for(lpc = 0; lpc < max; lpc++) {
453 xmlNode *match = getXpathResult(xpathObj, lpc);
454
455 if (!purge_xml_attributes(match) && (match == target)) {
456 crm_trace("ACLs deny user '%s' access to entire XML document",
457 user);
458 freeXpathObject(xpathObj);
459 return true;
460 }
461 }
462 crm_trace("ACLs deny user '%s' access to %s (%d %s)",
463 user, acl->xpath, max,
464 pcmk__plural_alt(max, "match", "matches"));
465 freeXpathObject(xpathObj);
466 }
467 }
468
469 if (!purge_xml_attributes(target)) {
470 crm_trace("ACLs deny user '%s' access to entire XML document", user);
471 return true;
472 }
473
474 if (doc->acls) {
475 g_list_free_full(doc->acls, free_acl);
476 doc->acls = NULL;
477
478 } else {
479 crm_trace("User '%s' without ACLs denied access to entire XML document",
480 user);
481 free_xml(target);
482 target = NULL;
483 }
484
485 if (target) {
486 *result = target;
487 }
488
489 return true;
490 }
491
492
493
494
495
496
497
498
499
500
501
502
503
504 static bool
505 implicitly_allowed(xmlNode *xml)
506 {
507 char *path = NULL;
508
509 for (xmlAttr *prop = xml->properties; prop != NULL; prop = prop->next) {
510 if (strcmp((const char *) prop->name, XML_ATTR_ID) != 0) {
511 return false;
512 }
513 }
514
515 path = xml_get_path(xml);
516 if (strstr(path, "/" XML_CIB_TAG_ACLS "/") != NULL) {
517 free(path);
518 return false;
519 }
520 free(path);
521
522 return true;
523 }
524
525 #define display_id(xml) (ID(xml)? ID(xml) : "<unset>")
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540 void
541 pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
542 {
543 xml_private_t *p = xml->_private;
544
545 if (pcmk_is_set(p->flags, pcmk__xf_created)) {
546 if (implicitly_allowed(xml)) {
547 crm_trace("Creation of <%s> scaffolding with id=\"%s\""
548 " is implicitly allowed",
549 crm_element_name(xml), display_id(xml));
550
551 } else if (pcmk__check_acl(xml, NULL, pcmk__xf_acl_write)) {
552 crm_trace("ACLs allow creation of <%s> with id=\"%s\"",
553 crm_element_name(xml), display_id(xml));
554
555 } else if (check_top) {
556 crm_trace("ACLs disallow creation of <%s> with id=\"%s\"",
557 crm_element_name(xml), display_id(xml));
558 pcmk_free_xml_subtree(xml);
559 return;
560
561 } else {
562 crm_notice("ACLs would disallow creation of %s<%s> with id=\"%s\" ",
563 ((xml == xmlDocGetRootElement(xml->doc))? "root element " : ""),
564 crm_element_name(xml), display_id(xml));
565 }
566 }
567
568 for (xmlNode *cIter = pcmk__xml_first_child(xml); cIter != NULL; ) {
569 xmlNode *child = cIter;
570 cIter = pcmk__xml_next(cIter);
571 pcmk__apply_creation_acl(child, true);
572 }
573 }
574
575 bool
576 xml_acl_denied(xmlNode *xml)
577 {
578 if (xml && xml->doc && xml->doc->_private){
579 xml_private_t *p = xml->doc->_private;
580
581 return pcmk_is_set(p->flags, pcmk__xf_acl_denied);
582 }
583 return false;
584 }
585
586 void
587 xml_acl_disable(xmlNode *xml)
588 {
589 if (xml_acl_enabled(xml)) {
590 xml_private_t *p = xml->doc->_private;
591
592
593 pcmk__apply_acl(xml);
594 pcmk__apply_creation_acl(xml, false);
595 pcmk__clear_xml_flags(p, pcmk__xf_acl_enabled);
596 }
597 }
598
599 bool
600 xml_acl_enabled(xmlNode *xml)
601 {
602 if (xml && xml->doc && xml->doc->_private){
603 xml_private_t *p = xml->doc->_private;
604
605 return pcmk_is_set(p->flags, pcmk__xf_acl_enabled);
606 }
607 return false;
608 }
609
610 bool
611 pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
612 {
613 CRM_ASSERT(xml);
614 CRM_ASSERT(xml->doc);
615 CRM_ASSERT(xml->doc->_private);
616
617 if (pcmk__tracking_xml_changes(xml, false) && xml_acl_enabled(xml)) {
618 int offset = 0;
619 xmlNode *parent = xml;
620 char buffer[MAX_XPATH_LEN];
621 xml_private_t *docp = xml->doc->_private;
622
623 offset = pcmk__element_xpath(NULL, xml, buffer, offset,
624 sizeof(buffer));
625 if (name) {
626 offset += snprintf(buffer + offset, MAX_XPATH_LEN - offset,
627 "[@%s]", name);
628 }
629 CRM_LOG_ASSERT(offset > 0);
630
631 if (docp->acls == NULL) {
632 crm_trace("User '%s' without ACLs denied %s access to %s",
633 docp->user, acl_to_text(mode), buffer);
634 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
635 return false;
636 }
637
638
639
640
641
642
643 if (name) {
644 xmlAttr *attr = xmlHasProp(xml, (pcmkXmlStr) name);
645
646 if (attr && mode == pcmk__xf_acl_create) {
647 mode = pcmk__xf_acl_write;
648 }
649 }
650
651 while (parent && parent->_private) {
652 xml_private_t *p = parent->_private;
653 if (test_acl_mode(p->flags, mode)) {
654 return true;
655
656 } else if (pcmk_is_set(p->flags, pcmk__xf_acl_deny)) {
657 crm_trace("%sACL denies user '%s' %s access to %s",
658 (parent != xml) ? "Parent " : "", docp->user,
659 acl_to_text(mode), buffer);
660 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
661 return false;
662 }
663 parent = parent->parent;
664 }
665
666 crm_trace("Default ACL denies user '%s' %s access to %s",
667 docp->user, acl_to_text(mode), buffer);
668 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
669 return false;
670 }
671
672 return true;
673 }
674
675
676
677
678
679
680
681
682 bool
683 pcmk_acl_required(const char *user)
684 {
685 if (pcmk__str_empty(user)) {
686 crm_trace("ACLs not required because no user set");
687 return false;
688
689 } else if (!strcmp(user, CRM_DAEMON_USER) || !strcmp(user, "root")) {
690 crm_trace("ACLs not required for privileged user %s", user);
691 return false;
692 }
693 crm_trace("ACLs required for %s", user);
694 return true;
695 }
696
697 char *
698 pcmk__uid2username(uid_t uid)
699 {
700 struct passwd *pwent = getpwuid(uid);
701
702 if (pwent == NULL) {
703 crm_perror(LOG_INFO, "Cannot get user details for user ID %d", uid);
704 return NULL;
705 }
706 return strdup(pwent->pw_name);
707 }
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727 const char *
728 pcmk__update_acl_user(xmlNode *request, const char *field,
729 const char *peer_user)
730 {
731 static const char *effective_user = NULL;
732 const char *requested_user = NULL;
733 const char *user = NULL;
734
735 if (effective_user == NULL) {
736 effective_user = pcmk__uid2username(geteuid());
737 if (effective_user == NULL) {
738 effective_user = strdup("#unprivileged");
739 CRM_CHECK(effective_user != NULL, return NULL);
740 crm_err("Unable to determine effective user, assuming unprivileged for ACLs");
741 }
742 }
743
744 requested_user = crm_element_value(request, XML_ACL_TAG_USER);
745 if (requested_user == NULL) {
746
747
748
749
750
751 requested_user = crm_element_value(request, field);
752 }
753
754 if (!pcmk__is_privileged(effective_user)) {
755
756
757
758 user = effective_user;
759
760 } else if (peer_user == NULL && requested_user == NULL) {
761
762
763
764 user = effective_user;
765
766 } else if (peer_user == NULL) {
767
768 user = requested_user;
769
770 } else if (!pcmk__is_privileged(peer_user)) {
771
772
773
774 user = peer_user;
775
776 } else if (requested_user == NULL) {
777
778 user = peer_user;
779
780 } else {
781
782 user = requested_user;
783 }
784
785
786 if (user != crm_element_value(request, XML_ACL_TAG_USER)) {
787 crm_xml_add(request, XML_ACL_TAG_USER, user);
788 }
789
790 if (field != NULL && user != crm_element_value(request, field)) {
791 crm_xml_add(request, field, user);
792 }
793
794 return requested_user;
795 }