/** * 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; }
/** * Generates an entry for a backup directory based on the provided DN. The DN must contain an RDN * component that specifies the path to the backup directory, and that directory must exist and be * a valid backup directory. * * @param entryDN The DN of the backup directory entry to retrieve. * @return The requested backup directory entry. * @throws DirectoryException If the specified directory does not exist or is not a valid backup * directory, or if the DN does not specify any backup directory. */ private Entry getBackupDirectoryEntry(DN entryDN) throws DirectoryException { // Make sure that the DN specifies a backup directory. AttributeType t = DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); AttributeValue v = entryDN.getRDN().getAttributeValue(t); if (v == null) { Message message = ERR_BACKUP_DN_DOES_NOT_SPECIFY_DIRECTORY.get(String.valueOf(entryDN)); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, backupBaseDN, null); } // Get a handle to the backup directory and the information that it // contains. BackupDirectory backupDirectory; try { backupDirectory = BackupDirectory.readBackupDirectoryDescriptor(v.getValue().toString()); } catch (ConfigException ce) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, ce); } Message message = ERR_BACKUP_INVALID_BACKUP_DIRECTORY.get(String.valueOf(entryDN), ce.getMessage()); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_BACKUP_ERROR_GETTING_BACKUP_DIRECTORY.get(getExceptionMessage(e)); throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); } // Construct the backup directory entry to return. LinkedHashMap<ObjectClass, String> ocMap = new LinkedHashMap<ObjectClass, String>(2); ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP); ObjectClass backupDirOC = DirectoryServer.getObjectClass(OC_BACKUP_DIRECTORY, true); ocMap.put(backupDirOC, OC_BACKUP_DIRECTORY); LinkedHashMap<AttributeType, List<Attribute>> opAttrs = new LinkedHashMap<AttributeType, List<Attribute>>(0); LinkedHashMap<AttributeType, List<Attribute>> userAttrs = new LinkedHashMap<AttributeType, List<Attribute>>(3); ArrayList<Attribute> attrList = new ArrayList<Attribute>(1); attrList.add(Attributes.create(t, v)); userAttrs.put(t, attrList); t = DirectoryServer.getAttributeType(ATTR_BACKUP_BACKEND_DN, true); attrList = new ArrayList<Attribute>(1); attrList.add( Attributes.create( t, AttributeValues.create(t, backupDirectory.getConfigEntryDN().toString()))); userAttrs.put(t, attrList); Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs); e.processVirtualAttributes(); return e; }
/** * Performs a successful LDAP bind using CRAM-MD5 using the dn: form of the authentication ID * using a long password (longer than 64 bytes). * * @throws Exception If an unexpected problem occurs. */ @Test() public void testLDAPBindSuccessWithDNAndLongPassword() throws Exception { TestCaseUtils.initializeTestBackend(true); String password = "******"; Entry e = TestCaseUtils.makeEntry( "dn: uid=test.user,o=test", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: test.user", "givenName: Test", "sn: User", "cn: Test User", "userPassword: "******"ds-pwp-password-policy-dn: cn=Clear UserPassword Policy," + "cn=Password Policies,cn=config"); InternalClientConnection conn = InternalClientConnection.getRootConnection(); AddOperation addOperation = conn.processAdd( e.getDN(), e.getObjectClasses(), e.getUserAttributes(), e.getOperationalAttributes()); assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); String[] args = { "--noPropertiesFile", "-h", "127.0.0.1", "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), "-o", "mech=CRAM-MD5", "-o", "authid=dn:uid=test.user,o=test", "-w", password, "-b", "", "-s", "base", "(objectClass=*)" }; assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0); }
/** {@inheritDoc} */ @Override() public void replaceEntry(Entry oldEntry, Entry newEntry, ModifyOperation modifyOperation) throws DirectoryException, CanceledOperationException { checkDiskSpace(modifyOperation); writerBegin(); DN entryDN = newEntry.getDN(); EntryContainer ec; if (rootContainer != null) { ec = rootContainer.getEntryContainer(entryDN); } else { Message message = ERR_ROOT_CONTAINER_NOT_INITIALIZED.get(getBackendID()); throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); } ec.sharedLock.lock(); try { ec.replaceEntry(oldEntry, newEntry, modifyOperation); } catch (DatabaseException e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } throw createDirectoryException(e); } finally { ec.sharedLock.unlock(); writerEnd(); } }
/** {@inheritDoc} */ @Override public Entry getEntry(DN entryDN) throws DirectoryException { // If the requested entry was null, then throw an exception. if (entryDN == null) { throw new DirectoryException( DirectoryServer.getServerErrorResultCode(), ERR_BACKEND_GET_ENTRY_NULL.get(getBackendID())); } // If the requested entry was the backend base entry, then retrieve it. if (entryDN.equals(backupBaseDN)) { return backupBaseEntry.duplicate(true); } // See if the requested entry was one level below the backend base entry. // If so, then it must point to a backup directory. Otherwise, it must be // two levels below the backup base entry and must point to a specific // backup. DN parentDN = entryDN.getParentDNInSuffix(); if (parentDN == null) { Message message = ERR_BACKUP_INVALID_BASE.get(String.valueOf(entryDN)); throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message); } else if (parentDN.equals(backupBaseDN)) { return getBackupDirectoryEntry(entryDN); } else if (backupBaseDN.equals(parentDN.getParentDNInSuffix())) { return getBackupEntry(entryDN); } else { Message message = ERR_BACKUP_INVALID_BASE.get(String.valueOf(entryDN)); throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, backupBaseDN, null); } }
/** {@inheritDoc} */ @Override public void replaceEntry(Entry oldEntry, Entry newEntry, ModifyOperation modifyOperation) throws DirectoryException { throw new DirectoryException( ResultCode.UNWILLING_TO_PERFORM, ERR_BACKEND_MODIFY_NOT_SUPPORTED.get(String.valueOf(oldEntry.getDN()), getBackendID())); }
/** * Gathers all of the attribute types in an entry along with the "objectclass" attribute type in a * List. The "objectclass" attribute is added to the list first so it is evaluated first. * * @param e Entry to gather the attributes for. * @return List containing the attribute types. */ private List<AttributeType> getAllAttrs(Entry e) { Map<AttributeType, List<Attribute>> attrMap = e.getUserAttributes(); Map<AttributeType, List<Attribute>> opAttrMap = e.getOperationalAttributes(); List<AttributeType> typeList = new LinkedList<AttributeType>(); Attribute attr = e.getObjectClassAttribute(); /* * When a search is not all attributes returned, the "objectclass" * attribute type is missing from the entry. */ if (attr != null) { AttributeType ocType = attr.getAttributeType(); typeList.add(ocType); } typeList.addAll(attrMap.keySet()); typeList.addAll(opAttrMap.keySet()); return typeList; }
/** * Performs a failed LDAP bind using CRAM-MD5 using the dn: form of the authentication ID with the * DN of a user that doesn't exist. * * @throws Exception If an unexpected problem occurs. */ @Test() public void testLDAPBindFailNoSuchUser() throws Exception { TestCaseUtils.initializeTestBackend(true); Entry e = TestCaseUtils.makeEntry( "dn: uid=test.user,o=test", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "uid: test.user", "givenName: Test", "sn: User", "cn: Test User", "userPassword: password"); InternalClientConnection conn = InternalClientConnection.getRootConnection(); AddOperation addOperation = conn.processAdd( e.getDN(), e.getObjectClasses(), e.getUserAttributes(), e.getOperationalAttributes()); assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); String[] args = { "--noPropertiesFile", "-h", "127.0.0.1", "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), "-o", "mech=CRAM-MD5", "-o", "authid=dn:uid=doesntexist,o=test", "-w", "password", "-b", "", "-s", "base", "(objectClass=*)" }; assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0); }
/** * Validates a number of password policy state constraints for the user. This will be called * before the offered credentials are checked. * * @param userEntry The entry for the user that is authenticating. * @param saslHandler The SASL mechanism handler if this is a SASL bind, or {@code null} for a * simple bind. * @throws DirectoryException If a problem occurs that should cause the bind to fail. */ protected void checkUnverifiedPasswordPolicyState( Entry userEntry, SASLMechanismHandler<?> saslHandler) throws DirectoryException { PasswordPolicyState pwPolicyState = (PasswordPolicyState) authPolicyState; PasswordPolicy policy = pwPolicyState.getAuthenticationPolicy(); boolean isSASLBind = saslHandler != null; // If the password policy is configured to track authentication failures or // keep the last login time and the associated backend is disabled, then we // may need to reject the bind immediately. if ((policy.getStateUpdateFailurePolicy() == PasswordPolicyCfgDefn.StateUpdateFailurePolicy.PROACTIVE) && ((policy.getLockoutFailureCount() > 0) || ((policy.getLastLoginTimeAttribute() != null) && (policy.getLastLoginTimeFormat() != null))) && ((DirectoryServer.getWritabilityMode() == WritabilityMode.DISABLED) || (backend.getWritabilityMode() == WritabilityMode.DISABLED))) { // This policy isn't applicable to root users, so if it's a root // user then ignore it. if (!DirectoryServer.isRootDN(userEntry.getName())) { throw new DirectoryException( ResultCode.INVALID_CREDENTIALS, ERR_BIND_OPERATION_WRITABILITY_DISABLED.get(userEntry.getName())); } } // Check to see if the authentication must be done in a secure // manner. If so, then the client connection must be secure. if (policy.isRequireSecureAuthentication() && !clientConnection.isSecure()) { if (isSASLBind) { if (!saslHandler.isSecure(saslMechanism)) { throw new DirectoryException( ResultCode.INVALID_CREDENTIALS, ERR_BIND_OPERATION_INSECURE_SASL_BIND.get(saslMechanism, userEntry.getName())); } } else { throw new DirectoryException( ResultCode.INVALID_CREDENTIALS, ERR_BIND_OPERATION_INSECURE_SIMPLE_BIND.get()); } } }
/** {@inheritDoc} */ @Override public boolean mayProxy(Entry proxyUser, Entry proxiedUser, Operation op) { boolean ret; if (!(ret = skipAccessCheck(proxyUser))) { AuthenticationInfo authInfo = new AuthenticationInfo(proxyUser, DirectoryServer.isRootDN(proxyUser.getDN())); AciLDAPOperationContainer operationContainer = new AciLDAPOperationContainer(op, proxiedUser, authInfo, ACI_PROXY); ret = accessAllowedEntry(operationContainer); } return ret; }
/** {@inheritDoc} */ @Override public boolean maySend(DN dn, Operation operation, SearchResultReference reference) { boolean ret; if (!(ret = skipAccessCheck(operation))) { Entry e = new Entry(dn, null, null, null); AttributeBuilder builder = new AttributeBuilder(refAttrType, ATTR_REFERRAL_URL); List<String> URLStrings = reference.getReferralURLs(); // Load the values, a bind rule might want to evaluate them. for (String URLString : URLStrings) { builder.add(AttributeValues.create(refAttrType, URLString)); } e.addAttribute(builder.toAttribute(), null); SearchResultEntry se = new SearchResultEntry(e); AciLDAPOperationContainer operationContainer = new AciLDAPOperationContainer(operation, (ACI_READ), se); operationContainer.setCurrentAttributeType(refAttrType); ret = accessAllowed(operationContainer); } return ret; }
private Integer getIntegerUserAttribute( Entry userEntry, String attributeTypeName, Arg1<Object> nonUniqueAttributeMessage, Arg2<Object, Object> cannotProcessAttributeMessage) { AttributeType attrType = DirectoryServer.getAttributeTypeOrDefault(attributeTypeName); List<Attribute> attrList = userEntry.getAttribute(attrType); if (attrList != null && attrList.size() == 1) { Attribute a = attrList.get(0); if (a.size() == 1) { ByteString v = a.iterator().next(); try { return Integer.valueOf(v.toString()); } catch (Exception e) { logger.traceException(e); logger.error(cannotProcessAttributeMessage.get(v, userEntry.getName())); } } else if (a.size() > 1) { logger.error(nonUniqueAttributeMessage.get(userEntry.getName())); } } return null; }
/** * Performs an access check against all of the attributes of an entry. The attributes that fail * access are removed from the entry. This method performs the processing needed for the * filterEntry method processing. * * @param container The search or compare container which has all of the information needed to * filter the attributes for this entry. * @param filteredEntry The partially filtered search result entry being returned to the client. */ private void filterEntry(AciLDAPOperationContainer container, Entry filteredEntry) { List<AttributeType> typeList = getAllAttrs(filteredEntry); for (AttributeType attrType : typeList) { if (container.hasAllUserAttributes() && !attrType.isOperational()) { continue; } if (container.hasAllOpAttributes() && attrType.isOperational()) { continue; } container.setCurrentAttributeType(attrType); if (!accessAllowed(container)) { filteredEntry.removeAttribute(attrType); } } }
/** {@inheritDoc} */ @Override() public void renameEntry(DN currentDN, Entry entry, ModifyDNOperation modifyDNOperation) throws DirectoryException, CanceledOperationException { checkDiskSpace(modifyDNOperation); writerBegin(); EntryContainer currentContainer; if (rootContainer != null) { currentContainer = rootContainer.getEntryContainer(currentDN); } else { Message message = ERR_ROOT_CONTAINER_NOT_INITIALIZED.get(getBackendID()); throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); } EntryContainer container = rootContainer.getEntryContainer(entry.getDN()); if (currentContainer != container) { // FIXME: No reason why we cannot implement a move between containers // since the containers share the same database environment. Message msg = WARN_JEB_FUNCTION_NOT_SUPPORTED.get(); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, msg); } currentContainer.sharedLock.lock(); try { currentContainer.renameEntry(currentDN, entry, modifyDNOperation); } catch (DatabaseException e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } throw createDirectoryException(e); } finally { currentContainer.sharedLock.unlock(); writerEnd(); } }
/** * Performs the processing necessary for a simple bind operation. * * @return {@code true} if processing should continue for the operation, or {@code false} if not. * @throws DirectoryException If a problem occurs that should cause the bind operation to fail. */ protected boolean processSimpleBind() throws DirectoryException { // See if this is an anonymous bind. If so, then determine whether // to allow it. ByteString simplePassword = getSimplePassword(); if (simplePassword == null || simplePassword.length() == 0) { return processAnonymousSimpleBind(); } // See if the bind DN is actually one of the alternate root DNs // defined in the server. If so, then replace it with the actual DN // for that user. DN actualRootDN = DirectoryServer.getActualRootBindDN(bindDN); if (actualRootDN != null) { bindDN = actualRootDN; } Entry userEntry; try { userEntry = backend.getEntry(bindDN); } catch (DirectoryException de) { logger.traceException(de); userEntry = null; if (de.getResultCode() == ResultCode.REFERRAL) { // Re-throw referral exceptions - these should be passed back // to the client. throw de; } else { // Replace other exceptions in case they expose any sensitive // information. throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, de.getMessageObject()); } } if (userEntry == null) { throw new DirectoryException( ResultCode.INVALID_CREDENTIALS, ERR_BIND_OPERATION_UNKNOWN_USER.get()); } else { setUserEntryDN(userEntry.getName()); } // Check to see if the user has a password. If not, then fail. // FIXME -- We need to have a way to enable/disable debugging. authPolicyState = AuthenticationPolicyState.forUser(userEntry, false); if (authPolicyState.isPasswordPolicy()) { // Account is managed locally. PasswordPolicyState pwPolicyState = (PasswordPolicyState) authPolicyState; PasswordPolicy policy = pwPolicyState.getAuthenticationPolicy(); AttributeType pwType = policy.getPasswordAttribute(); List<Attribute> pwAttr = userEntry.getAttribute(pwType); if (pwAttr == null || pwAttr.isEmpty()) { throw new DirectoryException( ResultCode.INVALID_CREDENTIALS, ERR_BIND_OPERATION_NO_PASSWORD.get()); } // Perform a number of password policy state checks for the // non-authenticated user. checkUnverifiedPasswordPolicyState(userEntry, null); // Invoke pre-operation plugins. if (!invokePreOpPlugins()) { return false; } // Determine whether the provided password matches any of the stored // passwords for the user. if (pwPolicyState.passwordMatches(simplePassword)) { setResultCode(ResultCode.SUCCESS); checkVerifiedPasswordPolicyState(userEntry, null); if (DirectoryServer.lockdownMode() && !ClientConnection.hasPrivilege(userEntry, BYPASS_LOCKDOWN)) { throw new DirectoryException( ResultCode.INVALID_CREDENTIALS, ERR_BIND_REJECTED_LOCKDOWN_MODE.get()); } setAuthenticationInfo( new AuthenticationInfo( userEntry, getBindDN(), DirectoryServer.isRootDN(userEntry.getName()))); // Set resource limits for the authenticated user. setResourceLimits(userEntry); // Perform any remaining processing for a successful simple // authentication. pwPolicyState.handleDeprecatedStorageSchemes(simplePassword); pwPolicyState.clearFailureLockout(); if (isFirstWarning) { pwPolicyState.setWarnedTime(); int numSeconds = pwPolicyState.getSecondsUntilExpiration(); LocalizableMessage m = WARN_BIND_PASSWORD_EXPIRING.get(secondsToTimeString(numSeconds)); pwPolicyState.generateAccountStatusNotification( AccountStatusNotificationType.PASSWORD_EXPIRING, userEntry, m, AccountStatusNotification.createProperties( pwPolicyState, false, numSeconds, null, null)); } if (isGraceLogin) { pwPolicyState.updateGraceLoginTimes(); } pwPolicyState.setLastLoginTime(); } else { setResultCode(ResultCode.INVALID_CREDENTIALS); setAuthFailureReason(ERR_BIND_OPERATION_WRONG_PASSWORD.get()); if (policy.getLockoutFailureCount() > 0) { generateAccountStatusNotificationForLockedBindAccount(userEntry, pwPolicyState); } } } else { // Check to see if the user is administratively disabled or locked. if (authPolicyState.isDisabled()) { throw new DirectoryException( ResultCode.INVALID_CREDENTIALS, ERR_BIND_OPERATION_ACCOUNT_DISABLED.get()); } // Invoke pre-operation plugins. if (!invokePreOpPlugins()) { return false; } if (authPolicyState.passwordMatches(simplePassword)) { setResultCode(ResultCode.SUCCESS); if (DirectoryServer.lockdownMode() && !ClientConnection.hasPrivilege(userEntry, BYPASS_LOCKDOWN)) { throw new DirectoryException( ResultCode.INVALID_CREDENTIALS, ERR_BIND_REJECTED_LOCKDOWN_MODE.get()); } setAuthenticationInfo( new AuthenticationInfo( userEntry, getBindDN(), DirectoryServer.isRootDN(userEntry.getName()))); // Set resource limits for the authenticated user. setResourceLimits(userEntry); } else { setResultCode(ResultCode.INVALID_CREDENTIALS); setAuthFailureReason(ERR_BIND_OPERATION_WRONG_PASSWORD.get()); } } return true; }
/** * 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; }
/** {@inheritDoc} */ @Override public void processSASLBind(BindOperation bindOperation) { ExternalSASLMechanismHandlerCfg config = currentConfig; AttributeType certificateAttributeType = this.certificateAttributeType; CertificateValidationPolicy validationPolicy = this.validationPolicy; // Get the client connection used for the bind request, and get the // security manager for that connection. If either are null, then fail. ClientConnection clientConnection = bindOperation.getClientConnection(); if (clientConnection == null) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); LocalizableMessage message = ERR_SASLEXTERNAL_NO_CLIENT_CONNECTION.get(); bindOperation.setAuthFailureReason(message); return; } if (!(clientConnection instanceof LDAPClientConnection)) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); LocalizableMessage message = ERR_SASLEXTERNAL_NOT_LDAP_CLIENT_INSTANCE.get(); bindOperation.setAuthFailureReason(message); return; } LDAPClientConnection lc = (LDAPClientConnection) clientConnection; Certificate[] clientCertChain = lc.getClientCertificateChain(); if ((clientCertChain == null) || (clientCertChain.length == 0)) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); LocalizableMessage message = ERR_SASLEXTERNAL_NO_CLIENT_CERT.get(); bindOperation.setAuthFailureReason(message); return; } // Get the certificate mapper to use to map the certificate to a user entry. DN certificateMapperDN = config.getCertificateMapperDN(); CertificateMapper<?> certificateMapper = DirectoryServer.getCertificateMapper(certificateMapperDN); // Use the Directory Server certificate mapper to map the client certificate // chain to a single user DN. Entry userEntry; try { userEntry = certificateMapper.mapCertificateToUser(clientCertChain); } catch (DirectoryException de) { logger.traceException(de); bindOperation.setResponseData(de); return; } // If the user DN is null, then we couldn't establish a mapping and // therefore the authentication failed. if (userEntry == null) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); LocalizableMessage message = ERR_SASLEXTERNAL_NO_MAPPING.get(); bindOperation.setAuthFailureReason(message); return; } else { bindOperation.setSASLAuthUserEntry(userEntry); } // Get the userCertificate attribute from the user's entry for use in the // validation process. List<Attribute> certAttrList = userEntry.getAttribute(certificateAttributeType); switch (validationPolicy) { case ALWAYS: if (certAttrList == null) { if (validationPolicy == CertificateValidationPolicy.ALWAYS) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); LocalizableMessage message = ERR_SASLEXTERNAL_NO_CERT_IN_ENTRY.get(userEntry.getName()); bindOperation.setAuthFailureReason(message); return; } } else { try { ByteString certBytes = ByteString.wrap(clientCertChain[0].getEncoded()); if (!find(certAttrList, certBytes)) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); LocalizableMessage message = ERR_SASLEXTERNAL_PEER_CERT_NOT_FOUND.get(userEntry.getName()); bindOperation.setAuthFailureReason(message); return; } } catch (Exception e) { logger.traceException(e); bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); LocalizableMessage message = ERR_SASLEXTERNAL_CANNOT_VALIDATE_CERT.get( userEntry.getName(), getExceptionMessage(e)); bindOperation.setAuthFailureReason(message); return; } } break; case IFPRESENT: if (certAttrList != null) { try { ByteString certBytes = ByteString.wrap(clientCertChain[0].getEncoded()); if (!find(certAttrList, certBytes)) { bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); LocalizableMessage message = ERR_SASLEXTERNAL_PEER_CERT_NOT_FOUND.get(userEntry.getName()); bindOperation.setAuthFailureReason(message); return; } } catch (Exception e) { logger.traceException(e); bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS); LocalizableMessage message = ERR_SASLEXTERNAL_CANNOT_VALIDATE_CERT.get( userEntry.getName(), getExceptionMessage(e)); bindOperation.setAuthFailureReason(message); return; } } } AuthenticationInfo authInfo = new AuthenticationInfo( userEntry, SASL_MECHANISM_EXTERNAL, DirectoryServer.isRootDN(userEntry.getName())); bindOperation.setAuthenticationInfo(authInfo); bindOperation.setResultCode(ResultCode.SUCCESS); }
private DN getDN(Entry e) { return e != null ? e.getDN() : DN.nullDN(); }
/** {@inheritDoc} */ @Override public void search(SearchOperation searchOperation) throws DirectoryException { // Get the base entry for the search, if possible. If it doesn't exist, // then this will throw an exception. DN baseDN = searchOperation.getBaseDN(); Entry baseEntry = getEntry(baseDN); // Look at the base DN and see if it's the backup base DN, a backup // directory entry DN, or a backup entry DN. DN parentDN; SearchScope scope = searchOperation.getScope(); SearchFilter filter = searchOperation.getFilter(); if (backupBaseDN.equals(baseDN)) { if ((scope == SearchScope.BASE_OBJECT || scope == SearchScope.WHOLE_SUBTREE) && filter.matchesEntry(baseEntry)) { searchOperation.returnEntry(baseEntry, null); } if (scope != SearchScope.BASE_OBJECT && !backupDirectories.isEmpty()) { AttributeType backupPathType = DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); for (File f : backupDirectories) { // Check to see if the descriptor file exists. If not, then skip this // backup directory. File descriptorFile = new File(f, BACKUP_DIRECTORY_DESCRIPTOR_FILE); if (!descriptorFile.exists()) { continue; } DN backupDirDN = makeChildDN(backupBaseDN, backupPathType, f.getAbsolutePath()); Entry backupDirEntry; try { backupDirEntry = getBackupDirectoryEntry(backupDirDN); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } continue; } if (filter.matchesEntry(backupDirEntry)) { searchOperation.returnEntry(backupDirEntry, null); } if (scope != SearchScope.SINGLE_LEVEL) { List<Attribute> attrList = backupDirEntry.getAttribute(backupPathType); if (attrList != null && !attrList.isEmpty()) { for (AttributeValue v : attrList.get(0)) { try { BackupDirectory backupDirectory = BackupDirectory.readBackupDirectoryDescriptor(v.getValue().toString()); AttributeType idType = DirectoryServer.getAttributeType(ATTR_BACKUP_ID, true); for (String backupID : backupDirectory.getBackups().keySet()) { DN backupEntryDN = makeChildDN(backupDirDN, idType, backupID); Entry backupEntry = getBackupEntry(backupEntryDN); if (filter.matchesEntry(backupEntry)) { searchOperation.returnEntry(backupEntry, null); } } } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } continue; } } } } } } } else if (backupBaseDN.equals(parentDN = baseDN.getParentDNInSuffix())) { Entry backupDirEntry = getBackupDirectoryEntry(baseDN); if ((scope == SearchScope.BASE_OBJECT || scope == SearchScope.WHOLE_SUBTREE) && filter.matchesEntry(backupDirEntry)) { searchOperation.returnEntry(backupDirEntry, null); } if (scope != SearchScope.BASE_OBJECT) { AttributeType t = DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); List<Attribute> attrList = backupDirEntry.getAttribute(t); if (attrList != null && !attrList.isEmpty()) { for (AttributeValue v : attrList.get(0)) { try { BackupDirectory backupDirectory = BackupDirectory.readBackupDirectoryDescriptor(v.getValue().toString()); AttributeType idType = DirectoryServer.getAttributeType(ATTR_BACKUP_ID, true); for (String backupID : backupDirectory.getBackups().keySet()) { DN backupEntryDN = makeChildDN(baseDN, idType, backupID); Entry backupEntry = getBackupEntry(backupEntryDN); if (filter.matchesEntry(backupEntry)) { searchOperation.returnEntry(backupEntry, null); } } } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } continue; } } } } } else { if (parentDN == null || !backupBaseDN.equals(parentDN.getParentDNInSuffix())) { Message message = ERR_BACKUP_NO_SUCH_ENTRY.get(String.valueOf(backupBaseDN)); throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message); } if (scope == SearchScope.BASE_OBJECT || scope == SearchScope.WHOLE_SUBTREE) { Entry backupEntry = getBackupEntry(baseDN); if (backupEntry == null) { Message message = ERR_BACKUP_NO_SUCH_ENTRY.get(String.valueOf(backupBaseDN)); throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message); } if (filter.matchesEntry(backupEntry)) { searchOperation.returnEntry(backupEntry, null); } } } }
/** * Performs the processing necessary for a SASL bind operation. * * @return {@code true} if processing should continue for the operation, or {@code false} if not. * @throws DirectoryException If a problem occurs that should cause the bind operation to fail. */ private boolean processSASLBind() throws DirectoryException { // Get the appropriate authentication handler for this request based // on the SASL mechanism. If there is none, then fail. SASLMechanismHandler<?> saslHandler = DirectoryServer.getSASLMechanismHandler(saslMechanism); if (saslHandler == null) { throw new DirectoryException( ResultCode.AUTH_METHOD_NOT_SUPPORTED, ERR_BIND_OPERATION_UNKNOWN_SASL_MECHANISM.get(saslMechanism)); } // Check to see if the client has sufficient permission to perform the bind. // NYI // Invoke pre-operation plugins. if (!invokePreOpPlugins()) { return false; } // Actually process the SASL bind. saslHandler.processSASLBind(this); // If the server is operating in lockdown mode, then we will need to // ensure that the authentication was successful and performed as a // root user to continue. Entry saslAuthUserEntry = getSASLAuthUserEntry(); if (DirectoryServer.lockdownMode()) { ResultCode resultCode = getResultCode(); if (resultCode != ResultCode.SASL_BIND_IN_PROGRESS && (resultCode != ResultCode.SUCCESS || saslAuthUserEntry == null || !ClientConnection.hasPrivilege(saslAuthUserEntry, BYPASS_LOCKDOWN))) { throw new DirectoryException( ResultCode.INVALID_CREDENTIALS, ERR_BIND_REJECTED_LOCKDOWN_MODE.get()); } } // Create the password policy state object. if (saslAuthUserEntry != null) { setUserEntryDN(saslAuthUserEntry.getName()); // FIXME -- Need to have a way to enable debugging. authPolicyState = AuthenticationPolicyState.forUser(saslAuthUserEntry, false); if (authPolicyState.isPasswordPolicy()) { // Account is managed locally: perform password policy checks that can // be completed before we have checked authentication was successful. checkUnverifiedPasswordPolicyState(saslAuthUserEntry, saslHandler); } } // Determine whether the authentication was successful and perform // any remaining password policy processing accordingly. ResultCode resultCode = getResultCode(); if (resultCode == ResultCode.SUCCESS) { if (authPolicyState != null && authPolicyState.isPasswordPolicy()) { checkVerifiedPasswordPolicyState(saslAuthUserEntry, saslHandler); PasswordPolicyState pwPolicyState = (PasswordPolicyState) authPolicyState; if (saslHandler.isPasswordBased(saslMechanism) && pwPolicyState.mustChangePassword()) { mustChangePassword = true; } if (isFirstWarning) { pwPolicyState.setWarnedTime(); int numSeconds = pwPolicyState.getSecondsUntilExpiration(); LocalizableMessage m = WARN_BIND_PASSWORD_EXPIRING.get(secondsToTimeString(numSeconds)); pwPolicyState.generateAccountStatusNotification( AccountStatusNotificationType.PASSWORD_EXPIRING, saslAuthUserEntry, m, AccountStatusNotification.createProperties( pwPolicyState, false, numSeconds, null, null)); } if (isGraceLogin) { pwPolicyState.updateGraceLoginTimes(); } pwPolicyState.setLastLoginTime(); } // Set appropriate resource limits for the user (note that SASL ANONYMOUS // does not have a user). if (saslAuthUserEntry != null) { setResourceLimits(saslAuthUserEntry); } } else if (resultCode == ResultCode.SASL_BIND_IN_PROGRESS) { // FIXME -- Is any special processing needed here? return false; } else { if (authPolicyState != null && authPolicyState.isPasswordPolicy()) { PasswordPolicyState pwPolicyState = (PasswordPolicyState) authPolicyState; if (saslHandler.isPasswordBased(saslMechanism) && pwPolicyState.getAuthenticationPolicy().getLockoutFailureCount() > 0) { generateAccountStatusNotificationForLockedBindAccount(saslAuthUserEntry, pwPolicyState); } } } return true; }
/** {@inheritDoc} */ @Override public void addEntry(Entry entry, AddOperation addOperation) throws DirectoryException { throw new DirectoryException( ResultCode.UNWILLING_TO_PERFORM, ERR_BACKEND_ADD_NOT_SUPPORTED.get(String.valueOf(entry.getDN()), getBackendID())); }
/** * Generates an entry for a backup based on the provided DN. The DN must have an RDN component * that specifies the backup ID, and the parent DN must have an RDN component that specifies the * backup directory. * * @param entryDN The DN of the backup entry to retrieve. * @return The requested backup entry. * @throws DirectoryException If the specified backup does not exist or is invalid. */ private Entry getBackupEntry(DN entryDN) throws DirectoryException { // First, get the backup ID from the entry DN. AttributeType idType = DirectoryServer.getAttributeType(ATTR_BACKUP_ID, true); AttributeValue idValue = entryDN.getRDN().getAttributeValue(idType); if (idValue == null) { Message message = ERR_BACKUP_NO_BACKUP_ID_IN_DN.get(String.valueOf(entryDN)); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); } String backupID = idValue.getValue().toString(); // Next, get the backup directory from the parent DN. DN parentDN = entryDN.getParentDNInSuffix(); if (parentDN == null) { Message message = ERR_BACKUP_NO_BACKUP_PARENT_DN.get(String.valueOf(entryDN)); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); } AttributeType t = DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); AttributeValue v = parentDN.getRDN().getAttributeValue(t); if (v == null) { Message message = ERR_BACKUP_NO_BACKUP_DIR_IN_DN.get(String.valueOf(entryDN)); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); } BackupDirectory backupDirectory; try { backupDirectory = BackupDirectory.readBackupDirectoryDescriptor(v.getValue().toString()); } catch (ConfigException ce) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, ce); } Message message = ERR_BACKUP_INVALID_BACKUP_DIRECTORY.get(String.valueOf(entryDN), ce.getMessageObject()); throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_BACKUP_ERROR_GETTING_BACKUP_DIRECTORY.get(getExceptionMessage(e)); throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); } BackupInfo backupInfo = backupDirectory.getBackupInfo(backupID); if (backupInfo == null) { Message message = ERR_BACKUP_NO_SUCH_BACKUP.get(backupID, backupDirectory.getPath()); throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message, parentDN, null); } // Construct the backup entry to return. LinkedHashMap<ObjectClass, String> ocMap = new LinkedHashMap<ObjectClass, String>(3); ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP); ObjectClass oc = DirectoryServer.getObjectClass(OC_BACKUP_INFO, true); ocMap.put(oc, OC_BACKUP_INFO); oc = DirectoryServer.getObjectClass(OC_EXTENSIBLE_OBJECT_LC, true); ocMap.put(oc, OC_EXTENSIBLE_OBJECT); LinkedHashMap<AttributeType, List<Attribute>> opAttrs = new LinkedHashMap<AttributeType, List<Attribute>>(0); LinkedHashMap<AttributeType, List<Attribute>> userAttrs = new LinkedHashMap<AttributeType, List<Attribute>>(); ArrayList<Attribute> attrList = new ArrayList<Attribute>(1); attrList.add(Attributes.create(idType, idValue)); userAttrs.put(idType, attrList); backupInfo.getBackupDirectory(); attrList = new ArrayList<Attribute>(1); attrList.add(Attributes.create(t, v)); userAttrs.put(t, attrList); Date backupDate = backupInfo.getBackupDate(); if (backupDate != null) { t = DirectoryServer.getAttributeType(ATTR_BACKUP_DATE, true); attrList = new ArrayList<Attribute>(1); attrList.add( Attributes.create( t, AttributeValues.create(t, GeneralizedTimeSyntax.format(backupDate)))); userAttrs.put(t, attrList); } t = DirectoryServer.getAttributeType(ATTR_BACKUP_COMPRESSED, true); attrList = new ArrayList<Attribute>(1); attrList.add(Attributes.create(t, BooleanSyntax.createBooleanValue(backupInfo.isCompressed()))); userAttrs.put(t, attrList); t = DirectoryServer.getAttributeType(ATTR_BACKUP_ENCRYPTED, true); attrList = new ArrayList<Attribute>(1); attrList.add(Attributes.create(t, BooleanSyntax.createBooleanValue(backupInfo.isEncrypted()))); userAttrs.put(t, attrList); t = DirectoryServer.getAttributeType(ATTR_BACKUP_INCREMENTAL, true); attrList = new ArrayList<Attribute>(1); attrList.add( Attributes.create(t, BooleanSyntax.createBooleanValue(backupInfo.isIncremental()))); userAttrs.put(t, attrList); HashSet<String> dependencies = backupInfo.getDependencies(); if (dependencies != null && !dependencies.isEmpty()) { t = DirectoryServer.getAttributeType(ATTR_BACKUP_DEPENDENCY, true); AttributeBuilder builder = new AttributeBuilder(t); for (String s : dependencies) { builder.add(AttributeValues.create(t, s)); } attrList = new ArrayList<Attribute>(1); attrList.add(builder.toAttribute()); userAttrs.put(t, attrList); } byte[] signedHash = backupInfo.getSignedHash(); if (signedHash != null) { t = DirectoryServer.getAttributeType(ATTR_BACKUP_SIGNED_HASH, true); attrList = new ArrayList<Attribute>(1); attrList.add(Attributes.create(t, AttributeValues.create(t, ByteString.wrap(signedHash)))); userAttrs.put(t, attrList); } byte[] unsignedHash = backupInfo.getUnsignedHash(); if (unsignedHash != null) { t = DirectoryServer.getAttributeType(ATTR_BACKUP_UNSIGNED_HASH, true); attrList = new ArrayList<Attribute>(1); attrList.add(Attributes.create(t, AttributeValues.create(t, ByteString.wrap(unsignedHash)))); userAttrs.put(t, attrList); } HashMap<String, String> properties = backupInfo.getBackupProperties(); if (properties != null && !properties.isEmpty()) { for (Map.Entry<String, String> e : properties.entrySet()) { t = DirectoryServer.getAttributeType(toLowerCase(e.getKey()), true); attrList = new ArrayList<Attribute>(1); attrList.add(Attributes.create(t, AttributeValues.create(t, e.getValue()))); userAttrs.put(t, attrList); } } Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs); e.processVirtualAttributes(); return e; }
/** {@inheritDoc} */ @Override public long numSubordinates(DN entryDN, boolean subtree) throws DirectoryException { // If the requested entry was null, then return undefined. if (entryDN == null) { return -1; } // If the requested entry was the backend base entry, then return // the number of backup directories. if (backupBaseDN.equals(entryDN)) { long count = 0; for (File f : backupDirectories) { // Check to see if the descriptor file exists. If not, then skip this // backup directory. File descriptorFile = new File(f, BACKUP_DIRECTORY_DESCRIPTOR_FILE); if (!descriptorFile.exists()) { continue; } // If subtree is included, count the number of entries for each // backup directory. if (subtree) { try { BackupDirectory backupDirectory = BackupDirectory.readBackupDirectoryDescriptor(f.getPath()); count += backupDirectory.getBackups().keySet().size(); } catch (Exception e) { return -1; } } count++; } return count; } // See if the requested entry was one level below the backend base entry. // If so, then it must point to a backup directory. Otherwise, it must be // two levels below the backup base entry and must point to a specific // backup. DN parentDN = entryDN.getParentDNInSuffix(); if (parentDN == null) { return -1; } else if (backupBaseDN.equals(parentDN)) { long count = 0; Entry backupDirEntry = getBackupDirectoryEntry(entryDN); AttributeType t = DirectoryServer.getAttributeType(ATTR_BACKUP_DIRECTORY_PATH, true); List<Attribute> attrList = backupDirEntry.getAttribute(t); if (attrList != null && !attrList.isEmpty()) { for (AttributeValue v : attrList.get(0)) { try { BackupDirectory backupDirectory = BackupDirectory.readBackupDirectoryDescriptor(v.getValue().toString()); count += backupDirectory.getBackups().keySet().size(); } catch (Exception e) { return -1; } } } return count; } else if (backupBaseDN.equals(parentDN.getParentDNInSuffix())) { return 0; } else { return -1; } }