1 /* 2 * ==================================================================== 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * ==================================================================== 20 * 21 * This software consists of voluntary contributions made by many 22 * individuals on behalf of the Apache Software Foundation. For more 23 * information on the Apache Software Foundation, please see 24 * <http://www.apache.org/>. 25 * 26 */ 27 package ch.boye.httpclientandroidlib.impl.auth; 28 29 import ch.boye.httpclientandroidlib.Header; 30 import ch.boye.httpclientandroidlib.HttpRequest; 31 import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; 32 import ch.boye.httpclientandroidlib.auth.AUTH; 33 import ch.boye.httpclientandroidlib.auth.AuthenticationException; 34 import ch.boye.httpclientandroidlib.auth.Credentials; 35 import ch.boye.httpclientandroidlib.auth.InvalidCredentialsException; 36 import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; 37 import ch.boye.httpclientandroidlib.auth.NTCredentials; 38 import ch.boye.httpclientandroidlib.message.BufferedHeader; 39 import ch.boye.httpclientandroidlib.util.Args; 40 import ch.boye.httpclientandroidlib.util.CharArrayBuffer; 41 42 /** 43 * NTLM is a proprietary authentication scheme developed by Microsoft 44 * and optimized for Windows platforms. 45 * 46 * @since 4.0 47 */ 48 @NotThreadSafe 49 public class NTLMScheme extends AuthSchemeBase { 50 51 enum State { 52 UNINITIATED, 53 CHALLENGE_RECEIVED, 54 MSG_TYPE1_GENERATED, 55 MSG_TYPE2_RECEVIED, 56 MSG_TYPE3_GENERATED, 57 FAILED, 58 } 59 60 private final NTLMEngine engine; 61 62 private State state; 63 private String challenge; 64 NTLMScheme(final NTLMEngine engine)65 public NTLMScheme(final NTLMEngine engine) { 66 super(); 67 Args.notNull(engine, "NTLM engine"); 68 this.engine = engine; 69 this.state = State.UNINITIATED; 70 this.challenge = null; 71 } 72 73 /** 74 * @since 4.3 75 */ NTLMScheme()76 public NTLMScheme() { 77 this(new NTLMEngineImpl()); 78 } 79 getSchemeName()80 public String getSchemeName() { 81 return "ntlm"; 82 } 83 getParameter(final String name)84 public String getParameter(final String name) { 85 // String parameters not supported 86 return null; 87 } 88 getRealm()89 public String getRealm() { 90 // NTLM does not support the concept of an authentication realm 91 return null; 92 } 93 isConnectionBased()94 public boolean isConnectionBased() { 95 return true; 96 } 97 98 @Override parseChallenge( final CharArrayBuffer buffer, final int beginIndex, final int endIndex)99 protected void parseChallenge( 100 final CharArrayBuffer buffer, 101 final int beginIndex, final int endIndex) throws MalformedChallengeException { 102 this.challenge = buffer.substringTrimmed(beginIndex, endIndex); 103 if (this.challenge.length() == 0) { 104 if (this.state == State.UNINITIATED) { 105 this.state = State.CHALLENGE_RECEIVED; 106 } else { 107 this.state = State.FAILED; 108 } 109 } else { 110 if (this.state.compareTo(State.MSG_TYPE1_GENERATED) < 0) { 111 this.state = State.FAILED; 112 throw new MalformedChallengeException("Out of sequence NTLM response message"); 113 } else if (this.state == State.MSG_TYPE1_GENERATED) { 114 this.state = State.MSG_TYPE2_RECEVIED; 115 } 116 } 117 } 118 authenticate( final Credentials credentials, final HttpRequest request)119 public Header authenticate( 120 final Credentials credentials, 121 final HttpRequest request) throws AuthenticationException { 122 NTCredentials ntcredentials = null; 123 try { 124 ntcredentials = (NTCredentials) credentials; 125 } catch (final ClassCastException e) { 126 throw new InvalidCredentialsException( 127 "Credentials cannot be used for NTLM authentication: " 128 + credentials.getClass().getName()); 129 } 130 String response = null; 131 if (this.state == State.FAILED) { 132 throw new AuthenticationException("NTLM authentication failed"); 133 } else if (this.state == State.CHALLENGE_RECEIVED) { 134 response = this.engine.generateType1Msg( 135 ntcredentials.getDomain(), 136 ntcredentials.getWorkstation()); 137 this.state = State.MSG_TYPE1_GENERATED; 138 } else if (this.state == State.MSG_TYPE2_RECEVIED) { 139 response = this.engine.generateType3Msg( 140 ntcredentials.getUserName(), 141 ntcredentials.getPassword(), 142 ntcredentials.getDomain(), 143 ntcredentials.getWorkstation(), 144 this.challenge); 145 this.state = State.MSG_TYPE3_GENERATED; 146 } else { 147 throw new AuthenticationException("Unexpected state: " + this.state); 148 } 149 final CharArrayBuffer buffer = new CharArrayBuffer(32); 150 if (isProxy()) { 151 buffer.append(AUTH.PROXY_AUTH_RESP); 152 } else { 153 buffer.append(AUTH.WWW_AUTH_RESP); 154 } 155 buffer.append(": NTLM "); 156 buffer.append(response); 157 return new BufferedHeader(buffer); 158 } 159 isComplete()160 public boolean isComplete() { 161 return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED; 162 } 163 164 } 165