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.bind.ntlm;
21
22
23 import org.apache.directory.server.core.CoreSession;
24 import org.apache.directory.server.core.authn.LdapPrincipal;
25 import org.apache.directory.server.core.interceptor.context.BindOperationContext;
26 import org.apache.directory.server.ldap.LdapSession;
27 import org.apache.directory.server.ldap.handlers.bind.AbstractSaslServer;
28 import org.apache.directory.server.ldap.handlers.bind.SaslConstants;
29 import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
30 import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
31 import org.apache.directory.shared.ldap.message.BindRequest;
32 import org.apache.directory.shared.ldap.name.LdapDN;
33 import org.apache.directory.shared.ldap.util.StringTools;
34
35 import javax.naming.Context;
36 import javax.naming.InvalidNameException;
37 import javax.security.sasl.SaslException;
38
39
40
41
42
43
44
45
46
47
48 public class NtlmSaslServer extends AbstractSaslServer
49 {
50
51 enum NegotiationState { INITIALIZED, TYPE_1_RECEIVED, TYPE_2_SENT, TYPE_3_RECEIVED, COMPLETED }
52
53
54 private NegotiationState state = NegotiationState.INITIALIZED;
55 private final NtlmProvider provider;
56
57
58 public NtlmSaslServer( NtlmProvider provider, BindRequest bindRequest, LdapSession ldapSession )
59 {
60 super( ldapSession, null, bindRequest );
61 this.provider = provider;
62 }
63
64
65
66
67
68 public String getMechanismName()
69 {
70 return SupportedSaslMechanisms.NTLM;
71 }
72
73
74 protected void responseRecieved()
75 {
76 switch ( state )
77 {
78 case INITIALIZED:
79 state = NegotiationState.TYPE_1_RECEIVED;
80 break;
81
82 case TYPE_1_RECEIVED:
83 throw new IllegalStateException( "Cannot receive NTLM message before sending Type 2 challenge." );
84
85 case TYPE_2_SENT:
86 state = NegotiationState.TYPE_3_RECEIVED;
87 break;
88
89 case TYPE_3_RECEIVED:
90 throw new IllegalStateException( "Cannot receive NTLM message after Type 3 has been received." );
91
92 case COMPLETED:
93 throw new IllegalStateException( "Sasl challenge response already completed." );
94 }
95 }
96
97
98 protected void responseSent()
99 {
100 switch ( state )
101 {
102 case INITIALIZED:
103 throw new IllegalStateException( "Cannot send Type 2 challenge before Type 1 response." );
104
105 case TYPE_1_RECEIVED:
106 state = NegotiationState.TYPE_2_SENT;
107 break;
108
109 case TYPE_2_SENT:
110 throw new IllegalStateException( "Cannot send Type 2 after it's already sent." );
111
112 case TYPE_3_RECEIVED:
113 state = NegotiationState.COMPLETED;
114 break;
115
116 case COMPLETED:
117 throw new IllegalStateException( "Sasl challenge response already completed." );
118 }
119 }
120
121
122
123
124
125 public byte[] evaluateResponse( byte[] response ) throws SaslException
126 {
127 if ( response == null )
128 {
129 throw new NullPointerException( "response was null" );
130 }
131
132 if ( response.length == 0 )
133 {
134 throw new IllegalArgumentException( "response with zero bytes" );
135 }
136
137 responseRecieved();
138 byte[] retval = null;
139
140 switch ( state )
141 {
142 case TYPE_1_RECEIVED:
143 try
144 {
145 retval = provider.generateChallenge( getLdapSession().getIoSession(), response );
146 }
147 catch ( Exception e )
148 {
149 throw new SaslException( "There was a failure during NTLM Type 1 message handling.", e );
150 }
151
152 break;
153
154 case TYPE_3_RECEIVED:
155 boolean result;
156 try
157 {
158 result = provider.authenticate( getLdapSession().getIoSession(), response );
159 LdapDN dn = getBindRequest().getName();
160 dn.normalize( getLdapSession().getLdapServer().getDirectoryService().getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
161 LdapPrincipal ldapPrincipal = new LdapPrincipal( dn, AuthenticationLevel.STRONG );
162 getLdapSession().putSaslProperty( SaslConstants.SASL_AUTHENT_USER, ldapPrincipal );
163 getLdapSession().putSaslProperty( Context.SECURITY_PRINCIPAL, getBindRequest().getName().toString() );
164 }
165 catch ( Exception e )
166 {
167 throw new SaslException( "There was a failure during NTLM Type 3 message handling.", e );
168 }
169
170 if ( ! result )
171 {
172 throw new SaslException( "Authentication occurred but the credentials were invalid." );
173 }
174
175 break;
176 }
177
178 responseSent();
179 return retval;
180 }
181
182
183
184
185
186 private CoreSession authenticate( String user, String password ) throws InvalidNameException, Exception
187 {
188 BindOperationContext bindContext = new BindOperationContext( getLdapSession().getCoreSession() );
189 bindContext.setDn( new LdapDN( user ) );
190 bindContext.setCredentials( StringTools.getBytesUtf8( password ) );
191
192 getAdminSession().getDirectoryService().getOperationManager().bind( bindContext );
193
194 return bindContext.getSession();
195 }
196
197
198
199
200
201 public boolean isComplete()
202 {
203 return state == NegotiationState.COMPLETED;
204 }
205 }