001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.server.ldap.handlers.bind.ntlm;
021
022
023 import javax.naming.Context;
024 import javax.naming.InvalidNameException;
025 import javax.security.sasl.SaslException;
026
027 import org.apache.directory.server.core.CoreSession;
028 import org.apache.directory.server.core.LdapPrincipal;
029 import org.apache.directory.server.core.interceptor.context.BindOperationContext;
030 import org.apache.directory.server.i18n.I18n;
031 import org.apache.directory.server.ldap.LdapSession;
032 import org.apache.directory.server.ldap.handlers.bind.AbstractSaslServer;
033 import org.apache.directory.server.ldap.handlers.bind.SaslConstants;
034 import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
035 import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
036 import org.apache.directory.shared.ldap.message.internal.InternalBindRequest;
037 import org.apache.directory.shared.ldap.name.DN;
038 import org.apache.directory.shared.ldap.util.StringTools;
039
040
041 /**
042 * A SaslServer implementation for NTLM based SASL mechanism. This is
043 * required unfortunately because the JDK's SASL provider does not support
044 * this mechanism.
045 *
046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047 * @version $$Rev$$
048 */
049 public class NtlmSaslServer extends AbstractSaslServer
050 {
051 /** The different states during a NTLM negotiation */
052 enum NegotiationState { INITIALIZED, TYPE_1_RECEIVED, TYPE_2_SENT, TYPE_3_RECEIVED, COMPLETED }
053
054 /** The current state */
055 private NegotiationState state = NegotiationState.INITIALIZED;
056 private final NtlmProvider provider;
057
058
059 public NtlmSaslServer( NtlmProvider provider, InternalBindRequest bindRequest, LdapSession ldapSession )
060 {
061 super( ldapSession, null, bindRequest );
062 this.provider = provider;
063 }
064
065
066 /**
067 * {@inheritDoc}
068 */
069 public String getMechanismName()
070 {
071 return SupportedSaslMechanisms.NTLM;
072 }
073
074
075 protected void responseRecieved()
076 {
077 switch ( state )
078 {
079 case INITIALIZED:
080 state = NegotiationState.TYPE_1_RECEIVED;
081 break;
082
083 case TYPE_1_RECEIVED:
084 throw new IllegalStateException( I18n.err( I18n.ERR_660 ) );
085
086 case TYPE_2_SENT:
087 state = NegotiationState.TYPE_3_RECEIVED;
088 break;
089
090 case TYPE_3_RECEIVED:
091 throw new IllegalStateException( I18n.err( I18n.ERR_661 ) );
092
093 case COMPLETED:
094 throw new IllegalStateException( I18n.err( I18n.ERR_662 ) );
095 }
096 }
097
098
099 protected void responseSent()
100 {
101 switch ( state )
102 {
103 case INITIALIZED:
104 throw new IllegalStateException( I18n.err( I18n.ERR_663 ) );
105
106 case TYPE_1_RECEIVED:
107 state = NegotiationState.TYPE_2_SENT;
108 break;
109
110 case TYPE_2_SENT:
111 throw new IllegalStateException( I18n.err( I18n.ERR_664 ) );
112
113 case TYPE_3_RECEIVED:
114 state = NegotiationState.COMPLETED;
115 break;
116
117 case COMPLETED:
118 throw new IllegalStateException( I18n.err( I18n.ERR_662 ) );
119 }
120 }
121
122
123 /**
124 * {@inheritDoc}
125 */
126 public byte[] evaluateResponse( byte[] response ) throws SaslException
127 {
128 if ( response == null )
129 {
130 throw new NullPointerException( I18n.err( I18n.ERR_666 ) );
131 }
132
133 if ( response.length == 0 )
134 {
135 throw new IllegalArgumentException( I18n.err( I18n.ERR_667 ) );
136 }
137
138 responseRecieved();
139 byte[] retval = null;
140
141 switch ( state )
142 {
143 case TYPE_1_RECEIVED:
144 try
145 {
146 retval = provider.generateChallenge( getLdapSession().getIoSession(), response );
147 }
148 catch ( Exception e )
149 {
150 throw new SaslException( I18n.err( I18n.ERR_668 ), e );
151 }
152
153 break;
154
155 case TYPE_3_RECEIVED:
156 boolean result;
157 try
158 {
159 result = provider.authenticate( getLdapSession().getIoSession(), response );
160 DN dn = getBindRequest().getName();
161 dn.normalize( getLdapSession().getLdapServer().getDirectoryService().getSchemaManager().getNormalizerMapping() );
162 LdapPrincipal ldapPrincipal = new LdapPrincipal( dn, AuthenticationLevel.STRONG );
163 getLdapSession().putSaslProperty( SaslConstants.SASL_AUTHENT_USER, ldapPrincipal );
164 getLdapSession().putSaslProperty( Context.SECURITY_PRINCIPAL, getBindRequest().getName().toString() );
165 }
166 catch ( Exception e )
167 {
168 throw new SaslException( I18n.err( I18n.ERR_669 ), e );
169 }
170
171 if ( ! result )
172 {
173 throw new SaslException( I18n.err( I18n.ERR_670 ) );
174 }
175
176 break;
177 }
178
179 responseSent();
180 return retval;
181 }
182
183
184 /**
185 * Try to authenticate the usr against the underlying LDAP server.
186 */
187 private CoreSession authenticate( String user, String password ) throws InvalidNameException, Exception
188 {
189 BindOperationContext bindContext = new BindOperationContext( getLdapSession().getCoreSession() );
190 bindContext.setDn( new DN( user ) );
191 bindContext.setCredentials( StringTools.getBytesUtf8( password ) );
192
193 getAdminSession().getDirectoryService().getOperationManager().bind( bindContext );
194
195 return bindContext.getSession();
196 }
197
198
199 /**
200 * {@inheritDoc}
201 */
202 public boolean isComplete()
203 {
204 return state == NegotiationState.COMPLETED;
205 }
206 }