root/lib/common/acl.c

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

DEFINITIONS

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

   1 /*
   2  * Copyright 2004-2022 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 #include <sys/types.h>
  14 #include <pwd.h>
  15 #include <string.h>
  16 #include <stdlib.h>
  17 #include <stdarg.h>
  18 
  19 #include <libxml/tree.h>
  20 
  21 #include <crm/crm.h>
  22 #include <crm/msg_xml.h>
  23 #include <crm/common/xml.h>
  24 #include <crm/common/xml_internal.h>
  25 #include "crmcommon_private.h"
  26 
  27 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             static struct qb_log_callsite *trace_cs = NULL;
 246             xmlNode *match = getXpathResult(xpathObj, lpc);
 247 
 248             nodepriv = match->_private;
 249             pcmk__set_xml_flags(nodepriv, acl->mode);
 250 
 251             /* Build a GString only if tracing is enabled.
 252              * Can't use pcmk__log_else() because the else_action would be
 253              * continue.
 254              */
 255             if (trace_cs == NULL) {
 256                 trace_cs = qb_log_callsite_get(__func__, __FILE__, "apply_acl",
 257                                                LOG_TRACE, __LINE__, 0);
 258             }
 259             if (crm_is_callsite_active(trace_cs, LOG_TRACE, 0)) {
 260                 GString *path = pcmk__element_xpath(match);
 261                 crm_trace("Applying %s ACL to %s matched by %s",
 262                           acl_to_text(acl->mode), (const char *) path->str,
 263                           acl->xpath);
 264                 g_string_free(path, TRUE);
 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("//" XML_CIB_TAG_ACLS,
 304                                          source, LOG_NEVER);
 305 
 306         pcmk__str_update(&docpriv->user, user);
 307 
 308         if (acls) {
 309             xmlNode *child = NULL;
 310 
 311             for (child = pcmk__xe_first_child(acls); child;
 312                  child = pcmk__xe_next(child)) {
 313                 const char *tag = crm_element_name(child);
 314 
 315                 if (!strcmp(tag, XML_ACL_TAG_USER)
 316                     || !strcmp(tag, XML_ACL_TAG_USERv1)) {
 317                     const char *id = crm_element_value(child, XML_ATTR_NAME);
 318 
 319                     if (id == NULL) {
 320                         id = crm_element_value(child, XML_ATTR_ID);
 321                     }
 322 
 323                     if (id && strcmp(id, user) == 0) {
 324                         crm_debug("Unpacking ACLs for user '%s'", id);
 325                         docpriv->acls = parse_acl_entry(acls, child, docpriv->acls);
 326                     }
 327                 } else if (!strcmp(tag, XML_ACL_TAG_GROUP)) {
 328                     const char *id = crm_element_value(child, XML_ATTR_NAME);
 329 
 330                     if (id == NULL) {
 331                         id = crm_element_value(child, XML_ATTR_ID);
 332                     }
 333 
 334                     if (id && pcmk__is_user_in_group(user,id)) {
 335                         crm_debug("Unpacking ACLs for group '%s'", id);
 336                         docpriv->acls = parse_acl_entry(acls, child, docpriv->acls);
 337                     }
 338                 }
 339             }
 340         }
 341     }
 342 }
 343 
 344 /*!
 345  * \internal
 346  * \brief Copy source to target and set xf_acl_enabled flag in target
 347  *
 348  * \param[in]     acl_source    XML with ACL definitions
 349  * \param[in,out] target        XML that ACLs will be applied to
 350  * \param[in]     user          Username whose ACLs need to be set
 351  */
 352 void
 353 pcmk__enable_acl(xmlNode *acl_source, xmlNode *target, const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 354 {
 355     pcmk__unpack_acl(acl_source, target, user);
 356     pcmk__set_xml_doc_flag(target, pcmk__xf_acl_enabled);
 357     pcmk__apply_acl(target);
 358 }
 359 
 360 static inline bool
 361 test_acl_mode(enum xml_private_flags allowed, enum xml_private_flags requested)
     /* [previous][next][first][last][top][bottom][index][help] */
 362 {
 363     if (pcmk_is_set(allowed, pcmk__xf_acl_deny)) {
 364         return false;
 365 
 366     } else if (pcmk_all_flags_set(allowed, requested)) {
 367         return true;
 368 
 369     } else if (pcmk_is_set(requested, pcmk__xf_acl_read)
 370                && pcmk_is_set(allowed, pcmk__xf_acl_write)) {
 371         return true;
 372 
 373     } else if (pcmk_is_set(requested, pcmk__xf_acl_create)
 374                && pcmk_any_flags_set(allowed, pcmk__xf_acl_write|pcmk__xf_created)) {
 375         return true;
 376     }
 377     return false;
 378 }
 379 
 380 /*!
 381  * \internal
 382  * \brief Rid XML tree of all unreadable nodes and node properties
 383  *
 384  * \param[in,out] xml   Root XML node to be purged of attributes
 385  *
 386  * \return true if this node or any of its children are readable
 387  *         if false is returned, xml will be freed
 388  *
 389  * \note This function is recursive
 390  */
 391 static bool
 392 purge_xml_attributes(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 393 {
 394     xmlNode *child = NULL;
 395     xmlAttr *xIter = NULL;
 396     bool readable_children = false;
 397     xml_node_private_t *nodepriv = xml->_private;
 398 
 399     if (test_acl_mode(nodepriv->flags, pcmk__xf_acl_read)) {
 400         crm_trace("%s[@id=%s] is readable", crm_element_name(xml), ID(xml));
 401         return true;
 402     }
 403 
 404     xIter = xml->properties;
 405     while (xIter != NULL) {
 406         xmlAttr *tmp = xIter;
 407         const char *prop_name = (const char *)xIter->name;
 408 
 409         xIter = xIter->next;
 410         if (strcmp(prop_name, XML_ATTR_ID) == 0) {
 411             continue;
 412         }
 413 
 414         xmlUnsetProp(xml, tmp->name);
 415     }
 416 
 417     child = pcmk__xml_first_child(xml);
 418     while ( child != NULL ) {
 419         xmlNode *tmp = child;
 420 
 421         child = pcmk__xml_next(child);
 422         readable_children |= purge_xml_attributes(tmp);
 423     }
 424 
 425     if (!readable_children) {
 426         free_xml(xml); /* Nothing readable under here, purge completely */
 427     }
 428     return readable_children;
 429 }
 430 
 431 /*!
 432  * \brief Copy ACL-allowed portions of specified XML
 433  *
 434  * \param[in]  user        Username whose ACLs should be used
 435  * \param[in]  acl_source  XML containing ACLs
 436  * \param[in]  xml         XML to be copied
 437  * \param[out] result      Copy of XML portions readable via ACLs
 438  *
 439  * \return true if xml exists and ACLs are required for user, false otherwise
 440  * \note If this returns true, caller should use \p result rather than \p xml
 441  */
 442 bool
 443 xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 444                       xmlNode **result)
 445 {
 446     GList *aIter = NULL;
 447     xmlNode *target = NULL;
 448     xml_doc_private_t *docpriv = NULL;
 449 
 450     *result = NULL;
 451     if ((xml == NULL) || !pcmk_acl_required(user)) {
 452         crm_trace("Not filtering XML because ACLs not required for user '%s'",
 453                   user);
 454         return false;
 455     }
 456 
 457     crm_trace("Filtering XML copy using user '%s' ACLs", user);
 458     target = copy_xml(xml);
 459     if (target == NULL) {
 460         return true;
 461     }
 462 
 463     pcmk__enable_acl(acl_source, target, user);
 464 
 465     docpriv = target->doc->_private;
 466     for(aIter = docpriv->acls; aIter != NULL && target; aIter = aIter->next) {
 467         int max = 0;
 468         xml_acl_t *acl = aIter->data;
 469 
 470         if (acl->mode != pcmk__xf_acl_deny) {
 471             /* Nothing to do */
 472 
 473         } else if (acl->xpath) {
 474             int lpc = 0;
 475             xmlXPathObjectPtr xpathObj = xpath_search(target, acl->xpath);
 476 
 477             max = numXpathResults(xpathObj);
 478             for(lpc = 0; lpc < max; lpc++) {
 479                 xmlNode *match = getXpathResult(xpathObj, lpc);
 480 
 481                 if (!purge_xml_attributes(match) && (match == target)) {
 482                     crm_trace("ACLs deny user '%s' access to entire XML document",
 483                               user);
 484                     freeXpathObject(xpathObj);
 485                     return true;
 486                 }
 487             }
 488             crm_trace("ACLs deny user '%s' access to %s (%d %s)",
 489                       user, acl->xpath, max,
 490                       pcmk__plural_alt(max, "match", "matches"));
 491             freeXpathObject(xpathObj);
 492         }
 493     }
 494 
 495     if (!purge_xml_attributes(target)) {
 496         crm_trace("ACLs deny user '%s' access to entire XML document", user);
 497         return true;
 498     }
 499 
 500     if (docpriv->acls) {
 501         g_list_free_full(docpriv->acls, free_acl);
 502         docpriv->acls = NULL;
 503 
 504     } else {
 505         crm_trace("User '%s' without ACLs denied access to entire XML document",
 506                   user);
 507         free_xml(target);
 508         target = NULL;
 509     }
 510 
 511     if (target) {
 512         *result = target;
 513     }
 514 
 515     return true;
 516 }
 517 
 518 /*!
 519  * \internal
 520  * \brief Check whether creation of an XML element is implicitly allowed
 521  *
 522  * Check whether XML is a "scaffolding" element whose creation is implicitly
 523  * allowed regardless of ACLs (that is, it is not in the ACL section and has
 524  * no attributes other than "id").
 525  *
 526  * \param[in] xml  XML element to check
 527  *
 528  * \return true if XML element is implicitly allowed, false otherwise
 529  */
 530 static bool
 531 implicitly_allowed(const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 532 {
 533     GString *path = NULL;
 534 
 535     for (xmlAttr *prop = xml->properties; prop != NULL; prop = prop->next) {
 536         if (strcmp((const char *) prop->name, XML_ATTR_ID) != 0) {
 537             return false;
 538         }
 539     }
 540 
 541     path = pcmk__element_xpath(xml);
 542     CRM_ASSERT(path != NULL);
 543 
 544     if (strstr((const char *) path->str, "/" XML_CIB_TAG_ACLS "/") != NULL) {
 545         g_string_free(path, TRUE);
 546         return false;
 547     }
 548 
 549     g_string_free(path, TRUE);
 550     return true;
 551 }
 552 
 553 #define display_id(xml) (ID(xml)? ID(xml) : "<unset>")
 554 
 555 /*!
 556  * \internal
 557  * \brief Drop XML nodes created in violation of ACLs
 558  *
 559  * Given an XML element, free all of its descendent nodes created in violation
 560  * of ACLs, with the exception of allowing "scaffolding" elements (i.e. those
 561  * that aren't in the ACL section and don't have any attributes other than
 562  * "id").
 563  *
 564  * \param[in,out] xml        XML to check
 565  * \param[in]     check_top  Whether to apply checks to argument itself
 566  *                           (if true, xml might get freed)
 567  *
 568  * \note This function is recursive
 569  */
 570 void
 571 pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
     /* [previous][next][first][last][top][bottom][index][help] */
 572 {
 573     xml_node_private_t *nodepriv = xml->_private;
 574 
 575     if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
 576         if (implicitly_allowed(xml)) {
 577             crm_trace("Creation of <%s> scaffolding with id=\"%s\""
 578                       " is implicitly allowed",
 579                       crm_element_name(xml), display_id(xml));
 580 
 581         } else if (pcmk__check_acl(xml, NULL, pcmk__xf_acl_write)) {
 582             crm_trace("ACLs allow creation of <%s> with id=\"%s\"",
 583                       crm_element_name(xml), display_id(xml));
 584 
 585         } else if (check_top) {
 586             crm_trace("ACLs disallow creation of <%s> with id=\"%s\"",
 587                       crm_element_name(xml), display_id(xml));
 588             pcmk_free_xml_subtree(xml);
 589             return;
 590 
 591         } else {
 592             crm_notice("ACLs would disallow creation of %s<%s> with id=\"%s\" ",
 593                        ((xml == xmlDocGetRootElement(xml->doc))? "root element " : ""),
 594                        crm_element_name(xml), display_id(xml));
 595         }
 596     }
 597 
 598     for (xmlNode *cIter = pcmk__xml_first_child(xml); cIter != NULL; ) {
 599         xmlNode *child = cIter;
 600         cIter = pcmk__xml_next(cIter); /* In case it is free'd */
 601         pcmk__apply_creation_acl(child, true);
 602     }
 603 }
 604 
 605 /*!
 606  * \brief Check whether or not an XML node is ACL-denied
 607  *
 608  * \param[in]  xml node to check
 609  *
 610  * \return true if XML node exists and is ACL-denied, false otherwise
 611  */
 612 bool
 613 xml_acl_denied(const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 614 {
 615     if (xml && xml->doc && xml->doc->_private){
 616         xml_doc_private_t *docpriv = xml->doc->_private;
 617 
 618         return pcmk_is_set(docpriv->flags, pcmk__xf_acl_denied);
 619     }
 620     return false;
 621 }
 622 
 623 void
 624 xml_acl_disable(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 625 {
 626     if (xml_acl_enabled(xml)) {
 627         xml_doc_private_t *docpriv = xml->doc->_private;
 628 
 629         /* Catch anything that was created but shouldn't have been */
 630         pcmk__apply_acl(xml);
 631         pcmk__apply_creation_acl(xml, false);
 632         pcmk__clear_xml_flags(docpriv, pcmk__xf_acl_enabled);
 633     }
 634 }
 635 
 636 /*!
 637  * \brief Check whether or not an XML node is ACL-enabled
 638  *
 639  * \param[in]  xml node to check
 640  *
 641  * \return true if XML node exists and is ACL-enabled, false otherwise
 642  */
 643 bool
 644 xml_acl_enabled(const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 645 {
 646     if (xml && xml->doc && xml->doc->_private){
 647         xml_doc_private_t *docpriv = xml->doc->_private;
 648 
 649         return pcmk_is_set(docpriv->flags, pcmk__xf_acl_enabled);
 650     }
 651     return false;
 652 }
 653 
 654 bool
 655 pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 656 {
 657     CRM_ASSERT(xml);
 658     CRM_ASSERT(xml->doc);
 659     CRM_ASSERT(xml->doc->_private);
 660 
 661     if (pcmk__tracking_xml_changes(xml, false) && xml_acl_enabled(xml)) {
 662         xmlNode *parent = xml;
 663         xml_doc_private_t *docpriv = xml->doc->_private;
 664         GString *xpath = NULL;
 665 
 666         if (docpriv->acls == NULL) {
 667             pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
 668 
 669             pcmk__log_else(LOG_TRACE, return false);
 670             xpath = pcmk__element_xpath(xml);
 671             if (name != NULL) {
 672                 pcmk__g_strcat(xpath, "[@", name, "]", NULL);
 673             }
 674 
 675             qb_log_from_external_source(__func__, __FILE__,
 676                                         "User '%s' without ACLs denied %s "
 677                                         "access to %s", LOG_TRACE, __LINE__, 0,
 678                                         docpriv->user, acl_to_text(mode),
 679                                         (const char *) xpath->str);
 680             g_string_free(xpath, TRUE);
 681             return false;
 682         }
 683 
 684         /* Walk the tree upwards looking for xml_acl_* flags
 685          * - Creating an attribute requires write permissions for the node
 686          * - Creating a child requires write permissions for the parent
 687          */
 688 
 689         if (name) {
 690             xmlAttr *attr = xmlHasProp(xml, (pcmkXmlStr) name);
 691 
 692             if (attr && mode == pcmk__xf_acl_create) {
 693                 mode = pcmk__xf_acl_write;
 694             }
 695         }
 696 
 697         while (parent && parent->_private) {
 698             xml_node_private_t *nodepriv = parent->_private;
 699             if (test_acl_mode(nodepriv->flags, mode)) {
 700                 return true;
 701 
 702             } else if (pcmk_is_set(nodepriv->flags, pcmk__xf_acl_deny)) {
 703                 pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
 704 
 705                 pcmk__log_else(LOG_TRACE, return false);
 706                 xpath = pcmk__element_xpath(xml);
 707                 if (name != NULL) {
 708                     pcmk__g_strcat(xpath, "[@", name, "]", NULL);
 709                 }
 710 
 711                 qb_log_from_external_source(__func__, __FILE__,
 712                                             "%sACL denies user '%s' %s access "
 713                                             "to %s", LOG_TRACE, __LINE__, 0,
 714                                             (parent != xml)? "Parent ": "",
 715                                             docpriv->user, acl_to_text(mode),
 716                                             (const char *) xpath->str);
 717                 g_string_free(xpath, TRUE);
 718                 return false;
 719             }
 720             parent = parent->parent;
 721         }
 722 
 723         pcmk__set_xml_doc_flag(xml, pcmk__xf_acl_denied);
 724 
 725         pcmk__log_else(LOG_TRACE, return false);
 726         xpath = pcmk__element_xpath(xml);
 727         if (name != NULL) {
 728             pcmk__g_strcat(xpath, "[@", name, "]", NULL);
 729         }
 730 
 731         qb_log_from_external_source(__func__, __FILE__,
 732                                     "Default ACL denies user '%s' %s access to "
 733                                     "%s", LOG_TRACE, __LINE__, 0,
 734                                     docpriv->user, acl_to_text(mode),
 735                                     (const char *) xpath->str);
 736         g_string_free(xpath, TRUE);
 737         return false;
 738     }
 739 
 740     return true;
 741 }
 742 
 743 /*!
 744  * \brief Check whether ACLs are required for a given user
 745  *
 746  * \param[in]  User name to check
 747  *
 748  * \return true if the user requires ACLs, false otherwise
 749  */
 750 bool
 751 pcmk_acl_required(const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 752 {
 753     if (pcmk__str_empty(user)) {
 754         crm_trace("ACLs not required because no user set");
 755         return false;
 756 
 757     } else if (!strcmp(user, CRM_DAEMON_USER) || !strcmp(user, "root")) {
 758         crm_trace("ACLs not required for privileged user %s", user);
 759         return false;
 760     }
 761     crm_trace("ACLs required for %s", user);
 762     return true;
 763 }
 764 
 765 char *
 766 pcmk__uid2username(uid_t uid)
     /* [previous][next][first][last][top][bottom][index][help] */
 767 {
 768     struct passwd *pwent = getpwuid(uid);
 769 
 770     if (pwent == NULL) {
 771         crm_perror(LOG_INFO, "Cannot get user details for user ID %d", uid);
 772         return NULL;
 773     }
 774     return strdup(pwent->pw_name);
 775 }
 776 
 777 /*!
 778  * \internal
 779  * \brief Set the ACL user field properly on an XML request
 780  *
 781  * Multiple user names are potentially involved in an XML request: the effective
 782  * user of the current process; the user name known from an IPC client
 783  * connection; and the user name obtained from the request itself, whether by
 784  * the current standard XML attribute name or an older legacy attribute name.
 785  * This function chooses the appropriate one that should be used for ACLs, sets
 786  * it in the request (using the standard attribute name, and the legacy name if
 787  * given), and returns it.
 788  *
 789  * \param[in,out] request    XML request to update
 790  * \param[in]     field      Alternate name for ACL user name XML attribute
 791  * \param[in]     peer_user  User name as known from IPC connection
 792  *
 793  * \return ACL user name actually used
 794  */
 795 const char *
 796 pcmk__update_acl_user(xmlNode *request, const char *field,
     /* [previous][next][first][last][top][bottom][index][help] */
 797                       const char *peer_user)
 798 {
 799     static const char *effective_user = NULL;
 800     const char *requested_user = NULL;
 801     const char *user = NULL;
 802 
 803     if (effective_user == NULL) {
 804         effective_user = pcmk__uid2username(geteuid());
 805         if (effective_user == NULL) {
 806             effective_user = strdup("#unprivileged");
 807             CRM_CHECK(effective_user != NULL, return NULL);
 808             crm_err("Unable to determine effective user, assuming unprivileged for ACLs");
 809         }
 810     }
 811 
 812     requested_user = crm_element_value(request, XML_ACL_TAG_USER);
 813     if (requested_user == NULL) {
 814         /* @COMPAT rolling upgrades <=1.1.11
 815          *
 816          * field is checked for backward compatibility with older versions that
 817          * did not use XML_ACL_TAG_USER.
 818          */
 819         requested_user = crm_element_value(request, field);
 820     }
 821 
 822     if (!pcmk__is_privileged(effective_user)) {
 823         /* We're not running as a privileged user, set or overwrite any existing
 824          * value for $XML_ACL_TAG_USER
 825          */
 826         user = effective_user;
 827 
 828     } else if (peer_user == NULL && requested_user == NULL) {
 829         /* No user known or requested, use 'effective_user' and make sure one is
 830          * set for the request
 831          */
 832         user = effective_user;
 833 
 834     } else if (peer_user == NULL) {
 835         /* No user known, trusting 'requested_user' */
 836         user = requested_user;
 837 
 838     } else if (!pcmk__is_privileged(peer_user)) {
 839         /* The peer is not a privileged user, set or overwrite any existing
 840          * value for $XML_ACL_TAG_USER
 841          */
 842         user = peer_user;
 843 
 844     } else if (requested_user == NULL) {
 845         /* Even if we're privileged, make sure there is always a value set */
 846         user = peer_user;
 847 
 848     } else {
 849         /* Legal delegation to 'requested_user' */
 850         user = requested_user;
 851     }
 852 
 853     // This requires pointer comparison, not string comparison
 854     if (user != crm_element_value(request, XML_ACL_TAG_USER)) {
 855         crm_xml_add(request, XML_ACL_TAG_USER, user);
 856     }
 857 
 858     if (field != NULL && user != crm_element_value(request, field)) {
 859         crm_xml_add(request, field, user);
 860     }
 861 
 862     return requested_user;
 863 }

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