This source file includes following definitions.
- free_acl
 
- pcmk__free_acls
 
- create_acl
 
- parse_acl_entry
 
- acl_to_text
 
- pcmk__apply_acl
 
- pcmk__unpack_acl
 
- test_acl_mode
 
- purge_xml_attributes
 
- xml_acl_filtered_copy
 
- implicitly_allowed
 
- pcmk__apply_creation_acl
 
- xml_acl_denied
 
- xml_acl_disable
 
- xml_acl_enabled
 
- pcmk__check_acl
 
- pcmk_acl_required
 
- pcmk__uid2username
 
- pcmk__update_acl_user
 
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   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)
     
  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)
     
  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)
     
  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         
  63         tag = crm_element_value(xml, XML_ACL_ATTR_TAGv1);
  64     }
  65     if (ref == NULL) {
  66         
  67         ref = crm_element_value(xml, XML_ACL_ATTR_REFv1);
  68     }
  69 
  70     if ((tag == NULL) && (ref == NULL) && (xpath == NULL)) {
  71         
  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         
 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 
 138 
 139 
 140 
 141 
 142 
 143 
 144 
 145 
 146 static GList *
 147 parse_acl_entry(xmlNode *acl_top, xmlNode *acl_entry, GList *acls)
     
 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, pcmk__xf_acl_read);
 189 
 190         } else if (strcmp(XML_ACL_TAG_WRITE, tag) == 0) {
 191             acls = create_acl(child, acls, pcmk__xf_acl_write);
 192 
 193         } else if (strcmp(XML_ACL_TAG_DENY, tag) == 0) {
 194             acls = create_acl(child, acls, pcmk__xf_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 
 207 
 208 
 209 
 210 
 211 
 212 
 213 
 214 
 215 
 216 
 217 
 218 
 219 
 220 
 221 
 222 
 223 
 224 
 225 
 226 
 227 static const char *
 228 acl_to_text(enum xml_private_flags flags)
     
 229 {
 230     if (pcmk_is_set(flags, pcmk__xf_acl_deny)) {
 231         return "deny";
 232 
 233     } else if (pcmk_any_flags_set(flags, pcmk__xf_acl_write|pcmk__xf_acl_create)) {
 234         return "read/write";
 235 
 236     } else if (pcmk_is_set(flags, pcmk__xf_acl_read)) {
 237         return "read";
 238     }
 239     return "none";
 240 }
 241 
 242 void
 243 pcmk__apply_acl(xmlNode *xml)
     
 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 
 281 
 282 
 283 
 284 
 285 
 286 
 287 void
 288 pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
     
 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)
     
 332 {
 333     if (pcmk_is_set(allowed, pcmk__xf_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, pcmk__xf_acl_read)
 340                && pcmk_is_set(allowed, pcmk__xf_acl_write)) {
 341         return true;
 342 
 343     } else if (pcmk_is_set(requested, pcmk__xf_acl_create)
 344                && pcmk_any_flags_set(allowed, pcmk__xf_acl_write|pcmk__xf_created)) {
 345         return true;
 346     }
 347     return false;
 348 }
 349 
 350 static bool
 351 purge_xml_attributes(xmlNode *xml)
     
 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, pcmk__xf_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); 
 386     }
 387     return readable_children;
 388 }
 389 
 390 
 391 
 392 
 393 
 394 
 395 
 396 
 397 
 398 
 399 
 400 
 401 
 402 bool
 403 xml_acl_filtered_copy(const char *user, xmlNode *acl_source, xmlNode *xml,
     
 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, pcmk__xf_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 != pcmk__xf_acl_deny) {
 433             
 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 
 482 
 483 
 484 
 485 
 486 
 487 
 488 
 489 
 490 
 491 
 492 static bool
 493 implicitly_allowed(xmlNode *xml)
     
 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 
 517 
 518 
 519 
 520 
 521 
 522 
 523 
 524 
 525 
 526 
 527 
 528 void
 529 pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
     
 530 {
 531     xml_private_t *p = xml->_private;
 532 
 533     if (pcmk_is_set(p->flags, pcmk__xf_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, pcmk__xf_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); 
 559         pcmk__apply_creation_acl(child, true);
 560     }
 561 }
 562 
 563 bool
 564 xml_acl_denied(xmlNode *xml)
     
 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, pcmk__xf_acl_denied);
 570     }
 571     return false;
 572 }
 573 
 574 void
 575 xml_acl_disable(xmlNode *xml)
     
 576 {
 577     if (xml_acl_enabled(xml)) {
 578         xml_private_t *p = xml->doc->_private;
 579 
 580         
 581         pcmk__apply_acl(xml);
 582         pcmk__apply_creation_acl(xml, false);
 583         pcmk__clear_xml_flags(p, pcmk__xf_acl_enabled);
 584     }
 585 }
 586 
 587 bool
 588 xml_acl_enabled(xmlNode *xml)
     
 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, pcmk__xf_acl_enabled);
 594     }
 595     return false;
 596 }
 597 
 598 bool
 599 pcmk__check_acl(xmlNode *xml, const char *name, enum xml_private_flags mode)
     
 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, pcmk__xf_acl_denied);
 623             return false;
 624         }
 625 
 626         
 627 
 628 
 629 
 630 
 631         if (name) {
 632             xmlAttr *attr = xmlHasProp(xml, (pcmkXmlStr) name);
 633 
 634             if (attr && mode == pcmk__xf_acl_create) {
 635                 mode = pcmk__xf_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, pcmk__xf_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, pcmk__xf_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, pcmk__xf_acl_denied);
 657         return false;
 658     }
 659 
 660     return true;
 661 }
 662 
 663 
 664 
 665 
 666 
 667 
 668 
 669 
 670 bool
 671 pcmk_acl_required(const char *user)
     
 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)
     
 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 
 699 
 700 
 701 
 702 
 703 
 704 
 705 
 706 
 707 
 708 
 709 
 710 
 711 
 712 
 713 
 714 
 715 const char *
 716 pcmk__update_acl_user(xmlNode *request, const char *field,
     
 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         
 735 
 736 
 737 
 738 
 739         requested_user = crm_element_value(request, field);
 740     }
 741 
 742     if (!pcmk__is_privileged(effective_user)) {
 743         
 744 
 745 
 746         user = effective_user;
 747 
 748     } else if (peer_user == NULL && requested_user == NULL) {
 749         
 750 
 751 
 752         user = effective_user;
 753 
 754     } else if (peer_user == NULL) {
 755         
 756         user = requested_user;
 757 
 758     } else if (!pcmk__is_privileged(peer_user)) {
 759         
 760 
 761 
 762         user = peer_user;
 763 
 764     } else if (requested_user == NULL) {
 765         
 766         user = peer_user;
 767 
 768     } else {
 769         
 770         user = requested_user;
 771     }
 772 
 773     
 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 }