winrm 1.5.0 → 1.6.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: ad05b5c1fe16cfa1586a6a001022d0463e87d9cb
4
- data.tar.gz: 07a2d85d2a0a9458049eaac2b23bbd68e518ae2d
3
+ metadata.gz: bc4d15623138a5d39c308936ad8e6738621317c0
4
+ data.tar.gz: 64064ba319d3d5d856ba346a383823ccd0054f40
5
5
  SHA512:
6
- metadata.gz: c2181da8210604d6703c2f694c3388e9e8f670fba9673a3fac68b1b9d257e2f0e3412f810b3c94966a57e7035e1d1b153ee815a7b993694c71885925d6bff09e
7
- data.tar.gz: 47d0c26bfab5a7f096091bf74f042ded6a25fe59683c83829907863176f5bbb337bb6b972fcc4e2d3d5977c150c65c81413c6044a23b75787b82ae8738e1fb38
6
+ metadata.gz: dab6833383b786a008734ef928a4f7ed2346b912f6e342fde139053053bede56296e097f17726e801b5ef7d0023ba9ee8b373ff7b5d545e78564c6ff13900f94
7
+ data.tar.gz: 5c7c43892944ed073c2cc66a6bf590e4164e052c6b6d596dec684408dd11594d3da8ea275f39d724d197571ce746ce65378a2e1f9a6394bae4955f4c09ed1efd
@@ -1,7 +1,9 @@
1
1
  AllCops:
2
2
  Exclude:
3
3
  - 'appveyor.yml'
4
+ - 'scripts/**/*'
4
5
  - 'lib/winrm/winrm_service.rb'
6
+ - 'lib/winrm/http/transport.rb'
5
7
 
6
8
  Style/Encoding:
7
9
  Enabled: true
@@ -5,3 +5,8 @@ rvm:
5
5
  - 2.1.0
6
6
  before_install:
7
7
  - gem update bundler
8
+
9
+ # This prevents testing branches that are created just for PRs
10
+ branches:
11
+ only:
12
+ - master
data/README.md CHANGED
@@ -38,7 +38,13 @@ As of version 1.5.0 `WinRM::WinRMWebService` methods `cmd`, `run_cmd`, `powershe
38
38
 
39
39
  Use the `run_cmd` and `run_powershell_script` of the `WinRM::CommandExecutor` class instead. The `CommandExecutor` allows multiple commands to be run from the same WinRM shell providing a significant performance improvement when issuing multiple calls.
40
40
 
41
+ #### NTLM/Negotiate
42
+ ```ruby
43
+ winrm = WinRM::WinRMWebService.new(endpoint, :negotiate, :user => myuser, :pass => mypass)
44
+ ```
45
+
41
46
  #### Plaintext
47
+ Note: It is strongly recommended that you use `:negotiate` instead of `:plaintext`. As the name infers, the `:plaintext` transport includes authentication credentials in plain text.
42
48
  ```ruby
43
49
  WinRM::WinRMWebService.new(endpoint, :plaintext, :user => myuser, :pass => mypass, :disable_sspi => true)
44
50
 
@@ -99,7 +105,7 @@ The `WinRMWebService` exposes a `logger` attribute and uses the [logging](https:
99
105
  winrm = WinRM::WinRMWebService.new(endpoint, :ssl, :user => myuser, :pass => mypass)
100
106
 
101
107
  # suppress warnings
102
- winrm.logger.warn = :error
108
+ winrm.logger.level = :error
103
109
 
104
110
  # Log to a file
105
111
  winrm.logger.add_appenders(Logging.appenders.file('error.log'))
data/Rakefile CHANGED
@@ -8,6 +8,14 @@ require 'bundler/gem_tasks'
8
8
  # Change to the directory of this file.
9
9
  Dir.chdir(File.expand_path('../', __FILE__))
10
10
 
11
+ desc 'Open a Pry console for this library'
12
+ task :console do
13
+ require 'pry'
14
+ require 'winrm'
15
+ ARGV.clear
16
+ Pry.start
17
+ end
18
+
11
19
  RSpec::Core::RakeTask.new(:spec) do |task|
12
20
  task.pattern = 'spec/*_spec.rb'
13
21
  task.rspec_opts = ['--color', '-f documentation']
@@ -1,5 +1,11 @@
1
1
  # WinRM Gem Changelog
2
2
 
3
+ # 1.6.0
4
+ - Adding `:negotiate` transport providing NTLM/Negotiate encryption of WinRM requests and responses
5
+ - Removed dependency on UUIDTools gem
6
+ - Extending accepted error codes for retry behavior to include `Errno::ETIMEDOUT`
7
+ - Correct deprecation warning for WinRMWebService.run_powershell_script
8
+
3
9
  # 1.5.0
4
10
  - Deprecating `WinRM::WinRMWebService` methods `cmd`, `run_cmd`, `powershell`, and `run_powershell_script` in favor of the `run_cmd` and `run_powershell_script` methods of the `WinRM::CommandExecutor` class. The `CommandExecutor` allows multiple commands to be run from the same WinRM shell providing a significant performance improvement when issuing multiple calls.
5
11
  - Added an `:ssl_peer_fingerprint` option to be used instead of `:no_ssl_peer_verification` and allows a specific certificate to be verified.
@@ -226,7 +226,7 @@ module WinRM
226
226
 
227
227
  RESCUE_EXCEPTIONS_ON_ESTABLISH = lambda do
228
228
  [
229
- Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED,
229
+ Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
230
230
  Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH,
231
231
  ::WinRM::WinRMHTTPTransportError, ::WinRM::WinRMAuthorizationError,
232
232
  HTTPClient::KeepAliveDisconnected,
@@ -128,6 +128,7 @@ module WinRM
128
128
  end
129
129
  end
130
130
 
131
+
131
132
  # Plain text, insecure, HTTP transport
132
133
  class HttpPlaintext < HttpTransport
133
134
  def initialize(endpoint, user, pass, opts)
@@ -139,6 +140,89 @@ module WinRM
139
140
  end
140
141
  end
141
142
 
143
+
144
+ # NTLM/Negotiate, secure, HTTP transport
145
+ class HttpNegotiate < HttpTransport
146
+ def initialize(endpoint, user, pass, opts)
147
+ super(endpoint)
148
+ no_sspi_auth!
149
+
150
+ user_parts = user.split('\\')
151
+ if(user_parts.length > 1)
152
+ opts[:domain] = user_parts[0]
153
+ user = user_parts[1]
154
+ end
155
+
156
+ @ntlmcli = Net::NTLM::Client.new(user, pass, opts)
157
+ @retryable = true
158
+ no_ssl_peer_verification! if opts[:no_ssl_peer_verification]
159
+ end
160
+
161
+ def send_request(message, auth_header = nil)
162
+ auth_header = init_auth if @ntlmcli.session.nil?
163
+
164
+ original_length = message.length
165
+
166
+ emessage = @ntlmcli.session.seal_message message
167
+ signature = @ntlmcli.session.sign_message message
168
+ seal = "\x10\x00\x00\x00#{signature}#{emessage}"
169
+
170
+ hdr = {
171
+ "Content-Type" => "multipart/encrypted;protocol=\"application/HTTP-SPNEGO-session-encrypted\";boundary=\"Encrypted Boundary\""
172
+ }
173
+ hdr.merge!(auth_header) if auth_header
174
+
175
+ body = [
176
+ "--Encrypted Boundary",
177
+ "Content-Type: application/HTTP-SPNEGO-session-encrypted",
178
+ "OriginalContent: type=application/soap+xml;charset=UTF-8;Length=#{original_length}",
179
+ "--Encrypted Boundary",
180
+ "Content-Type: application/octet-stream",
181
+ "#{seal}--Encrypted Boundary--",
182
+ ""
183
+ ].join("\r\n")
184
+
185
+ resp = @httpcli.post(@endpoint, body, hdr)
186
+ if resp.status == 401 && @retryable
187
+ @retryable = false
188
+ send_request(message, init_auth)
189
+ else
190
+ @retryable = true
191
+ decrypted_body = resp.body.empty? ? '' : winrm_decrypt(resp.body)
192
+ handler = WinRM::ResponseHandler.new(decrypted_body, resp.status)
193
+ handler.parse_to_xml()
194
+ end
195
+ end
196
+
197
+ private
198
+
199
+ def winrm_decrypt(str)
200
+ str.force_encoding('BINARY')
201
+ str.sub!(/^.*Content-Type: application\/octet-stream\r\n(.*)--Encrypted.*$/m, '\1')
202
+
203
+ signature = str[4..19]
204
+ message = @ntlmcli.session.unseal_message str[20..-1]
205
+ if @ntlmcli.session.verify_signature(signature, message)
206
+ message
207
+ else
208
+ raise WinRMWebServiceError, "Could not verify SOAP message."
209
+ end
210
+ end
211
+
212
+ def init_auth
213
+ @logger.debug "Initializing Negotiate for #{@service}"
214
+ auth1 = @ntlmcli.init_context
215
+ hdr = {"Authorization" => "Negotiate #{auth1.encode64}",
216
+ "Content-Type" => "application/soap+xml;charset=UTF-8"
217
+ }
218
+ @logger.debug "Sending HTTP POST for Negotiate Authentication"
219
+ r = @httpcli.post(@endpoint, "", hdr)
220
+ itok = r.header["WWW-Authenticate"].pop.split.last
221
+ auth3 = @ntlmcli.init_context itok
222
+ { "Authorization" => "Negotiate #{auth3.encode64}" }
223
+ end
224
+ end
225
+
142
226
  # Uses SSL to secure the transport
143
227
  class HttpSSL < HttpTransport
144
228
  def initialize(endpoint, user, pass, ca_trust_path = nil, opts)
@@ -209,15 +293,15 @@ module WinRM
209
293
  'protocol="application/HTTP-Kerberos-session-encrypted";' \
210
294
  'boundary="Encrypted Boundary"'
211
295
  }
212
-
213
- body = <<-EOF
214
- --Encrypted Boundary\r
215
- Content-Type: application/HTTP-Kerberos-session-encrypted\r
216
- OriginalContent: type=application/soap+xml;charset=UTF-8;Length=#{original_length + pad_len}\r
217
- --Encrypted Boundary\r
218
- Content-Type: application/octet-stream\r
219
- #{emsg}--Encrypted Boundary\r
220
- EOF
296
+ body = [
297
+ "--Encrypted Boundary",
298
+ "Content-Type: application/HTTP-Kerberos-session-encrypted",
299
+ "OriginalContent: type=application/soap+xml;charset=UTF-8;Length=#{original_length + pad_len}",
300
+ "--Encrypted Boundary",
301
+ "Content-Type: application/octet-stream",
302
+ "#{emsg}--Encrypted Boundary--",
303
+ ""
304
+ ].join("\r\n")
221
305
 
222
306
  resp = @httpcli.post(@endpoint, body, hdr)
223
307
  log_soap_message(resp.http_body.content)
@@ -17,7 +17,6 @@
17
17
  require 'httpclient'
18
18
  require 'builder'
19
19
  require 'gyoku'
20
- require 'uuidtools'
21
20
  require 'base64'
22
21
 
23
22
  # SOAP constants for WinRM
@@ -3,5 +3,5 @@
3
3
  # WinRM module
4
4
  module WinRM
5
5
  # The version of the WinRM library
6
- VERSION = '1.5.0'
6
+ VERSION = '1.6.0'
7
7
  end
@@ -16,6 +16,7 @@
16
16
 
17
17
  require 'nori'
18
18
  require 'rexml/document'
19
+ require 'securerandom'
19
20
  require 'winrm/command_executor'
20
21
  require_relative 'helpers/powershell_script'
21
22
 
@@ -36,6 +37,7 @@ module WinRM
36
37
  # @param [Hash] opts Misc opts for the various transports.
37
38
  # @see WinRM::HTTP::HttpTransport
38
39
  # @see WinRM::HTTP::HttpGSSAPI
40
+ # @see WinRM::HTTP::HttpNegotiate
39
41
  # @see WinRM::HTTP::HttpSSL
40
42
  def initialize(endpoint, transport = :kerberos, opts = {})
41
43
  @endpoint = endpoint
@@ -44,20 +46,32 @@ module WinRM
44
46
  @locale = DEFAULT_LOCALE
45
47
  setup_logger
46
48
  configure_retries(opts)
47
- case transport
48
- when :kerberos
49
- require 'gssapi'
50
- require 'gssapi/extensions'
51
- @xfer = HTTP::HttpGSSAPI.new(endpoint, opts[:realm], opts[:service], opts[:keytab], opts)
52
- when :plaintext
53
- @xfer = HTTP::HttpPlaintext.new(endpoint, opts[:user], opts[:pass], opts)
54
- when :ssl
55
- @xfer = HTTP::HttpSSL.new(endpoint, opts[:user], opts[:pass], opts[:ca_trust_path], opts)
56
- else
57
- raise "Invalid transport '#{transport}' specified, expected: kerberos, plaintext, ssl."
49
+ begin
50
+ @xfer = send "init_#{transport}_transport", opts.merge({endpoint: endpoint})
51
+ rescue NoMethodError => e
52
+ raise "Invalid transport '#{transport}' specified, expected: negotiate, kerberos, plaintext, ssl."
58
53
  end
59
54
  end
60
55
 
56
+ def init_negotiate_transport(opts)
57
+ require 'rubyntlm'
58
+ HTTP::HttpNegotiate.new(opts[:endpoint], opts[:user], opts[:pass], opts)
59
+ end
60
+
61
+ def init_kerberos_transport(opts)
62
+ require 'gssapi'
63
+ require 'gssapi/extensions'
64
+ HTTP::HttpGSSAPI.new(opts[:endpoint], opts[:realm], opts[:service], opts[:keytab], opts)
65
+ end
66
+
67
+ def init_plaintext_transport(opts)
68
+ HTTP::HttpPlaintext.new(opts[:endpoint], opts[:user], opts[:pass], opts)
69
+ end
70
+
71
+ def init_ssl_transport(opts)
72
+ HTTP::HttpSSL.new(opts[:endpoint], opts[:user], opts[:pass], opts[:ca_trust_path], opts)
73
+ end
74
+
61
75
  # Operation timeout.
62
76
  #
63
77
  # Unless specified the client receive timeout will be 10s + the operation
@@ -319,7 +333,7 @@ module WinRM
319
333
  # @param [String] an existing and open shell id to reuse
320
334
  # @return [Hash] :stdout and :stderr
321
335
  def run_powershell_script(script_file, &block)
322
- logger.warn("WinRM::WinRMWebService#run_powershell_script is deprecated. Use WinRM::CommandExecutor#run_cmd instead")
336
+ logger.warn("WinRM::WinRMWebService#run_powershell_script is deprecated. Use WinRM::CommandExecutor#run_powershell_script instead")
323
337
  create_executor do |executor|
324
338
  executor.run_powershell_script(script_file, &block)
325
339
  end
@@ -427,7 +441,7 @@ module WinRM
427
441
  "#{NS_ADDRESSING}:Address" => 'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous',
428
442
  :attributes! => {"#{NS_ADDRESSING}:Address" => {'mustUnderstand' => true}}},
429
443
  "#{NS_WSMAN_DMTF}:MaxEnvelopeSize" => @max_env_sz,
430
- "#{NS_ADDRESSING}:MessageID" => "uuid:#{UUIDTools::UUID.random_create.to_s.upcase}",
444
+ "#{NS_ADDRESSING}:MessageID" => "uuid:#{SecureRandom.uuid.to_s.upcase}",
431
445
  "#{NS_WSMAN_DMTF}:Locale/" => '',
432
446
  "#{NS_WSMAN_MSFT}:DataLocale/" => '',
433
447
  "#{NS_WSMAN_DMTF}:OperationTimeout" => @timeout,
@@ -459,6 +473,7 @@ module WinRM
459
473
  # another Receive request.
460
474
  # http://msdn.microsoft.com/en-us/library/cc251676.aspx
461
475
  if e.fault_code == '2150858793'
476
+ logger.debug("[WinRM] retrying receive request after timeout")
462
477
  retry
463
478
  else
464
479
  raise
@@ -5,11 +5,19 @@ describe 'issue 59', integration: true do
5
5
  end
6
6
 
7
7
  describe 'long running script without output' do
8
+ let(:logged_output) { StringIO.new }
9
+ let(:logger) { Logging.logger(logged_output) }
10
+
8
11
  it 'should not error' do
9
- out = @winrm.powershell('$ProgressPreference="SilentlyContinue";sleep 60; Write-Host "Hello"')
12
+ @winrm.set_timeout(1)
13
+ @winrm.logger = logger
14
+
15
+ out = @winrm.powershell('$ProgressPreference="SilentlyContinue";sleep 3; Write-Host "Hello"')
16
+
10
17
  expect(out).to have_exit_code 0
11
18
  expect(out).to have_stdout_match(/Hello/)
12
19
  expect(out).to have_no_stderr
20
+ expect(logged_output.string).to match(/retrying receive request/)
13
21
  end
14
22
  end
15
23
  end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+ require 'rubyntlm'
3
+ require 'winrm/http/transport'
4
+
5
+ describe WinRM::HTTP::HttpNegotiate, unit: true do
6
+ describe '#init' do
7
+ let(:endpoint) { 'some_endpoint' }
8
+ let(:domain) { 'some_domain' }
9
+ let(:user) { 'some_user' }
10
+ let(:password) { 'some_password' }
11
+ let(:options) { {} }
12
+
13
+ context 'user is not domain prefixed' do
14
+ it 'does not pass a domain to the NTLM client' do
15
+ expect(Net::NTLM::Client).to receive(:new).with(user, password, options)
16
+ WinRM::HTTP::HttpNegotiate.new(endpoint, user, password, options)
17
+ end
18
+ end
19
+
20
+ context 'user is domain prefixed' do
21
+ it 'passes prefixed domain to the NTLM client' do
22
+ expect(Net::NTLM::Client).to receive(:new) do |passed_user, passed_password, passed_options|
23
+ expect(passed_user).to eq user
24
+ expect(passed_password).to eq password
25
+ expect(passed_options[:domain]).to eq domain
26
+ end
27
+ WinRM::HTTP::HttpNegotiate.new(endpoint, "#{domain}\\#{user}", password, options)
28
+ end
29
+ end
30
+
31
+ context 'option is passed with a domain' do
32
+ let(:options) { { domain: domain } }
33
+
34
+ it 'passes domain option to the NTLM client' do
35
+ expect(Net::NTLM::Client).to receive(:new) do |passed_user, passed_password, passed_options|
36
+ expect(passed_user).to eq user
37
+ expect(passed_password).to eq password
38
+ expect(passed_options[:domain]).to eq domain
39
+ end
40
+ WinRM::HTTP::HttpNegotiate.new(endpoint, user, password, options)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -28,8 +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.4.0'
32
- s.add_runtime_dependency 'uuidtools', '~> 2.1.2'
31
+ s.add_runtime_dependency 'rubyntlm', '~> 0.5.0', '>= 0.5.3'
33
32
  s.add_runtime_dependency 'logging', ['>= 1.6.1', '< 3.0']
34
33
  s.add_runtime_dependency 'nori', '~> 2.0'
35
34
  s.add_runtime_dependency 'gyoku', '~> 1.0'
@@ -37,4 +36,5 @@ Gem::Specification.new do |s|
37
36
  s.add_development_dependency 'rspec', '~> 3.2'
38
37
  s.add_development_dependency 'rake', '~> 10.3'
39
38
  s.add_development_dependency 'rubocop', '~> 0.28'
39
+ s.add_development_dependency 'pry'
40
40
  end
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.5.0
4
+ version: 1.6.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-01-11 00:00:00.000000000 Z
12
+ date: 2016-01-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: gssapi
@@ -51,28 +51,20 @@ dependencies:
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.4.0
55
- type: :runtime
56
- prerelease: false
57
- version_requirements: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: 0.4.0
62
- - !ruby/object:Gem::Dependency
63
- name: uuidtools
64
- requirement: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
54
+ version: 0.5.0
55
+ - - ">="
67
56
  - !ruby/object:Gem::Version
68
- version: 2.1.2
57
+ version: 0.5.3
69
58
  type: :runtime
70
59
  prerelease: false
71
60
  version_requirements: !ruby/object:Gem::Requirement
72
61
  requirements:
73
62
  - - "~>"
74
63
  - !ruby/object:Gem::Version
75
- version: 2.1.2
64
+ version: 0.5.0
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 0.5.3
76
68
  - !ruby/object:Gem::Dependency
77
69
  name: logging
78
70
  requirement: !ruby/object:Gem::Requirement
@@ -177,6 +169,20 @@ dependencies:
177
169
  - - "~>"
178
170
  - !ruby/object:Gem::Version
179
171
  version: '0.28'
172
+ - !ruby/object:Gem::Dependency
173
+ name: pry
174
+ requirement: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ type: :development
180
+ prerelease: false
181
+ version_requirements: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
180
186
  description: |2
181
187
  Ruby library for Windows Remote Management
182
188
  email:
@@ -229,6 +235,7 @@ files:
229
235
  - spec/stubs/responses/soap_fault_v1.xml
230
236
  - spec/stubs/responses/soap_fault_v2.xml
231
237
  - spec/stubs/responses/wmi_error_v2.xml
238
+ - spec/transport_spec.rb
232
239
  - spec/winrm_options_spec.rb
233
240
  - spec/winrm_primitives_spec.rb
234
241
  - spec/wql_spec.rb