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-2023 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 typedef struct xml_acl_s {
  28         enum xml_private_flags mode;
  29         char *xpath;
  30 } xml_acl_t;
  31 
  32 static void
  33 free_acl(void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
  34 {
  35     if (data) {
  36         xml_acl_t *acl = data;
  37 
  38         free(acl->xpath);
  39         free(acl);
  40     }
  41 }
  42 
  43 void
  44 pcmk__free_acls(GList *acls)
     /* [previous][next][first][last][top][bottom][index][help] */
  45 {
  46     g_list_free_full(acls, free_acl);
  47 }
  48 
  49 static GList *
  50 create_acl(const xmlNode *xml, GList *acls, enum xml_private_flags mode)
     /* [previous][next][first][last][top][bottom][index][help] */
  51 {
  52     xml_acl_t *acl = NULL;
  53 
  54     const char *tag = crm_element_value(xml, XML_ACL_ATTR_TAG);
  55     const char *ref = crm_element_value(xml, XML_ACL_ATTR_REF);
  56     const char *xpath = crm_element_value(xml, XML_ACL_ATTR_XPATH);
  57     const char *attr = crm_element_value(xml, XML_ACL_ATTR_ATTRIBUTE);
  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         // Schema should prevent this, but to be safe ...
  70         crm_trace("Ignoring ACL <%s> element without selection criteria",
  71                   crm_element_name(xml));
  72         return NULL;
  73     }
  74 
  75     acl = calloc(1, sizeof (xml_acl_t));
  76     CRM_ASSERT(acl != NULL);
  77 
  78     acl->mode = mode;
  79     if (xpath) {
  80         acl->xpath = strdup(xpath);
  81         CRM_ASSERT(acl->xpath != NULL);
  82         crm_trace("Unpacked ACL <%s> element using xpath: %s",
  83                   crm_element_name(xml), acl->xpath);
  84 
  85     } else {
  86         GString *buf = g_string_sized_new(128);
  87 
  88         if ((ref != NULL) && (attr != NULL)) {
  89             // NOTE: schema currently does not allow this
  90             pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@" XML_ATTR_ID "='",
  91                            ref, "' and @", attr, "]", NULL);
  92 
  93         } else if (ref != NULL) {
  94             pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@" XML_ATTR_ID "='",
  95                            ref, "']", NULL);
  96 
  97         } else if (attr != NULL) {
  98             pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), "[@", attr, "]", NULL);
  99 
 100         } else {
 101             pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), NULL);
 102         }
 103 
 104         acl->xpath = strdup((const char *) buf->str);
 105         CRM_ASSERT(acl->xpath != NULL);
 106 
 107         g_string_free(buf, TRUE);
 108         crm_trace("Unpacked ACL <%s> element as xpath: %s",
 109                   crm_element_name(xml), acl->xpath);
 110     }
 111 
 112     return g_list_append(acls, acl);
 113 }
 114 
 115 /*!
 116  * \internal
 117  * \brief Unpack a user, group, or role subtree of the ACLs section
 118  *
 119  * \param[in]     acl_top    XML of entire ACLs section
 120  * \param[in]     acl_entry  XML of ACL element being unpacked
 121  * \param[in,out] acls       List of ACLs unpacked so far
 122  *
 123  * \return New head of (possibly modified) acls
 124  *
 125  * \note This function is recursive
 126  */
 127 static GList *
 128 parse_acl_entry(const xmlNode *acl_top, const xmlNode *acl_entry, GList *acls)
     /* [previous][next][first][last][top][bottom][index][help] */
 129 {
 130     xmlNode *child = NULL;
 131 
 132     for (child = pcmk__xe_first_child(acl_entry); child;
 133          child = pcmk__xe_next(child)) {
 134         const char *tag = crm_element_name(child);
 135         const char *kind = crm_element_value(child, XML_ACL_ATTR_KIND);
 136 
 137         if (strcmp(XML_ACL_TAG_PERMISSION, tag) == 0){
 138             CRM_ASSERT(kind != NULL);
 139             crm_trace("Unpacking ACL <%s> element of kind '%s'", tag, kind);
 140             tag = kind;
 141         } else {
 142             crm_trace("Unpacking ACL <%s> element", tag);
 143         }
 144 
 145         if (strcmp(XML_ACL_TAG_ROLE_REF, tag) == 0
 146                    || strcmp(XML_ACL_TAG_ROLE_REFv1, tag) == 0) {
 147             const char *ref_role = crm_element_value(child, XML_ATTR_ID);
 148 
 149             if (ref_role) {
 150                 xmlNode *role = NULL;
 151 
 152                 for (role = pcmk__xe_first_child(acl_top); role;
 153                      role = pcmk__xe_next(role)) {
 154                     if (!strcmp(XML_ACL_TAG_ROLE, (const char *) role->name)) {
 155                         const char *role_id = crm_element_value(role,
 156                                                                 XML_ATTR_ID);
 157 
 158                         if (role_id && strcmp(ref_role, role_id) == 0) {
 159                             crm_trace("Unpacking referenced role '%s' in ACL <%s> element",
 160                                       role_id, crm_element_name(acl_entry));
 161                             acls = parse_acl_entry(acl_top, role, acls);
 162                             break;
 163                         }
 164                     }
 165                 }
 166             }
 167 
 168         } else if (strcmp(XML_ACL_TAG_READ, tag) == 0) {
 169             acls = create_acl(child, acls, pcmk__xf_acl_read);
 170 
 171         } else if (strcmp(XML_ACL_TAG_WRITE, tag) == 0) {
 172             acls = create_acl(child, acls, pcmk__xf_acl_write);
 173 
 174         } else if (strcmp(XML_ACL_TAG_DENY, tag) == 0) {
 175             acls = create_acl(child, acls, pcmk__xf_acl_deny);
 176 
 177         } else {
 178             crm_warn("Ignoring unknown ACL %s '%s'",
 179                      (kind? "kind" : "element"), tag);
 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 static const char *
 209 acl_to_text(enum xml_private_flags flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 210 {
 211     if (pcmk_is_set(flags, pcmk__xf_acl_deny)) {
 212         return "deny";
 213 
 214     } else if (pcmk_any_flags_set(flags, pcmk__xf_acl_write|pcmk__xf_acl_create)) {
 215         return "read/write";
 216 
 217     } else if (pcmk_is_set(flags, pcmk__xf_acl_read)) {
 218         return "read";
 219     }
 220     return "none";
 221 }
 222 
 223 void
 224 pcmk__apply_acl(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226     GList *aIter = NULL;
 227     xml_doc_private_t *docpriv = xml->doc->_private;
 228     xml_node_private_t *nodepriv;
 229     xmlXPathObjectPtr xpathObj = NULL;
 230 
 231     if (!xml_acl_enabled(xml)) {
 232         crm_trace("Skipping ACLs for user '%s' because not enabled for this XML",
 233                   docpriv->user);
 234         return;
 235     }
 236 
 237     for (aIter = docpriv->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 
 247             nodepriv = match->_private;
 248             pcmk__set_xml_flags(nodepriv, acl->mode);
 249 
 250             // Build a GString only if tracing is enabled
 251             pcmk__if_tracing(
 252                 {
 253                     GString *path = pcmk__element_xpath(match);
 254                     crm_trace("Applying %s ACL to %s matched by %s",
 255                               acl_to_text(acl->mode), path->str, acl->xpath);
 256                     g_string_free(path, TRUE);
 257                 },
 258                 {}
 259             );
 260         }
 261         crm_trace("Applied %s ACL %s (%d match%s)",
 262                   acl_to_text(acl->mode), acl->xpath, max,
 263                   ((max == 1)? "" : "es"));
 264         freeXpathObject(xpathObj);
 265     }
 266 }
 267 
 268 /*!
 269  * \internal
 270  * \brief Unpack ACLs for a given user into the
 271  * metadata of the target XML tree
 272  *
 273  * Taking the description of ACLs from the source XML tree and
 274  * marking up the target XML tree with access information for the
 275  * given user by tacking it onto the relevant nodes
 276  *
 277  * \param[in]     source  XML with ACL definitions
 278  * \param[in,out] target  XML that ACLs will be applied to
 279  * \param[in]     user    Username whose ACLs need to be unpacked
 280  */
 281 void
 282 pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 283 {
 284     xml_doc_private_t *docpriv = NULL;
 285 
 286     if ((target == NULL) || (target->doc == NULL)
 287         || (target->doc->_private == NULL)) {
 288         return;
 289     }
 290 
 291     docpriv = target->doc->_private;
 292     if (!pcmk_acl_required(user)) {
 293         crm_trace("Not unpacking ACLs because not required for user '%s'",
 294                   user);
 295 
 296     } else if (docpriv->acls == NULL) {
 297         xmlNode *acls = get_xpath_object("//" XML_CIB_TAG_ACLS,
 298                                          source, LOG_NEVER);
 299 
 300         pcmk__str_update(&docpriv->user, user);
 301 
 302         if (acls) {
 303             xmlNode *child = NULL;
 304 
 305             for (child = pcmk__xe_first_child(acls); child;
 306                  child = pcmk__xe_next(child)) {
 307                 const char *tag = crm_element_name(child);
 308 
 309                 if (!strcmp(tag, XML_ACL_TAG_USER)
 310                     || !strcmp(tag, XML_ACL_TAG_USERv1)) {
 311                     const char *id = crm_element_value(child, XML_ATTR_NAME);
 312 
 313                     if (id == NULL) {
 314                         id = crm_element_value(child, XML_ATTR_ID);
 315                     }
 316 
 317                     if (id && strcmp(id, user) == 0) {
 318                         crm_debug("Unpacking ACLs for user '%s'", id);
 319                         docpriv->acls = parse_acl_entry(acls, child, docpriv->acls);
 320                     }
 321                 } else if (!strcmp(tag, XML_ACL_TAG_GROUP)) {
 322                     const char *id = crm_element_value(child, XML_ATTR_NAME);
 323 
 324                     if (id == NULL) {
 325                         id = crm_element_value(child, XML_ATTR_ID);
 326                     }
 327 
 328                     if (id && pcmk__is_user_in_group(user,id)) {
 329                         crm_debug("Unpacking ACLs for group '%s'", id);
 330                         docpriv->acls = parse_acl_entry(acls, child, docpriv->acls);
 331                     }
 332                 }
 333             }
 334         }
 335     }
 336 }
 337 
 338 /*!
 339  * \internal
 340  * \brief Copy source to target and set xf_acl_enabled flag in target
 341  *
 342  * \param[in]     acl_source    XML with ACL definitions
 343  * \param[in,out] target        XML that ACLs will be applied to
 344  * \param[in]     user          Username whose ACLs need to be set
 345  */
 346 void
 347 pcmk__enable_acl(xmlNode *acl_source, xmlNode *target, const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 348 {
 349     pcmk__unpack_acl(acl_source, target, user);
 350     pcmk__set_xml_doc_flag(target, pcmk__xf_acl_enabled);
 351     pcmk__apply_acl(target);
 352 }
 353 
 354 static inline bool
 355 test_acl_mode(enum xml_private_flags allowed, enum xml_private_flags requested)
     /* [previous][next][first][last][top][bottom][index][help] */
 356 {
 357     if (pcmk_is_set(allowed, pcmk__xf_acl_deny)) {
 358         return false;
 359 
 360     } else if (pcmk_all_flags_set(allowed, requested)) {
 361         return true;
 362 
 363     } else if (pcmk_is_set(requested, pcmk__xf_acl_read)
 364                && pcmk_is_set(allowed, pcmk__xf_acl_write)) {
 365         return true;
 366 
 367     } else if (pcmk_is_set(requested, pcmk__xf_acl_create)
 368                && pcmk_any_flags_set(allowed, pcmk__xf_acl_write|pcmk__xf_created)) {
 369         return true;
 370     }
 371     return false;
 372 }
 373 
 374 /*!
 375  * \internal
 376  * \brief Rid XML tree of all unreadable nodes and node properties
 377  *
 378  * \param[in,out] xml   Root XML node to be purged of attributes
 379  *
 380  * \return true if this node or any of its children are readable
 381  *         if false is returned, xml will be freed
 382  *
 383  * \note This function is recursive
 384  */
 385 static bool
 386 purge_xml_attributes(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 387 {
 388     xmlNode *child = NULL;
 389     xmlAttr *xIter = NULL;
 390     bool readable_children = false;
 391     xml_node_private_t *nodepriv = xml->_private;
 392 
 393     if (test_acl_mode(nodepriv->flags, pcmk__xf_acl_read)) {
 394         crm_trace("%s[@" XML_ATTR_ID "=%s] is readable",
 395                   crm_element_name(xml), ID(xml));
 396         return true;
 397     }
 398 
 399     xIter = xml->properties;
 400     while (xIter != NULL) {
 401         xmlAttr *tmp = xIter;
 402         const char *prop_name = (const char *)xIter->name;
 403 
 404         xIter = xIter->next;
 405         if (strcmp(prop_name, XML_ATTR_ID) == 0) {
 406             continue;
 407         }
 408 
 409         xmlUnsetProp(xml, tmp->name);
 410     }
 411 
 412     child = pcmk__xml_first_child(xml);
 413     while ( child != NULL ) {
 414         xmlNode *tmp = child;
 415 
 416         child = pcmk__xml_next(child);
 417         readable_children |= purge_xml_attributes(tmp);
 418     }
 419 
 420     if (!readable_children) {
 421         free_xml(xml); /* Nothing readable under here, purge completely */
 422     }
 423     return readable_children;
 424 }
 425 
 426 /*!
 427  * \brief Copy ACL-allowed portions of specified XML
 428  *
 429  * \param[in]  user        Username whose ACLs should be used
 430  * \param[in]  acl_source  XML containing ACLs
 431  * \param[in]  xml         XML to be copied
 432  * \param[out] result      Copy of XML portions readable via ACLs
 433  *
 434  * \return true if xml exists and ACLs are required for user, false otherwise
 435  * \note If this returns true, caller should use \p result rather than \p xml
 436  */
 437 bool
 438 xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 439                       xmlNode **result)
 440 {
 441     GList *aIter = NULL;
 442     xmlNode *target = NULL;
 443     xml_doc_private_t *docpriv = NULL;
 444 
 445     *result = NULL;
 446     if ((xml == NULL) || !pcmk_acl_required(user)) {
 447         crm_trace("Not filtering XML because ACLs not required for user '%s'",
 448                   user);
 449         return false;
 450     }
 451 
 452     crm_trace("Filtering XML copy using user '%s' ACLs", user);
 453     target = copy_xml(xml);
 454     if (target == NULL) {
 455         return true;
 456     }
 457 
 458     pcmk__enable_acl(acl_source, target, user);
 459 
 460     docpriv = target->doc->_private;
 461     for(aIter = docpriv->acls; aIter != NULL && target; aIter = aIter->next) {
 462         int max = 0;
 463         xml_acl_t *acl = aIter->data;
 464 
 465         if (acl->mode != pcmk__xf_acl_deny) {
 466             /* Nothing to do */
 467 
 468         } else if (acl->xpath) {
 469             int lpc = 0;
 470             xmlXPathObjectPtr xpathObj = xpath_search(target, acl->xpath);
 471 
 472             max = numXpathResults(xpathObj);
 473             for(lpc = 0; lpc < max; lpc++) {
 474                 xmlNode *match = getXpathResult(xpathObj, lpc);
 475 
 476                 if (!purge_xml_attributes(match) && (match == target)) {
 477                     crm_trace("ACLs deny user '%s' access to entire XML document",
 478                               user);
 479                     freeXpathObject(xpathObj);
 480                     return true;
 481                 }
 482             }
 483             crm_trace("ACLs deny user '%s' access to %s (%d %s)",
 484                       user, acl->xpath, max,
 485                       pcmk__plural_alt(max, "match", "matches"));
 486             freeXpathObject(xpathObj);
 487         }
 488     }
 489 
 490     if (!purge_xml_attributes(target)) {
 491         crm_trace("ACLs deny user '%s' access to entire XML document", user);
 492         return true;
 493     }
 494 
 495     if (docpriv->acls) {
 496         g_list_free_full(docpriv->acls, free_acl);
 497         docpriv->acls = NULL;
 498 
 499     } else {
 500         crm_trace("User '%s' without ACLs denied access to entire XML document",
 501                   user);
 502         free_xml(target);
 503         target = NULL;
 504     }
 505 
 506     if (target) {
 507         *result = target;
 508     }
 509 
 510     return true;
 511 }
 512 
 513 /*!
 514  * \internal
 515  * \brief Check whether creation of an XML element is implicitly allowed
 516  *
 517  * Check whether XML is a "scaffolding" element whose creation is implicitly
 518  * allowed regardless of ACLs (that is, it is not in the ACL section and has
 519  * no attributes other than "id").
 520  *
 521  * \param[in] xml  XML element to check
 522  *
 523  * \return true if XML element is implicitly allowed, false otherwise
 524  */
 525 static bool
 526 implicitly_allowed(const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 527 {
 528     GString *path = NULL;
 529 
 530     for (xmlAttr *prop = xml->properties; prop != NULL; prop = prop->next) {
 531         if (strcmp((const char *) prop->name, XML_ATTR_ID) != 0) {
 532             return false;
 533         }
 534     }
 535 
 536     path = pcmk__element_xpath(xml);
 537     CRM_ASSERT(path != NULL);
 538 
 539     if (strstr((const char *) path->str, "/" XML_CIB_TAG_ACLS "/") != NULL) {
 540         g_string_free(path, TRUE);
 541         return false;
 542     }
 543 
 544     g_string_free(path, TRUE);
 545     return true;
 546 }
 547 
 548 #define display_id(xml) (ID(xml)? ID(xml) : "<unset>")
 549 
 550 /*!
 551  * \internal
 552  * \brief Drop XML nodes created in violation of ACLs
 553  *
 554  * Given an XML element, free all of its descendant nodes created in violation
 555  * of ACLs, with the exception of allowing "scaffolding" elements (i.e. those
 556  * that aren't in the ACL section and don't have any attributes other than
 557  * "id").
 558  *
 559  * \param[in,out] xml        XML to check
 560  * \param[in]     check_top  Whether to apply checks to argument itself
 561  *                           (if true, xml might get freed)
 562  *
 563  * \note This function is recursive
 564  */
 565 void
 566 pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
     /* [previous][next][first][last][top][bottom][index][help] */
 567 {
 568     xml_node_private_t *nodepriv = xml->_private;
 569 
 570     if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
 571         if (implicitly_allowed(xml)) {
 572             crm_trace("Creation of <%s> scaffolding with id=\"%s\""
 573                       " is implicitly allowed",
 574                       crm_element_name(xml), display_id(xml));
 575 
 576         } else if (pcmk__check_acl(xml, NULL, pcmk__xf_acl_write)) {
 577             crm_trace("ACLs allow creation of <%s> with id=\"%s\"",
 578                       crm_element_name(xml), display_id(xml));
 579 
 580         } else if (check_top) {
 581             crm_trace("ACLs disallow creation of <%s> with id=\"%s\"",
 582                       crm_element_name(xml), display_id(xml));
 583             pcmk_free_xml_subtree(xml);
 584             return;
 585 
 586         } else {
 587             crm_notice("ACLs would disallow creation of %s<%s> with id=\"%s\"",
 588                        ((xml == xmlDocGetRootElement(xml->doc))? "root element " : ""),
 589                        crm_element_name(xml), display_id(xml));
 590         }
 591     }
 592 
 593     for (xmlNode *cIter = pcmk__xml_first_child(xml); cIter != NULL; ) {
 594         xmlNode *child = cIter;
 595         cIter = pcmk__xml_next(cIter); /* In case it is free'd */
 596         pcmk__apply_creation_acl(child, true);
 597     }
 598 }
 599 
 600 /*!
 601  * \brief Check whether or not an XML node is ACL-denied
 602  *
 603  * \param[in]  xml node to check
 604  *
 605  * \return true if XML node exists and is ACL-denied, false otherwise
 606  */
 607 bool
 608 xml_acl_denied(const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 609 {
 610     if (xml && xml->doc && xml->doc->_private){
 611         xml_doc_private_t *docpriv = xml->doc->_private;
 612 
 613         return pcmk_is_set(docpriv->flags, pcmk__xf_acl_denied);
 614     }
 615     return false;
 616 }
 617 
 618 void
 619 xml_acl_disable(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 620 {
 621     if (xml_acl_enabled(xml)) {
 622         xml_doc_private_t *docpriv = xml->doc->_private;
 623 
 624         /* Catch anything that was created but shouldn't have been */
 625         pcmk__apply_acl(xml);
 626         pcmk__apply_creation_acl(xml, false);
 627         pcmk__clear_xml_flags(docpriv, pcmk__xf_acl_enabled);
 628     }
 629 }
 630 
 631 /*!
 632  * \brief Check whether or not an XML node is ACL-enabled
 633  *
 634  * \param[in]  xml node to check
 635  *
 636  * \return true if XML node exists and is ACL-enabled, false otherwise
 637  */
 638 bool
 639 xml_acl_enabled(const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 640 {
 641     if (xml && xml->doc && xml->doc->_private){
 642         xml_doc_private_t *docpriv = xml->doc->_private;
 643 
 644         return pcmk_is_set(docpriv->flags, pcmk__xf_acl_enabled);
 645     }
 646     return false;
 647 }
 648 
 649 bool
 650 pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 651 {
 652     CRM_ASSERT(xml);
 653     CRM_ASSERT(xml->doc);
 654     CRM_ASSERT(xml->doc->_private);
 655 
 656     if (pcmk__tracking_xml_changes(xml, false) && xml_acl_enabled(xml)) {
 657         xmlNode *parent = xml;
 658         xml_doc_private_t *docpriv = xml->doc->_private;
 659         GString *xpath = NULL;
 660 
 661         if (docpriv->acls == NULL) {
 662             pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
 663 
 664             pcmk__if_tracing({}, return false);
 665             xpath = pcmk__element_xpath(xml);
 666             if (name != NULL) {
 667                 pcmk__g_strcat(xpath, "[@", name, "]", NULL);
 668             }
 669 
 670             qb_log_from_external_source(__func__, __FILE__,
 671                                         "User '%s' without ACLs denied %s "
 672                                         "access to %s", LOG_TRACE, __LINE__, 0,
 673                                         docpriv->user, acl_to_text(mode),
 674                                         (const char *) xpath->str);
 675             g_string_free(xpath, TRUE);
 676             return false;
 677         }
 678 
 679         /* Walk the tree upwards looking for xml_acl_* flags
 680          * - Creating an attribute requires write permissions for the node
 681          * - Creating a child requires write permissions for the parent
 682          */
 683 
 684         if (name) {
 685             xmlAttr *attr = xmlHasProp(xml, (pcmkXmlStr) name);
 686 
 687             if (attr && mode == pcmk__xf_acl_create) {
 688                 mode = pcmk__xf_acl_write;
 689             }
 690         }
 691 
 692         while (parent && parent->_private) {
 693             xml_node_private_t *nodepriv = parent->_private;
 694             if (test_acl_mode(nodepriv->flags, mode)) {
 695                 return true;
 696 
 697             } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_acl_deny)) {
 698                 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
 699 
 700                 pcmk__if_tracing({}, return false);
 701                 xpath = pcmk__element_xpath(xml);
 702                 if (name != NULL) {
 703                     pcmk__g_strcat(xpath, "[@", name, "]", NULL);
 704                 }
 705 
 706                 qb_log_from_external_source(__func__, __FILE__,
 707                                             "%sACL denies user '%s' %s access "
 708                                             "to %s", LOG_TRACE, __LINE__, 0,
 709                                             (parent != xml)? "Parent ": "",
 710                                             docpriv->user, acl_to_text(mode),
 711                                             (const char *) xpath->str);
 712                 g_string_free(xpath, TRUE);
 713                 return false;
 714             }
 715             parent = parent->parent;
 716         }
 717 
 718         pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
 719 
 720         pcmk__if_tracing({}, return false);
 721         xpath = pcmk__element_xpath(xml);
 722         if (name != NULL) {
 723             pcmk__g_strcat(xpath, "[@", name, "]", NULL);
 724         }
 725 
 726         qb_log_from_external_source(__func__, __FILE__,
 727                                     "Default ACL denies user '%s' %s access to "
 728                                     "%s", LOG_TRACE, __LINE__, 0,
 729                                     docpriv->user, acl_to_text(mode),
 730                                     (const char *) xpath->str);
 731         g_string_free(xpath, TRUE);
 732         return false;
 733     }
 734 
 735     return true;
 736 }
 737 
 738 /*!
 739  * \brief Check whether ACLs are required for a given user
 740  *
 741  * \param[in]  User name to check
 742  *
 743  * \return true if the user requires ACLs, false otherwise
 744  */
 745 bool
 746 pcmk_acl_required(const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 747 {
 748     if (pcmk__str_empty(user)) {
 749         crm_trace("ACLs not required because no user set");
 750         return false;
 751 
 752     } else if (!strcmp(user, CRM_DAEMON_USER) || !strcmp(user, "root")) {
 753         crm_trace("ACLs not required for privileged user %s", user);
 754         return false;
 755     }
 756     crm_trace("ACLs required for %s", user);
 757     return true;
 758 }
 759 
 760 char *
 761 pcmk__uid2username(uid_t uid)
     /* [previous][next][first][last][top][bottom][index][help] */
 762 {
 763     char *result = NULL;
 764     struct passwd *pwent = getpwuid(uid);
 765 
 766     if (pwent == NULL) {
 767         crm_perror(LOG_INFO, "Cannot get user details for user ID %d", uid);
 768         return NULL;
 769     }
 770     pcmk__str_update(&result, pwent->pw_name);
 771     return result;
 772 }
 773 
 774 /*!
 775  * \internal
 776  * \brief Set the ACL user field properly on an XML request
 777  *
 778  * Multiple user names are potentially involved in an XML request: the effective
 779  * user of the current process; the user name known from an IPC client
 780  * connection; and the user name obtained from the request itself, whether by
 781  * the current standard XML attribute name or an older legacy attribute name.
 782  * This function chooses the appropriate one that should be used for ACLs, sets
 783  * it in the request (using the standard attribute name, and the legacy name if
 784  * given), and returns it.
 785  *
 786  * \param[in,out] request    XML request to update
 787  * \param[in]     field      Alternate name for ACL user name XML attribute
 788  * \param[in]     peer_user  User name as known from IPC connection
 789  *
 790  * \return ACL user name actually used
 791  */
 792 const char *
 793 pcmk__update_acl_user(xmlNode *request, const char *field,
     /* [previous][next][first][last][top][bottom][index][help] */
 794                       const char *peer_user)
 795 {
 796     static const char *effective_user = NULL;
 797     const char *requested_user = NULL;
 798     const char *user = NULL;
 799 
 800     if (effective_user == NULL) {
 801         effective_user = pcmk__uid2username(geteuid());
 802         if (effective_user == NULL) {
 803             effective_user = strdup("#unprivileged");
 804             CRM_CHECK(effective_user != NULL, return NULL);
 805             crm_err("Unable to determine effective user, assuming unprivileged for ACLs");
 806         }
 807     }
 808 
 809     requested_user = crm_element_value(request, XML_ACL_TAG_USER);
 810     if (requested_user == NULL) {
 811         /* @COMPAT rolling upgrades <=1.1.11
 812          *
 813          * field is checked for backward compatibility with older versions that
 814          * did not use XML_ACL_TAG_USER.
 815          */
 816         requested_user = crm_element_value(request, field);
 817     }
 818 
 819     if (!pcmk__is_privileged(effective_user)) {
 820         /* We're not running as a privileged user, set or overwrite any existing
 821          * value for $XML_ACL_TAG_USER
 822          */
 823         user = effective_user;
 824 
 825     } else if (peer_user == NULL && requested_user == NULL) {
 826         /* No user known or requested, use 'effective_user' and make sure one is
 827          * set for the request
 828          */
 829         user = effective_user;
 830 
 831     } else if (peer_user == NULL) {
 832         /* No user known, trusting 'requested_user' */
 833         user = requested_user;
 834 
 835     } else if (!pcmk__is_privileged(peer_user)) {
 836         /* The peer is not a privileged user, set or overwrite any existing
 837          * value for $XML_ACL_TAG_USER
 838          */
 839         user = peer_user;
 840 
 841     } else if (requested_user == NULL) {
 842         /* Even if we're privileged, make sure there is always a value set */
 843         user = peer_user;
 844 
 845     } else {
 846         /* Legal delegation to 'requested_user' */
 847         user = requested_user;
 848     }
 849 
 850     // This requires pointer comparison, not string comparison
 851     if (user != crm_element_value(request, XML_ACL_TAG_USER)) {
 852         crm_xml_add(request, XML_ACL_TAG_USER, user);
 853     }
 854 
 855     if (field != NULL && user != crm_element_value(request, field)) {
 856         crm_xml_add(request, field, user);
 857     }
 858 
 859     return requested_user;
 860 }

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