mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

coulbeck
09.08.2007 5b48640c910cc20ca84f1811854f9e77934bad3e
Fix for issue #1317: Wrong Error Code For Improperly Formatted ACIs.

Define a new attribute syntax for the aci attribute. Invalid values are now rejected by the core server before getting to the access control evaluation.
1 files added
8 files modified
479 ■■■■ changed files
opends/resource/config/config.ldif 7 ●●●●● patch | view | raw | blame | history
opends/resource/schema/00-core.ldif 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java 148 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/authorization/dseecompat/AciMessages.java 28 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/CoreMessages.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/MessageHandler.java 3 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/AciSyntax.java 262 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/SchemaConstants.java 23 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/TestAccessLogger.java 2 ●●● patch | view | raw | blame | history
opends/resource/config/config.ldif
@@ -1260,6 +1260,13 @@
ds-cfg-syntax-class: org.opends.server.schema.AbsoluteSubtreeSpecificationSyntax
ds-cfg-syntax-enabled: true
dn: cn=Sun-defined Access Control Information,cn=Syntaxes,cn=config
objectClass: top
objectClass: ds-cfg-attribute-syntax
cn: Sun-defined Access Control Information
ds-cfg-syntax-class: org.opends.server.schema.AciSyntax
ds-cfg-syntax-enabled: true
dn: cn=Attribute Type Description,cn=Syntaxes,cn=config
objectClass: top
objectClass: ds-cfg-attribute-syntax
opends/resource/schema/00-core.ldif
@@ -357,7 +357,7 @@
  USAGE dSAOperation X-ORIGIN 'draft-ietf-ldup-subentry' )
attributeTypes: ( 2.16.840.1.113730.3.1.55 NAME 'aci'
  DESC 'Sun-defined access control information attribute type'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 USAGE directoryOperation
  SYNTAX 1.3.6.1.4.1.26027.1.3.4 USAGE directoryOperation
  X-ORIGIN 'Sun Java System Directory Server' )
attributeTypes: ( 2.16.840.1.113730.3.1.542 NAME 'nsUniqueId'
  DESC 'Sun-defined unique identifier' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
@@ -517,7 +517,7 @@
  DESC 'named subordinate reference object' STRUCTURAL MUST ref
  X-ORIGIN 'RFC 3296' )
objectClasses: ( 2.16.840.1.113719.2.142.6.1.1 NAME 'ldapSubEntry'
  DESC 'LDAP Subentry class, version 1'  SUP top STRUCTURAL  MAY ( cn )
  DESC 'LDAP Subentry class, version 1'  SUP top STRUCTURAL  MAY ( cn )
  X-ORIGIN 'draft-ietf-ldup-subentry' )
objectClasses: ( 1.3.6.1.4.1.7628.5.6.1.1 NAME 'inheritableLDAPSubEntry'
  DESC 'Inheritable LDAP Subentry class, version 1'  SUP ldapSubEntry
opends/src/server/org/opends/server/authorization/dseecompat/AciHandler.java
@@ -57,7 +57,7 @@
    public static AttributeType aciType;
    /**
     * Constructor that registers the message catalog, creates the ACI list
     * Constructor that creates the ACI list
     * class that manages the ACI list. Instantiates and registers the change
     * notification listener that is used to manage the ACI list on
     * modifications and the backend initialization listener that is used to
@@ -65,7 +65,6 @@
     * are initialized/finalized.
    */
    public AciHandler() {
        AciMessages.registerMessages();
        aciList = new AciList();
        AciListenerManager aciListenerMgr =
            new AciListenerManager(aciList);
@@ -88,11 +87,30 @@
                                 ModifyOperation operation,
                                 boolean skipAccessCheck) {
        Entry resourceEntry=container.getResourceEntry();
        DN dn=resourceEntry.getDN();
        List<Modification> modifications=container.getModifications();
        for(Modification m : modifications) {
            Attribute modAttr=m.getAttribute();
            AttributeType modType=modAttr.getAttributeType();
            if(modType.equals(aciType)) {
              /*
               * Check that the operation has modify privileges if
               * it contains an "aci" attribute type.
               */
              if (!operation.getClientConnection().
                   hasPrivilege(Privilege.MODIFY_ACL, operation)) {
                int  msgID  = MSGID_ACI_MODIFY_FAILED_PRIVILEGE;
                String message =
                     getMessage(msgID,
                                String.valueOf(container.getResourceDN()),
                                String.valueOf(container.getClientDN()));
                logError(ErrorLogCategory.ACCESS_CONTROL,
                         ErrorLogSeverity.SEVERE_WARNING,
                         message, msgID);
                return false;
              }
            }
            switch(m.getModificationType()) {
              case DELETE:
              case REPLACE:
@@ -157,46 +175,6 @@
                       }
                       break;
                   }
                   /*
                    Check if the modification type has an "aci" attribute type.
                    If so, check the syntax of that attribute value. Fail the
                    the operation if the syntax check fails.
                    */
                   if(modType.equals(aciType)) {
                       try {
                           /*
                            * Check that the operation has modify privileges if
                            * it contains an "aci" attribute type. Flip the
                            * boolean to false so this check isn't made again
                            * if there are several ACI values being added.
                            */
                           if(checkPrivileges) {
                            if (!operation.getClientConnection().
                               hasPrivilege(Privilege.MODIFY_ACL, operation)) {
                              int  msgID  =
                                    MSGID_ACI_MODIFY_FAILED_PRIVILEGE;
                              String message = getMessage(msgID,
                                      String.valueOf(container.getResourceDN()),
                                    String.valueOf(container.getClientDN()));
                              logError(ErrorLogCategory.ACCESS_CONTROL,
                                         ErrorLogSeverity.SEVERE_WARNING,
                                         message, msgID);
                              return false;
                            }
                            checkPrivileges=false;
                           }
                           Aci.decode(v.getValue(),dn);
                       } catch (AciException ex) {
                           int    msgID  = MSGID_ACI_MODIFY_FAILED_DECODE;
                           String message = getMessage(msgID,
                                   String.valueOf(dn),
                                   ex.getMessage());
                           logError(ErrorLogCategory.ACCESS_CONTROL,
                                    ErrorLogSeverity.SEVERE_WARNING,
                                    message, msgID);
                           return false;
                       }
                   }
               }
            }
        }
@@ -454,63 +432,6 @@
    }
    /**
     * Evaluate an entry to be added to see if it has any "aci"
     * attribute type. If it does, examines each "aci" attribute type
     * value for syntax errors. All of the "aci" attribute type values
     * must pass syntax check for the add operation to proceed. Any
     * entry with an "aci" attribute type must have "modify-acl"
     * privileges.
     *
     * @param entry  The entry to be examined.
     * @param operation The operation to to check privileges on.
     * @param clientDN The authorization DN.
     * @return True if the entry has no ACI attributes or if all of the "aci"
     * attributes values pass ACI syntax checking.
     */
    private boolean
    verifySyntax(Entry entry, Operation operation, DN clientDN) {
        if(entry.hasOperationalAttribute(aciType)) {
            /*
             * Check that the operation has "modify-acl" privileges since the
             * entry to be added has an "aci" attribute type.
             */
            if (!operation.getClientConnection().
                    hasPrivilege(Privilege.MODIFY_ACL, operation))  {
                int    msgID  = MSGID_ACI_ADD_FAILED_PRIVILEGE;
                String message = getMessage(msgID,
                        String.valueOf(entry.getDN()),
                        String.valueOf(clientDN));
                logError(ErrorLogCategory.ACCESS_CONTROL,
                         ErrorLogSeverity.SEVERE_WARNING,
                         message, msgID);
                return false;
            }
            List<Attribute> attributeList =
                entry.getOperationalAttribute(aciType, null);
            for (Attribute attribute : attributeList)
            {
                for (AttributeValue value : attribute.getValues())
                {
                    try {
                       DN dn=entry.getDN();
                       Aci.decode(value.getValue(),dn);
                    } catch (AciException ex) {
                        int    msgID  = MSGID_ACI_ADD_FAILED_DECODE;
                        String message = getMessage(msgID,
                                String.valueOf(entry.getDN()),
                                ex.getMessage());
                        logError(ErrorLogCategory.ACCESS_CONTROL,
                                 ErrorLogSeverity.SEVERE_WARNING,
                                 message, msgID);
                        return false;
                    }
                }
            }
        }
        return true;
    }
    /**
     * Check access using the accessAllowed method. The
     * LDAP add, compare, modify and delete operations use this function.
     * The other supported LDAP operations have more specialized checks.
@@ -534,11 +455,28 @@
        AciLDAPOperationContainer operationContainer =
                new AciLDAPOperationContainer(operation, ACI_ADD);
        boolean ret=isAllowed(operationContainer,operation);
        //LDAP add needs a verify ACI syntax step in case any
        //"aci" attribute types are being added.
        if(ret)
            ret=verifySyntax(operation.getEntryToAdd(), operation,
                             operationContainer.getClientDN());
        if(ret) {
          Entry entry = operation.getEntryToAdd();
          if(entry.hasOperationalAttribute(aciType)) {
            /*
             * Check that the operation has "modify-acl" privileges since the
             * entry to be added has an "aci" attribute type.
             */
            if (!operation.getClientConnection().
                 hasPrivilege(Privilege.MODIFY_ACL, operation))  {
              int    msgID  = MSGID_ACI_ADD_FAILED_PRIVILEGE;
              String message =
                   getMessage(msgID,
                              String.valueOf(entry.getDN()),
                              String.valueOf(operationContainer.getClientDN()));
              logError(ErrorLogCategory.ACCESS_CONTROL,
                       ErrorLogSeverity.SEVERE_WARNING,
                       message, msgID);
              ret = false;
            }
          }
        }
        return ret;
    }
opends/src/server/org/opends/server/authorization/dseecompat/AciMessages.java
@@ -474,24 +474,6 @@
        CATEGORY_MASK_ACCESS_CONTROL  | 45;
    /**
     * The message ID for the ACI message that will be generated when a client
     * attempts to add an entry with the "aci" attribute type
     * and the ACI decode failed because of an syntax error.  This takes one
     * argument, which is the message string thrown by the AciException.
     */
    public static final int MSGID_ACI_ADD_FAILED_DECODE =
        CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 46;
    /**
     * The message ID for the ACI message that will be generated when a client
     * attempts to perform a modification on an "aci" attribute type
     * and the ACI decode failed because of a syntax error.  This takes one
     * argument, which is the message string thrown by the AciException.
     */
    public static final int MSGID_ACI_MODIFY_FAILED_DECODE =
        CATEGORY_MASK_ACCESS_CONTROL | SEVERITY_MASK_SEVERE_WARNING | 47;
    /**
     * The message ID for the ACI message that will be generated when
     * an ACI decode failed because of an syntax error. This message is usually
     * generated by an invalid ACI that was added during import which
@@ -876,16 +858,6 @@
                "attribute type in the entry \"%s\" failed, because the" +
                "authorization DN \"%s\" lacked modify-acl privileges.");
        registerMessage(MSGID_ACI_ADD_FAILED_DECODE,
                "An attempt to add the entry \"%s\" containing" +
                " an aci attribute type failed because of the following" +
                " reason: %s");
        registerMessage(MSGID_ACI_MODIFY_FAILED_DECODE,
                "An attempt to modify an aci "+
                "attribute type in the entry \"%s\" failed "+
                "because of the following reason: %s");
        registerMessage(MSGID_ACI_ADD_LIST_FAILED_DECODE,
                "An attempt to decode an Access Control Instruction (ACI)" +
                " failed because of the following reason: %s");
opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -6666,7 +6666,7 @@
    registerMessage(MSGID_ADD_OP_INVALID_SYNTAX,
                    "Entry \"%s\" contains an value \"%s\" for attribute %s " +
                    "Entry \"%s\" contains a value \"%s\" for attribute %s " +
                    "that is invalid according to the syntax for that " +
                    "attribute:  %s.");
    registerMessage(MSGID_ADD_ATTR_IS_OBSOLETE,
opends/src/server/org/opends/server/messages/MessageHandler.java
@@ -28,6 +28,8 @@
import org.opends.server.authorization.dseecompat.AciMessages;
import java.util.concurrent.ConcurrentHashMap;
import java.util.IllegalFormatException;
@@ -281,6 +283,7 @@
    BackendMessages.registerMessages();
    ToolMessages.registerMessages();
    TaskMessages.registerMessages();
    AciMessages.registerMessages();
  }
opends/src/server/org/opends/server/schema/AciSyntax.java
New file
@@ -0,0 +1,262 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.schema;
import org.opends.server.api.ApproximateMatchingRule;
import org.opends.server.api.AttributeSyntax;
import org.opends.server.api.EqualityMatchingRule;
import org.opends.server.api.OrderingMatchingRule;
import org.opends.server.api.SubstringMatchingRule;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.ByteString;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.DN;
import org.opends.server.types.DebugLogLevel;
import static org.opends.server.loggers.Error.*;
import static org.opends.server.loggers.debug.DebugLogger.debugCaught;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.messages.SchemaMessages.*;
import static org.opends.server.schema.SchemaConstants.*;
import org.opends.server.authorization.dseecompat.Aci;
import org.opends.server.authorization.dseecompat.AciException;
/**
 * This class implements the access control information (aci) attribute syntax.
 */
public class AciSyntax
       extends AttributeSyntax
{
  // The default approximate matching rule for this syntax.
  private ApproximateMatchingRule defaultApproximateMatchingRule;
  // The default equality matching rule for this syntax.
  private EqualityMatchingRule defaultEqualityMatchingRule;
  // The default ordering matching rule for this syntax.
  private OrderingMatchingRule defaultOrderingMatchingRule;
  // The default substring matching rule for this syntax.
  private SubstringMatchingRule defaultSubstringMatchingRule;
  /**
   * Creates a new instance of this syntax.  Note that the only thing that
   * should be done here is to invoke the default constructor for the
   * superclass.  All initialization should be performed in the
   * <CODE>initializeSyntax</CODE> method.
   */
  public AciSyntax()
  {
    super();
  }
  /**
   * Initializes this attribute syntax based on the information in the provided
   * configuration entry.
   *
   * @param  configEntry  The configuration entry that contains the information
   *                      to use to initialize this attribute syntax.
   *
   * @throws  ConfigException  If an unrecoverable problem arises in the
   *                           process of performing the initialization.
   */
  public void initializeSyntax(ConfigEntry configEntry)
         throws ConfigException
  {
    // We don't need an approximate matching rule.
    defaultApproximateMatchingRule = null;
    defaultEqualityMatchingRule =
         DirectoryServer.getEqualityMatchingRule(EMR_CASE_IGNORE_IA5_OID);
    if (defaultEqualityMatchingRule == null)
    {
      logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR,
               MSGID_ATTR_SYNTAX_UNKNOWN_EQUALITY_MATCHING_RULE,
               EMR_CASE_IGNORE_IA5_OID, SYNTAX_ACI_NAME);
    }
    // We don't need an ordering matching rule.
    defaultOrderingMatchingRule = null;
    defaultSubstringMatchingRule =
         DirectoryServer.getSubstringMatchingRule(SMR_CASE_IGNORE_IA5_OID);
    if (defaultSubstringMatchingRule == null)
    {
      logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_ERROR,
               MSGID_ATTR_SYNTAX_UNKNOWN_SUBSTRING_MATCHING_RULE,
               SMR_CASE_IGNORE_IA5_OID, SYNTAX_ACI_NAME);
    }
  }
  /**
   * Retrieves the common name for this attribute syntax.
   *
   * @return  The common name for this attribute syntax.
   */
  public String getSyntaxName()
  {
    return SYNTAX_ACI_NAME;
  }
  /**
   * Retrieves the OID for this attribute syntax.
   *
   * @return  The OID for this attribute syntax.
   */
  public String getOID()
  {
    return SYNTAX_ACI_OID;
  }
  /**
   * Retrieves a description for this attribute syntax.
   *
   * @return  A description for this attribute syntax.
   */
  public String getDescription()
  {
    return SYNTAX_ACI_DESCRIPTION;
  }
  /**
   * Retrieves the default equality matching rule that will be used for
   * attributes with this syntax.
   *
   * @return  The default equality matching rule that will be used for
   *          attributes with this syntax, or <CODE>null</CODE> if equality
   *          matches will not be allowed for this type by default.
   */
  public EqualityMatchingRule getEqualityMatchingRule()
  {
    return defaultEqualityMatchingRule;
  }
  /**
   * Retrieves the default ordering matching rule that will be used for
   * attributes with this syntax.
   *
   * @return  The default ordering matching rule that will be used for
   *          attributes with this syntax, or <CODE>null</CODE> if ordering
   *          matches will not be allowed for this type by default.
   */
  public OrderingMatchingRule getOrderingMatchingRule()
  {
    return defaultOrderingMatchingRule;
  }
  /**
   * Retrieves the default substring matching rule that will be used for
   * attributes with this syntax.
   *
   * @return  The default substring matching rule that will be used for
   *          attributes with this syntax, or <CODE>null</CODE> if substring
   *          matches will not be allowed for this type by default.
   */
  public SubstringMatchingRule getSubstringMatchingRule()
  {
    return defaultSubstringMatchingRule;
  }
  /**
   * Retrieves the default approximate matching rule that will be used for
   * attributes with this syntax.
   *
   * @return  The default approximate matching rule that will be used for
   *          attributes with this syntax, or <CODE>null</CODE> if approximate
   *          matches will not be allowed for this type by default.
   */
  public ApproximateMatchingRule getApproximateMatchingRule()
  {
    return defaultApproximateMatchingRule;
  }
  /**
   * Indicates whether the provided value is acceptable for use in an attribute
   * with this syntax.  If it is not, then the reason may be appended to the
   * provided buffer.
   *
   * @param  value          The value for which to make the determination.
   * @param  invalidReason  The buffer to which the invalid reason should be
   *                        appended.
   *
   * @return  <CODE>true</CODE> if the provided value is acceptable for use with
   *          this syntax, or <CODE>false</CODE> if not.
   */
  public boolean valueIsAcceptable(ByteString value,
                                   StringBuilder invalidReason)
  {
    try
    {
      Aci.decode(value, DN.nullDN());
    }
    catch (AciException e)
    {
      if (debugEnabled())
      {
        debugCaught(DebugLogLevel.ERROR, e);
      }
      logError(ErrorLogCategory.ACCESS_CONTROL,
               ErrorLogSeverity.SEVERE_WARNING,
               e.getMessage(), e.getMessageID());
      invalidReason.append(e.getMessage());
      return false;
    }
    return true;
  }
}
opends/src/server/org/opends/server/schema/SchemaConstants.java
@@ -811,6 +811,29 @@
   /**
    * The OID for the aci attribute syntax.
    */
   public static final String SYNTAX_ACI_OID =
        OID_OPENDS_SERVER_ATTRIBUTE_SYNTAX_BASE + ".4";
  /**
   * The description for aci attribute syntax.
   */
  public static final String SYNTAX_ACI_DESCRIPTION =
       "Sun-defined Access Control Information";
  /**
   * The name for the aci attribute syntax.
   */
  public static final String SYNTAX_ACI_NAME = "ds-syntax-dseecompat-aci";
  /**
   * The description for the attribute type description attribute syntax.
   */
opends/tests/unit-tests-testng/src/server/org/opends/server/TestAccessLogger.java
@@ -260,7 +260,7 @@
    addCommonRequestElements(addOperation, buffer);
    buffer.append(" dn=\"");
    buffer.append(addOperation.getRawEntryDN());
    buffer.append("\" attibutes={");
    buffer.append("\" attributes={");
    Iterator<LDAPAttribute> attrIterator =
         addOperation.getRawAttributes().iterator();