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