1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.ldap.handlers.sasl.external.certificate;
21
22
23 import org.apache.commons.lang3.exception.ExceptionUtils;
24 import org.apache.directory.api.ldap.model.constants.AuthenticationLevel;
25 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
26 import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms;
27 import org.apache.directory.api.ldap.model.entry.Entry;
28 import org.apache.directory.api.ldap.model.entry.Value;
29 import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
30 import org.apache.directory.api.ldap.model.filter.EqualityNode;
31 import org.apache.directory.api.ldap.model.message.BindRequest;
32 import org.apache.directory.api.ldap.model.message.SearchScope;
33 import org.apache.directory.api.util.Strings;
34 import org.apache.directory.server.core.api.CoreSession;
35 import org.apache.directory.server.core.api.DirectoryService;
36 import org.apache.directory.server.core.api.LdapPrincipal;
37 import org.apache.directory.server.core.api.OperationEnum;
38 import org.apache.directory.server.core.api.OperationManager;
39 import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
40 import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
41 import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
42 import org.apache.directory.server.ldap.LdapServer;
43 import org.apache.directory.server.ldap.LdapSession;
44 import org.apache.directory.server.ldap.handlers.sasl.AbstractSaslServer;
45 import org.apache.directory.server.ldap.handlers.sasl.SaslConstants;
46 import org.apache.mina.filter.ssl.SslFilter;
47
48 import javax.naming.Context;
49 import javax.net.ssl.SSLSession;
50 import javax.security.sasl.SaslException;
51 import java.security.cert.Certificate;
52
53
54
55
56
57
58
59 public final class ExternalSaslServer extends AbstractSaslServer
60 {
61
62
63
64 private enum NegotiationState
65 {
66 INITIALIZED,
67 COMPLETED
68 }
69
70
71 private NegotiationState state;
72
73
74
75
76
77
78
79
80
81 ExternalSaslServer( LdapSession ldapSession, CoreSession adminSession, BindRequest bindRequest )
82 {
83 super( ldapSession, adminSession, bindRequest );
84 state = NegotiationState.INITIALIZED;
85 }
86
87
88
89
90
91 public String getMechanismName()
92 {
93 return SupportedSaslMechanisms.EXTERNAL;
94 }
95
96
97
98
99
100 public byte[] evaluateResponse( byte[] initialResponse ) throws SaslException
101 {
102 try
103 {
104 SSLSession sslSession = ( SSLSession ) getLdapSession().getIoSession().getAttribute( SslFilter.SSL_SESSION );
105 Certificate[] peerCertificates = sslSession.getPeerCertificates();
106
107 if ( null == peerCertificates || 1 > peerCertificates.length )
108 {
109 throw new SaslException( "No peer certificate provided - cancel bind." );
110 }
111
112 getLdapSession().setCoreSession( authenticate( peerCertificates[0] ) );
113 state = NegotiationState.COMPLETED;
114 }
115 catch ( Exception e )
116 {
117 throw new SaslException( "Error authentication using client certificate: " + ExceptionUtils.getStackTrace( e ), e );
118 }
119
120 return Strings.EMPTY_BYTES;
121 }
122
123
124
125
126
127
128
129 public boolean isComplete()
130 {
131 return state == NegotiationState.COMPLETED;
132 }
133
134
135
136
137
138
139 private CoreSession authenticate( Certificate peerCertificate ) throws Exception
140 {
141 LdapSession ldapSession = getLdapSession();
142 CoreSession adminSession = getAdminSession();
143 DirectoryService directoryService = adminSession.getDirectoryService();
144 LdapServer ldapServer = ldapSession.getLdapServer();
145 OperationManager operationManager = directoryService.getOperationManager();
146
147
148 EqualityNode<String> filter = new EqualityNode<>(
149 directoryService.getSchemaManager().getAttributeType( SchemaConstants.USER_CERTIFICATE_AT ),
150 new Value( peerCertificate.getEncoded() ) );
151
152 SearchOperationContext/interceptor/context/SearchOperationContext.html#SearchOperationContext">SearchOperationContext searchContext = new SearchOperationContext( directoryService.getAdminSession() );
153 searchContext.setDn( directoryService.getDnFactory().create( ldapServer.getSearchBaseDn() ) );
154 searchContext.setScope( SearchScope.SUBTREE );
155 searchContext.setFilter( filter );
156 searchContext.setSizeLimit( 1 );
157 searchContext.setNoAttributes( true );
158
159 try ( EntryFilteringCursor cursor = operationManager.search( searchContext ) )
160 {
161 if ( cursor.next() )
162 {
163 Entry entry = cursor.get();
164
165 BindOperationContext/api/interceptor/context/BindOperationContext.html#BindOperationContext">BindOperationContext bindContext = new BindOperationContext( ldapSession.getCoreSession() );
166 bindContext.setDn( entry.getDn() );
167 bindContext.setSaslMechanism( getMechanismName() );
168 bindContext.setSaslAuthId( getBindRequest().getName() );
169 bindContext.setIoSession( ldapSession.getIoSession() );
170 bindContext.setInterceptors( directoryService.getInterceptors( OperationEnum.BIND ) );
171
172 operationManager.bind( bindContext );
173
174 ldapSession.putSaslProperty( SaslConstants.SASL_AUTHENT_USER, new LdapPrincipal( directoryService.getSchemaManager(),
175 entry.getDn(), AuthenticationLevel.STRONG ) );
176 getLdapSession().putSaslProperty( Context.SECURITY_PRINCIPAL, getBindRequest().getName() );
177
178 return bindContext.getSession();
179 }
180
181 throw new LdapAuthenticationException( "Cannot authenticate user cert=" + peerCertificate );
182 }
183 }
184 }