class WinRM::HTTP::HttpGSSAPI
Uses Kerberos/GSSAPI to authenticate and encrypt messages
Public Class Methods
new(endpoint, realm, opts, service = nil)
click to toggle source
@param [String,URI] endpoint the WinRM webservice endpoint @param [String] realm the Kerberos realm we are authenticating to @param [String<optional>] service the service name, default is HTTP
Calls superclass method
WinRM::HTTP::HttpTransport.new
# File lib/winrm/http/transport.rb, line 291 def initialize(endpoint, realm, opts, service = nil) require 'gssapi' require 'gssapi/extensions' super(endpoint, opts) # Remove the GSSAPI auth from HTTPClient because we are doing our own thing no_sspi_auth! service ||= 'HTTP' @service = "#{service}/#{@endpoint.host}@#{realm}" no_ssl_peer_verification! if opts[:no_ssl_peer_verification] init_krb end
Public Instance Methods
send_request(message)
click to toggle source
Sends the SOAP payload to the WinRM service and returns the service's SOAP response. If an error occurrs an appropriate error is raised.
@param [String] The XML SOAP message @returns [REXML::Document] The parsed response body
# File lib/winrm/http/transport.rb, line 309 def send_request(message) resp = send_kerberos_request(message) if resp.status == 401 @logger.debug 'Got 401 - reinitializing Kerberos and retrying one more time' init_krb resp = send_kerberos_request(message) end handler = WinRM::ResponseHandler.new(winrm_decrypt(resp.http_body.content), resp.status) handler.parse_to_xml end
Private Instance Methods
init_krb()
click to toggle source
# File lib/winrm/http/transport.rb, line 349 def init_krb @logger.debug "Initializing Kerberos for #{@service}" @gsscli = GSSAPI::Simple.new(@endpoint.host, @service) token = @gsscli.init_context auth = Base64.strict_encode64 token hdr = { 'Authorization' => "Kerberos #{auth}", 'Connection' => 'Keep-Alive', 'Content-Type' => 'application/soap+xml;charset=UTF-8' } @logger.debug 'Sending HTTP POST for Kerberos Authentication' r = @httpcli.post(@endpoint, '', hdr) itok = r.header['WWW-Authenticate'].pop itok = itok.split.last itok = Base64.strict_decode64(itok) @gsscli.init_context(itok) end
send_kerberos_request(message)
click to toggle source
Sends the SOAP payload to the WinRM service and returns the service's HTTP response.
@param [String] The XML SOAP message @returns [Object] The HTTP response object
# File lib/winrm/http/transport.rb, line 329 def send_kerberos_request(message) log_soap_message(message) original_length = message.bytesize pad_len, emsg = winrm_encrypt(message) req_length = original_length + pad_len hdr = { 'Connection' => 'Keep-Alive', 'Content-Type' => 'multipart/encrypted;' \ 'protocol="application/HTTP-Kerberos-session-encrypted";boundary="Encrypted Boundary"' } resp = @httpcli.post( @endpoint, body(emsg, req_length, 'application/HTTP-Kerberos-session-encrypted'), hdr ) log_soap_message(resp.http_body.content) resp end
winrm_decrypt(str)
click to toggle source
@return [String] the unencrypted response string
# File lib/winrm/http/transport.rb, line 415 def winrm_decrypt(str) @logger.debug "Decrypting SOAP message:\n#{str}" iov_cnt = 3 iov = FFI::MemoryPointer.new(GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * iov_cnt) iov0 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(FFI::Pointer.new(iov.address)) iov0[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_HEADER | \ GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_FLAG_ALLOCATE) iov1 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new( FFI::Pointer.new(iov.address + (GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * 1)) ) iov1[:type] = GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_DATA iov2 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new( FFI::Pointer.new(iov.address + (GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * 2)) ) iov2[:type] = GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_DATA str.force_encoding('BINARY') str.sub!(%r{^.*Content-Type: application\/octet-stream\r\n(.*)--Encrypted.*$}m, '\1') len = str.unpack('L').first iov_data = str.unpack("La#{len}a*") iov0[:buffer].value = iov_data[1] iov1[:buffer].value = iov_data[2] min_stat = FFI::MemoryPointer.new :uint32 conf_state = FFI::MemoryPointer.new :uint32 conf_state.write_int(1) qop_state = FFI::MemoryPointer.new :uint32 qop_state.write_int(0) maj_stat = GSSAPI::LibGSSAPI.gss_unwrap_iov( min_stat, @gsscli.context, conf_state, qop_state, iov, iov_cnt ) @logger.debug "SOAP message decrypted (MAJ: #{maj_stat}, " \ "MIN: #{min_stat.read_int}):\n#{iov1[:buffer].value}" iov1[:buffer].value end
winrm_encrypt(str)
click to toggle source
@return [String] the encrypted request string
# File lib/winrm/http/transport.rb, line 372 def winrm_encrypt(str) @logger.debug "Encrypting SOAP message:\n#{str}" iov_cnt = 3 iov = FFI::MemoryPointer.new(GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * iov_cnt) iov0 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new(FFI::Pointer.new(iov.address)) iov0[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_HEADER | \ GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_FLAG_ALLOCATE) iov1 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new( FFI::Pointer.new(iov.address + (GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * 1)) ) iov1[:type] = GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_DATA iov1[:buffer].value = str iov2 = GSSAPI::LibGSSAPI::GssIOVBufferDesc.new( FFI::Pointer.new(iov.address + (GSSAPI::LibGSSAPI::GssIOVBufferDesc.size * 2)) ) iov2[:type] = (GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_TYPE_PADDING | \ GSSAPI::LibGSSAPI::GSS_IOV_BUFFER_FLAG_ALLOCATE) conf_state = FFI::MemoryPointer.new :uint32 min_stat = FFI::MemoryPointer.new :uint32 GSSAPI::LibGSSAPI.gss_wrap_iov( min_stat, @gsscli.context, 1, GSSAPI::LibGSSAPI::GSS_C_QOP_DEFAULT, conf_state, iov, iov_cnt ) token = [iov0[:buffer].length].pack('L') token += iov0[:buffer].value token += iov1[:buffer].value pad_len = iov2[:buffer].length token += iov2[:buffer].value if pad_len > 0 [pad_len, token] end