/** * 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. * @throws DirectoryException If a modified ACI could not be decoded. */ private boolean verifySyntax(Entry entry, Operation operation, DN clientDN) throws DirectoryException { 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)) { Message message = INFO_ACI_ADD_FAILED_PRIVILEGE.get( String.valueOf(entry.getDN()), String.valueOf(clientDN)); logError(message); return false; } List<Attribute> attributeList = entry.getOperationalAttribute(aciType, null); for (Attribute attribute : attributeList) { for (AttributeValue value : attribute) { try { DN dn = entry.getDN(); Aci.decode(value.getValue(), dn); } catch (AciException ex) { Message message = WARN_ACI_ADD_FAILED_DECODE.get(String.valueOf(entry.getDN()), ex.getMessage()); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } } } return true; }
/** * Creates the allow and deny ACI lists based on the provided target match context. These lists * are stored in the evaluation context. * * @param candidates List of all possible ACI candidates. * @param targetMatchCtx Target matching context to use for testing each ACI. */ private void createApplicableList( LinkedList<Aci> candidates, AciTargetMatchContext targetMatchCtx) { LinkedList<Aci> denys = new LinkedList<Aci>(); LinkedList<Aci> allows = new LinkedList<Aci>(); for (Aci aci : candidates) { if (Aci.isApplicable(aci, targetMatchCtx)) { if (aci.hasAccessType(EnumAccessType.DENY)) { denys.add(aci); } if (aci.hasAccessType(EnumAccessType.ALLOW)) { allows.add(aci); } } if (targetMatchCtx.getTargAttrFiltersMatch()) { targetMatchCtx.setTargAttrFiltersMatch(false); } } targetMatchCtx.setAllowList(allows); targetMatchCtx.setDenyList(denys); }
/** * Checks to see if a LDAP modification is allowed access. * * @param container The structure containing the LDAP modifications * @param operation The operation to check modify privileges on. operation to check and the * evaluation context to apply the check against. * @param skipAccessCheck True if access checking should be skipped. * @return True if access is allowed. * @throws DirectoryException If a modified ACI could not be decoded. */ private boolean aciCheckMods( AciLDAPOperationContainer container, LocalBackendModifyOperation operation, boolean skipAccessCheck) throws DirectoryException { Entry resourceEntry = container.getResourceEntry(); DN dn = resourceEntry.getDN(); List<Modification> modifications = operation.getModifications(); for (Modification m : modifications) { Attribute modAttr = m.getAttribute(); AttributeType modAttrType = modAttr.getAttributeType(); if (modAttrType.equals(aciType)) { /* * Check that the operation has modify privileges if it contains * an "aci" attribute type. */ if (!operation.getClientConnection().hasPrivilege(Privilege.MODIFY_ACL, operation)) { Message message = INFO_ACI_MODIFY_FAILED_PRIVILEGE.get( String.valueOf(container.getResourceDN()), String.valueOf(container.getClientDN())); logError(message); return false; } } // This access check handles the case where all attributes of this // type are being replaced or deleted. If only a subset is being // deleted than this access check is skipped. ModificationType modType = m.getModificationType(); if (((modType == ModificationType.DELETE) && modAttr.isEmpty()) || ((modType == ModificationType.REPLACE) || (modType == ModificationType.INCREMENT))) { /* * Check if we have rights to delete all values of an attribute * type in the resource entry. */ if (resourceEntry.hasAttribute(modAttrType)) { container.setCurrentAttributeType(modAttrType); List<Attribute> attrList = resourceEntry.getAttribute(modAttrType, modAttr.getOptions()); if (attrList != null) { for (Attribute a : attrList) { for (AttributeValue v : a) { container.setCurrentAttributeValue(v); container.setRights(ACI_WRITE_DELETE); if (!skipAccessCheck && !accessAllowed(container)) { return false; } } } } } } if (!modAttr.isEmpty()) { for (AttributeValue v : modAttr) { container.setCurrentAttributeType(modAttrType); switch (m.getModificationType()) { case ADD: case REPLACE: container.setCurrentAttributeValue(v); container.setRights(ACI_WRITE_ADD); if (!skipAccessCheck && !accessAllowed(container)) { return false; } break; case DELETE: container.setCurrentAttributeValue(v); container.setRights(ACI_WRITE_DELETE); if (!skipAccessCheck && !accessAllowed(container)) { return false; } break; case INCREMENT: Entry modifiedEntry = operation.getModifiedEntry(); List<Attribute> modifiedAttrs = modifiedEntry.getAttribute(modAttrType, modAttr.getOptions()); if (modifiedAttrs != null) { for (Attribute attr : modifiedAttrs) { for (AttributeValue val : attr) { container.setCurrentAttributeValue(val); container.setRights(ACI_WRITE_ADD); if (!skipAccessCheck && !accessAllowed(container)) { return false; } } } } 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 (modAttrType.equals(aciType) || modAttrType.equals(globalAciType)) { try { // A global ACI needs a NULL DN, not the DN of the // modification. if (modAttrType.equals(globalAciType)) { dn = DN.nullDN(); } Aci.decode(v.getValue(), dn); } catch (AciException ex) { Message message = WARN_ACI_MODIFY_FAILED_DECODE.get(String.valueOf(dn), ex.getMessage()); throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); } } } } } return true; }
/** * Performs the test of the deny and allow access lists using the provided evaluation context. The * deny list is checked first. * * @param evalCtx The evaluation context to use. * @return True if access is allowed. */ private boolean testApplicableLists(AciEvalContext evalCtx) { EnumEvalResult res; evalCtx.setEvalReason(EnumEvalReason.NO_REASON); LinkedList<Aci> denys = evalCtx.getDenyList(); LinkedList<Aci> allows = evalCtx.getAllowList(); // If allows list is empty and not doing geteffectiverights return // false. evalCtx.setDenyEval(true); if (allows.isEmpty() && !(evalCtx.isGetEffectiveRightsEval() && !evalCtx.hasRights(ACI_SELF) && evalCtx.isTargAttrFilterMatchAciEmpty())) { evalCtx.setEvalReason(EnumEvalReason.NO_ALLOW_ACIS); evalCtx.setDecidingAci(null); return false; } for (Aci denyAci : denys) { res = Aci.evaluate(evalCtx, denyAci); // Failure could be returned if a system limit is hit or // search fails if (res.equals(EnumEvalResult.FAIL)) { evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI); evalCtx.setDecidingAci(denyAci); return false; } else if (res.equals(EnumEvalResult.TRUE)) { if (evalCtx.isGetEffectiveRightsEval() && !evalCtx.hasRights(ACI_SELF) && !evalCtx.isTargAttrFilterMatchAciEmpty()) { // Iterate to next only if deny ACI contains a targattrfilters // keyword. if (AciEffectiveRights.setTargAttrAci(evalCtx, denyAci, true)) { continue; } evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI); evalCtx.setDecidingAci(denyAci); return false; } else { evalCtx.setEvalReason(EnumEvalReason.EVALUATED_DENY_ACI); evalCtx.setDecidingAci(denyAci); return false; } } } // Now check the allows -- flip the deny flag to false first. evalCtx.setDenyEval(false); for (Aci allowAci : allows) { res = Aci.evaluate(evalCtx, allowAci); if (res.equals(EnumEvalResult.TRUE)) { if (evalCtx.isGetEffectiveRightsEval() && !evalCtx.hasRights(ACI_SELF) && !evalCtx.isTargAttrFilterMatchAciEmpty()) { // Iterate to next only if deny ACI contains a targattrfilters // keyword. if (AciEffectiveRights.setTargAttrAci(evalCtx, allowAci, false)) { continue; } evalCtx.setEvalReason(EnumEvalReason.EVALUATED_ALLOW_ACI); evalCtx.setDecidingAci(allowAci); return true; } else { evalCtx.setEvalReason(EnumEvalReason.EVALUATED_ALLOW_ACI); evalCtx.setDecidingAci(allowAci); return true; } } } // Nothing matched fall through. evalCtx.setEvalReason(EnumEvalReason.NO_MATCHED_ALLOWS_ACIS); evalCtx.setDecidingAci(null); return false; }