001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027package org.apache.http.impl.auth; 028 029import org.apache.http.Header; 030import org.apache.http.HttpRequest; 031import org.apache.http.auth.AUTH; 032import org.apache.http.auth.AuthenticationException; 033import org.apache.http.auth.Credentials; 034import org.apache.http.auth.InvalidCredentialsException; 035import org.apache.http.auth.MalformedChallengeException; 036import org.apache.http.auth.NTCredentials; 037import org.apache.http.message.BufferedHeader; 038import org.apache.http.util.Args; 039import org.apache.http.util.CharArrayBuffer; 040 041/** 042 * NTLM is a proprietary authentication scheme developed by Microsoft 043 * and optimized for Windows platforms. 044 * 045 * @since 4.0 046 */ 047public class NTLMScheme extends AuthSchemeBase { 048 049 enum State { 050 UNINITIATED, 051 CHALLENGE_RECEIVED, 052 MSG_TYPE1_GENERATED, 053 MSG_TYPE2_RECEVIED, 054 MSG_TYPE3_GENERATED, 055 FAILED, 056 } 057 058 private final NTLMEngine engine; 059 060 private State state; 061 private String challenge; 062 063 public NTLMScheme(final NTLMEngine engine) { 064 super(); 065 Args.notNull(engine, "NTLM engine"); 066 this.engine = engine; 067 this.state = State.UNINITIATED; 068 this.challenge = null; 069 } 070 071 /** 072 * @since 4.3 073 */ 074 public NTLMScheme() { 075 this(new NTLMEngineImpl()); 076 } 077 078 @Override 079 public String getSchemeName() { 080 return "ntlm"; 081 } 082 083 @Override 084 public String getParameter(final String name) { 085 // String parameters not supported 086 return null; 087 } 088 089 @Override 090 public String getRealm() { 091 // NTLM does not support the concept of an authentication realm 092 return null; 093 } 094 095 @Override 096 public boolean isConnectionBased() { 097 return true; 098 } 099 100 @Override 101 protected void parseChallenge( 102 final CharArrayBuffer buffer, 103 final int beginIndex, final int endIndex) throws MalformedChallengeException { 104 this.challenge = buffer.substringTrimmed(beginIndex, endIndex); 105 if (this.challenge.isEmpty()) { 106 if (this.state == State.UNINITIATED) { 107 this.state = State.CHALLENGE_RECEIVED; 108 } else { 109 this.state = State.FAILED; 110 } 111 } else { 112 if (this.state.compareTo(State.MSG_TYPE1_GENERATED) < 0) { 113 this.state = State.FAILED; 114 throw new MalformedChallengeException("Out of sequence NTLM response message"); 115 } else if (this.state == State.MSG_TYPE1_GENERATED) { 116 this.state = State.MSG_TYPE2_RECEVIED; 117 } 118 } 119 } 120 121 @Override 122 public Header authenticate( 123 final Credentials credentials, 124 final HttpRequest request) throws AuthenticationException { 125 NTCredentials ntcredentials = null; 126 try { 127 ntcredentials = (NTCredentials) credentials; 128 } catch (final ClassCastException e) { 129 throw new InvalidCredentialsException( 130 "Credentials cannot be used for NTLM authentication: " 131 + credentials.getClass().getName()); 132 } 133 String response = null; 134 if (this.state == State.FAILED) { 135 throw new AuthenticationException("NTLM authentication failed"); 136 } else if (this.state == State.CHALLENGE_RECEIVED) { 137 response = this.engine.generateType1Msg( 138 ntcredentials.getDomain(), 139 ntcredentials.getWorkstation()); 140 this.state = State.MSG_TYPE1_GENERATED; 141 } else if (this.state == State.MSG_TYPE2_RECEVIED) { 142 response = this.engine.generateType3Msg( 143 ntcredentials.getUserName(), 144 ntcredentials.getPassword(), 145 ntcredentials.getDomain(), 146 ntcredentials.getWorkstation(), 147 this.challenge); 148 this.state = State.MSG_TYPE3_GENERATED; 149 } else { 150 throw new AuthenticationException("Unexpected state: " + this.state); 151 } 152 final CharArrayBuffer buffer = new CharArrayBuffer(32); 153 if (isProxy()) { 154 buffer.append(AUTH.PROXY_AUTH_RESP); 155 } else { 156 buffer.append(AUTH.WWW_AUTH_RESP); 157 } 158 buffer.append(": NTLM "); 159 buffer.append(response); 160 return new BufferedHeader(buffer); 161 } 162 163 @Override 164 public boolean isComplete() { 165 return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED; 166 } 167 168}