1 /*
2  * Copyright 2004-2019 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
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 "crmcommon_private.h"
25 
26 #define MAX_XPATH_LEN	4096
27 
28 typedef struct xml_acl_s {
29         enum xml_private_flags mode;
30         char *xpath;
31 } xml_acl_t;
32 
33 static void
__xml_acl_free(void * data)34 __xml_acl_free(void *data)
35 {
36     if (data) {
37         xml_acl_t *acl = data;
38 
39         free(acl->xpath);
40         free(acl);
41     }
42 }
43 
44 void
pcmk__free_acls(GList * acls)45 pcmk__free_acls(GList *acls)
46 {
47     g_list_free_full(acls, __xml_acl_free);
48 }
49 
50 static GList *
__xml_acl_create(xmlNode * xml,GList * acls,enum xml_private_flags mode)51 __xml_acl_create(xmlNode *xml, GList *acls, enum xml_private_flags mode)
52 {
53     xml_acl_t *acl = NULL;
54 
55     const char *tag = crm_element_value(xml, XML_ACL_ATTR_TAG);
56     const char *ref = crm_element_value(xml, XML_ACL_ATTR_REF);
57     const char *xpath = crm_element_value(xml, XML_ACL_ATTR_XPATH);
58 
59     if (tag == NULL) {
60         // @COMPAT rolling upgrades <=1.1.11
61         tag = crm_element_value(xml, XML_ACL_ATTR_TAGv1);
62     }
63     if (ref == NULL) {
64         // @COMPAT rolling upgrades <=1.1.11
65         ref = crm_element_value(xml, XML_ACL_ATTR_REFv1);
66     }
67 
68     if ((tag == NULL) && (ref == NULL) && (xpath == NULL)) {
69         crm_trace("No criteria %p", xml);
70         return NULL;
71     }
72 
73     acl = calloc(1, sizeof (xml_acl_t));
74     if (acl) {
75         const char *attr = crm_element_value(xml, XML_ACL_ATTR_ATTRIBUTE);
76 
77         acl->mode = mode;
78         if (xpath) {
79             acl->xpath = strdup(xpath);
80             crm_trace("Using xpath: %s", acl->xpath);
81 
82         } else {
83             int offset = 0;
84             char buffer[MAX_XPATH_LEN];
85 
86             if (tag) {
87                 offset += snprintf(buffer + offset, MAX_XPATH_LEN - offset,
88                                    "//%s", tag);
89             } else {
90                 offset += snprintf(buffer + offset, MAX_XPATH_LEN - offset,
91                                    "//*");
92             }
93 
94             if (ref || attr) {
95                 offset += snprintf(buffer + offset, MAX_XPATH_LEN - offset,
96                                    "[");
97             }
98 
99             if (ref) {
100                 offset += snprintf(buffer + offset, MAX_XPATH_LEN - offset,
101                                    "@id='%s'", ref);
102             }
103 
104             if (ref && attr) {
105                 offset += snprintf(buffer + offset, MAX_XPATH_LEN - offset,
106                                    " and ");
107             }
108 
109             if (attr) {
110                 offset += snprintf(buffer + offset, MAX_XPATH_LEN - offset,
111                                    "@%s", attr);
112             }
113 
114             if (ref || attr) {
115                 offset += snprintf(buffer + offset, MAX_XPATH_LEN - offset,
116                                    "]");
117             }
118 
119             CRM_LOG_ASSERT(offset > 0);
120             acl->xpath = strdup(buffer);
121             crm_trace("Built xpath: %s", acl->xpath);
122         }
123 
124         acls = g_list_append(acls, acl);
125     }
126     return acls;
127 }
128 
129 static GList *
__xml_acl_parse_entry(xmlNode * acl_top,xmlNode * acl_entry,GList * acls)130 __xml_acl_parse_entry(xmlNode *acl_top, xmlNode *acl_entry, GList *acls)
131 {
132     xmlNode *child = NULL;
133 
134     for (child = __xml_first_child_element(acl_entry); child;
135          child = __xml_next_element(child)) {
136         const char *tag = crm_element_name(child);
137         const char *kind = crm_element_value(child, XML_ACL_ATTR_KIND);
138 
139         if (strcmp(XML_ACL_TAG_PERMISSION, tag) == 0){
140             tag = kind;
141         }
142 
143         crm_trace("Processing %s %p", tag, child);
144         if (tag == NULL) {
145             CRM_ASSERT(tag != NULL);
146 
147         } else if (strcmp(XML_ACL_TAG_ROLE_REF, tag) == 0
148                    || strcmp(XML_ACL_TAG_ROLE_REFv1, tag) == 0) {
149             const char *ref_role = crm_element_value(child, XML_ATTR_ID);
150 
151             if (ref_role) {
152                 xmlNode *role = NULL;
153 
154                 for (role = __xml_first_child_element(acl_top); role;
155                      role = __xml_next_element(role)) {
156                     if (!strcmp(XML_ACL_TAG_ROLE, (const char *) role->name)) {
157                         const char *role_id = crm_element_value(role,
158                                                                 XML_ATTR_ID);
159 
160                         if (role_id && strcmp(ref_role, role_id) == 0) {
161                             crm_debug("Unpacking referenced role: %s", role_id);
162                             acls = __xml_acl_parse_entry(acl_top, role, acls);
163                             break;
164                         }
165                     }
166                 }
167             }
168 
169         } else if (strcmp(XML_ACL_TAG_READ, tag) == 0) {
170             acls = __xml_acl_create(child, acls, xpf_acl_read);
171 
172         } else if (strcmp(XML_ACL_TAG_WRITE, tag) == 0) {
173             acls = __xml_acl_create(child, acls, xpf_acl_write);
174 
175         } else if (strcmp(XML_ACL_TAG_DENY, tag) == 0) {
176             acls = __xml_acl_create(child, acls, xpf_acl_deny);
177 
178         } else {
179             crm_warn("Unknown ACL entry: %s/%s", tag, kind);
180         }
181     }
182 
183     return acls;
184 }
185 
186 /*
187     <acls>
188       <acl_target id="l33t-haxor"><role id="auto-l33t-haxor"/></acl_target>
189       <acl_role id="auto-l33t-haxor">
190         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
191       </acl_role>
192       <acl_target id="niceguy">
193         <role id="observer"/>
194       </acl_target>
195       <acl_role id="observer">
196         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
197         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
198         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
199       </acl_role>
200       <acl_target id="badidea"><role id="auto-badidea"/></acl_target>
201       <acl_role id="auto-badidea">
202         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
203         <acl_permission id="badidea-resources-2" kind="deny" reference="dummy-meta_attributes"/>
204       </acl_role>
205     </acls>
206 */
207 
208 #ifdef SUSE_ACL_COMPAT
209 static const char *
__xml_acl_to_text(enum xml_private_flags flags)210 __xml_acl_to_text(enum xml_private_flags flags)
211 {
212     if (is_set(flags, xpf_acl_deny)) {
213         return "deny";
214 
215     } else if (is_set(flags, xpf_acl_write)) {
216         return "read/write";
217 
218     } else if (is_set(flags, xpf_acl_read)) {
219         return "read";
220     }
221     return "none";
222 }
223 #endif
224 
225 void
pcmk__apply_acl(xmlNode * xml)226 pcmk__apply_acl(xmlNode *xml)
227 {
228     GListPtr aIter = NULL;
229     xml_private_t *p = xml->doc->_private;
230     xmlXPathObjectPtr xpathObj = NULL;
231 
232     if (xml_acl_enabled(xml) == FALSE) {
233         crm_trace("Not applying ACLs for %s", p->user);
234         return;
235     }
236 
237     for (aIter = p->acls; aIter != NULL; aIter = aIter->next) {
238         int max = 0, lpc = 0;
239         xml_acl_t *acl = aIter->data;
240 
241         xpathObj = xpath_search(xml, acl->xpath);
242         max = numXpathResults(xpathObj);
243 
244         for (lpc = 0; lpc < max; lpc++) {
245             xmlNode *match = getXpathResult(xpathObj, lpc);
246             char *path = xml_get_path(match);
247 
248             p = match->_private;
249             crm_trace("Applying %x to %s for %s", acl->mode, path, acl->xpath);
250 
251 #ifdef SUSE_ACL_COMPAT
252             if (is_not_set(p->flags, acl->mode)
253                 && (is_set(p->flags, xpf_acl_read)
254                     || is_set(p->flags, xpf_acl_write)
255                     || is_set(p->flags, xpf_acl_deny))) {
256                 crm_config_warn("Configuration element %s is matched by "
257                                 "multiple ACL rules, only the first applies "
258                                 "('%s' wins over '%s')",
259                                 path, __xml_acl_to_text(p->flags),
260                                 __xml_acl_to_text(acl->mode));
261                 free(path);
262                 continue;
263             }
264 #endif
265             p->flags |= acl->mode;
266             free(path);
267         }
268         crm_trace("Now enforcing ACL: %s (%d matches)", acl->xpath, max);
269         freeXpathObject(xpathObj);
270     }
271 
272     p = xml->_private;
273     if (is_not_set(p->flags, xpf_acl_read)
274         && is_not_set(p->flags, xpf_acl_write)) {
275 
276         p->flags |= xpf_acl_deny;
277         p = xml->doc->_private;
278         crm_info("Enforcing default ACL for %s to %s",
279                  p->user, crm_element_name(xml));
280     }
281 
282 }
283 
284 void
pcmk__unpack_acl(xmlNode * source,xmlNode * target,const char * user)285 pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
286 {
287 #if ENABLE_ACL
288     xml_private_t *p = NULL;
289 
290     if ((target == NULL) || (target->doc == NULL)
291         || (target->doc->_private == NULL)) {
292         return;
293     }
294 
295     p = target->doc->_private;
296     if (pcmk_acl_required(user) == FALSE) {
297         crm_trace("no acls needed for '%s'", user);
298 
299     } else if (p->acls == NULL) {
300         xmlNode *acls = get_xpath_object("//" XML_CIB_TAG_ACLS,
301                                          source, LOG_TRACE);
302 
303         free(p->user);
304         p->user = strdup(user);
305 
306         if (acls) {
307             xmlNode *child = NULL;
308 
309             for (child = __xml_first_child_element(acls); child;
310                  child = __xml_next_element(child)) {
311                 const char *tag = crm_element_name(child);
312 
313                 if (!strcmp(tag, XML_ACL_TAG_USER)
314                     || !strcmp(tag, XML_ACL_TAG_USERv1)) {
315                     const char *id = crm_element_value(child, XML_ATTR_ID);
316 
317                     if (id && strcmp(id, user) == 0) {
318                         crm_debug("Unpacking ACLs for %s", id);
319                         p->acls = __xml_acl_parse_entry(acls, child, p->acls);
320                     }
321                 }
322             }
323         }
324     }
325 #endif
326 }
327 
328 static inline bool
__xml_acl_mode_test(enum xml_private_flags allowed,enum xml_private_flags requested)329 __xml_acl_mode_test(enum xml_private_flags allowed,
330                     enum xml_private_flags requested)
331 {
332     if (is_set(allowed, xpf_acl_deny)) {
333         return FALSE;
334 
335     } else if (is_set(allowed, requested)) {
336         return TRUE;
337 
338     } else if (is_set(requested, xpf_acl_read)
339                && is_set(allowed, xpf_acl_write)) {
340         return TRUE;
341 
342     } else if (is_set(requested, xpf_acl_create)
343                && is_set(allowed, xpf_acl_write)) {
344         return TRUE;
345 
346     } else if (is_set(requested, xpf_acl_create)
347                && is_set(allowed, xpf_created)) {
348         return TRUE;
349     }
350     return FALSE;
351 }
352 
353 /* rc = TRUE if orig_cib has been filtered
354  * That means '*result' rather than 'xml' should be exploited afterwards
355  */
356 static bool
__xml_purge_attributes(xmlNode * xml)357 __xml_purge_attributes(xmlNode *xml)
358 {
359     xmlNode *child = NULL;
360     xmlAttr *xIter = NULL;
361     bool readable_children = FALSE;
362     xml_private_t *p = xml->_private;
363 
364     if (__xml_acl_mode_test(p->flags, xpf_acl_read)) {
365         crm_trace("%s[@id=%s] is readable", crm_element_name(xml), ID(xml));
366         return TRUE;
367     }
368 
369     xIter = xml->properties;
370     while (xIter != NULL) {
371         xmlAttr *tmp = xIter;
372         const char *prop_name = (const char *)xIter->name;
373 
374         xIter = xIter->next;
375         if (strcmp(prop_name, XML_ATTR_ID) == 0) {
376             continue;
377         }
378 
379         xmlUnsetProp(xml, tmp->name);
380     }
381 
382     child = __xml_first_child(xml);
383     while ( child != NULL ) {
384         xmlNode *tmp = child;
385 
386         child = __xml_next(child);
387         readable_children |= __xml_purge_attributes(tmp);
388     }
389 
390     if (readable_children == FALSE) {
391         free_xml(xml); /* Nothing readable under here, purge completely */
392     }
393     return readable_children;
394 }
395 
396 bool
xml_acl_filtered_copy(const char * user,xmlNode * acl_source,xmlNode * xml,xmlNode ** result)397 xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml,
398                       xmlNode **result)
399 {
400     GListPtr aIter = NULL;
401     xmlNode *target = NULL;
402     xml_private_t *p = NULL;
403     xml_private_t *doc = NULL;
404 
405     *result = NULL;
406     if (xml == NULL || pcmk_acl_required(user) == FALSE) {
407         crm_trace("no acls needed for '%s'", user);
408         return FALSE;
409     }
410 
411     crm_trace("filtering copy of %p for '%s'", xml, user);
412     target = copy_xml(xml);
413     if (target == NULL) {
414         return TRUE;
415     }
416 
417     pcmk__unpack_acl(acl_source, target, user);
418     pcmk__set_xml_flag(target, xpf_acl_enabled);
419     pcmk__apply_acl(target);
420 
421     doc = target->doc->_private;
422     for(aIter = doc->acls; aIter != NULL && target; aIter = aIter->next) {
423         int max = 0;
424         xml_acl_t *acl = aIter->data;
425 
426         if (acl->mode != xpf_acl_deny) {
427             /* Nothing to do */
428 
429         } else if (acl->xpath) {
430             int lpc = 0;
431             xmlXPathObjectPtr xpathObj = xpath_search(target, acl->xpath);
432 
433             max = numXpathResults(xpathObj);
434             for(lpc = 0; lpc < max; lpc++) {
435                 xmlNode *match = getXpathResult(xpathObj, lpc);
436 
437                 crm_trace("Purging attributes from %s", acl->xpath);
438                 if (__xml_purge_attributes(match) == FALSE && match == target) {
439                     crm_trace("No access to the entire document for %s", user);
440                     freeXpathObject(xpathObj);
441                     return TRUE;
442                 }
443             }
444             crm_trace("Enforced ACL %s (%d matches)", acl->xpath, max);
445             freeXpathObject(xpathObj);
446         }
447     }
448 
449     p = target->_private;
450     if (is_set(p->flags, xpf_acl_deny)
451         && (__xml_purge_attributes(target) == FALSE)) {
452         crm_trace("No access to the entire document for %s", user);
453         return TRUE;
454     }
455 
456     if (doc->acls) {
457         g_list_free_full(doc->acls, __xml_acl_free);
458         doc->acls = NULL;
459 
460     } else {
461         crm_trace("Ordinary user '%s' cannot access the CIB without any defined ACLs",
462                   doc->user);
463         free_xml(target);
464         target = NULL;
465     }
466 
467     if (target) {
468         *result = target;
469     }
470 
471     return TRUE;
472 }
473 
474 /*!
475  * \internal
476  * \brief Check whether creation of an XML element is implicitly allowed
477  *
478  * Check whether XML is a "scaffolding" element whose creation is implicitly
479  * allowed regardless of ACLs (that is, it is not in the ACL section and has
480  * no attributes other than "id").
481  *
482  * \param[in] xml  XML element to check
483  *
484  * \return TRUE if XML element is implicitly allowed, FALSE otherwise
485  */
486 static bool
implicitly_allowed(xmlNode * xml)487 implicitly_allowed(xmlNode *xml)
488 {
489     char *path = NULL;
490 
491     for (xmlAttr *prop = xml->properties; prop != NULL; prop = prop->next) {
492         if (strcmp((const char *) prop->name, XML_ATTR_ID) != 0) {
493             return FALSE;
494         }
495     }
496 
497     path = xml_get_path(xml);
498     if (strstr(path, "/" XML_CIB_TAG_ACLS "/") != NULL) {
499         free(path);
500         return FALSE;
501     }
502     free(path);
503 
504     return TRUE;
505 }
506 
507 #define display_id(xml) (ID(xml)? ID(xml) : "<unset>")
508 
509 /*!
510  * \internal
511  * \brief Drop XML nodes created in violation of ACLs
512  *
513  * Given an XML element, free all of its descendent nodes created in violation
514  * of ACLs, with the exception of allowing "scaffolding" elements (i.e. those
515  * that aren't in the ACL section and don't have any attributes other than
516  * "id").
517  *
518  * \param[in,out] xml        XML to check
519  * \param[in]     check_top  Whether to apply checks to argument itself
520  *                           (if TRUE, xml might get freed)
521  */
522 void
pcmk__post_process_acl(xmlNode * xml,bool check_top)523 pcmk__post_process_acl(xmlNode *xml, bool check_top)
524 {
525     xml_private_t *p = xml->_private;
526 
527     if (is_set(p->flags, xpf_created)) {
528         if (implicitly_allowed(xml)) {
529             crm_trace("Creation of <%s> scaffolding with id=\"%s\""
530                       " is implicitly allowed",
531                       crm_element_name(xml), display_id(xml));
532 
533         } else if (pcmk__check_acl(xml, NULL, xpf_acl_write)) {
534             crm_trace("ACLs allow creation of <%s> with id=\"%s\"",
535                       crm_element_name(xml), display_id(xml));
536 
537         } else if (check_top) {
538             crm_trace("ACLs disallow creation of <%s> with id=\"%s\"",
539                       crm_element_name(xml), display_id(xml));
540             pcmk_free_xml_subtree(xml);
541             return;
542 
543         } else {
544             crm_trace("ACLs would disallow creation of <%s> with id=\"%s\"",
545                       crm_element_name(xml), display_id(xml));
546         }
547     }
548 
549     for (xmlNode *cIter = __xml_first_child(xml); cIter != NULL; ) {
550         xmlNode *child = cIter;
551         cIter = __xml_next(cIter); /* In case it is free'd */
552         pcmk__post_process_acl(child, TRUE);
553     }
554 }
555 
556 bool
xml_acl_denied(xmlNode * xml)557 xml_acl_denied(xmlNode *xml)
558 {
559     if (xml && xml->doc && xml->doc->_private){
560         xml_private_t *p = xml->doc->_private;
561 
562         return is_set(p->flags, xpf_acl_denied);
563     }
564     return FALSE;
565 }
566 
567 void
xml_acl_disable(xmlNode * xml)568 xml_acl_disable(xmlNode *xml)
569 {
570     if (xml_acl_enabled(xml)) {
571         xml_private_t *p = xml->doc->_private;
572 
573         /* Catch anything that was created but shouldn't have been */
574         pcmk__apply_acl(xml);
575         pcmk__post_process_acl(xml, FALSE);
576         clear_bit(p->flags, xpf_acl_enabled);
577     }
578 }
579 
580 bool
xml_acl_enabled(xmlNode * xml)581 xml_acl_enabled(xmlNode *xml)
582 {
583     if (xml && xml->doc && xml->doc->_private){
584         xml_private_t *p = xml->doc->_private;
585 
586         return is_set(p->flags, xpf_acl_enabled);
587     }
588     return FALSE;
589 }
590 
591 bool
pcmk__check_acl(xmlNode * xml,const char * name,enum xml_private_flags mode)592 pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
593 {
594     CRM_ASSERT(xml);
595     CRM_ASSERT(xml->doc);
596     CRM_ASSERT(xml->doc->_private);
597 
598 #if ENABLE_ACL
599     if (pcmk__tracking_xml_changes(xml, FALSE) && xml_acl_enabled(xml)) {
600         int offset = 0;
601         xmlNode *parent = xml;
602         char buffer[MAX_XPATH_LEN];
603         xml_private_t *docp = xml->doc->_private;
604 
605         if (docp->acls == NULL) {
606             crm_trace("Ordinary user %s cannot access the CIB without any defined ACLs",
607                       docp->user);
608             pcmk__set_xml_flag(xml, xpf_acl_denied);
609             return FALSE;
610         }
611 
612         offset = pcmk__element_xpath(NULL, xml, buffer, offset,
613                                      sizeof(buffer));
614         if (name) {
615             offset += snprintf(buffer + offset, MAX_XPATH_LEN - offset,
616                                "[@%s]", name);
617         }
618         CRM_LOG_ASSERT(offset > 0);
619 
620         /* Walk the tree upwards looking for xml_acl_* flags
621          * - Creating an attribute requires write permissions for the node
622          * - Creating a child requires write permissions for the parent
623          */
624 
625         if (name) {
626             xmlAttr *attr = xmlHasProp(xml, (const xmlChar *)name);
627 
628             if (attr && mode == xpf_acl_create) {
629                 mode = xpf_acl_write;
630             }
631         }
632 
633         while (parent && parent->_private) {
634             xml_private_t *p = parent->_private;
635             if (__xml_acl_mode_test(p->flags, mode)) {
636                 return TRUE;
637 
638             } else if (is_set(p->flags, xpf_acl_deny)) {
639                 crm_trace("%x access denied to %s: parent", mode, buffer);
640                 pcmk__set_xml_flag(xml, xpf_acl_denied);
641                 return FALSE;
642             }
643             parent = parent->parent;
644         }
645 
646         crm_trace("%x access denied to %s: default", mode, buffer);
647         pcmk__set_xml_flag(xml, xpf_acl_denied);
648         return FALSE;
649     }
650 #endif
651 
652     return TRUE;
653 }
654 
655 bool
pcmk_acl_required(const char * user)656 pcmk_acl_required(const char *user)
657 {
658 #if ENABLE_ACL
659     if (user == NULL || strlen(user) == 0) {
660         crm_trace("no user set");
661         return FALSE;
662 
663     } else if (strcmp(user, CRM_DAEMON_USER) == 0) {
664         return FALSE;
665 
666     } else if (strcmp(user, "root") == 0) {
667         return FALSE;
668     }
669     crm_trace("ACLs required for %s", user);
670     return TRUE;
671 #else
672     crm_trace("ACLs not supported");
673     return FALSE;
674 #endif
675 }
676 
677 #if ENABLE_ACL
678 char *
uid2username(uid_t uid)679 uid2username(uid_t uid)
680 {
681     struct passwd *pwent = getpwuid(uid);
682 
683     if (pwent == NULL) {
684         crm_perror(LOG_INFO, "Cannot get user details for user ID %d", uid);
685         return NULL;
686     }
687     return strdup(pwent->pw_name);
688 }
689 
690 const char *
crm_acl_get_set_user(xmlNode * request,const char * field,const char * peer_user)691 crm_acl_get_set_user(xmlNode *request, const char *field, const char *peer_user)
692 {
693     static const char *effective_user = NULL;
694     const char *requested_user = NULL;
695     const char *user = NULL;
696 
697     if (effective_user == NULL) {
698         effective_user = uid2username(geteuid());
699         if (effective_user == NULL) {
700             effective_user = strdup("#unprivileged");
701             CRM_CHECK(effective_user != NULL, return NULL);
702             crm_err("Unable to determine effective user, assuming unprivileged for ACLs");
703         }
704     }
705 
706     requested_user = crm_element_value(request, XML_ACL_TAG_USER);
707     if (requested_user == NULL) {
708         /* @COMPAT rolling upgrades <=1.1.11
709          *
710          * field is checked for backward compatibility with older versions that
711          * did not use XML_ACL_TAG_USER.
712          */
713         requested_user = crm_element_value(request, field);
714     }
715 
716     if (is_privileged(effective_user) == FALSE) {
717         /* We're not running as a privileged user, set or overwrite any existing
718          * value for $XML_ACL_TAG_USER
719          */
720         user = effective_user;
721 
722     } else if (peer_user == NULL && requested_user == NULL) {
723         /* No user known or requested, use 'effective_user' and make sure one is
724          * set for the request
725          */
726         user = effective_user;
727 
728     } else if (peer_user == NULL) {
729         /* No user known, trusting 'requested_user' */
730         user = requested_user;
731 
732     } else if (is_privileged(peer_user) == FALSE) {
733         /* The peer is not a privileged user, set or overwrite any existing
734          * value for $XML_ACL_TAG_USER
735          */
736         user = peer_user;
737 
738     } else if (requested_user == NULL) {
739         /* Even if we're privileged, make sure there is always a value set */
740         user = peer_user;
741 
742     } else {
743         /* Legal delegation to 'requested_user' */
744         user = requested_user;
745     }
746 
747     // This requires pointer comparison, not string comparison
748     if (user != crm_element_value(request, XML_ACL_TAG_USER)) {
749         crm_xml_add(request, XML_ACL_TAG_USER, user);
750     }
751 
752     if (field != NULL && user != crm_element_value(request, field)) {
753         crm_xml_add(request, field, user);
754     }
755 
756     return requested_user;
757 }
758 #endif
759