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 2010-2016 ForgeRock AS. 016 */ 017package org.opends.server.authorization.dseecompat; 018 019import static org.opends.messages.AccessControlMessages.*; 020import static org.opends.server.util.StaticUtils.*; 021 022import java.util.HashSet; 023import java.util.Set; 024import java.util.regex.Pattern; 025 026import org.forgerock.i18n.LocalizableMessage; 027import org.forgerock.opendj.ldap.ByteSequence; 028import org.forgerock.opendj.ldap.DN; 029 030/** 031 * The Aci class represents ACI strings. 032 */ 033public class Aci implements Comparable<Aci> 034{ 035 036 /** 037 * The body of the ACI is the version, name and permission-bind rule 038 * pairs. 039 */ 040 private AciBody body; 041 042 /** 043 * The ACI targets. 044 */ 045 private AciTargets targets; 046 047 /** 048 * Version that we support. 049 */ 050 public static final String supportedVersion="3.0"; 051 052 /** 053 * String representation of the ACI used. 054 */ 055 private String aciString; 056 057 /** 058 * The DN of the entry containing this ACI. 059 */ 060 private final DN dn; 061 062 /** 063 * Regular expression matching a word group. 064 */ 065 public static final String WORD_GROUP="(\\w+)"; 066 067 /** 068 * Regular expression matching a word group at the start of a 069 * pattern. 070 */ 071 public static final String WORD_GROUP_START_PATTERN = "^" + WORD_GROUP; 072 073 /** 074 * Regular expression matching a white space. 075 */ 076 public static final String ZERO_OR_MORE_WHITESPACE="\\s*"; 077 078 /** 079 * Regular expression matching a white space at the start of a pattern. 080 */ 081 public static final String ZERO_OR_MORE_WHITESPACE_START_PATTERN = 082 "^" + ZERO_OR_MORE_WHITESPACE ; 083 084 /** 085 * Regular expression matching a white space at the end of a pattern. 086 */ 087 private static final String ZERO_OR_MORE_WHITESPACE_END_PATTERN = 088 ZERO_OR_MORE_WHITESPACE + "$"; 089 090 /** 091 * Regular expression matching a ACL statement separator. 092 */ 093 public static final String ACI_STATEMENT_SEPARATOR = 094 ZERO_OR_MORE_WHITESPACE + ";" + ZERO_OR_MORE_WHITESPACE; 095 096 /** 097 * This regular expression is used to do a quick syntax check 098 * when an ACI is being decoded. 099 */ 100 private static final String aciRegex = 101 ZERO_OR_MORE_WHITESPACE_START_PATTERN + AciTargets.targetsRegex + 102 ZERO_OR_MORE_WHITESPACE + AciBody.bodyRegx + 103 ZERO_OR_MORE_WHITESPACE_END_PATTERN; 104 105 106 /** 107 * Regular expression that graciously matches an attribute type name. Must 108 * begin with an ASCII letter or digit, and contain only ASCII letters, 109 * digit characters, hyphens, semi-colons and underscores. It also allows 110 * the special shorthand characters "*" for all user attributes and "+" for 111 * all operational attributes. 112 */ 113 public static final String ATTR_NAME = 114 "((?i)[a-z\\d]{1}[[a-z]\\d-_.]*(?-i)|\\*{1}|\\+{1})"; 115 116 /** 117 * Regular expression matching a LDAP URL. 118 */ 119 public static final String LDAP_URL = ZERO_OR_MORE_WHITESPACE + 120 "(ldap:///[^\\|]+)"; 121 122 /** 123 * String used to check for NULL ldap URL. 124 */ 125 public static final String NULL_LDAP_URL = "ldap:///"; 126 127 /** 128 * Regular expression used to match token that joins expressions (||). 129 */ 130 public static final String LOGICAL_OR = "\\|\\|"; 131 132 /** 133 * Regular expression used to match an open parenthesis. 134 */ 135 public static final String OPEN_PAREN = "\\("; 136 137 /** 138 * Regular expression used to match a closed parenthesis. 139 */ 140 public static final String CLOSED_PAREN = "\\)"; 141 142 /** 143 * Regular expression used to match a single equal sign. 144 */ 145 public static final String EQUAL_SIGN = "={1}"; 146 147 /** 148 * Regular expression the matches "*". 149 */ 150 public static final String ALL_USER_ATTRS_WILD_CARD = 151 ZERO_OR_MORE_WHITESPACE + 152 "\\*" + ZERO_OR_MORE_WHITESPACE; 153 154 /** 155 * Regular expression the matches "+". 156 */ 157 public static final String ALL_OP_ATTRS_WILD_CARD = 158 ZERO_OR_MORE_WHITESPACE + 159 "\\+" + ZERO_OR_MORE_WHITESPACE; 160 161 /** 162 * Regular expression used to do quick check of OID string. 163 */ 164 private static final String OID_NAME = "[\\d.\\*]*"; 165 166 /** 167 * Regular expression that matches one or more OID_NAME's separated by 168 * the "||" token. 169 */ 170 private static final String oidListRegex = ZERO_OR_MORE_WHITESPACE + 171 OID_NAME + ZERO_OR_MORE_WHITESPACE + "(" + 172 LOGICAL_OR + ZERO_OR_MORE_WHITESPACE + OID_NAME + 173 ZERO_OR_MORE_WHITESPACE + ")*"; 174 175 /** 176 * ACI_ADD is used to set the container rights for a LDAP add operation. 177 */ 178 public static final int ACI_ADD = 0x0020; 179 180 /** 181 * ACI_DELETE is used to set the container rights for a LDAP 182 * delete operation. 183 */ 184 public static final int ACI_DELETE = 0x0010; 185 186 /** 187 * ACI_READ is used to set the container rights for a LDAP 188 * search operation. 189 */ 190 public static final int ACI_READ = 0x0004; 191 192 /** 193 * ACI_WRITE is used to set the container rights for a LDAP 194 * modify operation. 195 */ 196 public static final int ACI_WRITE = 0x0008; 197 198 /** 199 * ACI_COMPARE is used to set the container rights for a LDAP 200 * compare operation. 201 */ 202 public static final int ACI_COMPARE = 0x0001; 203 204 /** 205 * ACI_SEARCH is used to set the container rights a LDAP search operation. 206 */ 207 public static final int ACI_SEARCH = 0x0002; 208 209 /** 210 * ACI_SELF is used for the SELFWRITE right. 211 */ 212 public static final int ACI_SELF = 0x0040; 213 214 /** 215 * ACI_ALL is used to as a mask for all of the above. These 216 * six below are not masked by the ACI_ALL. 217 */ 218 public static final int ACI_ALL = 0x007F; 219 220 /** 221 * ACI_PROXY is used for the PROXY right. 222 */ 223 public static final int ACI_PROXY = 0x0080; 224 225 /** 226 * ACI_IMPORT is used to set the container rights for a LDAP 227 * modify dn operation. 228 */ 229 public static final int ACI_IMPORT = 0x0100; 230 231 /** 232 * ACI_EXPORT is used to set the container rights for a LDAP 233 * modify dn operation. 234 */ 235 public static final int ACI_EXPORT = 0x0200; 236 237 /** 238 * ACI_WRITE_ADD is used by the LDAP modify operation. 239 */ 240 public static final int ACI_WRITE_ADD = 0x800; 241 242 /** 243 * ACI_WRITE_DELETE is used by the LDAP modify operation. 244 */ 245 public static final int ACI_WRITE_DELETE = 0x400; 246 247 /** 248 * ACI_SKIP_PROXY_CHECK is used to bypass the proxy access check. 249 */ 250 public static final int ACI_SKIP_PROXY_CHECK = 0x400000; 251 252 /** 253 * TARGATTRFILTER_ADD is used to specify that a 254 * targattrfilters ADD operation was seen in the ACI. For example, 255 * given an ACI with: 256 * 257 * (targattrfilters="add=mail:(mail=*@example.com)") 258 * 259 * The TARGATTRFILTERS_ADD flag would be set during ACI parsing in the 260 * TargAttrFilters class. 261 */ 262 public static final int TARGATTRFILTERS_ADD = 0x1000; 263 264 /** 265 * TARGATTRFILTER_DELETE is used to specify that a 266 * targattrfilters DELETE operation was seen in the ACI. For example, 267 * given an ACI with: 268 * 269 * (targattrfilters="del=mail:(mail=*@example.com)") 270 * 271 * The TARGATTRFILTERS_DELETE flag would be set during ACI parsing in the 272 * TargAttrFilters class. 273 */ 274 public static final int TARGATTRFILTERS_DELETE = 0x2000; 275 276 /** 277 * Used by the control evaluation access check. 278 */ 279 public static final int ACI_CONTROL = 0x4000; 280 281 /** 282 * Used by the extended operation access check. 283 */ 284 public static final int ACI_EXT_OP = 0x8000; 285 286 /** 287 * ACI_ATTR_STAR_MATCHED is the flag set when the evaluation reason of a 288 * AciHandler.maysend ACI_READ access evaluation was the result of an 289 * ACI targetattr all attributes expression (targetattr="*") target match. 290 * For this flag to be set, there must be only one ACI matching. 291 * 292 * This flag and ACI_FOUND_ATTR_RULE are used in the 293 * AciHandler.filterEntry.accessAllowedAttrs method to skip access 294 * evaluation if the flag is ACI_ATTR_STAR_MATCHED (all attributes match) 295 * and the attribute type is not operational. 296 */ 297 public static final int ACI_USER_ATTR_STAR_MATCHED = 0x0008; 298 299 /** 300 * ACI_FOUND_USER_ATTR_RULE is the flag set when the evaluation reason of a 301 * AciHandler.maysend ACI_READ access evaluation was the result of an 302 * ACI targetattr specific user attribute expression 303 * (targetattr="some user attribute type") target match. 304 */ 305 public static final int ACI_FOUND_USER_ATTR_RULE = 0x0010; 306 307 /** 308 * ACI_OP_ATTR_PLUS_MATCHED is the flag set when the evaluation reason of a 309 * AciHandler.maysend ACI_READ access evaluation was the result of an 310 * ACI targetattr all operational attributes expression (targetattr="+") 311 * target match. For this flag to be set, there must be only one 312 * ACI matching. 313 * 314 * This flag and ACI_FOUND_OP_ATTR_RULE are used in the 315 * AciHandler.filterEntry.accessAllowedAttrs method to skip access 316 * evaluation if the flag is ACI_OP_ATTR_PLUS_MATCHED (all operational 317 * attributes match) and the attribute type is operational. 318 */ 319 public static final int ACI_OP_ATTR_PLUS_MATCHED = 0x0004; 320 321 /** 322 * ACI_FOUND_OP_ATTR_RULE is the flag set when the evaluation reason of a 323 * AciHandler.maysend ACI_READ access evaluation was the result of an 324 * ACI targetattr specific operational attribute expression 325 * (targetattr="some operational attribute type") target match. 326 */ 327 public static final int ACI_FOUND_OP_ATTR_RULE = 0x0020; 328 329 /** 330 * ACI_NULL is used to set the container rights to all zeros. Used 331 * by LDAP modify. 332 */ 333 public static final int ACI_NULL = 0x0000; 334 335 /** 336 * Construct a new Aci from the provided arguments. 337 * @param input The string representation of the ACI. 338 * @param dn The DN of entry containing the ACI. 339 * @param body The body of the ACI. 340 * @param targets The targets of the ACI. 341 */ 342 private Aci(String input, DN dn, AciBody body, AciTargets targets) { 343 this.aciString = input; 344 this.dn=dn; 345 this.body=body; 346 this.targets=targets; 347 } 348 349 /** 350 * Decode an ACI byte string. 351 * @param byteString The ByteString containing the ACI string. 352 * @param dn DN of the ACI entry. 353 * @return Returns a decoded ACI representing the string argument. 354 * @throws AciException If the parsing of the ACI string fails. 355 */ 356 public static Aci decode (ByteSequence byteString, DN dn) 357 throws AciException { 358 String input=byteString.toString(); 359 //Perform a quick pattern check against the string to catch any 360 //obvious syntax errors. 361 if (!Pattern.matches(aciRegex, input)) { 362 LocalizableMessage message = WARN_ACI_SYNTAX_GENERAL_PARSE_FAILED.get(input); 363 throw new AciException(message); 364 } 365 //Decode the body first. 366 AciBody body=AciBody.decode(input); 367 //Create a substring from the start of the string to start of 368 //the body. That should be the target. 369 String targetStr = input.substring(0, body.getMatcherStartPos()); 370 //Decode that target string using the substring. 371 AciTargets targets=AciTargets.decode(targetStr, dn); 372 return new Aci(input, dn, body, targets); 373 } 374 375 /** 376 * Return the string representation of the ACI. This was the string that 377 * was used to create the Aci class. 378 * @return A string representation of the ACI. 379 */ 380 @Override 381 public String toString() { 382 return aciString; 383 } 384 385 /** 386 * Returns the targets of the ACI. 387 * @return Any AciTargets of the ACI. There may be no targets 388 * so this might be null. 389 */ 390 public AciTargets getTargets() { 391 return targets; 392 } 393 394 /** 395 * Return the DN of the entry containing the ACI. 396 * @return The DN of the entry containing the ACI. 397 */ 398 public DN getDN() { 399 return dn; 400 } 401 402 /** 403 * Test if the given ACI is applicable using the target match information 404 * provided. The ACI target can have seven keywords at this time: 405 * 406 * These two base decision on the resource entry DN: 407 * 408 * 1. target - checked in isTargetApplicable. 409 * 2. targetscope - checked in isTargetApplicable. 410 * 411 * These three base decision on resource entry attributes: 412 * 413 * 3. targetfilter - checked in isTargetFilterApplicable. 414 * 4. targetattr - checked in isTargetAttrApplicable. 415 * 5. targattrfilters - checked in isTargAttrFiltersApplicable. 416 * 417 * These two base decisions on a resource entry built by the ACI handler 418 * that only contains a DN: 419 * 6. targetcontrol - check in isTargetControlApplicable. 420 * 7. extop - check in isExtOpApplicable. 421 * 422 * Six and seven are specific to the check being done: targetcontrol when a 423 * control is being evaluated and extop when an extended operation is 424 * evaluated. None of the attribute based keywords should be checked 425 * when a control or extended op is being evaluated, because one 426 * of those attribute keywords rule might incorrectly make an ACI 427 * applicable that shouldn't be. This can happen by erroneously basing 428 * their decision on the ACI handler generated stub resource entry. For 429 * example, a "(targetattr != userpassword)" rule would match the generated 430 * stub resource entry, even though a control or extended op might be 431 * denied. 432 * 433 * What is allowed is the target and targetscope keywords, since the DN is 434 * known, so they are checked along with the correct method for the access 435 * check (isTargetControlApplicable for control and 436 * isTExtOpApplicable for extended operations). See comments in code 437 * where these checks are done. 438 * 439 * @param aci The ACI to test. 440 * @param matchCtx The target matching context containing all the info 441 * needed to match ACI targets. 442 * @return True if this ACI targets are applicable or match. 443 */ 444 public static boolean isApplicable(Aci aci, AciTargetMatchContext matchCtx) { 445 if(matchCtx.hasRights(ACI_EXT_OP)) { 446 //Extended operation is being evaluated. 447 return AciTargets.isTargetApplicable(aci, matchCtx) && 448 AciTargets.isExtOpApplicable(aci, matchCtx); 449 } else if(matchCtx.hasRights(ACI_CONTROL)) { 450 //Control is being evaluated. 451 return AciTargets.isTargetApplicable(aci, matchCtx) && 452 AciTargets.isTargetControlApplicable(aci, matchCtx); 453 } else { 454 //If an ACI has extOp or targetControl targets skip it because the 455 //matchCtx right does not contain either ACI_EXT_OP or ACI_CONTROL at 456 //this point. 457 return hasNoExtOpOrTargetControl(aci.getTargets()) 458 && haveSimilarRights(aci, matchCtx) 459 && AciTargets.isTargetApplicable(aci, matchCtx) 460 && AciTargets.isTargetFilterApplicable(aci, matchCtx) 461 && AciTargets.isTargAttrFiltersApplicable(aci, matchCtx) 462 && AciTargets.isTargetAttrApplicable(aci, matchCtx); 463 } 464 } 465 466 private static boolean hasNoExtOpOrTargetControl(AciTargets aciTargets) 467 { 468 return aciTargets.getExtOp() == null 469 && aciTargets.getTargetControl() == null; 470 } 471 472 private static boolean haveSimilarRights(Aci aci, 473 AciTargetMatchContext matchCtx) 474 { 475 return aci.hasRights(matchCtx.getRights()) 476 || (aci.hasRights(ACI_SEARCH| ACI_READ) 477 && matchCtx.hasRights(ACI_SEARCH | ACI_READ)); 478 } 479 480 /** 481 * Check if the body of the ACI matches the rights specified. 482 * @param rights Bit mask representing the rights to match. 483 * @return True if the body's rights match one of the rights specified. 484 */ 485 public boolean hasRights(int rights) { 486 return body.hasRights(rights); 487 } 488 489 /** 490 * Re-direct has access type to the body's hasAccessType method. 491 * @param accessType The access type to match. 492 * @return True if the body's hasAccessType determines a permission 493 * contains this access type (allow or deny are valid types). 494 */ 495 public boolean hasAccessType(EnumAccessType accessType) { 496 return body.hasAccessType(accessType); 497 } 498 499 /** 500 * Evaluate this ACI using the evaluation context provided. Re-direct 501 * that calls the body's evaluate method. 502 * @param evalCtx The evaluation context to evaluate with. 503 * @return EnumEvalResult that contains the evaluation result of this 504 * aci evaluation. 505 */ 506 private EnumEvalResult evaluate(AciEvalContext evalCtx) { 507 return body.evaluate(evalCtx); 508 } 509 510 /** 511 * Static class used to evaluate an ACI and evaluation context. 512 * @param evalCtx The context to evaluate with. 513 * @param aci The ACI to evaluate. 514 * @return EnumEvalResult that contains the evaluation result of the aci 515 * evaluation. 516 */ 517 public static EnumEvalResult evaluate(AciEvalContext evalCtx, Aci aci) { 518 return aci.evaluate(evalCtx); 519 } 520 521 /** 522 * Returns the name string of this ACI. 523 * @return The name string. 524 */ 525 public String getName() { 526 return this.body.getName(); 527 } 528 529 530 /** 531 * Decode an OIDs expression string. 532 * 533 * @param expr A string representing the OID expression. 534 * @param msg A message to be used if there is an exception. 535 * 536 * @return Return a hash set of verified OID strings parsed from the OID 537 * expression. 538 * 539 * @throws AciException If the specified expression string is invalid. 540 */ 541 public static Set<String> decodeOID(String expr, LocalizableMessage msg) 542 throws AciException { 543 Set<String> OIDs = new HashSet<>(); 544 //Quick check to see if the expression is valid. 545 if (Pattern.matches(oidListRegex, expr)) { 546 // Remove the spaces in the oid string and 547 // split the list. 548 Pattern separatorPattern = 549 Pattern.compile(LOGICAL_OR); 550 String oidString = 551 expr.replaceAll(ZERO_OR_MORE_WHITESPACE, ""); 552 String[] oidArray= 553 separatorPattern.split(oidString); 554 //More careful analysis of each OID string. 555 for(String oid : oidArray) { 556 verifyOid(oid); 557 OIDs.add(oid); 558 } 559 } else { 560 throw new AciException(msg); 561 } 562 return OIDs; 563 } 564 565 /** 566 * Verify the specified OID string. 567 * 568 * @param oidStr The string representing an OID. 569 * 570 * @throws AciException If the specified string is invalid. 571 */ 572 private static void verifyOid(String oidStr) throws AciException { 573 int pos=0, length=oidStr.length(); 574 char c; 575 if("*".equals(oidStr)) 576 { 577 return; 578 } 579 boolean lastWasPeriod = false; 580 while (pos < length && ((c = oidStr.charAt(pos++)) != ' ')) { 581 if (c == '.') { 582 if (lastWasPeriod) { 583 LocalizableMessage message = WARN_ACI_SYNTAX_DOUBLE_PERIOD_IN_NUMERIC_OID.get( 584 oidStr, pos-1); 585 throw new AciException(message); 586 } 587 lastWasPeriod = true; 588 } else if (! isDigit(c)) { 589 LocalizableMessage message = 590 WARN_ACI_SYNTAX_ILLEGAL_CHAR_IN_NUMERIC_OID.get(oidStr, c, pos-1); 591 throw new AciException(message); 592 } else { 593 lastWasPeriod = false; 594 } 595 } 596 } 597 598 /** 599 * Compares this Aci with the provided Aci based on a natural order. 600 * This order will be first hierarchical (ancestors will come before 601 * descendants) and then alphabetical by attribute name(s) and 602 * value(s). 603 * 604 * @param aci The Aci against which to compare this Aci. 605 * 606 * @return A negative integer if this Aci should come before the 607 * provided Aci, a positive integer if this Aci should come 608 * after the provided Aci, or zero if there is no difference 609 * with regard to ordering. 610 */ 611 @Override 612 public int compareTo(Aci aci) 613 { 614 return this.aciString.compareTo(aci.toString()); 615 } 616 }