michael@0: /* michael@0: * ==================================================================== michael@0: * michael@0: * Licensed to the Apache Software Foundation (ASF) under one or more michael@0: * contributor license agreements. See the NOTICE file distributed with michael@0: * this work for additional information regarding copyright ownership. michael@0: * The ASF licenses this file to You under the Apache License, Version 2.0 michael@0: * (the "License"); you may not use this file except in compliance with michael@0: * the License. You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: * ==================================================================== michael@0: * michael@0: * This software consists of voluntary contributions made by many michael@0: * individuals on behalf of the Apache Software Foundation. For more michael@0: * information on the Apache Software Foundation, please see michael@0: * . michael@0: * michael@0: */ michael@0: michael@0: package ch.boye.httpclientandroidlib.impl.auth; michael@0: michael@0: import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; michael@0: michael@0: import ch.boye.httpclientandroidlib.Header; michael@0: import ch.boye.httpclientandroidlib.HttpRequest; michael@0: import ch.boye.httpclientandroidlib.auth.AUTH; michael@0: import ch.boye.httpclientandroidlib.auth.AuthenticationException; michael@0: import ch.boye.httpclientandroidlib.auth.Credentials; michael@0: import ch.boye.httpclientandroidlib.auth.InvalidCredentialsException; michael@0: import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; michael@0: import ch.boye.httpclientandroidlib.auth.NTCredentials; michael@0: import ch.boye.httpclientandroidlib.impl.auth.AuthSchemeBase; michael@0: import ch.boye.httpclientandroidlib.message.BufferedHeader; michael@0: import ch.boye.httpclientandroidlib.util.CharArrayBuffer; michael@0: michael@0: /** michael@0: * NTLM is a proprietary authentication scheme developed by Microsoft michael@0: * and optimized for Windows platforms. michael@0: * michael@0: * @since 4.0 michael@0: */ michael@0: @NotThreadSafe michael@0: public class NTLMScheme extends AuthSchemeBase { michael@0: michael@0: enum State { michael@0: UNINITIATED, michael@0: CHALLENGE_RECEIVED, michael@0: MSG_TYPE1_GENERATED, michael@0: MSG_TYPE2_RECEVIED, michael@0: MSG_TYPE3_GENERATED, michael@0: FAILED, michael@0: } michael@0: michael@0: private final NTLMEngine engine; michael@0: michael@0: private State state; michael@0: private String challenge; michael@0: michael@0: public NTLMScheme(final NTLMEngine engine) { michael@0: super(); michael@0: if (engine == null) { michael@0: throw new IllegalArgumentException("NTLM engine may not be null"); michael@0: } michael@0: this.engine = engine; michael@0: this.state = State.UNINITIATED; michael@0: this.challenge = null; michael@0: } michael@0: michael@0: public String getSchemeName() { michael@0: return "ntlm"; michael@0: } michael@0: michael@0: public String getParameter(String name) { michael@0: // String parameters not supported michael@0: return null; michael@0: } michael@0: michael@0: public String getRealm() { michael@0: // NTLM does not support the concept of an authentication realm michael@0: return null; michael@0: } michael@0: michael@0: public boolean isConnectionBased() { michael@0: return true; michael@0: } michael@0: michael@0: @Override michael@0: protected void parseChallenge( michael@0: final CharArrayBuffer buffer, michael@0: int beginIndex, int endIndex) throws MalformedChallengeException { michael@0: String challenge = buffer.substringTrimmed(beginIndex, endIndex); michael@0: if (challenge.length() == 0) { michael@0: if (this.state == State.UNINITIATED) { michael@0: this.state = State.CHALLENGE_RECEIVED; michael@0: } else { michael@0: this.state = State.FAILED; michael@0: } michael@0: this.challenge = null; michael@0: } else { michael@0: this.state = State.MSG_TYPE2_RECEVIED; michael@0: this.challenge = challenge; michael@0: } michael@0: } michael@0: michael@0: public Header authenticate( michael@0: final Credentials credentials, michael@0: final HttpRequest request) throws AuthenticationException { michael@0: NTCredentials ntcredentials = null; michael@0: try { michael@0: ntcredentials = (NTCredentials) credentials; michael@0: } catch (ClassCastException e) { michael@0: throw new InvalidCredentialsException( michael@0: "Credentials cannot be used for NTLM authentication: " michael@0: + credentials.getClass().getName()); michael@0: } michael@0: String response = null; michael@0: if (this.state == State.CHALLENGE_RECEIVED || this.state == State.FAILED) { michael@0: response = this.engine.generateType1Msg( michael@0: ntcredentials.getDomain(), michael@0: ntcredentials.getWorkstation()); michael@0: this.state = State.MSG_TYPE1_GENERATED; michael@0: } else if (this.state == State.MSG_TYPE2_RECEVIED) { michael@0: response = this.engine.generateType3Msg( michael@0: ntcredentials.getUserName(), michael@0: ntcredentials.getPassword(), michael@0: ntcredentials.getDomain(), michael@0: ntcredentials.getWorkstation(), michael@0: this.challenge); michael@0: this.state = State.MSG_TYPE3_GENERATED; michael@0: } else { michael@0: throw new AuthenticationException("Unexpected state: " + this.state); michael@0: } michael@0: CharArrayBuffer buffer = new CharArrayBuffer(32); michael@0: if (isProxy()) { michael@0: buffer.append(AUTH.PROXY_AUTH_RESP); michael@0: } else { michael@0: buffer.append(AUTH.WWW_AUTH_RESP); michael@0: } michael@0: buffer.append(": NTLM "); michael@0: buffer.append(response); michael@0: return new BufferedHeader(buffer); michael@0: } michael@0: michael@0: public boolean isComplete() { michael@0: return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED; michael@0: } michael@0: michael@0: }