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