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