mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com> (cherry picked from commit d08369ad06b6d5ef801f79493c2aa4bdaeab1b83) Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
296 lines
12 KiB
Java
296 lines
12 KiB
Java
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
package rdpclient.ntlmssp;
|
|
|
|
import com.cloud.utils.ConstantTimeComparator;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import rdpclient.ntlmssp.asn1.NegoItem;
|
|
import rdpclient.ntlmssp.asn1.TSRequest;
|
|
import rdpclient.rdp.RdpConstants;
|
|
import streamer.ByteBuffer;
|
|
import streamer.Element;
|
|
import streamer.Link;
|
|
import streamer.OneTimeSwitch;
|
|
import streamer.Pipeline;
|
|
import streamer.PipelineImpl;
|
|
import streamer.debug.MockSink;
|
|
import streamer.debug.MockSource;
|
|
|
|
/**
|
|
* @see http://msdn.microsoft.com/en-us/library/cc236642.aspx
|
|
*/
|
|
public class ServerNtlmsspChallenge extends OneTimeSwitch implements NtlmConstants {
|
|
|
|
protected NtlmState ntlmState;
|
|
|
|
public ServerNtlmsspChallenge(String id, NtlmState state) {
|
|
super(id);
|
|
ntlmState = state;
|
|
}
|
|
|
|
@Override
|
|
protected void handleOneTimeData(ByteBuffer buf, Link link) {
|
|
if (buf == null)
|
|
return;
|
|
|
|
if (verbose)
|
|
System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
|
|
|
|
// Extract server challenge, extract server flags.
|
|
|
|
// Parse TSRequest in BER format
|
|
TSRequest request = new TSRequest("TSRequest");
|
|
request.readTag(buf);
|
|
|
|
ByteBuffer negoToken = ((NegoItem)request.negoTokens.tags[0]).negoToken.value;
|
|
ntlmState.challengeMessage = negoToken.toByteArray(); // Store message for MIC calculation in AUTH message
|
|
|
|
parseNtlmChallenge(negoToken);
|
|
|
|
negoToken.unref();
|
|
buf.unref();
|
|
switchOff();
|
|
}
|
|
|
|
public void parseNtlmChallenge(ByteBuffer buf) {
|
|
|
|
// Signature: "NTLMSSP\0"
|
|
String signature = buf.readVariableString(RdpConstants.CHARSET_8);
|
|
if (!ConstantTimeComparator.compareStrings(signature, NTLMSSP))
|
|
throw new RuntimeException("Unexpected NTLM message singature: \"" + signature + "\". Expected signature: \"" + NTLMSSP + "\". Data: " + buf + ".");
|
|
|
|
// MessageType (CHALLENGE)
|
|
int messageType = buf.readSignedIntLE();
|
|
if (messageType != NtlmConstants.CHALLENGE)
|
|
throw new RuntimeException("Unexpected NTLM message type: " + messageType + ". Expected type: CHALLENGE (" + NtlmConstants.CHALLENGE + "). Data: " + buf
|
|
+ ".");
|
|
|
|
// TargetName
|
|
ntlmState.serverTargetName = readStringByDescription(buf);
|
|
|
|
// NegotiateFlags
|
|
ntlmState.negotiatedFlags = new NegoFlags(buf.readSignedIntLE());
|
|
if (verbose)
|
|
System.out.println("[" + this + "] INFO: Server negotiate flags: " + ntlmState.negotiatedFlags + ".");
|
|
|
|
// ServerChallenge
|
|
ByteBuffer challenge = buf.readBytes(8);
|
|
ntlmState.serverChallenge = challenge.toByteArray();
|
|
if (verbose)
|
|
System.out.println("[" + this + "] INFO: Server challenge: " + challenge + ".");
|
|
challenge.unref();
|
|
|
|
// Reserved/context
|
|
buf.skipBytes(8);
|
|
|
|
// TargetInfo
|
|
ByteBuffer targetInfo = readBlockByDescription(buf);
|
|
|
|
// Store raw target info block for Type3 message
|
|
ntlmState.serverTargetInfo = targetInfo.toByteArray();
|
|
|
|
// Parse target info block
|
|
parseTargetInfo(targetInfo);
|
|
targetInfo.unref();
|
|
|
|
// OS Version, NTLM revision, 8 bytes, Optional. Ignore it.
|
|
|
|
// Ignore rest of buffer with allocated blocks
|
|
|
|
buf.unref();
|
|
}
|
|
|
|
public void parseTargetInfo(ByteBuffer buf) {
|
|
// Parse attribute list
|
|
|
|
while (buf.remainderLength() > 0) {
|
|
int type = buf.readUnsignedShortLE();
|
|
int length = buf.readUnsignedShortLE();
|
|
|
|
if (type == MSV_AV_EOL)
|
|
// End of list
|
|
break;
|
|
|
|
ByteBuffer data = buf.readBytes(length);
|
|
parseAttribute(data, type, length);
|
|
data.unref();
|
|
}
|
|
}
|
|
|
|
public void parseAttribute(ByteBuffer buf, int type, int length) {
|
|
switch (type) {
|
|
case MSV_AV_NETBIOS_DOMAIN_NAME:
|
|
ntlmState.serverNetbiosDomainName = buf.readString(length, RdpConstants.CHARSET_16);
|
|
break;
|
|
case MSV_AV_NETBIOS_COMPUTER_NAME:
|
|
ntlmState.serverNetbiosComputerName = buf.readString(length, RdpConstants.CHARSET_16);
|
|
break;
|
|
case MSV_AV_DNS_DOMAIN_NAME:
|
|
ntlmState.serverDnsDomainName = buf.readString(length, RdpConstants.CHARSET_16);
|
|
break;
|
|
case MSV_AV_DNS_COMPUTER_NAME:
|
|
ntlmState.serverDnsComputerName = buf.readString(length, RdpConstants.CHARSET_16);
|
|
break;
|
|
case MSV_AV_DNS_TREE_NAME:
|
|
ntlmState.serverDnsTreeName = buf.readString(length, RdpConstants.CHARSET_16);
|
|
break;
|
|
|
|
case MSV_AV_TIMESTAMP:
|
|
ByteBuffer tmp = buf.readBytes(length);
|
|
ntlmState.serverTimestamp = tmp.toByteArray();
|
|
//*DEBUG*/System.out.println("Server timestamp: "+tmp.toPlainHexString());
|
|
tmp.unref();
|
|
break;
|
|
|
|
default:
|
|
// Ignore
|
|
//throw new RuntimeException("[" + this + "] ERROR: Unknown NTLM target info attribute: " + type + ". Data: " + buf + ".");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Read NTLM wide string, by it description. Buffer offset must point to
|
|
* beginning of NTLM message signature.
|
|
*
|
|
* @param buf
|
|
* buffer with cursor pointing to description
|
|
* @return
|
|
*/
|
|
public static String readStringByDescription(ByteBuffer buf) {
|
|
ByteBuffer block = readBlockByDescription(buf);
|
|
String value = block.readString(block.length, RdpConstants.CHARSET_16);
|
|
block.unref();
|
|
|
|
return value;
|
|
}
|
|
|
|
public static ByteBuffer readBlockByDescription(ByteBuffer buf) {
|
|
int blockLength = buf.readUnsignedShortLE(); // In bytes
|
|
int allocatedSpace = buf.readUnsignedShortLE();
|
|
int offset = buf.readSignedIntLE();
|
|
|
|
if (allocatedSpace < blockLength)
|
|
blockLength = allocatedSpace;
|
|
|
|
if (offset > buf.length || offset < 0 || offset + allocatedSpace > buf.length)
|
|
throw new RuntimeException("ERROR: NTLM block is too long. Allocated space: " + allocatedSpace + ", block offset: " + offset + ", data: "
|
|
+ buf + ".");
|
|
|
|
// Move cursor to position of allocated block, starting from beginning of
|
|
// this buffer
|
|
int storedCursor = buf.cursor;
|
|
buf.cursor = offset;
|
|
|
|
// Read string
|
|
ByteBuffer value = buf.readBytes(blockLength);
|
|
|
|
// Restore cursor
|
|
buf.cursor = storedCursor;
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Example.
|
|
*/
|
|
public static void main(String args[]) {
|
|
// System.setProperty("streamer.Link.debug", "true");
|
|
System.setProperty("streamer.Element.debug", "true");
|
|
// System.setProperty("streamer.Pipeline.debug", "true");
|
|
|
|
/* @formatter:off */
|
|
byte[] packet = new byte[] {
|
|
0x30, (byte) 0x82, 0x01, 0x02, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 258 bytes
|
|
(byte) 0xa0, 0x03, // TAG: [0] (constructed) LEN: 3 bytes
|
|
0x02, 0x01, 0x03, // TAG: [UNIVERSAL 2] (primitive) "INTEGER" LEN: 1 bytes, Version: 0x3
|
|
(byte) 0xa1, (byte) 0x81, (byte) 0xfa, // TAG: [1] (constructed) LEN: 250 bytes
|
|
0x30, (byte) 0x81, (byte) 0xf7, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 247 bytes
|
|
0x30, (byte) 0x81, (byte) 0xf4, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 244 bytes
|
|
(byte) 0xa0, (byte) 0x81, (byte) 0xf1, // TAG: [0] (constructed) LEN: 241 bytes
|
|
0x04, (byte) 0x81, (byte) 0xee, // TAG: [UNIVERSAL 4] (primitive) "OCTET STRING" LEN: 238 bytes
|
|
|
|
0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, // "NTLMSSP\0"
|
|
|
|
0x02, 0x00, 0x00, 0x00, // MessageType (CHALLENGE)
|
|
0x1e, 0x00, 0x1e, 0x00, 0x38, 0x00, 0x00, 0x00, // TargetName (length: 30, allocated space: 30, offset: 56)
|
|
0x35, (byte) 0x82, (byte) 0x8a, (byte) 0xe2, // NegotiateFlags
|
|
0x52, (byte) 0xbe, (byte) 0x83, (byte) 0xd1, (byte) 0xf8, (byte) 0x80, 0x16, 0x6a, // ServerChallenge
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved
|
|
(byte) 0x98, 0x00, (byte) 0x98, 0x00, 0x56, 0x00, 0x00, 0x00, // TargetInfo (length: 152, allocated space: 152, offset: 86)
|
|
0x06, 0x03, (byte) 0xd7, 0x24, 0x00, 0x00, 0x00, 0x0f, // Version (6.3, build 9431) , NTLM current revision: 15
|
|
|
|
|
|
0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // Target name value: "WIN-LO419B2LSR0"
|
|
|
|
// Target Info value:
|
|
|
|
// Attribute list
|
|
|
|
0x02, 0x00, // Item Type: NetBIOS domain name (0x0002, LE)
|
|
0x1e, 0x00, // Item Length: 30 (LE)
|
|
0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0"
|
|
|
|
0x01, 0x00, // Item Type: NetBIOS computer name (0x0001, LE)
|
|
0x1e, 0x00, // Item Length: 30 (LE)
|
|
0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0"
|
|
|
|
0x04, 0x00, // Item Type: DNS domain name (0x0004, LE)
|
|
0x1e, 0x00, // Item Length: 30 (LE)
|
|
0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0"
|
|
|
|
0x03, 0x00, // Item Type: DNS computer name (0x0003, LE)
|
|
0x1e, 0x00, // Item Length: 30 (LE)
|
|
0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0"
|
|
|
|
0x07, 0x00, // Item Type: Timestamp (0x0007, LE)
|
|
0x08, 0x00, // Item Length: 8 (LE)
|
|
(byte) 0x99, 0x4f, 0x02, (byte) 0xd8, (byte) 0xf4, (byte) 0xaf, (byte) 0xce, 0x01, // TODO
|
|
|
|
// Attribute: End of list
|
|
0x00, 0x00,
|
|
0x00, 0x00,
|
|
};
|
|
/* @formatter:on */
|
|
|
|
MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet, new byte[] {1, 2, 3}));
|
|
NtlmState state = new NtlmState();
|
|
Element ntlmssp_challenge = new ServerNtlmsspChallenge("ntlmssp_challenge", state);
|
|
Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers());
|
|
Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3}));
|
|
|
|
Pipeline pipeline = new PipelineImpl("test");
|
|
pipeline.add(source, ntlmssp_challenge, sink, mainSink);
|
|
pipeline.link("source", "ntlmssp_challenge", "mainSink");
|
|
pipeline.link("ntlmssp_challenge >" + OTOUT, "sink");
|
|
pipeline.runMainLoop("source", STDOUT, false, false);
|
|
|
|
// Check state challenge
|
|
byte[] challenge = new byte[] {0x52, (byte)0xbe, (byte)0x83, (byte)0xd1, (byte)0xf8, (byte)0x80, 0x16, 0x6a};
|
|
if (state.serverChallenge == null)
|
|
throw new RuntimeException("Challenge was not extracted from server NTLMSSP Challenge packet.");
|
|
if (!Arrays.equals(challenge, state.serverChallenge))
|
|
throw new RuntimeException("Challenge was extracted from server NTLMSSP Challenge packet is not equal to expected. Actual value: "
|
|
+ state.serverChallenge + ", expected value: " + challenge + ".");
|
|
|
|
}
|
|
|
|
}
|