root/lib/common/acl.c

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

DEFINITIONS

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

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

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