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             pcmk__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         pcmk__xa_remove(tmp, true);
 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     pcmk__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     pcmk__assert((xml != NULL) && (xml->doc != NULL)
 661                  && (xml->doc->_private != NULL));
 662 
 663     if (pcmk__tracking_xml_changes(xml, false) && xml_acl_enabled(xml)) {
 664         xmlNode *parent = xml;
 665         xml_doc_private_t *docpriv = xml->doc->_private;
 666         GString *xpath = NULL;
 667 
 668         if (docpriv->acls == NULL) {
 669             pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
 670 
 671             pcmk__if_tracing({}, return false);
 672             xpath = pcmk__element_xpath(xml);
 673             if (name != NULL) {
 674                 pcmk__g_strcat(xpath, "[@", name, "]", NULL);
 675             }
 676 
 677             qb_log_from_external_source(__func__, __FILE__,
 678                                         "User '%s' without ACLs denied %s "
 679                                         "access to %s", LOG_TRACE, __LINE__, 0,
 680                                         docpriv->user, acl_to_text(mode),
 681                                         (const char *) xpath->str);
 682             g_string_free(xpath, TRUE);
 683             return false;
 684         }
 685 
 686         /* Walk the tree upwards looking for xml_acl_* flags
 687          * - Creating an attribute requires write permissions for the node
 688          * - Creating a child requires write permissions for the parent
 689          */
 690 
 691         if (name) {
 692             xmlAttr *attr = xmlHasProp(xml, (pcmkXmlStr) name);
 693 
 694             if (attr && mode == pcmk__xf_acl_create) {
 695                 mode = pcmk__xf_acl_write;
 696             }
 697         }
 698 
 699         while (parent && parent->_private) {
 700             xml_node_private_t *nodepriv = parent->_private;
 701             if (test_acl_mode(nodepriv->flags, mode)) {
 702                 return true;
 703 
 704             } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_acl_deny)) {
 705                 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
 706 
 707                 pcmk__if_tracing({}, return false);
 708                 xpath = pcmk__element_xpath(xml);
 709                 if (name != NULL) {
 710                     pcmk__g_strcat(xpath, "[@", name, "]", NULL);
 711                 }
 712 
 713                 qb_log_from_external_source(__func__, __FILE__,
 714                                             "%sACL denies user '%s' %s access "
 715                                             "to %s", LOG_TRACE, __LINE__, 0,
 716                                             (parent != xml)? "Parent ": "",
 717                                             docpriv->user, acl_to_text(mode),
 718                                             (const char *) xpath->str);
 719                 g_string_free(xpath, TRUE);
 720                 return false;
 721             }
 722             parent = parent->parent;
 723         }
 724 
 725         pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
 726 
 727         pcmk__if_tracing({}, return false);
 728         xpath = pcmk__element_xpath(xml);
 729         if (name != NULL) {
 730             pcmk__g_strcat(xpath, "[@", name, "]", NULL);
 731         }
 732 
 733         qb_log_from_external_source(__func__, __FILE__,
 734                                     "Default ACL denies user '%s' %s access to "
 735                                     "%s", LOG_TRACE, __LINE__, 0,
 736                                     docpriv->user, acl_to_text(mode),
 737                                     (const char *) xpath->str);
 738         g_string_free(xpath, TRUE);
 739         return false;
 740     }
 741 
 742     return true;
 743 }
 744 
 745 /*!
 746  * \brief Check whether ACLs are required for a given user
 747  *
 748  * \param[in]  User name to check
 749  *
 750  * \return true if the user requires ACLs, false otherwise
 751  */
 752 bool
 753 pcmk_acl_required(const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 754 {
 755     if (pcmk__str_empty(user)) {
 756         crm_trace("ACLs not required because no user set");
 757         return false;
 758 
 759     } else if (!strcmp(user, CRM_DAEMON_USER) || !strcmp(user, "root")) {
 760         crm_trace("ACLs not required for privileged user %s", user);
 761         return false;
 762     }
 763     crm_trace("ACLs required for %s", user);
 764     return true;
 765 }
 766 
 767 char *
 768 pcmk__uid2username(uid_t uid)
     /* [previous][next][first][last][top][bottom][index][help] */
 769 {
 770     struct passwd *pwent = getpwuid(uid);
 771 
 772     if (pwent == NULL) {
 773         crm_perror(LOG_INFO, "Cannot get user details for user ID %d", uid);
 774         return NULL;
 775     }
 776     return pcmk__str_copy(pwent->pw_name);
 777 }
 778 
 779 /*!
 780  * \internal
 781  * \brief Set the ACL user field properly on an XML request
 782  *
 783  * Multiple user names are potentially involved in an XML request: the effective
 784  * user of the current process; the user name known from an IPC client
 785  * connection; and the user name obtained from the request itself, whether by
 786  * the current standard XML attribute name or an older legacy attribute name.
 787  * This function chooses the appropriate one that should be used for ACLs, sets
 788  * it in the request (using the standard attribute name, and the legacy name if
 789  * given), and returns it.
 790  *
 791  * \param[in,out] request    XML request to update
 792  * \param[in]     field      Alternate name for ACL user name XML attribute
 793  * \param[in]     peer_user  User name as known from IPC connection
 794  *
 795  * \return ACL user name actually used
 796  */
 797 const char *
 798 pcmk__update_acl_user(xmlNode *request, const char *field,
     /* [previous][next][first][last][top][bottom][index][help] */
 799                       const char *peer_user)
 800 {
 801     static const char *effective_user = NULL;
 802     const char *requested_user = NULL;
 803     const char *user = NULL;
 804 
 805     if (effective_user == NULL) {
 806         effective_user = pcmk__uid2username(geteuid());
 807         if (effective_user == NULL) {
 808             effective_user = pcmk__str_copy("#unprivileged");
 809             crm_err("Unable to determine effective user, assuming unprivileged for ACLs");
 810         }
 811     }
 812 
 813     requested_user = crm_element_value(request, PCMK_XE_ACL_TARGET);
 814     if (requested_user == NULL) {
 815         /* @COMPAT rolling upgrades <=1.1.11
 816          *
 817          * field is checked for backward compatibility with older versions that
 818          * did not use PCMK_XE_ACL_TARGET.
 819          */
 820         requested_user = crm_element_value(request, field);
 821     }
 822 
 823     if (!pcmk__is_privileged(effective_user)) {
 824         /* We're not running as a privileged user, set or overwrite any existing
 825          * value for PCMK_XE_ACL_TARGET
 826          */
 827         user = effective_user;
 828 
 829     } else if (peer_user == NULL && requested_user == NULL) {
 830         /* No user known or requested, use 'effective_user' and make sure one is
 831          * set for the request
 832          */
 833         user = effective_user;
 834 
 835     } else if (peer_user == NULL) {
 836         /* No user known, trusting 'requested_user' */
 837         user = requested_user;
 838 
 839     } else if (!pcmk__is_privileged(peer_user)) {
 840         /* The peer is not a privileged user, set or overwrite any existing
 841          * value for PCMK_XE_ACL_TARGET
 842          */
 843         user = peer_user;
 844 
 845     } else if (requested_user == NULL) {
 846         /* Even if we're privileged, make sure there is always a value set */
 847         user = peer_user;
 848 
 849     } else {
 850         /* Legal delegation to 'requested_user' */
 851         user = requested_user;
 852     }
 853 
 854     // This requires pointer comparison, not string comparison
 855     if (user != crm_element_value(request, PCMK_XE_ACL_TARGET)) {
 856         crm_xml_add(request, PCMK_XE_ACL_TARGET, user);
 857     }
 858 
 859     if (field != NULL && user != crm_element_value(request, field)) {
 860         crm_xml_add(request, field, user);
 861     }
 862 
 863     return requested_user;
 864 }

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