View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.server.ldap.handlers.sasl.ntlm;
21  
22  
23  import javax.naming.Context;
24  import javax.security.sasl.SaslException;
25  
26  import org.apache.directory.api.ldap.model.constants.AuthenticationLevel;
27  import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms;
28  import org.apache.directory.api.ldap.model.message.BindRequest;
29  import org.apache.directory.api.ldap.model.name.Dn;
30  import org.apache.directory.api.util.Strings;
31  import org.apache.directory.server.core.api.CoreSession;
32  import org.apache.directory.server.core.api.LdapPrincipal;
33  import org.apache.directory.server.core.api.interceptor.context.BindOperationContext;
34  import org.apache.directory.server.i18n.I18n;
35  import org.apache.directory.server.ldap.LdapSession;
36  import org.apache.directory.server.ldap.handlers.sasl.AbstractSaslServer;
37  import org.apache.directory.server.ldap.handlers.sasl.SaslConstants;
38  
39  
40  /**
41   * A SaslServer implementation for NTLM based SASL mechanism.  This is
42   * required unfortunately because the JDK's SASL provider does not support
43   * this mechanism.
44   *
45   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
46   */
47  public class NtlmSaslServer extends AbstractSaslServer
48  {
49      /** The different states during a NTLM negotiation */
50      enum NegotiationState
51      {
52          INITIALIZED, TYPE_1_RECEIVED, TYPE_2_SENT, TYPE_3_RECEIVED, COMPLETED
53      }
54  
55      /** The current state */
56      private NegotiationState state = NegotiationState.INITIALIZED;
57      private final NtlmProvider provider;
58  
59  
60      public NtlmSaslServer( NtlmProvider provider, BindRequest bindRequest, LdapSession ldapSession,
61          CoreSession adminSession )
62      {
63          super( ldapSession, adminSession, bindRequest );
64          this.provider = provider;
65      }
66  
67  
68      /**
69       * {@inheritDoc}
70       */
71      public String getMechanismName()
72      {
73          return SupportedSaslMechanisms.NTLM;
74      }
75  
76  
77      protected void responseRecieved()
78      {
79          switch ( state )
80          {
81              case INITIALIZED:
82                  state = NegotiationState.TYPE_1_RECEIVED;
83                  break;
84  
85              case TYPE_1_RECEIVED:
86                  throw new IllegalStateException( I18n.err( I18n.ERR_660 ) );
87  
88              case TYPE_2_SENT:
89                  state = NegotiationState.TYPE_3_RECEIVED;
90                  break;
91  
92              case TYPE_3_RECEIVED:
93                  throw new IllegalStateException( I18n.err( I18n.ERR_661 ) );
94  
95              case COMPLETED:
96                  throw new IllegalStateException( I18n.err( I18n.ERR_662 ) );
97  
98              default:
99                  throw new IllegalStateException( "Unexpected negotiation state " + state );
100         }
101     }
102 
103 
104     protected void responseSent()
105     {
106         switch ( state )
107         {
108             case INITIALIZED:
109                 throw new IllegalStateException( I18n.err( I18n.ERR_663 ) );
110 
111             case TYPE_1_RECEIVED:
112                 state = NegotiationState.TYPE_2_SENT;
113                 break;
114 
115             case TYPE_2_SENT:
116                 throw new IllegalStateException( I18n.err( I18n.ERR_664 ) );
117 
118             case TYPE_3_RECEIVED:
119                 state = NegotiationState.COMPLETED;
120                 break;
121 
122             case COMPLETED:
123                 throw new IllegalStateException( I18n.err( I18n.ERR_662 ) );
124 
125             default:
126                 throw new IllegalStateException( "Unexpected negotiation state " + state );
127         }
128     }
129 
130 
131     /**
132      * {@inheritDoc}
133      */
134     public byte[] evaluateResponse( byte[] response ) throws SaslException
135     {
136         if ( response == null )
137         {
138             throw new IllegalArgumentException( I18n.err( I18n.ERR_666 ) );
139         }
140 
141         if ( response.length == 0 )
142         {
143             throw new IllegalArgumentException( I18n.err( I18n.ERR_667 ) );
144         }
145 
146         responseRecieved();
147         byte[] retval = null;
148 
149         switch ( state )
150         {
151             case TYPE_1_RECEIVED:
152                 try
153                 {
154                     retval = provider.generateChallenge( getLdapSession().getIoSession(), response );
155                 }
156                 catch ( Exception e )
157                 {
158                     throw new SaslException( I18n.err( I18n.ERR_668 ), e );
159                 }
160 
161                 break;
162 
163             case TYPE_3_RECEIVED:
164                 boolean result;
165                 try
166                 {
167                     result = provider.authenticate( getLdapSession().getIoSession(), response );
168                     Dn dn = getBindRequest().getDn();
169                     
170                     if ( dn == null )
171                     {
172                         dn = new Dn( getLdapSession().getLdapServer().getDirectoryService().getSchemaManager() );
173                     }
174                     else if ( !dn.isSchemaAware() ) 
175                     {
176                         dn = new Dn( getLdapSession().getLdapServer().getDirectoryService().getSchemaManager(), dn );
177                     }
178                     
179                     LdapPrincipalre/api/LdapPrincipal.html#LdapPrincipal">LdapPrincipal ldapPrincipal = new LdapPrincipal( getAdminSession().getDirectoryService()
180                         .getSchemaManager(),
181                         dn, AuthenticationLevel.STRONG );
182                     getLdapSession().putSaslProperty( SaslConstants.SASL_AUTHENT_USER, ldapPrincipal );
183                     getLdapSession()
184                         .putSaslProperty( Context.SECURITY_PRINCIPAL, getBindRequest().getName() );
185                 }
186                 catch ( Exception e )
187                 {
188                     throw new SaslException( I18n.err( I18n.ERR_669 ), e );
189                 }
190 
191                 if ( !result )
192                 {
193                     throw new SaslException( I18n.err( I18n.ERR_670 ) );
194                 }
195 
196                 break;
197 
198             case INITIALIZED:
199             case TYPE_2_SENT:
200             case COMPLETED:
201             default:
202                 throw new IllegalStateException( "Unexpected negotiation state " + state );
203         }
204 
205         responseSent();
206         return retval;
207     }
208 
209 
210     /**
211      * Try to authenticate the usr against the underlying LDAP server.
212      */
213     private CoreSession authenticate( String user, String password ) throws Exception
214     {
215         BindOperationContexti/interceptor/context/BindOperationContext.html#BindOperationContext">BindOperationContext bindContext = new BindOperationContext( getLdapSession().getCoreSession() );
216         bindContext.setDn( new Dn( user ) );
217         bindContext.setCredentials( Strings.getBytesUtf8( password ) );
218 
219         getAdminSession().getDirectoryService().getOperationManager().bind( bindContext );
220 
221         return bindContext.getSession();
222     }
223 
224 
225     /**
226      * {@inheritDoc}
227      */
228     public boolean isComplete()
229     {
230         return state == NegotiationState.COMPLETED;
231     }
232 }