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. test_acl_mode
  9. purge_xml_attributes
  10. xml_acl_filtered_copy
  11. implicitly_allowed
  12. pcmk__apply_creation_acl
  13. xml_acl_denied
  14. xml_acl_disable
  15. xml_acl_enabled
  16. pcmk__check_acl
  17. pcmk_acl_required
  18. pcmk__uid2username
  19. pcmk__update_acl_user

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

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