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
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.extensions;
028
029
030
031 import java.security.cert.Certificate;
032 import java.security.cert.X509Certificate;
033 import javax.security.auth.x500.X500Principal;
034 import java.util.concurrent.locks.Lock;
035
036 import org.opends.messages.Message;
037 import org.opends.server.admin.std.server.SubjectEqualsDNCertificateMapperCfg;
038 import org.opends.server.api.CertificateMapper;
039 import org.opends.server.config.ConfigException;
040 import org.opends.server.core.DirectoryServer;
041 import org.opends.server.loggers.debug.DebugTracer;
042 import org.opends.server.types.DebugLogLevel;
043 import org.opends.server.types.DirectoryException;
044 import org.opends.server.types.DN;
045 import org.opends.server.types.Entry;
046 import org.opends.server.types.InitializationException;
047 import org.opends.server.types.LockManager;
048 import org.opends.server.types.ResultCode;
049
050 import static org.opends.server.loggers.debug.DebugLogger.*;
051 import static org.opends.messages.ExtensionMessages.*;
052 import static org.opends.server.util.StaticUtils.*;
053
054
055
056 /**
057 * This class implements a very simple Directory Server certificate mapper that
058 * will map a certificate to a user only if the subject of the peer certificate
059 * exactly matches the DN of a user in the Directory Server.
060 */
061 public class SubjectEqualsDNCertificateMapper
062 extends CertificateMapper<SubjectEqualsDNCertificateMapperCfg>
063 {
064 /**
065 * The tracer object for the debug logger.
066 */
067 private static final DebugTracer TRACER = getTracer();
068
069 /**
070 * Creates a new instance of this certificate mapper. Note that all actual
071 * initialization should be done in the
072 * <CODE>initializeCertificateMapper</CODE> method.
073 */
074 public SubjectEqualsDNCertificateMapper()
075 {
076 super();
077
078 }
079
080
081
082 /**
083 * {@inheritDoc}
084 */
085 public void initializeCertificateMapper(SubjectEqualsDNCertificateMapperCfg
086 configuration)
087 throws ConfigException, InitializationException
088 {
089 // No initialization is required.
090 }
091
092
093
094 /**
095 * Establishes a mapping between the information in the provided certificate
096 * chain to the DN of a single user in the Directory Server.
097 *
098 * @param certificateChain The certificate chain presented by the client
099 * during SSL negotiation. The peer certificate
100 * will be listed first, followed by the ordered
101 * issuer chain as appropriate.
102 *
103 * @return The DN of the one user to whom the mapping was established, or
104 * <CODE>null</CODE> if no mapping was established and no special
105 * message is required to send back to the client.
106 *
107 * @throws DirectoryException If a problem occurred while attempting to
108 * establish the mapping. This may include
109 * internal failures, a mapping which matches
110 * multiple users, or any other case in which an
111 * error message should be returned to the
112 * client.
113 */
114 public Entry mapCertificateToUser(Certificate[] certificateChain)
115 throws DirectoryException
116 {
117 // Make sure that a peer certificate was provided.
118 if ((certificateChain == null) || (certificateChain.length == 0))
119 {
120 Message message = ERR_SEDCM_NO_PEER_CERTIFICATE.get();
121 throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
122 }
123
124
125 // Get the first certificate in the chain. It must be an X.509 certificate.
126 X509Certificate peerCertificate;
127 try
128 {
129 peerCertificate = (X509Certificate) certificateChain[0];
130 }
131 catch (Exception e)
132 {
133 if (debugEnabled())
134 {
135 TRACER.debugCaught(DebugLogLevel.ERROR, e);
136 }
137
138 Message message = ERR_SEDCM_PEER_CERT_NOT_X509.get(
139 String.valueOf(certificateChain[0].getType()));
140 throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
141 }
142
143
144 // Get the subject from the peer certificate and decode it as a DN.
145 X500Principal peerPrincipal = peerCertificate.getSubjectX500Principal();
146 DN subjectDN;
147 try
148 {
149 subjectDN = DN.decode(peerPrincipal.getName(X500Principal.RFC2253));
150 }
151 catch (Exception e)
152 {
153 if (debugEnabled())
154 {
155 TRACER.debugCaught(DebugLogLevel.ERROR, e);
156 }
157
158 Message message = ERR_SEDCM_CANNOT_DECODE_SUBJECT_AS_DN.get(
159 String.valueOf(peerPrincipal), getExceptionMessage(e));
160 throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
161 }
162
163
164 // Acquire a read lock on the user entry. If this fails, then so will the
165 // certificate mapping.
166 Lock readLock = null;
167 for (int i=0; i < 3; i++)
168 {
169 readLock = LockManager.lockRead(subjectDN);
170 if (readLock != null)
171 {
172 break;
173 }
174 }
175
176 if (readLock == null)
177 {
178 Message message =
179 ERR_SEDCM_CANNOT_LOCK_ENTRY.get(String.valueOf(subjectDN));
180 throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
181 }
182
183
184 // Retrieve the entry with the specified DN from the directory.
185 Entry userEntry;
186 try
187 {
188 userEntry = DirectoryServer.getEntry(subjectDN);
189 }
190 catch (DirectoryException de)
191 {
192 if (debugEnabled())
193 {
194 TRACER.debugCaught(DebugLogLevel.ERROR, de);
195 }
196
197 Message message = ERR_SEDCM_CANNOT_GET_ENTRY.get(
198 String.valueOf(subjectDN), de.getMessageObject());
199 throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
200 de);
201 }
202 catch (Exception e)
203 {
204 if (debugEnabled())
205 {
206 TRACER.debugCaught(DebugLogLevel.ERROR, e);
207 }
208
209 Message message = ERR_SEDCM_CANNOT_GET_ENTRY.get(
210 String.valueOf(subjectDN), getExceptionMessage(e));
211 throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message,
212 e);
213 }
214 finally
215 {
216 LockManager.unlock(subjectDN, readLock);
217 }
218
219
220 if (userEntry == null)
221 {
222 Message message = ERR_SEDCM_NO_USER_FOR_DN.get(String.valueOf(subjectDN));
223 throw new DirectoryException(ResultCode.INVALID_CREDENTIALS, message);
224 }
225 else
226 {
227 return userEntry;
228 }
229 }
230 }
231