class WinRM::HTTP::HttpNegotiate

NTLM/Negotiate, secure, HTTP transport

Public Class Methods

new(endpoint, user, pass, opts) click to toggle source
Calls superclass method WinRM::HTTP::HttpTransport::new
# File lib/winrm/http/transport.rb, line 148
def initialize(endpoint, user, pass, opts)
  super(endpoint, opts)
  require 'rubyntlm'
  no_sspi_auth!

  user_parts = user.split('\\')
  if user_parts.length > 1
    opts[:domain] = user_parts[0]
    user = user_parts[1]
  end

  @ntlmcli = Net::NTLM::Client.new(user, pass, opts)
  @retryable = true
  no_ssl_peer_verification! if opts[:no_ssl_peer_verification]
  @ssl_peer_fingerprint = opts[:ssl_peer_fingerprint]
  @httpcli.ssl_config.set_trust_ca(opts[:ca_trust_path]) if opts[:ca_trust_path]
end

Public Instance Methods

send_request(message) click to toggle source
# File lib/winrm/http/transport.rb, line 166
def send_request(message)
  ssl_peer_fingerprint_verification!
  init_auth if @ntlmcli.session.nil?
  log_soap_message(message)

  hdr = {
    'Content-Type' => 'multipart/encrypted;'\
      'protocol="application/HTTP-SPNEGO-session-encrypted";boundary="Encrypted Boundary"'
  }

  resp = @httpcli.post(@endpoint, body(seal(message), message.bytesize), hdr)
  verify_ssl_fingerprint(resp.peer_cert)
  if resp.status == 401 && @retryable
    @retryable = false
    init_auth
    send_request(message)
  else
    @retryable = true
    decrypted_body = winrm_decrypt(resp)
    log_soap_message(decrypted_body)
    WinRM::ResponseHandler.new(decrypted_body, resp.status).parse_to_xml
  end
end

Private Instance Methods

channel_binding(response) click to toggle source
# File lib/winrm/http/transport.rb, line 248
def channel_binding(response)
  if response.peer_cert.nil?
    nil
  else
    cert = if RUBY_PLATFORM == 'java'
             OpenSSL::X509::Certificate.new(response.peer_cert.cert.getEncoded)
           else
             response.peer_cert
           end
    Net::NTLM::ChannelBinding.create(OpenSSL::X509::Certificate.new(cert))
  end
end
init_auth() click to toggle source
# File lib/winrm/http/transport.rb, line 228
def init_auth
  @logger.debug "Initializing Negotiate for #{@endpoint}"
  auth1 = @ntlmcli.init_context
  hdr = {
    'Authorization' => "Negotiate #{auth1.encode64}",
    'Content-Type' => 'application/soap+xml;charset=UTF-8'
  }
  @logger.debug 'Sending HTTP POST for Negotiate Authentication'
  r = @httpcli.post(@endpoint, '', hdr)
  verify_ssl_fingerprint(r.peer_cert)
  auth_header = r.header['WWW-Authenticate'].pop
  unless auth_header
    msg = "Unable to parse authorization header. Headers: #{r.headers}\r\nBody: #{r.body}"
    raise WinRMHTTPTransportError.new(msg, r.status_code)
  end
  itok = auth_header.split.last
  auth3 = @ntlmcli.init_context(itok, channel_binding(r))
  issue_challenge_response(auth3)
end
issue_challenge_response(negotiate) click to toggle source
# File lib/winrm/http/transport.rb, line 213
def issue_challenge_response(negotiate)
  auth_header = {
    'Authorization' => "Negotiate #{negotiate.encode64}",
    'Content-Type' => 'application/soap+xml;charset=UTF-8'
  }

  # OMI Server on Linux requires an empty payload with the new auth header to proceed
  # because the config check for max payload size will otherwise break the auth handshake
  # given the OMI server does not support that check
  @httpcli.post(@endpoint, '', auth_header)

  # return an empty hash of headers for subsequent requests to use
  {}
end
seal(message) click to toggle source
# File lib/winrm/http/transport.rb, line 192
def seal(message)
  emessage = @ntlmcli.session.seal_message message
  signature = @ntlmcli.session.sign_message message
  "\x10\x00\x00\x00#{signature}#{emessage}"
end
winrm_decrypt(resp) click to toggle source
# File lib/winrm/http/transport.rb, line 198
def winrm_decrypt(resp)
  # OMI server doesn't always respond to encrypted messages with encrypted responses over SSL
  return resp.body if resp.header['Content-Type'].first =~ %r{\Aapplication\/soap\+xml}i
  return '' if resp.body.empty?

  str = resp.body.force_encoding('BINARY')
  str.sub!(%r{^.*Content-Type: application\/octet-stream\r\n(.*)--Encrypted.*$}m, '\1')

  signature = str[4..19]
  message = @ntlmcli.session.unseal_message str[20..-1]
  return message if @ntlmcli.session.verify_signature(signature, message)

  raise WinRMHTTPTransportError, 'Could not decrypt NTLM message.'
end