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 }