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 2006-2008 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2016 ForgeRock AS.
016 */
017package org.opends.server.extensions;
018
019import java.security.cert.Certificate;
020import java.security.cert.X509Certificate;
021import javax.security.auth.x500.X500Principal;
022
023import org.forgerock.i18n.LocalizableMessage;
024import org.forgerock.opendj.server.config.server.SubjectEqualsDNCertificateMapperCfg;
025import org.opends.server.api.CertificateMapper;
026import org.forgerock.opendj.config.server.ConfigException;
027import org.opends.server.core.DirectoryServer;
028import org.forgerock.i18n.slf4j.LocalizedLogger;
029import org.opends.server.types.*;
030import org.forgerock.opendj.ldap.DN;
031import org.forgerock.opendj.ldap.ResultCode;
032import static org.opends.messages.ExtensionMessages.*;
033import static org.opends.server.util.StaticUtils.*;
034
035/**
036 * This class implements a very simple Directory Server certificate mapper that
037 * will map a certificate to a user only if the subject of the peer certificate
038 * exactly matches the DN of a user in the Directory Server.
039 */
040public class SubjectEqualsDNCertificateMapper
041       extends CertificateMapper<SubjectEqualsDNCertificateMapperCfg>
042{
043  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
044
045  /**
046   * Creates a new instance of this certificate mapper.  Note that all actual
047   * initialization should be done in the
048   * <CODE>initializeCertificateMapper</CODE> method.
049   */
050  public SubjectEqualsDNCertificateMapper()
051  {
052    super();
053  }
054
055  @Override
056  public void initializeCertificateMapper(SubjectEqualsDNCertificateMapperCfg
057                                               configuration)
058         throws ConfigException, InitializationException
059  {
060    // No initialization is required.
061  }
062
063  /**
064   * Establishes a mapping between the information in the provided certificate
065   * chain to the DN of a single user in the Directory Server.
066   *
067   * @param  certificateChain  The certificate chain presented by the client
068   *                           during SSL negotiation.  The peer certificate
069   *                           will be listed first, followed by the ordered
070   *                           issuer chain as appropriate.
071   *
072   * @return  The DN of the one user to whom the mapping was established, or
073   *          <CODE>null</CODE> if no mapping was established and no special
074   *         message is required to send back to the client.
075   *
076   * @throws  DirectoryException  If a problem occurred while attempting to
077   *                              establish the mapping.  This may include
078   *                              internal failures, a mapping which matches
079   *                              multiple users, or any other case in which an
080   *                              error message should be returned to the
081   *                              client.
082   */
083  @Override
084  public Entry mapCertificateToUser(Certificate[] certificateChain)
085         throws DirectoryException
086  {
087    // Make sure that a peer certificate was provided.
088    if (certificateChain == null || certificateChain.length == 0)
089    {
090      LocalizableMessage message = ERR_SEDCM_NO_PEER_CERTIFICATE.get();
091      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
092    }
093
094    // Get the first certificate in the chain.  It must be an X.509 certificate.
095    X509Certificate peerCertificate;
096    try
097    {
098      peerCertificate = (X509Certificate) certificateChain[0];
099    }
100    catch (Exception e)
101    {
102      logger.traceException(e);
103
104      LocalizableMessage message = ERR_SEDCM_PEER_CERT_NOT_X509.get(certificateChain[0].getType());
105      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
106    }
107
108    // Get the subject from the peer certificate and decode it as a DN.
109    X500Principal peerPrincipal = peerCertificate.getSubjectX500Principal();
110    DN subjectDN;
111    try
112    {
113      subjectDN = DN.valueOf(peerPrincipal.getName(X500Principal.RFC2253));
114    }
115    catch (Exception e)
116    {
117      logger.traceException(e);
118
119      LocalizableMessage message = ERR_SEDCM_CANNOT_DECODE_SUBJECT_AS_DN.get(peerPrincipal, getExceptionMessage(e));
120      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
121    }
122
123    // Retrieve the entry with the specified DN from the directory.
124    Entry userEntry;
125    try
126    {
127      userEntry = DirectoryServer.getEntry(subjectDN);
128    }
129    catch (DirectoryException de)
130    {
131      logger.traceException(de);
132
133      LocalizableMessage message = ERR_SEDCM_CANNOT_GET_ENTRY.get(subjectDN, de.getMessageObject());
134      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message, de);
135    }
136    catch (Exception e)
137    {
138      logger.traceException(e);
139
140      LocalizableMessage message = ERR_SEDCM_CANNOT_GET_ENTRY.get(subjectDN, getExceptionMessage(e));
141      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message, e);
142    }
143
144    if (userEntry == null)
145    {
146      LocalizableMessage message = ERR_SEDCM_NO_USER_FOR_DN.get(subjectDN);
147      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
148    }
149    else
150    {
151      return userEntry;
152    }
153  }
154}