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-2021 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     GList *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             pcmk__set_xml_flags(p, acl->mode);
 270             free(path);
 271         }
 272         crm_trace("Applied %s ACL %s (%d match%s)",
 273                   acl_to_text(acl->mode), acl->xpath, max,
 274                   ((max == 1)? "" : "es"));
 275         freeXpathObject(xpathObj);
 276     }
 277 }
 278 
 279 /*!
 280  * \internal
 281  * \brief Unpack ACLs for a given user
 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_private_t *p = NULL;
 291 
 292     if ((target == NULL) || (target->doc == NULL)
 293         || (target->doc->_private == NULL)) {
 294         return;
 295     }
 296 
 297     p = 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 (p->acls == NULL) {
 303         xmlNode *acls = get_xpath_object("//" XML_CIB_TAG_ACLS,
 304                                          source, LOG_NEVER);
 305 
 306         free(p->user);
 307         p->user = strdup(user);
 308 
 309         if (acls) {
 310             xmlNode *child = NULL;
 311 
 312             for (child = pcmk__xe_first_child(acls); child;
 313                  child = pcmk__xe_next(child)) {
 314                 const char *tag = crm_element_name(child);
 315 
 316                 if (!strcmp(tag, XML_ACL_TAG_USER)
 317                     || !strcmp(tag, XML_ACL_TAG_USERv1)) {
 318                     const char *id = crm_element_value(child, XML_ATTR_ID);
 319 
 320                     if (id && strcmp(id, user) == 0) {
 321                         crm_debug("Unpacking ACLs for user '%s'", id);
 322                         p->acls = parse_acl_entry(acls, child, p->acls);
 323                     }
 324                 }
 325             }
 326         }
 327     }
 328 }
 329 
 330 static inline bool
 331 test_acl_mode(enum xml_private_flags allowed, enum xml_private_flags requested)
     /* [previous][next][first][last][top][bottom][index][help] */
 332 {
 333     if (pcmk_is_set(allowed, xpf_acl_deny)) {
 334         return false;
 335 
 336     } else if (pcmk_all_flags_set(allowed, requested)) {
 337         return true;
 338 
 339     } else if (pcmk_is_set(requested, xpf_acl_read)
 340                && pcmk_is_set(allowed, xpf_acl_write)) {
 341         return true;
 342 
 343     } else if (pcmk_is_set(requested, xpf_acl_create)
 344                && pcmk_any_flags_set(allowed, xpf_acl_write|xpf_created)) {
 345         return true;
 346     }
 347     return false;
 348 }
 349 
 350 static bool
 351 purge_xml_attributes(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 352 {
 353     xmlNode *child = NULL;
 354     xmlAttr *xIter = NULL;
 355     bool readable_children = false;
 356     xml_private_t *p = xml->_private;
 357 
 358     if (test_acl_mode(p->flags, xpf_acl_read)) {
 359         crm_trace("%s[@id=%s] is readable", crm_element_name(xml), ID(xml));
 360         return true;
 361     }
 362 
 363     xIter = xml->properties;
 364     while (xIter != NULL) {
 365         xmlAttr *tmp = xIter;
 366         const char *prop_name = (const char *)xIter->name;
 367 
 368         xIter = xIter->next;
 369         if (strcmp(prop_name, XML_ATTR_ID) == 0) {
 370             continue;
 371         }
 372 
 373         xmlUnsetProp(xml, tmp->name);
 374     }
 375 
 376     child = pcmk__xml_first_child(xml);
 377     while ( child != NULL ) {
 378         xmlNode *tmp = child;
 379 
 380         child = pcmk__xml_next(child);
 381         readable_children |= purge_xml_attributes(tmp);
 382     }
 383 
 384     if (!readable_children) {
 385         free_xml(xml); /* Nothing readable under here, purge completely */
 386     }
 387     return readable_children;
 388 }
 389 
 390 /*!
 391  * \internal
 392  * \brief Copy ACL-allowed portions of specified XML
 393  *
 394  * \param[in]  user        Username whose ACLs should be used
 395  * \param[in]  acl_source  XML containing ACLs
 396  * \param[in]  xml         XML to be copied
 397  * \param[out] result      Copy of XML portions readable via ACLs
 398  *
 399  * \return true if xml exists and ACLs are required for user, false otherwise
 400  * \note If this returns true, caller should use \p result rather than \p xml
 401  */
 402 bool
 403 xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 404                       xmlNode **result)
 405 {
 406     GList *aIter = NULL;
 407     xmlNode *target = NULL;
 408     xml_private_t *doc = NULL;
 409 
 410     *result = NULL;
 411     if ((xml == NULL) || !pcmk_acl_required(user)) {
 412         crm_trace("Not filtering XML because ACLs not required for user '%s'",
 413                   user);
 414         return false;
 415     }
 416 
 417     crm_trace("Filtering XML copy using user '%s' ACLs", user);
 418     target = copy_xml(xml);
 419     if (target == NULL) {
 420         return true;
 421     }
 422 
 423     pcmk__unpack_acl(acl_source, target, user);
 424     pcmk__set_xml_doc_flag(target, xpf_acl_enabled);
 425     pcmk__apply_acl(target);
 426 
 427     doc = target->doc->_private;
 428     for(aIter = doc->acls; aIter != NULL && target; aIter = aIter->next) {
 429         int max = 0;
 430         xml_acl_t *acl = aIter->data;
 431 
 432         if (acl->mode != xpf_acl_deny) {
 433             /* Nothing to do */
 434 
 435         } else if (acl->xpath) {
 436             int lpc = 0;
 437             xmlXPathObjectPtr xpathObj = xpath_search(target, acl->xpath);
 438 
 439             max = numXpathResults(xpathObj);
 440             for(lpc = 0; lpc < max; lpc++) {
 441                 xmlNode *match = getXpathResult(xpathObj, lpc);
 442 
 443                 if (!purge_xml_attributes(match) && (match == target)) {
 444                     crm_trace("ACLs deny user '%s' access to entire XML document",
 445                               user);
 446                     freeXpathObject(xpathObj);
 447                     return true;
 448                 }
 449             }
 450             crm_trace("ACLs deny user '%s' access to %s (%d %s)",
 451                       user, acl->xpath, max,
 452                       pcmk__plural_alt(max, "match", "matches"));
 453             freeXpathObject(xpathObj);
 454         }
 455     }
 456 
 457     if (!purge_xml_attributes(target)) {
 458         crm_trace("ACLs deny user '%s' access to entire XML document", user);
 459         return true;
 460     }
 461 
 462     if (doc->acls) {
 463         g_list_free_full(doc->acls, free_acl);
 464         doc->acls = NULL;
 465 
 466     } else {
 467         crm_trace("User '%s' without ACLs denied access to entire XML document",
 468                   user);
 469         free_xml(target);
 470         target = NULL;
 471     }
 472 
 473     if (target) {
 474         *result = target;
 475     }
 476 
 477     return true;
 478 }
 479 
 480 /*!
 481  * \internal
 482  * \brief Check whether creation of an XML element is implicitly allowed
 483  *
 484  * Check whether XML is a "scaffolding" element whose creation is implicitly
 485  * allowed regardless of ACLs (that is, it is not in the ACL section and has
 486  * no attributes other than "id").
 487  *
 488  * \param[in] xml  XML element to check
 489  *
 490  * \return true if XML element is implicitly allowed, false otherwise
 491  */
 492 static bool
 493 implicitly_allowed(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 494 {
 495     char *path = NULL;
 496 
 497     for (xmlAttr *prop = xml->properties; prop != NULL; prop = prop->next) {
 498         if (strcmp((const char *) prop->name, XML_ATTR_ID) != 0) {
 499             return false;
 500         }
 501     }
 502 
 503     path = xml_get_path(xml);
 504     if (strstr(path, "/" XML_CIB_TAG_ACLS "/") != NULL) {
 505         free(path);
 506         return false;
 507     }
 508     free(path);
 509 
 510     return true;
 511 }
 512 
 513 #define display_id(xml) (ID(xml)? ID(xml) : "<unset>")
 514 
 515 /*!
 516  * \internal
 517  * \brief Drop XML nodes created in violation of ACLs
 518  *
 519  * Given an XML element, free all of its descendent nodes created in violation
 520  * of ACLs, with the exception of allowing "scaffolding" elements (i.e. those
 521  * that aren't in the ACL section and don't have any attributes other than
 522  * "id").
 523  *
 524  * \param[in,out] xml        XML to check
 525  * \param[in]     check_top  Whether to apply checks to argument itself
 526  *                           (if true, xml might get freed)
 527  */
 528 void
 529 pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
     /* [previous][next][first][last][top][bottom][index][help] */
 530 {
 531     xml_private_t *p = xml->_private;
 532 
 533     if (pcmk_is_set(p->flags, xpf_created)) {
 534         if (implicitly_allowed(xml)) {
 535             crm_trace("Creation of <%s> scaffolding with id=\"%s\""
 536                       " is implicitly allowed",
 537                       crm_element_name(xml), display_id(xml));
 538 
 539         } else if (pcmk__check_acl(xml, NULL, xpf_acl_write)) {
 540             crm_trace("ACLs allow creation of <%s> with id=\"%s\"",
 541                       crm_element_name(xml), display_id(xml));
 542 
 543         } else if (check_top) {
 544             crm_trace("ACLs disallow creation of <%s> with id=\"%s\"",
 545                       crm_element_name(xml), display_id(xml));
 546             pcmk_free_xml_subtree(xml);
 547             return;
 548 
 549         } else {
 550             crm_notice("ACLs would disallow creation of %s<%s> with id=\"%s\" ",
 551                        ((xml == xmlDocGetRootElement(xml->doc))? "root element " : ""),
 552                        crm_element_name(xml), display_id(xml));
 553         }
 554     }
 555 
 556     for (xmlNode *cIter = pcmk__xml_first_child(xml); cIter != NULL; ) {
 557         xmlNode *child = cIter;
 558         cIter = pcmk__xml_next(cIter); /* In case it is free'd */
 559         pcmk__apply_creation_acl(child, true);
 560     }
 561 }
 562 
 563 bool
 564 xml_acl_denied(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 565 {
 566     if (xml && xml->doc && xml->doc->_private){
 567         xml_private_t *p = xml->doc->_private;
 568 
 569         return pcmk_is_set(p->flags, xpf_acl_denied);
 570     }
 571     return false;
 572 }
 573 
 574 void
 575 xml_acl_disable(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 576 {
 577     if (xml_acl_enabled(xml)) {
 578         xml_private_t *p = xml->doc->_private;
 579 
 580         /* Catch anything that was created but shouldn't have been */
 581         pcmk__apply_acl(xml);
 582         pcmk__apply_creation_acl(xml, false);
 583         pcmk__clear_xml_flags(p, xpf_acl_enabled);
 584     }
 585 }
 586 
 587 bool
 588 xml_acl_enabled(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 589 {
 590     if (xml && xml->doc && xml->doc->_private){
 591         xml_private_t *p = xml->doc->_private;
 592 
 593         return pcmk_is_set(p->flags, xpf_acl_enabled);
 594     }
 595     return false;
 596 }
 597 
 598 bool
 599 pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 600 {
 601     CRM_ASSERT(xml);
 602     CRM_ASSERT(xml->doc);
 603     CRM_ASSERT(xml->doc->_private);
 604 
 605     if (pcmk__tracking_xml_changes(xml, false) && xml_acl_enabled(xml)) {
 606         int offset = 0;
 607         xmlNode *parent = xml;
 608         char buffer[MAX_XPATH_LEN];
 609         xml_private_t *docp = xml->doc->_private;
 610 
 611         offset = pcmk__element_xpath(NULL, xml, buffer, offset,
 612                                      sizeof(buffer));
 613         if (name) {
 614             offset += snprintf(buffer + offset, MAX_XPATH_LEN - offset,
 615                                "[@%s]", name);
 616         }
 617         CRM_LOG_ASSERT(offset > 0);
 618 
 619         if (docp->acls == NULL) {
 620             crm_trace("User '%s' without ACLs denied %s access to %s",
 621                       docp->user, acl_to_text(mode), buffer);
 622             pcmk__set_xml_doc_flag(xml, xpf_acl_denied);
 623             return false;
 624         }
 625 
 626         /* Walk the tree upwards looking for xml_acl_* flags
 627          * - Creating an attribute requires write permissions for the node
 628          * - Creating a child requires write permissions for the parent
 629          */
 630 
 631         if (name) {
 632             xmlAttr *attr = xmlHasProp(xml, (pcmkXmlStr) name);
 633 
 634             if (attr && mode == xpf_acl_create) {
 635                 mode = xpf_acl_write;
 636             }
 637         }
 638 
 639         while (parent && parent->_private) {
 640             xml_private_t *p = parent->_private;
 641             if (test_acl_mode(p->flags, mode)) {
 642                 return true;
 643 
 644             } else if (pcmk_is_set(p->flags, xpf_acl_deny)) {
 645                 crm_trace("%sACL denies user '%s' %s access to %s",
 646                           (parent != xml) ? "Parent " : "", docp->user,
 647                           acl_to_text(mode), buffer);
 648                 pcmk__set_xml_doc_flag(xml, xpf_acl_denied);
 649                 return false;
 650             }
 651             parent = parent->parent;
 652         }
 653 
 654         crm_trace("Default ACL denies user '%s' %s access to %s",
 655                   docp->user, acl_to_text(mode), buffer);
 656         pcmk__set_xml_doc_flag(xml, xpf_acl_denied);
 657         return false;
 658     }
 659 
 660     return true;
 661 }
 662 
 663 /*!
 664  * \brief Check whether ACLs are required for a given user
 665  *
 666  * \param[in]  User name to check
 667  *
 668  * \return true if the user requires ACLs, false otherwise
 669  */
 670 bool
 671 pcmk_acl_required(const char *user)
     /* [previous][next][first][last][top][bottom][index][help] */
 672 {
 673     if (pcmk__str_empty(user)) {
 674         crm_trace("ACLs not required because no user set");
 675         return false;
 676 
 677     } else if (!strcmp(user, CRM_DAEMON_USER) || !strcmp(user, "root")) {
 678         crm_trace("ACLs not required for privileged user %s", user);
 679         return false;
 680     }
 681     crm_trace("ACLs required for %s", user);
 682     return true;
 683 }
 684 
 685 char *
 686 pcmk__uid2username(uid_t uid)
     /* [previous][next][first][last][top][bottom][index][help] */
 687 {
 688     struct passwd *pwent = getpwuid(uid);
 689 
 690     if (pwent == NULL) {
 691         crm_perror(LOG_INFO, "Cannot get user details for user ID %d", uid);
 692         return NULL;
 693     }
 694     return strdup(pwent->pw_name);
 695 }
 696 
 697 /*!
 698  * \internal
 699  * \brief Set the ACL user field properly on an XML request
 700  *
 701  * Multiple user names are potentially involved in an XML request: the effective
 702  * user of the current process; the user name known from an IPC client
 703  * connection; and the user name obtained from the request itself, whether by
 704  * the current standard XML attribute name or an older legacy attribute name.
 705  * This function chooses the appropriate one that should be used for ACLs, sets
 706  * it in the request (using the standard attribute name, and the legacy name if
 707  * given), and returns it.
 708  *
 709  * \param[in,out] request    XML request to update
 710  * \param[in]     field      Alternate name for ACL user name XML attribute
 711  * \param[in]     peer_user  User name as known from IPC connection
 712  *
 713  * \return ACL user name actually used
 714  */
 715 const char *
 716 pcmk__update_acl_user(xmlNode *request, const char *field,
     /* [previous][next][first][last][top][bottom][index][help] */
 717                       const char *peer_user)
 718 {
 719     static const char *effective_user = NULL;
 720     const char *requested_user = NULL;
 721     const char *user = NULL;
 722 
 723     if (effective_user == NULL) {
 724         effective_user = pcmk__uid2username(geteuid());
 725         if (effective_user == NULL) {
 726             effective_user = strdup("#unprivileged");
 727             CRM_CHECK(effective_user != NULL, return NULL);
 728             crm_err("Unable to determine effective user, assuming unprivileged for ACLs");
 729         }
 730     }
 731 
 732     requested_user = crm_element_value(request, XML_ACL_TAG_USER);
 733     if (requested_user == NULL) {
 734         /* @COMPAT rolling upgrades <=1.1.11
 735          *
 736          * field is checked for backward compatibility with older versions that
 737          * did not use XML_ACL_TAG_USER.
 738          */
 739         requested_user = crm_element_value(request, field);
 740     }
 741 
 742     if (!pcmk__is_privileged(effective_user)) {
 743         /* We're not running as a privileged user, set or overwrite any existing
 744          * value for $XML_ACL_TAG_USER
 745          */
 746         user = effective_user;
 747 
 748     } else if (peer_user == NULL && requested_user == NULL) {
 749         /* No user known or requested, use 'effective_user' and make sure one is
 750          * set for the request
 751          */
 752         user = effective_user;
 753 
 754     } else if (peer_user == NULL) {
 755         /* No user known, trusting 'requested_user' */
 756         user = requested_user;
 757 
 758     } else if (!pcmk__is_privileged(peer_user)) {
 759         /* The peer is not a privileged user, set or overwrite any existing
 760          * value for $XML_ACL_TAG_USER
 761          */
 762         user = peer_user;
 763 
 764     } else if (requested_user == NULL) {
 765         /* Even if we're privileged, make sure there is always a value set */
 766         user = peer_user;
 767 
 768     } else {
 769         /* Legal delegation to 'requested_user' */
 770         user = requested_user;
 771     }
 772 
 773     // This requires pointer comparison, not string comparison
 774     if (user != crm_element_value(request, XML_ACL_TAG_USER)) {
 775         crm_xml_add(request, XML_ACL_TAG_USER, user);
 776     }
 777 
 778     if (field != NULL && user != crm_element_value(request, field)) {
 779         crm_xml_add(request, field, user);
 780     }
 781 
 782     return requested_user;
 783 }

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