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