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 }