root/lib/common/acl.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. free_acl
  2. pcmk__free_acls
  3. create_acl
  4. parse_acl_entry
  5. acl_to_text
  6. pcmk__apply_acl
  7. pcmk__unpack_acl
  8. pcmk__enable_acl
  9. test_acl_mode
  10. purge_xml_attributes
  11. xml_acl_filtered_copy
  12. implicitly_allowed
  13. pcmk__apply_creation_acl
  14. xml_acl_denied
  15. xml_acl_disable
  16. xml_acl_enabled
  17. pcmk__check_acl
  18. pcmk_acl_required
  19. pcmk__uid2username
  20. pcmk__update_acl_user

   1 /*
   2  * Copyright 2004-2022 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 <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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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         // @COMPAT rolling upgrades <=1.1.11
  63         tag = crm_element_value(xml, XML_ACL_ATTR_TAGv1);
  64     }
  65     if (ref == NULL) {
  66         // @COMPAT rolling upgrades <=1.1.11
  67         ref = crm_element_value(xml, XML_ACL_ATTR_REFv1);
  68     }
  69 
  70     if ((tag == NULL) && (ref == NULL) && (xpath == NULL)) {
  71         // Schema should prevent this, but to be safe ...
  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         // NOTE: schema currently does not allow this
 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  * \internal
 138  * \brief Unpack a user, group, or role subtree of the ACLs section
 139  *
 140  * \param[in]     acl_top    XML of entire ACLs section
 141  * \param[in]     acl_entry  XML of ACL element being unpacked
 142  * \param[in,out] acls       List of ACLs unpacked so far
 143  *
 144  * \return New head of (possibly modified) acls
 145  */
 146 static GList *
 147 parse_acl_entry(xmlNode *acl_top, xmlNode *acl_entry, GList *acls)
     /* [previous][next][first][last][top][bottom][index][help] */
 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     <acls>
 207       <acl_target id="l33t-haxor"><role id="auto-l33t-haxor"/></acl_target>
 208       <acl_role id="auto-l33t-haxor">
 209         <acl_permission id="crook-nothing" kind="deny" xpath="/cib"/>
 210       </acl_role>
 211       <acl_target id="niceguy">
 212         <role id="observer"/>
 213       </acl_target>
 214       <acl_role id="observer">
 215         <acl_permission id="observer-read-1" kind="read" xpath="/cib"/>
 216         <acl_permission id="observer-write-1" kind="write" xpath="//nvpair[@name='stonith-enabled']"/>
 217         <acl_permission id="observer-write-2" kind="write" xpath="//nvpair[@name='target-role']"/>
 218       </acl_role>
 219       <acl_target id="badidea"><role id="auto-badidea"/></acl_target>
 220       <acl_role id="auto-badidea">
 221         <acl_permission id="badidea-resources" kind="read" xpath="//meta_attributes"/>
 222         <acl_permission id="badidea-resources-2" kind="deny" reference="dummy-meta_attributes"/>
 223       </acl_role>
 224     </acls>
 225 */
 226 
 227 static const char *
 228 acl_to_text(enum xml_private_flags flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  * \internal
 281  * \brief Unpack ACLs for a given user
 282  *
 283  * \param[in]     source  XML with ACL definitions
 284  * \param[in,out] target  XML that ACLs will be applied to
 285  * \param[in]     user    Username whose ACLs need to be unpacked
 286  */
 287 void
 288 pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  * \internal
 331  * \brief Copy source to target and set xf_acl_enabled flag in target
 332  *
 333  * \param[in]     acl_source    XML with ACL definitions
 334  * \param[in,out] target        XML that ACLs will be applied to
 335  * \param[in]     user          Username whose ACLs need to be set
 336  */
 337 void
 338 pcmk__enable_acl(xmlNode *acl_source, xmlNode *target, const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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); /* Nothing readable under here, purge completely */
 401     }
 402     return readable_children;
 403 }
 404 
 405 /*!
 406  * \brief Copy ACL-allowed portions of specified XML
 407  *
 408  * \param[in]  user        Username whose ACLs should be used
 409  * \param[in]  acl_source  XML containing ACLs
 410  * \param[in]  xml         XML to be copied
 411  * \param[out] result      Copy of XML portions readable via ACLs
 412  *
 413  * \return true if xml exists and ACLs are required for user, false otherwise
 414  * \note If this returns true, caller should use \p result rather than \p xml
 415  */
 416 bool
 417 xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 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             /* Nothing to do */
 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  * \internal
 494  * \brief Check whether creation of an XML element is implicitly allowed
 495  *
 496  * Check whether XML is a "scaffolding" element whose creation is implicitly
 497  * allowed regardless of ACLs (that is, it is not in the ACL section and has
 498  * no attributes other than "id").
 499  *
 500  * \param[in] xml  XML element to check
 501  *
 502  * \return true if XML element is implicitly allowed, false otherwise
 503  */
 504 static bool
 505 implicitly_allowed(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  * \internal
 529  * \brief Drop XML nodes created in violation of ACLs
 530  *
 531  * Given an XML element, free all of its descendent nodes created in violation
 532  * of ACLs, with the exception of allowing "scaffolding" elements (i.e. those
 533  * that aren't in the ACL section and don't have any attributes other than
 534  * "id").
 535  *
 536  * \param[in,out] xml        XML to check
 537  * \param[in]     check_top  Whether to apply checks to argument itself
 538  *                           (if true, xml might get freed)
 539  */
 540 void
 541 pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
     /* [previous][next][first][last][top][bottom][index][help] */
 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); /* In case it is free'd */
 571         pcmk__apply_creation_acl(child, true);
 572     }
 573 }
 574 
 575 bool
 576 xml_acl_denied(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 588 {
 589     if (xml_acl_enabled(xml)) {
 590         xml_private_t *p = xml->doc->_private;
 591 
 592         /* Catch anything that was created but shouldn't have been */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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         /* Walk the tree upwards looking for xml_acl_* flags
 639          * - Creating an attribute requires write permissions for the node
 640          * - Creating a child requires write permissions for the parent
 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  * \brief Check whether ACLs are required for a given user
 677  *
 678  * \param[in]  User name to check
 679  *
 680  * \return true if the user requires ACLs, false otherwise
 681  */
 682 bool
 683 pcmk_acl_required(const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  * \internal
 711  * \brief Set the ACL user field properly on an XML request
 712  *
 713  * Multiple user names are potentially involved in an XML request: the effective
 714  * user of the current process; the user name known from an IPC client
 715  * connection; and the user name obtained from the request itself, whether by
 716  * the current standard XML attribute name or an older legacy attribute name.
 717  * This function chooses the appropriate one that should be used for ACLs, sets
 718  * it in the request (using the standard attribute name, and the legacy name if
 719  * given), and returns it.
 720  *
 721  * \param[in,out] request    XML request to update
 722  * \param[in]     field      Alternate name for ACL user name XML attribute
 723  * \param[in]     peer_user  User name as known from IPC connection
 724  *
 725  * \return ACL user name actually used
 726  */
 727 const char *
 728 pcmk__update_acl_user(xmlNode *request, const char *field,
     /* [previous][next][first][last][top][bottom][index][help] */
 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         /* @COMPAT rolling upgrades <=1.1.11
 747          *
 748          * field is checked for backward compatibility with older versions that
 749          * did not use XML_ACL_TAG_USER.
 750          */
 751         requested_user = crm_element_value(request, field);
 752     }
 753 
 754     if (!pcmk__is_privileged(effective_user)) {
 755         /* We're not running as a privileged user, set or overwrite any existing
 756          * value for $XML_ACL_TAG_USER
 757          */
 758         user = effective_user;
 759 
 760     } else if (peer_user == NULL && requested_user == NULL) {
 761         /* No user known or requested, use 'effective_user' and make sure one is
 762          * set for the request
 763          */
 764         user = effective_user;
 765 
 766     } else if (peer_user == NULL) {
 767         /* No user known, trusting 'requested_user' */
 768         user = requested_user;
 769 
 770     } else if (!pcmk__is_privileged(peer_user)) {
 771         /* The peer is not a privileged user, set or overwrite any existing
 772          * value for $XML_ACL_TAG_USER
 773          */
 774         user = peer_user;
 775 
 776     } else if (requested_user == NULL) {
 777         /* Even if we're privileged, make sure there is always a value set */
 778         user = peer_user;
 779 
 780     } else {
 781         /* Legal delegation to 'requested_user' */
 782         user = requested_user;
 783     }
 784 
 785     // This requires pointer comparison, not string comparison
 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 }

/* [previous][next][first][last][top][bottom][index][help] */