001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2008 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 */ 027package org.opends.server.authorization.dseecompat; 028 029import static org.opends.server.authorization.dseecompat.Aci.*; 030 031import java.util.LinkedList; 032import java.util.List; 033import java.util.Set; 034 035import org.forgerock.opendj.ldap.ByteString; 036import org.opends.server.core.DirectoryServer; 037import org.opends.server.types.Attribute; 038import org.opends.server.types.AttributeType; 039import org.opends.server.types.Attributes; 040import org.opends.server.types.Entry; 041 042/** 043 * This class implements the dseecompat geteffectiverights evaluation. 044 */ 045public class AciEffectiveRights { 046 047 /** 048 * Value used when a aclRights attribute was seen in the search operation 049 * attribute set. 050 */ 051 private static final int ACL_RIGHTS = 0x001; 052 053 /** 054 * Value used when a aclRightsInfo attribute was seen in the search operation 055 * attribute set. 056 */ 057 private static final int ACL_RIGHTS_INFO = 0x002; 058 059 /** 060 * Value used when an ACI has a targattrfilters keyword match and the result 061 * of the access check was a deny. 062 */ 063 private static final int ACL_TARGATTR_DENY_MATCH = 0x004; 064 065 /** 066 * Value used when an ACI has a targattrfilters keyword match and the result 067 * of the access check was an allow. 068 */ 069 private static final int ACL_TARGATTR_ALLOW_MATCH = 0x008; 070 071 /** 072 * String used to build attribute type name when an aclRights result needs to 073 * be added to the return entry. 074 */ 075 private static final String aclRightsAttrStr = "aclRights"; 076 077 /** 078 * String used to build attribute type name when an AclRightsInfo result needs 079 * to be added to the return entry. 080 */ 081 private static final String aclRightsInfoAttrStr = "aclRightsInfo"; 082 083 /** 084 * String used to build attribute type name when an entryLevel rights 085 * attribute type name needs to be added to the return entry. 086 */ 087 private static final String entryLevelStr = "entryLevel"; 088 089 /** 090 * String used to build attribute type name when an attributeLevel rights 091 * attribute type name needs to be added to the return entry. 092 */ 093 private static final String attributeLevelStr = "attributeLevel"; 094 095 /** 096 * The string that is used as the attribute type name when an aclRights 097 * entryLevel evaluation needs to be added to the return entry. 098 */ 099 private static final String aclRightsEntryLevelStr= 100 aclRightsAttrStr + ";" + entryLevelStr; 101 102 /** 103 * The string that is used as the attribute type name when an aclRights 104 * attribute level evaluation needs to be added to the return entry. This 105 * string has the attribute type name used in the evaluation appended to it to 106 * form the final attribute type name. 107 */ 108 private static final String aclRightsAttributeLevelStr= 109 aclRightsAttrStr + ";" + attributeLevelStr; 110 111 /** 112 * The string used to build attribute type name when an attribute level 113 * aclRightsInfo attribute needs to be added to the return entry. This string 114 * has the attribute type name used in the evaluation appended to it to form 115 * the final attribute type name. 116 */ 117 private static final String aclRightsInfoAttrLogsStr = 118 aclRightsInfoAttrStr + ";logs;attributeLevel"; 119 120 /** 121 * The string used to build attribute type name when an entryLevel 122 * aclRightsInfo attribute needs to be added to the return entry. 123 */ 124 private static final String aclRightsInfoEntryLogsStr = 125 aclRightsInfoAttrStr + ";logs;entryLevel"; 126 127 /** 128 * Attribute type used in access evaluation to see if the geteffectiverights 129 * related to the "aclRights" attribute can be performed. 130 */ 131 private static AttributeType aclRights; 132 133 /** 134 * Attribute type used in access evaluation to see if the geteffectiverights 135 * related to the "aclRightsInfo" attribute can be performed. 136 */ 137 private static AttributeType aclRightsInfo; 138 139 /** Attribute type used in the geteffectiverights selfwrite evaluation. */ 140 private static AttributeType dnAttributeType; 141 142 /**The distinguishedName string. */ 143 private static final String dnAttrStr = "distinguishedname"; 144 145 /** 146 * String used to fill in the summary status field when access was allowed. 147 */ 148 private static String ALLOWED="access allowed"; 149 150 /** 151 * String used to fill in the summary status field when access was not 152 * allowed. 153 */ 154 private static String NOT_ALLOWED="access not allowed"; 155 156 /** Evaluated as anonymous user. Used to fill in summary field. */ 157 private static String anonymous="anonymous"; 158 159 /** Format used to build the summary string. */ 160 private static String summaryFormatStr = 161 "acl_summary(%s): %s(%s) on entry/attr(%s, %s) to (%s)" + 162 " (not proxied) ( reason: %s %s)"; 163 164 /** 165 * Strings below represent access denied or allowed evaluation reasons. Used 166 * to fill in the summary status field. Access evaluated an allow ACI. 167 */ 168 private static String EVALUATED_ALLOW="evaluated allow"; 169 170 /** Access evaluated a deny ACI. */ 171 private static String EVALUATED_DENY="evaluated deny"; 172 173 /** Access evaluated deny because there were no allow ACIs. */ 174 private static String NO_ALLOWS="no acis matched the resource"; 175 176 /** Access evaluated deny because no allow or deny ACIs evaluated. */ 177 private static String NO_ALLOWS_MATCHED="no acis matched the subject"; 178 179 /** Access evaluated allow because the clientDN has bypass-acl privileges. */ 180 private static String SKIP_ACI="user has bypass-acl privileges"; 181 182 //TODO add support for the modify-acl privilege? 183 184 /** 185 * Attempts to add the geteffectiverights asked for in the search to the entry 186 * being returned. The two geteffectiverights attributes that can be requested 187 * are: aclRights and aclRightsInfo. The aclRightsInfo attribute will return 188 * a summary string describing in human readable form, a summary of each 189 * requested evaluation result. Here is a sample aclRightsInfo summary: 190 * 191 * acl_summary(main): access_not_allowed(proxy) on 192 * entry/attr(uid=proxieduser,ou=acis,dc=example,dc=com, NULL) to 193 * (uid=superuser,ou=acis,dc=example,dc=com) (not proxied) 194 * (reason: no acis matched the resource ) 195 * 196 * The aclRights attribute will return a simple 197 * string with the following format: 198 * 199 * add:0,delete:0,read:1,write:?,proxy:0 200 * 201 * A 0 represents access denied, 1 access allowed and ? that evaluation 202 * depends on a value of an attribute (targattrfilter keyword present in ACI). 203 * 204 * There are two levels of rights information: 205 * 206 * 1. entryLevel - entry level rights information 207 * 2. attributeLevel - attribute level rights information 208 * 209 * The attribute type names are built up using subtypes: 210 * 211 * aclRights;entryLevel - aclRights entry level presentation 212 * aclRightsInfo;log;entryLevel;{right} - aclRightsInfo entry level 213 * presentation for each type of right (proxy, read, write, add, 214 * delete). 215 * aclRights;attributeLevel;{attributeType name} - aclRights attribute 216 * level presentation for each attribute type requested. 217 * aclRights;attributeLevel;logs;{right};{attributeType name} 218 * - aclRightsInfo attribute level presentation for each attribute 219 * type requested. 220 * 221 * @param handler The ACI handler to use in the evaluation. 222 * @param searchAttributes The attributes requested in the search. 223 * @param container The LDAP operation container to use in the evaluations. 224 * @param e The entry to add the rights attributes to. 225 * @param skipCheck True if ACI evaluation was skipped because bypass-acl 226 * privilege was found. 227 */ 228 public static void addRightsToEntry(AciHandler handler, 229 Set<String> searchAttributes, 230 AciLDAPOperationContainer container, final Entry e, 231 boolean skipCheck) 232 { 233 if (aclRights == null) 234 { 235 aclRights = DirectoryServer.getAttributeType(aclRightsAttrStr 236 .toLowerCase()); 237 } 238 239 if (aclRightsInfo == null) 240 { 241 aclRightsInfo = DirectoryServer.getAttributeType(aclRightsInfoAttrStr 242 .toLowerCase()); 243 } 244 245 if (dnAttributeType == null) 246 { 247 dnAttributeType = DirectoryServer.getAttributeType(dnAttrStr); 248 } 249 250 // Check if the attributes aclRights and aclRightsInfo were requested and 251 // add attributes less those two attributes to a new list of attribute 252 // types. 253 List<AttributeType> nonRightsAttrs = new LinkedList<>(); 254 int attrMask = ACI_NULL; 255 for (String a : searchAttributes) 256 { 257 if (aclRightsAttrStr.equalsIgnoreCase(a)) 258 { 259 attrMask |= ACL_RIGHTS; 260 } 261 else if (aclRightsInfoAttrStr.equalsIgnoreCase(a)) 262 { 263 attrMask |= ACL_RIGHTS_INFO; 264 } 265 else 266 { 267 // Check for shorthands for user attributes "*" or operational "+". 268 if ("*".equals(a)) 269 { 270 // Add objectclass. 271 AttributeType ocType = DirectoryServer.getObjectClassAttributeType(); 272 nonRightsAttrs.add(ocType); 273 nonRightsAttrs.addAll(e.getUserAttributes().keySet()); 274 } 275 else if ("+".equals(a)) 276 { 277 nonRightsAttrs.addAll(e.getOperationalAttributes().keySet()); 278 } 279 else 280 { 281 nonRightsAttrs.add(DirectoryServer.getAttributeTypeOrDefault(a.toLowerCase())); 282 } 283 } 284 } 285 286 // If the special geteffectiverights attributes were not found or 287 // the user does not have both bypass-acl privs and is not allowed to 288 // perform rights evaluation -- return the entry unchanged. 289 if (attrMask == ACI_NULL 290 || (!skipCheck && !rightsAccessAllowed(container, handler, attrMask))) 291 { 292 return; 293 } 294 295 // From here on out, geteffectiverights evaluation is being performed and 296 // the container will be manipulated. First set the flag that 297 // geteffectiverights evaluation's underway and to use the authZid for 298 // authorizationDN (they might be the same). 299 container.setGetEffectiveRightsEval(); 300 container.useAuthzid(true); 301 302 // If no attributes were requested return only entryLevel rights, else 303 // return attributeLevel rights and entryLevel rights. Always try and 304 // return the specific attribute rights if they exist. 305 if (!nonRightsAttrs.isEmpty()) 306 { 307 addAttributeLevelRights(container, handler, attrMask, e, nonRightsAttrs, 308 skipCheck, false); 309 } 310 addAttributeLevelRights(container, handler, attrMask, e, container 311 .getSpecificAttributes(), skipCheck, true); 312 addEntryLevelRights(container, handler, attrMask, e, skipCheck); 313 } 314 315 316 317 /** 318 * Perform the attributeLevel rights evaluation on a list of specified 319 * attribute types. Each attribute has an access check done for the following 320 * rights: search, read, compare, add, delete, proxy, selfwrite_add, 321 * selfwrite_delete and write. The special rights, selfwrite_add and 322 * selfwrite_delete, use the authZid as the attribute value to evaluate 323 * against the attribute type being evaluated. The selfwrite_add performs the 324 * access check using the ACI_WRITE_ADD right and selfwrite_delete uses 325 * ACI_WRITE_ADD right. The write right is made complicated by the 326 * targattrfilters keyword, which might depend on an unknown value of an 327 * attribute type. For this case a dummy attribute value is used to try and 328 * determine if a "?" needs to be placed in the rights string. The special 329 * flag ACI_SKIP_PROXY_CHECK is always set, so that proxy evaluation is 330 * bypassed in the Aci Handler's accessAllowed method. 331 * 332 * @param container 333 * The LDAP operation container to use in the evaluations. 334 * @param handler 335 * The Aci Handler to use in the access evaluations. 336 * @param mask 337 * Mask specifying what rights attribute processing to perform 338 * (aclRights or aclRightsInfo or both). 339 * @param retEntry 340 * The entry to return. 341 * @param attrList 342 * The list of attribute types to iterate over. 343 * @param skipCheck 344 * True if ACI evaluation was skipped because bypass-acl privilege 345 * was found. 346 * @param specificAttr 347 * True if this evaluation is result of specific attributes sent in 348 * the request. 349 */ 350 private static void addAttributeLevelRights( 351 AciLDAPOperationContainer container, AciHandler handler, int mask, 352 final Entry retEntry, List<AttributeType> attrList, 353 boolean skipCheck, boolean specificAttr) 354 { 355 if (attrList == null) 356 { 357 return; 358 } 359 360 for(AttributeType a : attrList) { 361 StringBuilder evalInfo=new StringBuilder(); 362 container.setCurrentAttributeType(a); 363 container.setCurrentAttributeValue(null); 364 //Perform search check and append results. 365 container.setRights(ACI_SEARCH | ACI_SKIP_PROXY_CHECK); 366 evalInfo.append(rightsString(container, handler, skipCheck, "search")); 367 addAttrLevelRightsInfo(container, mask, a, retEntry, "search"); 368 evalInfo.append(','); 369 //Perform read check and append results. 370 container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK); 371 evalInfo.append(rightsString(container, handler, skipCheck, "read")); 372 addAttrLevelRightsInfo(container, mask, a, retEntry, "read"); 373 evalInfo.append(','); 374 //Perform compare and append results. 375 container.setRights(ACI_COMPARE | ACI_SKIP_PROXY_CHECK); 376 evalInfo.append(rightsString(container, handler, skipCheck, "compare")); 377 addAttrLevelRightsInfo(container, mask, a, retEntry, "compare"); 378 evalInfo.append(','); 379 //Write right is more complicated. Create a dummy value and set that as 380 //the attribute's value. Call the special writeRightsString method, rather 381 //than rightsString. 382 ByteString val= ByteString.valueOf("dum###Val"); 383 container.setCurrentAttributeValue(val); 384 evalInfo.append(attributeLevelWriteRights(container, handler, skipCheck)); 385 addAttrLevelRightsInfo(container, mask, a, retEntry, "write"); 386 evalInfo.append(','); 387 //Perform both selfwrite_add and selfwrite_delete and append results. 388 ByteString val1 = ByteString.valueOf(container.getClientDN().toString()); 389 if(!specificAttr) 390 { 391 container.setCurrentAttributeType(dnAttributeType); 392 } 393 container.setCurrentAttributeValue(val1); 394 container.setRights(ACI_WRITE_ADD | ACI_SKIP_PROXY_CHECK); 395 evalInfo.append(rightsString(container, handler, skipCheck, 396 "selfwrite_add")); 397 addAttrLevelRightsInfo(container, mask, a, retEntry, "selfwrite_add"); 398 evalInfo.append(','); 399 container.setRights(ACI_WRITE_DELETE | ACI_SKIP_PROXY_CHECK); 400 evalInfo.append(rightsString(container, handler, skipCheck, 401 "selfwrite_delete")); 402 addAttrLevelRightsInfo(container, mask, a, retEntry, "selfwrite_delete"); 403 evalInfo.append(','); 404 container.setCurrentAttributeType(a); 405 container.setCurrentAttributeValue(null); 406 container.setRights(ACI_PROXY | ACI_SKIP_PROXY_CHECK); 407 evalInfo.append(rightsString(container, handler, skipCheck, "proxy")); 408 addAttrLevelRightsInfo(container, mask, a, retEntry, "proxy"); 409 //It is possible that only the aclRightsInfo attribute type was requested. 410 //Only add the aclRights information if the aclRights attribute type was 411 //seen. 412 if(hasAttrMask(mask, ACL_RIGHTS)) { 413 String typeStr=aclRightsAttributeLevelStr + ";" + 414 a.getNormalizedPrimaryName(); 415 AttributeType attributeType = DirectoryServer 416 .getDefaultAttributeType(typeStr); 417 Attribute attr = Attributes.create(attributeType, evalInfo 418 .toString()); 419 //It is possible that the user might have specified the same attributes 420 //in both the search and the specific attribute part of the control. 421 //Only try to add the attribute type if it already hasn't been added. 422 if(!retEntry.hasAttribute(attributeType)) 423 { 424 retEntry.addAttribute(attr,null); 425 } 426 } 427 } 428 container.setCurrentAttributeValue(null); 429 container.setCurrentAttributeType(null); 430 } 431 432 433 434 /** 435 * Perform the attributeLevel write rights evaluation. The issue here is that 436 * an ACI could contain a targattrfilters keyword that matches the attribute 437 * being evaluated. There is no way of knowing if the filter part of the 438 * targattrfilter would be successful or not. So if the ACI that allowed 439 * access, has an targattrfilter keyword, a "?" is used as the result of the 440 * write (depends on attribute value). If the allow ACI doesn't contain a 441 * targattrfilters keyword than a "1" is added. If the ACI denies then a "0" 442 * is added. If the skipCheck flag is true, then a 1 is used for the write 443 * access, since the client DN has bypass privs. 444 * 445 * @param container 446 * The LDAP operation container to use in the evaluations. 447 * @param handler 448 * The Aci Handler to use in the access evaluations. 449 * @param skipCheck 450 * True if ACI evaluation was skipped because bypass-acl privilege 451 * was found. 452 * @return A string representing the rights information. 453 */ 454 private static String attributeLevelWriteRights( 455 AciLDAPOperationContainer container, AciHandler handler, 456 boolean skipCheck) 457 { 458 StringBuilder resString=new StringBuilder(); 459 //If the user has bypass-acl privs and the authzid is equal to the 460 //authorization dn, create a right string with a '1' and a valid 461 //summary. If the user has bypass-acl privs and is querying for 462 //another authzid or they don't have privs -- fall through. 463 if(skipCheck && container.isAuthzidAuthorizationDN()) { 464 resString.append("write").append(":1"); 465 container.setEvaluationResult(EnumEvalReason.SKIP_ACI, null); 466 container.setEvalSummary(createSummary(container, true)); 467 } else { 468 // Reset everything. 469 container.resetEffectiveRightsParams(); 470 //Reset name. 471 container.setTargAttrFiltersAciName(null); 472 container.setRights(ACI_WRITE_ADD | ACI_SKIP_PROXY_CHECK); 473 final boolean addRet = handler.accessAllowed(container) 474 && container.getTargAttrFiltersAciName() == null; 475 container.setRights(ACI_WRITE_DELETE | ACI_SKIP_PROXY_CHECK); 476 final boolean delRet = handler.accessAllowed(container) 477 && container.getTargAttrFiltersAciName() == null; 478 //If both booleans are true, then access was allowed by ACIs that did 479 //not contain targattrfilters. 480 if(addRet && delRet) { 481 resString.append("write").append(":1"); 482 } else { 483 //If there is an ACI name then an ACI with a targattrfilters allowed, 484 //access. A '?' is needed because that evaluation really depends on an 485 //unknown attribute value, not the dummy value. If there is no ACI 486 //then one of the above access checks failed and a '0' is needed. 487 if(container.getTargAttrFiltersAciName() != null) { 488 resString.append("write").append(":?"); 489 } else { 490 resString.append("write").append(":0"); 491 } 492 } 493 } 494 return resString.toString(); 495 } 496 497 498 499 /** 500 * Perform entryLevel rights evaluation. The rights string is added to the 501 * entry if the aclRights attribute was seen in the search's requested 502 * attribute set. 503 * 504 * @param container 505 * The LDAP operation container to use in the evaluations. 506 * @param handler 507 * The Aci Handler to use in the access evaluations. 508 * @param mask 509 * Mask specifying what rights attribute processing to perform 510 * (aclRights or aclRightsInfo or both). 511 * @param retEntry 512 * The entry to return. 513 * @param skipCheck 514 * True if ACI evaluation was skipped because bypass-acl privilege 515 * was found. 516 */ 517 private static void addEntryLevelRights(AciLDAPOperationContainer container, 518 AciHandler handler, int mask, final Entry retEntry, 519 boolean skipCheck) 520 { 521 //Perform access evaluations for rights: add, delete, read, write, proxy. 522 StringBuilder evalInfo=new StringBuilder(); 523 container.setCurrentAttributeType(null); 524 container.setRights(ACI_ADD | ACI_SKIP_PROXY_CHECK); 525 evalInfo.append(rightsString(container, handler, skipCheck, "add")); 526 addEntryLevelRightsInfo(container, mask, retEntry, "add"); 527 evalInfo.append(','); 528 container.setCurrentAttributeType(null); 529 container.setRights(ACI_DELETE | ACI_SKIP_PROXY_CHECK); 530 evalInfo.append(rightsString(container, handler, skipCheck, "delete")); 531 addEntryLevelRightsInfo(container, mask, retEntry, "delete"); 532 evalInfo.append(','); 533 //The read right needs the entry with the full set of attributes. This was 534 //saved in the Aci Handlers maysend method. 535 container.setCurrentAttributeType(null); 536 container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK); 537 evalInfo.append(rightsString(container, handler, skipCheck, "read")); 538 addEntryLevelRightsInfo(container, mask, retEntry, "read"); 539 evalInfo.append(','); 540 //Switch back to the entry from the Aci Handler's filterentry method. 541 container.setCurrentAttributeType(null); 542 container.setRights(ACI_WRITE| ACI_SKIP_PROXY_CHECK); 543 evalInfo.append(rightsString(container, handler, skipCheck, "write")); 544 addEntryLevelRightsInfo(container, mask, retEntry, "write"); 545 evalInfo.append(','); 546 container.setCurrentAttributeType(null); 547 container.setRights(ACI_PROXY| ACI_SKIP_PROXY_CHECK); 548 evalInfo.append(rightsString(container, handler, skipCheck, "proxy")); 549 addEntryLevelRightsInfo(container, mask, retEntry, "proxy"); 550 if(hasAttrMask(mask, ACL_RIGHTS)) { 551 AttributeType attributeType= 552 DirectoryServer.getDefaultAttributeType(aclRightsEntryLevelStr); 553 Attribute attr = Attributes.create(attributeType, evalInfo.toString()); 554 retEntry.addAttribute(attr,null); 555 } 556 } 557 558 /** 559 * Create the rights for aclRights attributeLevel or entryLevel rights 560 * evaluation. The only right needing special treatment is the read right 561 * with no current attribute type set in the container. For that case the 562 * accessAllowedEntry method is used instead of the accessAllowed method. 563 * 564 * @param container The LDAP operation container to use in the evaluations. 565 * @param handler The Aci Handler to use in the access evaluations. 566 * @param skipCheck True if ACI evaluation was skipped because bypass-acl 567 * privilege was found. 568 * @param rightStr String used representation of the right we are evaluating. 569 * @return A string representing the aclRights for the current right and 570 * attribute type/value combinations. 571 */ 572 private static 573 String rightsString(AciLDAPOperationContainer container, 574 AciHandler handler, 575 boolean skipCheck, String rightStr){ 576 StringBuilder resString=new StringBuilder(); 577 container.resetEffectiveRightsParams(); 578 //If the user has bypass-acl privs and the authzid is equal to the 579 //authorization dn, create a right string with a '1' and a valid 580 //summary. If the user has bypass-acl privs and is querying for 581 //another authzid or they don't have privs -- fall through. 582 if(skipCheck && container.isAuthzidAuthorizationDN()) { 583 resString.append(rightStr).append(":1"); 584 container.setEvaluationResult(EnumEvalReason.SKIP_ACI, null); 585 container.setEvalSummary(createSummary(container, true)); 586 } else { 587 boolean ret; 588 //Check if read right check, if so do accessAllowedEntry. 589 if(container.hasRights(ACI_READ) && 590 container.getCurrentAttributeType() == null) 591 { 592 ret=handler.accessAllowedEntry(container); 593 } 594 else 595 { 596 ret=handler.accessAllowed(container); 597 } 598 599 resString.append(rightStr).append(ret ? ":1" : ":0"); 600 } 601 return resString.toString(); 602 } 603 604 605 /** 606 * Check that access is allowed on the aclRights and/or aclRightsInfo 607 * attribute types. 608 * 609 * @param container The LDAP operation container to use in the evaluations. 610 * @param handler The Aci Handler to use in the access evaluations. 611 * @param mask Mask specifying what rights attribute processing to perform 612 * (aclRights or aclRightsInfo or both). 613 * @return True if access to the geteffectiverights attribute types are 614 * allowed. 615 */ 616 private static 617 boolean rightsAccessAllowed(AciLDAPOperationContainer container, 618 AciHandler handler, int mask) { 619 boolean retRight=true, retInfo=true; 620 if(hasAttrMask(mask, ACL_RIGHTS)) { 621 container.setCurrentAttributeType(aclRights); 622 container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK); 623 retRight=handler.accessAllowed(container); 624 } 625 if(hasAttrMask(mask, ACL_RIGHTS_INFO)) { 626 container.setCurrentAttributeType(aclRightsInfo); 627 container.setRights(ACI_READ | ACI_SKIP_PROXY_CHECK); 628 retInfo=handler.accessAllowed(container); 629 } 630 return retRight && retInfo; 631 } 632 633 634 /** 635 * Add aclRightsInfo attributeLevel information to the entry. This is the 636 * summary string built from the last access check. 637 * 638 * @param container The LDAP operation container to use in the evaluations. 639 * @param mask Mask specifying what rights attribute processing to perform 640 * (aclRights or aclRightsInfo or both). 641 * @param aType The attribute type to use in building the attribute type name. 642 * @param retEntry The entry to add the rights information to. 643 * @param rightStr The string representation of the rights evaluated. 644 */ 645 private static 646 void addAttrLevelRightsInfo(AciLDAPOperationContainer container, int mask, 647 AttributeType aType, Entry retEntry, 648 String rightStr) { 649 650 //Check if the aclRightsInfo attribute was requested. 651 if(hasAttrMask(mask,ACL_RIGHTS_INFO)) { 652 //Build the attribute type. 653 String typeStr= 654 aclRightsInfoAttrLogsStr + ";" + rightStr + ";" + 655 aType.getPrimaryName(); 656 AttributeType attributeType= 657 DirectoryServer.getDefaultAttributeType(typeStr); 658 Attribute attr = Attributes.create(attributeType, 659 container.getEvalSummary()); 660 // The attribute type might have already been added, probably 661 // not but it is possible. 662 if(!retEntry.hasAttribute(attributeType)) 663 { 664 retEntry.addAttribute(attr,null); 665 } 666 } 667 } 668 669 /** 670 * Add aclRightsInfo entryLevel rights to the entry to be returned. This is 671 * the summary string built from the last access check. 672 * 673 * @param container The LDAP operation container to use in the evaluations. 674 * @param mask Mask specifying what rights attribute processing to perform 675 * (aclRights or aclRightsInfo or both). 676 * @param retEntry The entry to add the rights information to. 677 * @param rightStr The string representation of the rights evaluated. 678 */ 679 private static 680 void addEntryLevelRightsInfo(AciLDAPOperationContainer container, int mask, 681 Entry retEntry, 682 String rightStr) { 683 684 //Check if the aclRightsInfo attribute was requested. 685 if(hasAttrMask(mask,ACL_RIGHTS_INFO)) { 686 String typeStr = aclRightsInfoEntryLogsStr + ";" + rightStr; 687 AttributeType attributeType= 688 DirectoryServer.getDefaultAttributeType(typeStr); 689 Attribute attr = Attributes.create(attributeType, 690 container.getEvalSummary()); 691 retEntry.addAttribute(attr,null); 692 } 693 } 694 695 /** 696 * Check if the provided mask has a specific rights attr value. 697 * 698 * @param mask The mask with the attribute flags. 699 * @param rightsAttr The rights attr value to check for. 700 * @return True if the mask contains the rights attr value. 701 */ 702 private static boolean hasAttrMask(int mask, int rightsAttr) { 703 return (mask & rightsAttr) != 0; 704 } 705 706 707 /** 708 * Create the summary string used in the aclRightsInfo log string. 709 * 710 * @param evalCtx The evaluation context to gather information from. 711 * @param evalRet The value returned from the access evaluation. 712 * @return A summary of the ACI evaluation 713 */ 714 public static String createSummary(AciEvalContext evalCtx, boolean evalRet) 715 { 716 String srcStr = "main"; 717 String accessStatus = evalRet ? ALLOWED : NOT_ALLOWED; 718 719 //Try and determine what reason string to use. 720 String accessReason = getEvalReason(evalCtx.getEvalReason()); 721 StringBuilder decideAci = 722 getDecidingAci(evalCtx.getEvalReason(), evalCtx.getDecidingAciName()); 723 724 //Only manipulate the evaluation context's targattrfilters ACI name 725 //if not a selfwrite evaluation and the context's targattrfilter match 726 //hashtable is not empty. 727 if(!evalCtx.isTargAttrFilterMatchAciEmpty() && 728 !evalCtx.hasRights(ACI_SELF)) { 729 //If the allow list was empty then access is '0'. 730 if(evalCtx.getAllowList().isEmpty()) { 731 evalCtx.setTargAttrFiltersAciName(null); 732 } else if(evalRet) { 733 //The evaluation returned true, clear the evaluation context's 734 //targattrfilters ACI name only if a deny targattrfilters ACI 735 //was not seen. It could remove the allow. 736 if(!evalCtx.hasTargAttrFiltersMatchOp(ACL_TARGATTR_DENY_MATCH)) 737 { 738 evalCtx.setTargAttrFiltersAciName(null); 739 } 740 } else { 741 //The evaluation returned false. If the reason was an 742 //explicit deny evaluation by a non-targattrfilters ACI, clear 743 //the evaluation context's targattrfilters ACI name since targattrfilter 744 //evaluation is pretty much ignored during geteffectiverights eval. 745 //Else, it was a non-explicit deny, if there is not a targattrfilters 746 //ACI that might have granted access the deny stands, else there is 747 //a targattrfilters ACI that might grant access. 748 if(evalCtx.getEvalReason() == EnumEvalReason.EVALUATED_DENY_ACI) 749 { 750 evalCtx.setTargAttrFiltersAciName(null); 751 } 752 else if(!evalCtx.hasTargAttrFiltersMatchOp(ACL_TARGATTR_ALLOW_MATCH)) 753 { 754 evalCtx.setTargAttrFiltersAciName(null); 755 } 756 } 757 } 758 //Actually build the string. 759 String user=anonymous; 760 if(!evalCtx.getClientDN().isRootDN()) 761 { 762 user=evalCtx.getClientDN().toString(); 763 } 764 String right=evalCtx.rightToString(); 765 AttributeType aType=evalCtx.getCurrentAttributeType(); 766 String attrStr="NULL"; 767 if(aType != null) 768 { 769 attrStr=aType.getPrimaryName(); 770 } 771 if(evalCtx.getTargAttrFiltersAciName() != null) 772 { 773 decideAci.append(", access depends on attr value"); 774 } 775 return String.format(summaryFormatStr, srcStr, accessStatus, 776 right,evalCtx.getResourceDN().toString(),attrStr, user, 777 accessReason, decideAci.toString()); 778 } 779 780 private static String getEvalReason(EnumEvalReason evalReason) 781 { 782 if (evalReason == EnumEvalReason.EVALUATED_ALLOW_ACI) 783 { 784 return EVALUATED_ALLOW; 785 } 786 else if (evalReason == EnumEvalReason.EVALUATED_DENY_ACI) 787 { 788 return EVALUATED_DENY; 789 } 790 else if (evalReason == EnumEvalReason.NO_ALLOW_ACIS) 791 { 792 return NO_ALLOWS; 793 } 794 else if (evalReason == EnumEvalReason.NO_MATCHED_ALLOWS_ACIS) 795 { 796 return NO_ALLOWS_MATCHED; 797 } 798 else if (evalReason == EnumEvalReason.SKIP_ACI) 799 { 800 return SKIP_ACI; 801 } 802 return ""; 803 } 804 805 private static StringBuilder getDecidingAci(EnumEvalReason evalReason, 806 String decidingAciName) 807 { 808 StringBuilder decideAci = new StringBuilder(); 809 if (evalReason == EnumEvalReason.EVALUATED_ALLOW_ACI) 810 { 811 decideAci.append(", deciding_aci: ").append(decidingAciName); 812 } 813 else if (evalReason == EnumEvalReason.EVALUATED_DENY_ACI) 814 { 815 decideAci.append(", deciding_aci: ").append(decidingAciName); 816 } 817 return decideAci; 818 } 819 820 /** 821 * If the specified ACI is in the targattrfilters hashtable contained in the 822 * evaluation context, set the evaluation context's targattrfilters match 823 * variable to either ACL_TARGATTR_DENY_MATCH or ACL_TARGATTR_ALLOW_MATCH 824 * depending on the value of the variable denyAci. 825 * 826 * @param evalCtx The evaluation context to evaluate and save information to. 827 * @param aci The ACI to match. 828 * @param denyAci True if the evaluation was a allow, false if the 829 * evaluation was an deny or the ACI is not in the table. 830 * @return True if the ACI was found in the hashtable. 831 */ 832 public static 833 boolean setTargAttrAci(AciEvalContext evalCtx, Aci aci, boolean denyAci) { 834 if(evalCtx.hasTargAttrFiltersMatchAci(aci)) { 835 int flag = denyAci ? ACL_TARGATTR_DENY_MATCH : ACL_TARGATTR_ALLOW_MATCH; 836 evalCtx.setTargAttrFiltersMatchOp(flag); 837 return true; 838 } 839 return false; 840 } 841 842 /** 843 * Finalizes static variables on shutdown so that we release the memory 844 * associated with them (for the unit tests) and get fresh copies if we're 845 * doing an in-core restart. 846 */ 847 public static void finalizeOnShutdown() { 848 AciEffectiveRights.aclRights = null; 849 AciEffectiveRights.aclRightsInfo = null; 850 AciEffectiveRights.dnAttributeType = null; 851 } 852}