001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2006-2008 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS
026 */
027package org.opends.server.extensions;
028
029
030
031import java.security.cert.Certificate;
032import java.security.cert.X509Certificate;
033import javax.security.auth.x500.X500Principal;
034
035import org.forgerock.i18n.LocalizableMessage;
036import org.opends.server.admin.std.server.SubjectEqualsDNCertificateMapperCfg;
037import org.opends.server.api.CertificateMapper;
038import org.forgerock.opendj.config.server.ConfigException;
039import org.opends.server.core.DirectoryServer;
040import org.forgerock.i18n.slf4j.LocalizedLogger;
041import org.opends.server.types.*;
042import org.forgerock.opendj.ldap.ResultCode;
043import static org.opends.messages.ExtensionMessages.*;
044import static org.opends.server.util.StaticUtils.*;
045
046/**
047 * This class implements a very simple Directory Server certificate mapper that
048 * will map a certificate to a user only if the subject of the peer certificate
049 * exactly matches the DN of a user in the Directory Server.
050 */
051public class SubjectEqualsDNCertificateMapper
052       extends CertificateMapper<SubjectEqualsDNCertificateMapperCfg>
053{
054  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
055
056  /**
057   * Creates a new instance of this certificate mapper.  Note that all actual
058   * initialization should be done in the
059   * <CODE>initializeCertificateMapper</CODE> method.
060   */
061  public SubjectEqualsDNCertificateMapper()
062  {
063    super();
064  }
065
066
067
068  /** {@inheritDoc} */
069  @Override
070  public void initializeCertificateMapper(SubjectEqualsDNCertificateMapperCfg
071                                               configuration)
072         throws ConfigException, InitializationException
073  {
074    // No initialization is required.
075  }
076
077
078
079  /**
080   * Establishes a mapping between the information in the provided certificate
081   * chain to the DN of a single user in the Directory Server.
082   *
083   * @param  certificateChain  The certificate chain presented by the client
084   *                           during SSL negotiation.  The peer certificate
085   *                           will be listed first, followed by the ordered
086   *                           issuer chain as appropriate.
087   *
088   * @return  The DN of the one user to whom the mapping was established, or
089   *          <CODE>null</CODE> if no mapping was established and no special
090   *         message is required to send back to the client.
091   *
092   * @throws  DirectoryException  If a problem occurred while attempting to
093   *                              establish the mapping.  This may include
094   *                              internal failures, a mapping which matches
095   *                              multiple users, or any other case in which an
096   *                              error message should be returned to the
097   *                              client.
098   */
099  @Override
100  public Entry mapCertificateToUser(Certificate[] certificateChain)
101         throws DirectoryException
102  {
103    // Make sure that a peer certificate was provided.
104    if (certificateChain == null || certificateChain.length == 0)
105    {
106      LocalizableMessage message = ERR_SEDCM_NO_PEER_CERTIFICATE.get();
107      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
108    }
109
110
111    // Get the first certificate in the chain.  It must be an X.509 certificate.
112    X509Certificate peerCertificate;
113    try
114    {
115      peerCertificate = (X509Certificate) certificateChain[0];
116    }
117    catch (Exception e)
118    {
119      logger.traceException(e);
120
121      LocalizableMessage message = ERR_SEDCM_PEER_CERT_NOT_X509.get(certificateChain[0].getType());
122      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
123    }
124
125
126    // Get the subject from the peer certificate and decode it as a DN.
127    X500Principal peerPrincipal = peerCertificate.getSubjectX500Principal();
128    DN subjectDN;
129    try
130    {
131      subjectDN = DN.valueOf(peerPrincipal.getName(X500Principal.RFC2253));
132    }
133    catch (Exception e)
134    {
135      logger.traceException(e);
136
137      LocalizableMessage message = ERR_SEDCM_CANNOT_DECODE_SUBJECT_AS_DN.get(peerPrincipal, getExceptionMessage(e));
138      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
139    }
140
141    // Retrieve the entry with the specified DN from the directory.
142    Entry userEntry;
143    try
144    {
145      userEntry = DirectoryServer.getEntry(subjectDN);
146    }
147    catch (DirectoryException de)
148    {
149      logger.traceException(de);
150
151      LocalizableMessage message = ERR_SEDCM_CANNOT_GET_ENTRY.get(subjectDN, de.getMessageObject());
152      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message, de);
153    }
154    catch (Exception e)
155    {
156      logger.traceException(e);
157
158      LocalizableMessage message = ERR_SEDCM_CANNOT_GET_ENTRY.get(subjectDN, getExceptionMessage(e));
159      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message, e);
160    }
161
162    if (userEntry == null)
163    {
164      LocalizableMessage message = ERR_SEDCM_NO_USER_FOR_DN.get(subjectDN);
165      throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
166    }
167    else
168    {
169      return userEntry;
170    }
171  }
172}
173