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 +4 -4
- data/appveyor.yml +1 -10
- data/changelog.md +4 -0
- data/lib/winrm/http/transport.rb +12 -6
- data/lib/winrm/version.rb +1 -1
- data/lib/winrm/winrm_service.rb +18 -4
- data/spec/issue_184_spec.rb +67 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/stubs/responses/get_command_output_response.xml.erb +13 -0
- data/spec/transport_spec.rb +80 -0
- data/winrm.gemspec +1 -1
- metadata +6 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 334ae5a75dc6e3249c316caa13eb582aa1930239
|
4
|
+
data.tar.gz: a8b620912892adef39c78d3f89c73a0530078404
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
|
data/lib/winrm/http/transport.rb
CHANGED
@@ -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
|
-
|
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
|
228
|
-
def initialize(endpoint, user, pass,
|
234
|
+
class BasicAuthSSL < HttpTransport
|
235
|
+
def initialize(endpoint, user, pass, opts)
|
229
236
|
super(endpoint)
|
230
237
|
@httpcli.set_auth(endpoint, user, pass)
|
231
|
-
|
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
data/lib/winrm/winrm_service.rb
CHANGED
@@ -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
|
-
|
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
|
257
|
-
|
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>
|
data/spec/transport_spec.rb
CHANGED
@@ -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.
|
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.
|
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-
|
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.
|
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.
|
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
|