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