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