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

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