0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
0N/A * Common Development and Distribution License, Version 1.0 only
0N/A * (the "License"). You may not use this file except in compliance
0N/A * with the License.
0N/A *
0N/A * You can obtain a copy of the license at
0N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE
0N/A * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
0N/A * See the License for the specific language governing permissions
0N/A * and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at
0N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
0N/A * add the following below this CDDL HEADER, with the fields enclosed
873N/A * by brackets "[]" replaced with your own identifying information:
0N/A * Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A *
0N/A *
4495N/A * Copyright 2006-2009 Sun Microsystems, Inc.
0N/A */
0N/Apackage org.opends.server.extensions;
2086N/Aimport org.opends.messages.Message;
0N/A
0N/A
0N/A
4134N/Aimport java.security.cert.Certificate;
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.List;
0N/A
1008N/Aimport org.opends.server.admin.server.ConfigurationChangeListener;
1008N/Aimport org.opends.server.admin.std.server.ExternalSASLMechanismHandlerCfg;
1787N/Aimport org.opends.server.admin.std.server.SASLMechanismHandlerCfg;
824N/Aimport org.opends.server.api.CertificateMapper;
0N/Aimport org.opends.server.api.ClientConnection;
0N/Aimport org.opends.server.api.SASLMechanismHandler;
0N/Aimport org.opends.server.config.ConfigException;
0N/Aimport org.opends.server.core.BindOperation;
0N/Aimport org.opends.server.core.DirectoryServer;
0N/A
0N/Aimport static org.opends.server.config.ConfigConstants.*;
1400N/Aimport static org.opends.server.loggers.debug.DebugLogger.*;
1400N/Aimport org.opends.server.loggers.debug.DebugTracer;
4134N/Aimport org.opends.server.protocols.ldap.LDAPClientConnection;
4134N/Aimport org.opends.server.types.*;
2086N/Aimport static org.opends.messages.ExtensionMessages.*;
2086N/A
0N/Aimport static org.opends.server.util.ServerConstants.*;
0N/Aimport static org.opends.server.util.StaticUtils.*;
0N/A
0N/A
0N/A
0N/A/**
0N/A * This class provides an implementation of a SASL mechanism that relies on some
0N/A * form of authentication that has already been done outside the LDAP layer. At
0N/A * the present time, this implementation only provides support for SSL-based
0N/A * clients that presented their own certificate to the Directory Server during
0N/A * the negotiation process. Future implementations may be updated to look in
0N/A * other places to find and evaluate this external authentication information.
0N/A */
0N/Apublic class ExternalSASLMechanismHandler
1008N/A extends SASLMechanismHandler<ExternalSASLMechanismHandlerCfg>
1008N/A implements ConfigurationChangeListener<
1008N/A ExternalSASLMechanismHandlerCfg>
0N/A{
1400N/A /**
1400N/A * The tracer object for the debug logger.
1400N/A */
1400N/A private static final DebugTracer TRACER = getTracer();
1400N/A
0N/A // The attribute type that should hold the certificates to use for the
0N/A // validation.
0N/A private AttributeType certificateAttributeType;
0N/A
0N/A // Indicates whether to attempt to validate the certificate presented by the
0N/A // client with a certificate in the user's entry.
0N/A private CertificateValidationPolicy validationPolicy;
0N/A
1008N/A // The current configuration for this SASL mechanism handler.
1008N/A private ExternalSASLMechanismHandlerCfg currentConfig;
1008N/A
0N/A
0N/A
0N/A /**
0N/A * Creates a new instance of this SASL mechanism handler. No initialization
0N/A * should be done in this method, as it should all be performed in the
0N/A * <CODE>initializeSASLMechanismHandler</CODE> method.
0N/A */
0N/A public ExternalSASLMechanismHandler()
0N/A {
0N/A super();
0N/A }
0N/A
0N/A
0N/A
0N/A /**
761N/A * {@inheritDoc}
0N/A */
761N/A @Override()
1008N/A public void initializeSASLMechanismHandler(
1008N/A ExternalSASLMechanismHandlerCfg configuration)
0N/A throws ConfigException, InitializationException
0N/A {
1008N/A configuration.addExternalChangeListener(this);
1008N/A currentConfig = configuration;
0N/A
0N/A // See if we should attempt to validate client certificates against those in
0N/A // the corresponding user's entry.
1008N/A switch (configuration.getCertificateValidationPolicy())
0N/A {
1008N/A case NEVER:
1008N/A validationPolicy = CertificateValidationPolicy.NEVER;
1008N/A break;
1008N/A case IFPRESENT:
1008N/A validationPolicy = CertificateValidationPolicy.IFPRESENT;
1008N/A break;
1008N/A case ALWAYS:
1008N/A validationPolicy = CertificateValidationPolicy.ALWAYS;
1008N/A break;
0N/A }
0N/A
0N/A
0N/A // Get the attribute type to use for validating the certificates. If none
0N/A // is provided, then default to the userCertificate type.
2239N/A certificateAttributeType = configuration.getCertificateAttribute();
0N/A if (certificateAttributeType == null)
0N/A {
2239N/A certificateAttributeType =
2239N/A DirectoryServer.getAttributeType(DEFAULT_VALIDATION_CERT_ATTRIBUTE,
2239N/A true);
0N/A }
0N/A
0N/A
0N/A DirectoryServer.registerSASLMechanismHandler(SASL_MECHANISM_EXTERNAL, this);
0N/A }
0N/A
0N/A
0N/A
0N/A /**
761N/A * {@inheritDoc}
0N/A */
761N/A @Override()
0N/A public void finalizeSASLMechanismHandler()
0N/A {
1008N/A currentConfig.removeExternalChangeListener(this);
0N/A DirectoryServer.deregisterSASLMechanismHandler(SASL_MECHANISM_EXTERNAL);
0N/A }
0N/A
0N/A
0N/A
0N/A
0N/A /**
761N/A * {@inheritDoc}
0N/A */
761N/A @Override()
0N/A public void processSASLBind(BindOperation bindOperation)
0N/A {
1008N/A ExternalSASLMechanismHandlerCfg config = currentConfig;
1008N/A AttributeType certificateAttributeType = this.certificateAttributeType;
1008N/A CertificateValidationPolicy validationPolicy = this.validationPolicy;
1008N/A
1008N/A
0N/A // Get the client connection used for the bind request, and get the
0N/A // security manager for that connection. If either are null, then fail.
0N/A ClientConnection clientConnection = bindOperation.getClientConnection();
4134N/A if (clientConnection == null) {
0N/A bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
2086N/A Message message = ERR_SASLEXTERNAL_NO_CLIENT_CONNECTION.get();
2086N/A bindOperation.setAuthFailureReason(message);
0N/A return;
0N/A }
0N/A
4134N/A if(!(clientConnection instanceof LDAPClientConnection)) {
4134N/A bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
4154N/A Message message = ERR_SASLEXTERNAL_NOT_LDAP_CLIENT_INSTANCE.get();
4134N/A bindOperation.setAuthFailureReason(message);
4134N/A return;
0N/A }
4134N/A LDAPClientConnection lc = (LDAPClientConnection) clientConnection;
4134N/A Certificate[] clientCertChain = lc.getClientCertificateChain();
4134N/A if ((clientCertChain == null) || (clientCertChain.length == 0)) {
0N/A bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
2086N/A Message message = ERR_SASLEXTERNAL_NO_CLIENT_CERT.get();
2086N/A bindOperation.setAuthFailureReason(message);
0N/A return;
0N/A }
0N/A
0N/A
824N/A // Get the certificate mapper to use to map the certificate to a user entry.
1008N/A DN certificateMapperDN = config.getCertificateMapperDN();
2624N/A CertificateMapper<?> certificateMapper =
824N/A DirectoryServer.getCertificateMapper(certificateMapperDN);
824N/A
824N/A
0N/A // Use the Directory Server certificate mapper to map the client certificate
0N/A // chain to a single user DN.
0N/A Entry userEntry;
0N/A try
0N/A {
824N/A userEntry = certificateMapper.mapCertificateToUser(clientCertChain);
0N/A }
0N/A catch (DirectoryException de)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, de);
868N/A }
0N/A
0N/A bindOperation.setResponseData(de);
0N/A return;
0N/A }
0N/A
0N/A
0N/A // If the user DN is null, then we couldn't establish a mapping and
0N/A // therefore the authentication failed.
0N/A if (userEntry == null)
0N/A {
0N/A bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
0N/A
2086N/A Message message = ERR_SASLEXTERNAL_NO_MAPPING.get();
2086N/A bindOperation.setAuthFailureReason(message);
0N/A return;
0N/A }
0N/A else
0N/A {
0N/A bindOperation.setSASLAuthUserEntry(userEntry);
0N/A }
0N/A
0N/A
0N/A // Get the userCertificate attribute from the user's entry for use in the
0N/A // validation process.
0N/A List<Attribute> certAttrList =
0N/A userEntry.getAttribute(certificateAttributeType);
0N/A switch (validationPolicy)
0N/A {
0N/A case ALWAYS:
0N/A if (certAttrList == null)
0N/A {
0N/A if (validationPolicy == CertificateValidationPolicy.ALWAYS)
0N/A {
0N/A bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
0N/A
2086N/A Message message = ERR_SASLEXTERNAL_NO_CERT_IN_ENTRY.get(
2086N/A String.valueOf(userEntry.getDN()));
2086N/A bindOperation.setAuthFailureReason(message);
0N/A return;
0N/A }
0N/A }
0N/A else
0N/A {
0N/A try
0N/A {
0N/A byte[] certBytes = clientCertChain[0].getEncoded();
0N/A AttributeValue v =
4134N/A AttributeValues.create(
4134N/A certificateAttributeType, ByteString.wrap(certBytes));
0N/A
0N/A boolean found = false;
0N/A for (Attribute a : certAttrList)
0N/A {
3853N/A if (a.contains(v))
0N/A {
0N/A found = true;
0N/A break;
0N/A }
0N/A }
0N/A
0N/A if (! found)
0N/A {
0N/A bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
0N/A
2086N/A Message message = ERR_SASLEXTERNAL_PEER_CERT_NOT_FOUND.get(
2086N/A String.valueOf(userEntry.getDN()));
2086N/A bindOperation.setAuthFailureReason(message);
0N/A return;
0N/A }
0N/A }
0N/A catch (Exception e)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
0N/A
0N/A bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
0N/A
2086N/A Message message = ERR_SASLEXTERNAL_CANNOT_VALIDATE_CERT.get(
2086N/A String.valueOf(userEntry.getDN()),
2086N/A getExceptionMessage(e));
2086N/A bindOperation.setAuthFailureReason(message);
0N/A return;
0N/A }
0N/A }
0N/A break;
0N/A
0N/A case IFPRESENT:
0N/A if (certAttrList != null)
0N/A {
0N/A try
0N/A {
0N/A byte[] certBytes = clientCertChain[0].getEncoded();
0N/A AttributeValue v =
4134N/A AttributeValues.create(
4134N/A certificateAttributeType, ByteString.wrap(certBytes));
0N/A
0N/A boolean found = false;
0N/A for (Attribute a : certAttrList)
0N/A {
3853N/A if (a.contains(v))
0N/A {
0N/A found = true;
0N/A break;
0N/A }
0N/A }
0N/A
0N/A if (! found)
0N/A {
0N/A bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
0N/A
2086N/A Message message = ERR_SASLEXTERNAL_PEER_CERT_NOT_FOUND.get(
2086N/A String.valueOf(userEntry.getDN()));
2086N/A bindOperation.setAuthFailureReason(message);
0N/A return;
0N/A }
0N/A }
0N/A catch (Exception e)
0N/A {
868N/A if (debugEnabled())
868N/A {
1400N/A TRACER.debugCaught(DebugLogLevel.ERROR, e);
868N/A }
0N/A
0N/A bindOperation.setResultCode(ResultCode.INVALID_CREDENTIALS);
0N/A
2086N/A Message message = ERR_SASLEXTERNAL_CANNOT_VALIDATE_CERT.get(
2086N/A String.valueOf(userEntry.getDN()),
2086N/A getExceptionMessage(e));
2086N/A bindOperation.setAuthFailureReason(message);
0N/A return;
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A AuthenticationInfo authInfo =
773N/A new AuthenticationInfo(userEntry, SASL_MECHANISM_EXTERNAL,
4495N/A bindOperation.getSASLCredentials(),
4495N/A DirectoryServer.isRootDN(userEntry.getDN()));
761N/A bindOperation.setAuthenticationInfo(authInfo);
0N/A bindOperation.setResultCode(ResultCode.SUCCESS);
0N/A }
0N/A
0N/A
0N/A
0N/A /**
761N/A * {@inheritDoc}
0N/A */
761N/A @Override()
0N/A public boolean isPasswordBased(String mechanism)
0N/A {
0N/A // This is not a password-based mechanism.
0N/A return false;
0N/A }
0N/A
0N/A
0N/A
0N/A /**
761N/A * {@inheritDoc}
0N/A */
761N/A @Override()
0N/A public boolean isSecure(String mechanism)
0N/A {
0N/A // This may be considered a secure mechanism.
0N/A return true;
0N/A }
1008N/A
1008N/A
1008N/A
1008N/A /**
1008N/A * {@inheritDoc}
1008N/A */
1787N/A @Override()
1787N/A public boolean isConfigurationAcceptable(
1787N/A SASLMechanismHandlerCfg configuration,
2086N/A List<Message> unacceptableReasons)
1787N/A {
1787N/A ExternalSASLMechanismHandlerCfg config =
1787N/A (ExternalSASLMechanismHandlerCfg) configuration;
1787N/A return isConfigurationChangeAcceptable(config, unacceptableReasons);
1787N/A }
1787N/A
1787N/A
1787N/A
1787N/A /**
1787N/A * {@inheritDoc}
1787N/A */
1008N/A public boolean isConfigurationChangeAcceptable(
1008N/A ExternalSASLMechanismHandlerCfg configuration,
2086N/A List<Message> unacceptableReasons)
1008N/A {
2624N/A return true;
1008N/A }
1008N/A
1008N/A
1008N/A
1008N/A /**
1008N/A * {@inheritDoc}
1008N/A */
1008N/A public ConfigChangeResult applyConfigurationChange(
1008N/A ExternalSASLMechanismHandlerCfg configuration)
1008N/A {
1008N/A ResultCode resultCode = ResultCode.SUCCESS;
1008N/A boolean adminActionRequired = false;
2086N/A ArrayList<Message> messages = new ArrayList<Message>();
1008N/A
1008N/A
1008N/A // See if we should attempt to validate client certificates against those in
1008N/A // the corresponding user's entry.
1008N/A CertificateValidationPolicy newValidationPolicy =
1008N/A CertificateValidationPolicy.ALWAYS;
1008N/A switch (configuration.getCertificateValidationPolicy())
1008N/A {
1008N/A case NEVER:
1008N/A newValidationPolicy = CertificateValidationPolicy.NEVER;
1008N/A break;
1008N/A case IFPRESENT:
1008N/A newValidationPolicy = CertificateValidationPolicy.IFPRESENT;
1008N/A break;
1008N/A case ALWAYS:
1008N/A newValidationPolicy = CertificateValidationPolicy.ALWAYS;
1008N/A break;
1008N/A }
1008N/A
1008N/A
1008N/A // Get the attribute type to use for validating the certificates. If none
1008N/A // is provided, then default to the userCertificate type.
2239N/A AttributeType newCertificateType = configuration.getCertificateAttribute();
1008N/A if (newCertificateType == null)
1008N/A {
2239N/A newCertificateType =
2239N/A DirectoryServer.getAttributeType(DEFAULT_VALIDATION_CERT_ATTRIBUTE,
2239N/A true);
1008N/A }
1008N/A
1008N/A
1008N/A if (resultCode == ResultCode.SUCCESS)
1008N/A {
1008N/A validationPolicy = newValidationPolicy;
1008N/A certificateAttributeType = newCertificateType;
1008N/A currentConfig = configuration;
1008N/A }
1008N/A
1008N/A
1008N/A return new ConfigChangeResult(resultCode, adminActionRequired, messages);
1008N/A }
0N/A}
0N/A