/*
* Copyright (c) 2016 Dell EMC Software
* All Rights Reserved
*/
package com.iwave.ext.windows.winrm.ntlm.state;
import org.apache.http.Header;
import org.apache.http.HttpClientConnection;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHeaderElement;
import org.apache.http.message.BasicHeaderValueFormatter;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import com.iwave.ext.windows.winrm.ntlm.NTLMConstants;
import com.iwave.ext.windows.winrm.ntlm.NTLMCrypt;
import com.iwave.ext.windows.winrm.ntlm.NTLMEncryptedEntity;
/**
* This state will ping-pong back and forth with DecryptingState in order to continue encrypting/decrypting messages. We need
* to encrypt all requests
*/
public class EncryptingState extends NTLMState {
/** The NTLM crypt mechanism to use to encrypt/decrypt messages. */
private NTLMCrypt crypt;
/**
* The boundary. Although testing has indicated that any value would work, the specification found at
* https://msdn.microsoft.com/en-us/library/cc251576.aspx explicitly states that "Encrypted Boundary" must be used, so
* we'll use that.
*/
public static final String BOUNDARY = "Encrypted Boundary";
/**
* Instantiates a state that will handle encryption for NTLM.
*
* @param crypt
* the encryption mechanism to use
*/
public EncryptingState(NTLMCrypt crypt) {
this.crypt = crypt;
}
@Override
public boolean accepts(HttpRequest message) {
// The communication has been determined to be encrypted, so we need to send everything encrypted
return true;
}
@Override
public void handle(HttpRequest request, HttpClientConnection conn, HttpContext context) {
request.setHeader(buildContentTypeHeader());
HttpEntityEnclosingRequest wrapper = (HttpEntityEnclosingRequest) request;
wrapper.setEntity(new NTLMEncryptedEntity(wrapper.getEntity(), crypt, BOUNDARY));
// Need to modify the content length of the request, otherwise there are decryption problems
request.setHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(wrapper.getEntity().getContentLength()));
}
/**
* Builds the new content type header. This is as per NTLM specification.
*
* @return the new content type header
*/
private Header buildContentTypeHeader() {
NameValuePair[] nvps = new NameValuePair[] {
new BasicNameValuePair(NTLMConstants.PROTOCOL, NTLMConstants.CONTENT_TYPE_FOR_SPNEGO),
new BasicNameValuePair(NTLMConstants.BOUNDARY, BOUNDARY) };
BasicHeaderElement elem = new BasicHeaderElement(NTLMConstants.CONTENT_TYPE_FOR_MULTIPART_ENCRYPTED, null, nvps);
return new BasicHeader(HttpHeaders.CONTENT_TYPE, BasicHeaderValueFormatter.formatHeaderElement(elem, false, null));
}
@Override
public NTLMState getNextState() {
return new DecryptingState(crypt);
}
}