winrm 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aa96ea262bfb740bcf397684129464232f76321f
4
- data.tar.gz: ca5894916562e3d5243c2099f8a439b10922d140
3
+ metadata.gz: 334ae5a75dc6e3249c316caa13eb582aa1930239
4
+ data.tar.gz: a8b620912892adef39c78d3f89c73a0530078404
5
5
  SHA512:
6
- metadata.gz: 87d111e8d6597aeb11a24dcf2a33f569426c48bd91940c25e27f461c4ca2e4d2c939eaa518f1b479e35f163700559fa152a5346c0ffd71693f3021bedeb77dfa
7
- data.tar.gz: f349d8858850cb6f3017b67cab69fc8c843f5d188eecaec95903e7f7bedb098160babfe7fa83e58d2b77999dc23c8a0d6891617d6c4d069daf8a2c1d8c7a2fe4
6
+ metadata.gz: fc00c205dfee32d1b15fb3940dfc2355a1bac7910793df0f42ba260da3cf2f8c19c42769a0646c1ad18e65825add9d5d42fa6f780fdf5634697f3fd06854fdf8
7
+ data.tar.gz: b5fd3e292e0042efa567a385a38de49335692e57f4ff2cd5210a19d7a15aa2fdb2540f899c682d61f88aa25c75560e2ba3a86f4698e4e54b1ca572bdc7c99408
data/appveyor.yml CHANGED
@@ -12,16 +12,6 @@ environment:
12
12
  - ruby_version: "21"
13
13
  winrm_endpoint: http://localhost:5985/wsman
14
14
 
15
- - ruby_version: "21"
16
- winrm_auth_type: ssl
17
- winrm_endpoint: https://localhost:5986/wsman
18
- winrm_no_ssl_peer_verification: true
19
-
20
- - ruby_version: "21"
21
- winrm_auth_type: ssl
22
- winrm_endpoint: https://localhost:5986/wsman
23
- use_ssl_peer_fingerprint: true
24
-
25
15
  clone_folder: c:\projects\winrm
26
16
  clone_depth: 1
27
17
  branches:
@@ -35,6 +25,7 @@ install:
35
25
  - ps: winrm create winrm/config/Listener?Address=*+Transport=HTTPS "@{Hostname=`"localhost`";CertificateThumbprint=`"$($env:winrm_cert)`"}"
36
26
  - ps: winrm set winrm/config/client/auth '@{Basic="true"}'
37
27
  - ps: winrm set winrm/config/service/auth '@{Basic="true"}'
28
+ - ps: winrm set winrm/config/service/auth '@{CbtHardeningLevel="Strict"}'
38
29
  - ps: winrm set winrm/config/service '@{AllowUnencrypted="true"}'
39
30
  - ps: $env:PATH="C:\Ruby$env:ruby_version\bin;$env:PATH"
40
31
  - ps: Write-Host $env:PATH
data/changelog.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # WinRM Gem Changelog
2
2
 
3
+ # 1.7.0
4
+ - Bump rubyntlm gem to 0.6.0 to get channel binding support for HTTPS fixing connections to endoints with `CbtHardeningLevel` set to `Strict`
5
+ - Fix for parsing binary data in command output
6
+
3
7
  # 1.6.1
4
8
  - Use codepage 437 by default on os versions older than Windows 7 and Windows Server 2008 R2
5
9
 
@@ -145,6 +145,7 @@ module WinRM
145
145
  class HttpNegotiate < HttpTransport
146
146
  def initialize(endpoint, user, pass, opts)
147
147
  super(endpoint)
148
+ require 'rubyntlm'
148
149
  no_sspi_auth!
149
150
 
150
151
  user_parts = user.split('\\')
@@ -156,9 +157,12 @@ module WinRM
156
157
  @ntlmcli = Net::NTLM::Client.new(user, pass, opts)
157
158
  @retryable = true
158
159
  no_ssl_peer_verification! if opts[:no_ssl_peer_verification]
160
+ @ssl_peer_fingerprint = opts[:ssl_peer_fingerprint]
161
+ @httpcli.ssl_config.set_trust_ca(opts[:ca_trust_path]) if opts[:ca_trust_path]
159
162
  end
160
163
 
161
164
  def send_request(message, auth_header = nil)
165
+ ssl_peer_fingerprint_verification!
162
166
  auth_header = init_auth if @ntlmcli.session.nil?
163
167
 
164
168
  original_length = message.length
@@ -183,6 +187,7 @@ module WinRM
183
187
  ].join("\r\n")
184
188
 
185
189
  resp = @httpcli.post(@endpoint, body, hdr)
190
+ verify_ssl_fingerprint(resp.peer_cert)
186
191
  if resp.status == 401 && @retryable
187
192
  @retryable = false
188
193
  send_request(message, init_auth)
@@ -217,22 +222,23 @@ module WinRM
217
222
  }
218
223
  @logger.debug "Sending HTTP POST for Negotiate Authentication"
219
224
  r = @httpcli.post(@endpoint, "", hdr)
225
+ verify_ssl_fingerprint(r.peer_cert)
220
226
  itok = r.header["WWW-Authenticate"].pop.split.last
221
- auth3 = @ntlmcli.init_context itok
227
+ binding = r.peer_cert.nil? ? nil : Net::NTLM::ChannelBinding.create(r.peer_cert)
228
+ auth3 = @ntlmcli.init_context(itok, binding)
222
229
  { "Authorization" => "Negotiate #{auth3.encode64}" }
223
230
  end
224
231
  end
225
232
 
226
233
  # Uses SSL to secure the transport
227
- class HttpSSL < HttpTransport
228
- def initialize(endpoint, user, pass, ca_trust_path = nil, opts)
234
+ class BasicAuthSSL < HttpTransport
235
+ def initialize(endpoint, user, pass, opts)
229
236
  super(endpoint)
230
237
  @httpcli.set_auth(endpoint, user, pass)
231
- @httpcli.ssl_config.set_trust_ca(ca_trust_path) unless ca_trust_path.nil?
232
- no_sspi_auth! if opts[:disable_sspi]
233
- basic_auth_only! if opts[:basic_auth_only]
238
+ basic_auth_only!
234
239
  no_ssl_peer_verification! if opts[:no_ssl_peer_verification]
235
240
  @ssl_peer_fingerprint = opts[:ssl_peer_fingerprint]
241
+ @httpcli.ssl_config.set_trust_ca(opts[:ca_trust_path]) if opts[:ca_trust_path]
236
242
  end
237
243
  end
238
244
 
data/lib/winrm/version.rb CHANGED
@@ -3,5 +3,5 @@
3
3
  # WinRM module
4
4
  module WinRM
5
5
  # The version of the WinRM library
6
- VERSION = '1.6.1'
6
+ VERSION = '1.7.0'
7
7
  end
@@ -54,7 +54,6 @@ module WinRM
54
54
  end
55
55
 
56
56
  def init_negotiate_transport(opts)
57
- require 'rubyntlm'
58
57
  HTTP::HttpNegotiate.new(opts[:endpoint], opts[:user], opts[:pass], opts)
59
58
  end
60
59
 
@@ -69,7 +68,11 @@ module WinRM
69
68
  end
70
69
 
71
70
  def init_ssl_transport(opts)
72
- HTTP::HttpSSL.new(opts[:endpoint], opts[:user], opts[:pass], opts[:ca_trust_path], opts)
71
+ if opts[:basic_auth_only]
72
+ HTTP::BasicAuthSSL.new(opts[:endpoint], opts[:user], opts[:pass], opts)
73
+ else
74
+ HTTP::HttpNegotiate.new(opts[:endpoint], opts[:user], opts[:pass], opts)
75
+ end
73
76
  end
74
77
 
75
78
  # Operation timeout.
@@ -253,8 +256,19 @@ module WinRM
253
256
  REXML::XPath.match(resp_doc, "//#{NS_WIN_SHELL}:Stream").each do |n|
254
257
  next if n.text.nil? || n.text.empty?
255
258
 
256
- # decode and strip off BOM which win 2008R2 applies
257
- stream = { n.attributes['Name'].to_sym => Base64.decode64(n.text).force_encoding('utf-8').sub("\xEF\xBB\xBF", "") }
259
+ # decode and replace invalid unicode characters
260
+ decoded_text = Base64.decode64(n.text).force_encoding('utf-8')
261
+ if ! decoded_text.valid_encoding?
262
+ if decoded_text.respond_to?(:scrub!)
263
+ decoded_text.scrub!
264
+ else
265
+ decoded_text = decoded_text.encode('utf-16', invalid: :replace, undef: :replace)
266
+ .encode('utf-8')
267
+ end
268
+ end
269
+
270
+ # remove BOM which 2008R2 applies
271
+ stream = { n.attributes['Name'].to_sym => decoded_text.sub('\xEF\xBB\xBF', '') }
258
272
  output[:data] << stream
259
273
  yield stream[:stdout], stream[:stderr] if block_given?
260
274
  end
@@ -0,0 +1,67 @@
1
+ # encoding: UTF-8
2
+ require 'winrm/winrm_service'
3
+ require 'rexml/document'
4
+ require 'erb'
5
+ require 'base64'
6
+
7
+ describe 'issue 184', unit: true do
8
+ let(:shell_id) { 'shell-123' }
9
+ let(:command_id) { 123 }
10
+ let(:test_data_xml_template) do
11
+ ERB.new(File.read('spec/stubs/responses/get_command_output_response.xml.erb'))
12
+ end
13
+ let(:service) do
14
+ WinRM::WinRMWebService.new(
15
+ 'http://dummy/wsman',
16
+ :plaintext,
17
+ user: 'dummy',
18
+ pass: 'dummy')
19
+ end
20
+
21
+ describe 'response doc stdout with invalid UTF-8 characters' do
22
+ let(:test_data_stdout) { 'ffff' } # Base64-decodes to '}\xF7\xDF', an invalid sequence
23
+ let(:test_data_stderr) { '' }
24
+ let(:test_data_xml) { test_data_xml_template.result(binding) }
25
+
26
+ before do
27
+ allow(service).to receive(:send_get_output_message).and_return(
28
+ REXML::Document.new(test_data_xml)
29
+ )
30
+ end
31
+
32
+ it 'does not raise an ArgumentError: invalid byte sequence in UTF-8' do
33
+ begin
34
+ expect(
35
+ service.get_command_output(shell_id, command_id)
36
+ ).not_to raise_error
37
+ rescue RSpec::Expectations::ExpectationNotMetError => e
38
+ expect(e.message).not_to include 'ArgumentError'
39
+ end
40
+ end
41
+
42
+ it 'does not have an empty stdout' do
43
+ expect(
44
+ service.get_command_output(shell_id, command_id)[:data][0][:stdout]
45
+ ).not_to be_empty
46
+ end
47
+ end
48
+
49
+ describe 'response doc stdout with valid UTF-8' do
50
+ let(:test_data_raw) { '✓1234-äöü' }
51
+ let(:test_data_stdout) { Base64.encode64(test_data_raw) }
52
+ let(:test_data_stderr) { '' }
53
+ let(:test_data_xml) { test_data_xml_template.result(binding) }
54
+
55
+ before do
56
+ allow(service).to receive(:send_get_output_message).and_return(
57
+ REXML::Document.new(test_data_xml)
58
+ )
59
+ end
60
+
61
+ it 'decodes to match input data' do
62
+ expect(
63
+ service.get_command_output(shell_id, command_id)[:data][0][:stdout]
64
+ ).to eq(test_data_raw)
65
+ end
66
+ end
67
+ end
data/spec/spec_helper.rb CHANGED
@@ -33,7 +33,6 @@ module ConnectionHelper
33
33
  config[:options][:ssl_peer_fingerprint] = ENV['winrm_cert']
34
34
  end
35
35
  config[:endpoint] = ENV['winrm_endpoint'] if ENV['winrm_endpoint']
36
- config[:auth_type] = ENV['winrm_auth_type'] if ENV['winrm_auth_type']
37
36
  end
38
37
 
39
38
  def merge_config_option_from_environment(config, key)
@@ -0,0 +1,13 @@
1
+ <s:Envelope xmlns:a='http://schemas.xmlsoap.org/ws/2004/08/addressing' xml:lang='en-US' xmlns:p='http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd' xmlns:rsp='http://schemas.microsoft.com/wbem/wsman/1/windows/shell' xmlns:s='http://www.w3.org/2003/05/soap-envelope' xmlns:w='http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd'>
2
+ <s:Header>
3
+ </s:Header>
4
+ <s:Body>
5
+ <rsp:ReceiveResponse>
6
+ <rsp:Stream CommandId='<%= command_id %>' Name='stdout'><%= test_data_stdout %></rsp:Stream>
7
+ <rsp:Stream CommandId='<%= command_id %>' End='true' Name='stderr'><%= test_data_stderr %></rsp:Stream>
8
+ <rsp:CommandState CommandId='<%= command_id %>' State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done'>
9
+ <rsp:ExitCode>0</rsp:ExitCode>
10
+ </rsp:CommandState>
11
+ </rsp:ReceiveResponse>
12
+ </s:Body>
13
+ </s:Envelope>
@@ -42,3 +42,83 @@ describe WinRM::HTTP::HttpNegotiate, unit: true do
42
42
  end
43
43
  end
44
44
  end
45
+
46
+ describe 'WinRM connection', integration: true do
47
+ let(:winrm_connection) do
48
+ endpoint = config[:endpoint].dup
49
+ if auth_type == :ssl
50
+ endpoint.sub!('5985', '5986')
51
+ endpoint.sub!('http', 'https')
52
+ end
53
+ winrm = WinRM::WinRMWebService.new(
54
+ endpoint, auth_type, options)
55
+ winrm.logger.level = :error
56
+ winrm
57
+ end
58
+ let(:options) do
59
+ opts = {}
60
+ opts[:user] = config[:options][:user]
61
+ opts[:pass] = config[:options][:pass]
62
+ opts[:basic_auth_only] = basic_auth_only
63
+ opts[:no_ssl_peer_verification] = no_ssl_peer_verification
64
+ opts[:ssl_peer_fingerprint] = ssl_peer_fingerprint
65
+ opts
66
+ end
67
+ let(:basic_auth_only) { false }
68
+ let(:no_ssl_peer_verification) { false }
69
+ let(:ssl_peer_fingerprint) { nil }
70
+
71
+ subject(:output) do
72
+ executor = winrm_connection.create_executor
73
+ executor.run_cmd('ipconfig')
74
+ end
75
+
76
+ shared_examples 'a valid_connection' do
77
+ it 'has a 0 exit code' do
78
+ expect(subject).to have_exit_code 0
79
+ end
80
+
81
+ it 'includes command output' do
82
+ expect(subject).to have_stdout_match(/Windows IP Configuration/)
83
+ end
84
+
85
+ it 'has no errors' do
86
+ expect(subject).to have_no_stderr
87
+ end
88
+ end
89
+
90
+ context 'HttpPlaintext' do
91
+ let(:basic_auth_only) { true }
92
+ let(:auth_type) { :plaintext }
93
+
94
+ it_behaves_like 'a valid_connection'
95
+ end
96
+
97
+ context 'HttpNegotiate' do
98
+ let(:auth_type) { :negotiate }
99
+
100
+ it_behaves_like 'a valid_connection'
101
+ end
102
+
103
+ context 'BasicAuthSSL', skip: ENV['winrm_cert'].nil? do
104
+ let(:basic_auth_only) { true }
105
+ let(:auth_type) { :ssl }
106
+ let(:no_ssl_peer_verification) { true }
107
+
108
+ it_behaves_like 'a valid_connection'
109
+ end
110
+
111
+ context 'Negotiate over SSL', skip: ENV['winrm_cert'].nil? do
112
+ let(:auth_type) { :ssl }
113
+ let(:no_ssl_peer_verification) { true }
114
+
115
+ it_behaves_like 'a valid_connection'
116
+ end
117
+
118
+ context 'SSL fingerprint', skip: ENV['winrm_cert'].nil? do
119
+ let(:auth_type) { :ssl }
120
+ let(:ssl_peer_fingerprint) { ENV['winrm_cert'] }
121
+
122
+ it_behaves_like 'a valid_connection'
123
+ end
124
+ end
data/winrm.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.required_ruby_version = '>= 1.9.0'
29
29
  s.add_runtime_dependency 'gssapi', '~> 1.2'
30
30
  s.add_runtime_dependency 'httpclient', '~> 2.2', '>= 2.2.0.2'
31
- s.add_runtime_dependency 'rubyntlm', '~> 0.5.0', '>= 0.5.3'
31
+ s.add_runtime_dependency 'rubyntlm', '~> 0.6.0'
32
32
  s.add_runtime_dependency 'logging', ['>= 1.6.1', '< 3.0']
33
33
  s.add_runtime_dependency 'nori', '~> 2.0'
34
34
  s.add_runtime_dependency 'gyoku', '~> 1.0'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: winrm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Wanek
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-02-08 00:00:00.000000000 Z
12
+ date: 2016-02-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: gssapi
@@ -51,20 +51,14 @@ dependencies:
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.5.0
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- version: 0.5.3
54
+ version: 0.6.0
58
55
  type: :runtime
59
56
  prerelease: false
60
57
  version_requirements: !ruby/object:Gem::Requirement
61
58
  requirements:
62
59
  - - "~>"
63
60
  - !ruby/object:Gem::Version
64
- version: 0.5.0
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: 0.5.3
61
+ version: 0.6.0
68
62
  - !ruby/object:Gem::Dependency
69
63
  name: logging
70
64
  requirement: !ruby/object:Gem::Requirement
@@ -224,12 +218,14 @@ files:
224
218
  - spec/command_executor_spec.rb
225
219
  - spec/config-example.yml
226
220
  - spec/exception_spec.rb
221
+ - spec/issue_184_spec.rb
227
222
  - spec/issue_59_spec.rb
228
223
  - spec/matchers.rb
229
224
  - spec/output_spec.rb
230
225
  - spec/powershell_spec.rb
231
226
  - spec/response_handler_spec.rb
232
227
  - spec/spec_helper.rb
228
+ - spec/stubs/responses/get_command_output_response.xml.erb
233
229
  - spec/stubs/responses/open_shell_v1.xml
234
230
  - spec/stubs/responses/open_shell_v2.xml
235
231
  - spec/stubs/responses/soap_fault_v1.xml