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