winrm 1.2.0 → 1.3.0.dev.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2c3acfe714f1a55013dc15af581c52eb75c1ae0e
4
- data.tar.gz: df2ca6f9c29b249058aebb111f1c42631a033294
3
+ metadata.gz: 6e51474e140d25ef1680a4c431b1bcdee6185fd0
4
+ data.tar.gz: 6c1cb088c144cf90125121f25727f277d0f7a3f2
5
5
  SHA512:
6
- metadata.gz: 76440fae9da8a3b989afee26d092e6cf1ae7c2cc9824ae87c3b4aa6d4826f5b88193af75ba29610fcb819e0447278cd108218b0d96d3b35f5db0a8799e597345
7
- data.tar.gz: 314d60dd2cecd17d2f306b90c4ec30be04a2bd7957cbb09e057082fa7376c22296e35056baf6c7f8dbfeb3ab9c78f48f477aa5a4547a3a5ab65275e05de6192c
6
+ metadata.gz: 7dc6b9dfa831f92c35488bef5cdf25e54e44289b20b1c61fcd7bafffea6cb361f98bb05fa8a7022ba74f9b1c903fa7e0ec95cf54ad053017ea9f80b27824e769
7
+ data.tar.gz: 00000ad60eaf4ea00f4c55814c74af0270dc80840e7eaf3c2b3e89c9e70005678121895a4274d4acd51552ca4b2fdeeb27b88aa4dc8beee0f9e207b36f463890
data/README.md CHANGED
@@ -8,6 +8,9 @@ not limitted to, running batch scripts, powershell scripts and fetching WMI
8
8
  variables. For more information on WinRM, please visit Microsoft's WinRM
9
9
  site: http://msdn.microsoft.com/en-us/library/aa384426(v=VS.85).aspx
10
10
 
11
+ ## Supported WinRM Versions
12
+ WinRM 1.1 is supported, however 2.0 and higher is recommended. [See MSDN](http://technet.microsoft.com/en-us/library/ff520073(v=ws.10).aspx) for information about WinRM versions and supported operating systems.
13
+
11
14
  ## Install
12
15
  `gem install -r winrm` then on the server `winrm quickconfig` as admin
13
16
 
@@ -128,18 +131,14 @@ To run the integration tests you will need a Windows box with the WinRM service
128
131
  2. Copy the config-example.yml to config.yml - edit this file with your WinRM connection details.
129
132
  3. Run `bundle exec rake integration`
130
133
 
131
- ## My Info
134
+ ## WinRM Author
132
135
  * Twitter: [@zentourist](https://twitter.com/zentourist)
133
136
  * BLOG: [http://distributed-frostbite.blogspot.com/](http://distributed-frostbite.blogspot.com/)
134
137
  * Add me in LinkedIn: [http://www.linkedin.com/in/danwanek](http://www.linkedin.com/in/danwanek)
135
138
  * Find me on irc.freenode.net in #ruby-lang (zenChild)
136
139
 
137
- ## Contributors
138
- Many thanks to the following for their many patches....
139
- * Seth Chisamore (https://github.com/schisamo)
140
+ ## Maintainers
140
141
  * Paul Morton (https://github.com/pmorton)
142
+ * Shawn Neal (https://github.com/sneal)
141
143
 
142
- ## Disclaimer
143
- If you see something that could be done better or would like to help out in the development of this code please feel free to clone the repository and send me patches.
144
-
145
- `git clone git://github.com/WinRb/WinRM.git` or add an [issue](https://github.com/WinRb/WinRM/issues) on GitHub
144
+ [Contributors](https://github.com/WinRb/WinRM/graphs/contributors)
data/Rakefile CHANGED
@@ -18,6 +18,7 @@ end
18
18
  RSpec::Core::RakeTask.new(:integration) do |task|
19
19
  task.pattern = "test/spec/*_spec.rb"
20
20
  task.rspec_opts = [ '--color', '-f documentation' ]
21
+ task.rspec_opts << '-tintegration'
21
22
  end
22
23
 
23
- task :default => "spec"
24
+ task :default => "spec"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.0
1
+ 1.3.0.dev.1
data/bin/rwinrm ADDED
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright 2014 Shawn Neal <sneal@sneal.net>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ $:.push File.expand_path('../../lib', __FILE__)
18
+
19
+ require 'readline'
20
+ require 'optparse'
21
+ require 'winrm'
22
+
23
+ def parse_options
24
+ options = {}
25
+ optparse = OptionParser.new do |opts|
26
+ opts.banner = "Usage: rwinrm endpoint [options]"
27
+
28
+ options[:auth_type] = :plaintext
29
+ options[:basic_auth_only] = true
30
+ options[:endpoint] = ARGV[0]
31
+
32
+ opts.on('-u', '--user username', String, 'WinRM user name') do |v|
33
+ options[:user] = v
34
+ end
35
+
36
+ opts.on('-p', '--pass password', String, 'WinRM user password') do |v|
37
+ options[:pass] = v
38
+ end
39
+
40
+ opts.on('-h', '--help', 'Display this screen') do
41
+ puts opts
42
+ exit
43
+ end
44
+ end
45
+
46
+ optparse.parse!
47
+ raise OptionParser::MissingArgument.new(:endpoint) if options[:endpoint].nil?
48
+
49
+ options
50
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument
51
+ puts $!.message
52
+ puts optparse
53
+ exit 1
54
+ end
55
+
56
+ def repl(options)
57
+ client = WinRM::WinRMWebService.new(
58
+ options[:endpoint],
59
+ options[:auth_type].to_sym,
60
+ options)
61
+
62
+ shell_id = client.open_shell()
63
+ command_id = client.run_command(shell_id, 'cmd')
64
+
65
+ read_thread = Thread.new do
66
+ client.get_command_output(shell_id, command_id) do |stdout, stderr|
67
+ STDOUT.write stdout
68
+ STDERR.write stderr
69
+ end
70
+ end
71
+
72
+ while buf = Readline.readline('', true)
73
+ if buf =~ /^exit/
74
+ read_thread.exit()
75
+ client.cleanup_command(shell_id, command_id)
76
+ client.close_shell(shell_id)
77
+ exit 0
78
+ else
79
+ client.write_stdin(shell_id, command_id, "#{buf}\r\n")
80
+ end
81
+ end
82
+ rescue Interrupt
83
+ # ctrl-c
84
+ rescue StandardError => e
85
+ puts e.message
86
+ exit 1
87
+ end
88
+
89
+ repl(parse_options())
@@ -1,34 +1,44 @@
1
- =begin
2
- This file is part of WinRM; the Ruby library for Microsoft WinRM.
3
-
4
- Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
5
-
6
- Licensed under the Apache License, Version 2.0 (the "License");
7
- you may not use this file except in compliance with the License.
8
- You may obtain a copy of the License at
9
-
10
- http://www.apache.org/licenses/LICENSE-2.0
11
-
12
- Unless required by applicable law or agreed to in writing, software
13
- distributed under the License is distributed on an "AS IS" BASIS,
14
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- See the License for the specific language governing permissions and
16
- limitations under the License.
17
- =end
1
+ # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
18
14
 
19
15
  module WinRM
20
- # Generic WinRM SOAP Error
21
- class WinRMWebServiceError < StandardError
22
- end
16
+
17
+ # WinRM base class for errors
18
+ class WinRMError < StandardError; end
23
19
 
24
20
  # Authorization Error
25
- class WinRMAuthorizationError < StandardError
26
- end
21
+ class WinRMAuthorizationError < WinRMError; end
27
22
 
28
23
  # A Fault returned in the SOAP response. The XML node is a WSManFault
29
- class WinRMWSManFault < StandardError; end
24
+ class WinRMWSManFault < WinRMError
25
+ attr_reader :fault_code
26
+ attr_reader :fault_description
27
+
28
+ def initialize(fault_description, fault_code)
29
+ @fault_description = fault_description
30
+ @fault_code = fault_code
31
+ super("[WSMAN ERROR CODE: #{fault_code}]: #{fault_description}")
32
+ end
33
+ end
30
34
 
31
- # Bad HTTP Transport
32
- class WinRMHTTPTransportError < StandardError; end
33
- end # WinRM
35
+ # non-200 response without a SOAP fault
36
+ class WinRMHTTPTransportError < WinRMError
37
+ attr_reader :status_code
34
38
 
39
+ def initialize(msg, status_code)
40
+ @status_code = status_code
41
+ super(msg + " (#{status_code}).")
42
+ end
43
+ end
44
+ end
@@ -1,20 +1,16 @@
1
- =begin
2
- This file is part of WinRM; the Ruby library for Microsoft WinRM.
3
-
4
- Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
5
-
6
- Licensed under the Apache License, Version 2.0 (the "License");
7
- you may not use this file except in compliance with the License.
8
- You may obtain a copy of the License at
9
-
10
- http://www.apache.org/licenses/LICENSE-2.0
11
-
12
- Unless required by applicable law or agreed to in writing, software
13
- distributed under the License is distributed on an "AS IS" BASIS,
14
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- See the License for the specific language governing permissions and
16
- limitations under the License.
17
- =end
1
+ # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
18
14
 
19
15
  # Format an ISO8601 Duration
20
16
  module Iso8601Duration
@@ -0,0 +1,70 @@
1
+ # Copyright 2014 Shawn Neal <sneal@sneal.net>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'rexml/document'
16
+
17
+ module WinRM
18
+ # Handles the raw WinRM HTTP response. Returns the body as an XML doc
19
+ # or raises the appropriate WinRM error if the response is an error.
20
+ class ResponseHandler
21
+
22
+ # @param [String] The raw unparsed response body, if any
23
+ # @param [Integer] The HTTP response status code
24
+ def initialize(response_body, status_code)
25
+ @response_body = response_body
26
+ @status_code = status_code
27
+ end
28
+
29
+ # Processes the response from the WinRM service and either returns an XML
30
+ # doc or raises an appropriate error.
31
+ #
32
+ # @returns [REXML::Document] The parsed response body
33
+ def parse_to_xml()
34
+ raise_if_error()
35
+ response_xml
36
+ end
37
+
38
+ private
39
+
40
+ def response_xml()
41
+ @response_xml ||= REXML::Document.new(@response_body)
42
+ rescue REXML::ParseException => e
43
+ raise WinRMHTTPTransportError.new("Unable to parse WinRM response: #{e.message}", @status_code)
44
+ end
45
+
46
+ def raise_if_error()
47
+ return if @status_code == 200
48
+ raise_if_auth_error()
49
+ raise_if_soap_fault()
50
+ raise_transport_error()
51
+ end
52
+
53
+ def raise_if_auth_error()
54
+ raise WinRMAuthorizationError.new if @status_code == 401
55
+ end
56
+
57
+ def raise_if_soap_fault()
58
+ soap_errors = REXML::XPath.match(response_xml, "//#{NS_SOAP_ENV}:Body/#{NS_SOAP_ENV}:Fault/*")
59
+ if !soap_errors.empty?
60
+ fault = REXML::XPath.first(soap_errors, "//#{NS_WSMAN_FAULT}:WSManFault")
61
+ raise WinRMWSManFault.new(fault.to_s, fault.attributes['Code'])
62
+ end
63
+ end
64
+
65
+ def raise_transport_error()
66
+ raise WinRMHTTPTransportError.new('Bad HTTP response returned from server', @status_code)
67
+ end
68
+
69
+ end
70
+ end
@@ -1,20 +1,18 @@
1
- =begin
2
- This file is part of WinRM; the Ruby library for Microsoft WinRM.
3
-
4
- Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
5
-
6
- Licensed under the Apache License, Version 2.0 (the "License");
7
- you may not use this file except in compliance with the License.
8
- You may obtain a copy of the License at
9
-
10
- http://www.apache.org/licenses/LICENSE-2.0
11
-
12
- Unless required by applicable law or agreed to in writing, software
13
- distributed under the License is distributed on an "AS IS" BASIS,
14
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- See the License for the specific language governing permissions and
16
- limitations under the License.
17
- =end
1
+ # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative 'response_handler'
18
16
 
19
17
  module WinRM
20
18
  module HTTP
@@ -33,20 +31,18 @@ module WinRM
33
31
  @logger = Logging.logger[self]
34
32
  end
35
33
 
34
+ # Sends the SOAP payload to the WinRM service and returns the service's
35
+ # SOAP response. If an error occurrs an appropriate error is raised.
36
+ #
37
+ # @param [String] The XML SOAP message
38
+ # @returns [REXML::Document] The parsed response body
36
39
  def send_request(message)
40
+ log_soap_message(message)
37
41
  hdr = {'Content-Type' => 'application/soap+xml;charset=UTF-8', 'Content-Length' => message.length}
38
42
  resp = @httpcli.post(@endpoint, message, hdr)
39
- if(resp.status == 200)
40
- # Version 1.1 of WinRM adds the namespaces in the document instead of the envelope so we have to
41
- # add them ourselves here. This should have no affect version 2.
42
- doc = Nokogiri::XML(resp.http_body.content)
43
- doc.collect_namespaces.each_pair do |k,v|
44
- doc.root.add_namespace((k.split(/:/).last),v) unless doc.namespaces.has_key?(k)
45
- end
46
- return doc
47
- else
48
- raise WinRMHTTPTransportError, "Bad HTTP response returned from server (#{resp.status})."
49
- end
43
+ log_soap_message(resp.http_body.content)
44
+ handler = WinRM::ResponseHandler.new(resp.http_body.content, resp.status)
45
+ handler.parse_to_xml()
50
46
  end
51
47
 
52
48
  # We'll need this to force basic authentication if desired
@@ -66,6 +62,19 @@ module WinRM
66
62
  @httpcli.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
67
63
  end
68
64
 
65
+ protected
66
+
67
+ def log_soap_message(message)
68
+ return unless @logger.debug?
69
+
70
+ xml_msg = REXML::Document.new(message)
71
+ formatter = REXML::Formatters::Pretty.new(2)
72
+ formatter.compact = true
73
+ formatter.write(xml_msg, @logger)
74
+ @logger.debug("\n")
75
+ rescue StandardError => e
76
+ @logger.debug("Couldn't log SOAP request/response: #{e.message} - #{message}")
77
+ end
69
78
  end
70
79
 
71
80
 
@@ -100,20 +109,42 @@ module WinRM
100
109
  def initialize(endpoint, realm, service = nil, keytab = nil, opts)
101
110
  super(endpoint)
102
111
  # Remove the GSSAPI auth from HTTPClient because we are doing our own thing
103
- auths = @httpcli.www_auth.instance_variable_get('@authenticator')
104
- auths.delete_if {|i| i.is_a?(HTTPClient::SSPINegotiateAuth)}
112
+ no_sspi_auth!
105
113
  service ||= 'HTTP'
106
114
  @service = "#{service}/#{@endpoint.host}@#{realm}"
107
115
  init_krb
108
116
  end
109
117
 
110
- def set_auth(user,pass)
111
- # raise Error
118
+ # Sends the SOAP payload to the WinRM service and returns the service's
119
+ # SOAP response. If an error occurrs an appropriate error is raised.
120
+ #
121
+ # @param [String] The XML SOAP message
122
+ # @returns [REXML::Document] The parsed response body
123
+ def send_request(message)
124
+ resp = send_kerberos_request(message)
125
+
126
+ if resp.status == 401
127
+ @logger.debug "Got 401 - reinitializing Kerberos and retrying one more time"
128
+ init_krb
129
+ resp = send_kerberos_request(message)
130
+ end
131
+
132
+ handler = WinRM::ResponseHandler.new(winrm_decrypt(resp.http_body.content), resp.status)
133
+ handler.parse_to_xml()
112
134
  end
113
135
 
114
- def send_request(msg)
115
- original_length = msg.length
116
- pad_len, emsg = winrm_encrypt(msg)
136
+
137
+ private
138
+
139
+ # Sends the SOAP payload to the WinRM service and returns the service's
140
+ # HTTP response.
141
+ #
142
+ # @param [String] The XML SOAP message
143
+ # @returns [Object] The HTTP response object
144
+ def send_kerberos_request(message)
145
+ log_soap_message(message)
146
+ original_length = message.length
147
+ pad_len, emsg = winrm_encrypt(message)
117
148
  hdr = {
118
149
  "Connection" => "Keep-Alive",
119
150
  "Content-Type" => "multipart/encrypted;protocol=\"application/HTTP-Kerberos-session-encrypted\";boundary=\"Encrypted Boundary\""
@@ -128,15 +159,11 @@ Content-Type: application/octet-stream\r
128
159
  #{emsg}--Encrypted Boundary\r
129
160
  EOF
130
161
 
131
- r = @httpcli.post(@endpoint, body, hdr)
132
-
133
- winrm_decrypt(r.http_body.content)
162
+ resp = @httpcli.post(@endpoint, body, hdr)
163
+ log_soap_message(resp.http_body.content)
164
+ resp
134
165
  end
135
166
 
136
-
137
- private
138
-
139
-
140
167
  def init_krb
141
168
  @logger.debug "Initializing Kerberos for #{@service}"
142
169
  @gsscli = GSSAPI::Simple.new(@endpoint.host, @service)
@@ -218,7 +245,7 @@ Content-Type: application/octet-stream\r
218
245
 
219
246
  @logger.debug "SOAP message decrypted (MAJ: #{maj_stat}, MIN: #{min_stat.read_int}):\n#{iov1[:buffer].value}"
220
247
 
221
- Nokogiri::XML(iov1[:buffer].value)
248
+ iov1[:buffer].value
222
249
  end
223
250
 
224
251
  end
@@ -1,26 +1,22 @@
1
- =begin
2
- This file is part of WinRM; the Ruby library for Microsoft WinRM.
3
-
4
- Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
5
-
6
- Licensed under the Apache License, Version 2.0 (the "License");
7
- you may not use this file except in compliance with the License.
8
- You may obtain a copy of the License at
9
-
10
- http://www.apache.org/licenses/LICENSE-2.0
11
-
12
- Unless required by applicable law or agreed to in writing, software
13
- distributed under the License is distributed on an "AS IS" BASIS,
14
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- See the License for the specific language governing permissions and
16
- limitations under the License.
17
- =end
1
+ # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
18
14
 
19
15
  require 'httpclient'
20
- require 'savon'
16
+ require 'builder'
17
+ require 'gyoku'
21
18
  require 'uuidtools'
22
19
  require 'base64'
23
- require 'nokogiri'
24
20
 
25
21
  module WinRM
26
22
  NS_SOAP_ENV ='s' # http://www.w3.org/2003/05/soap-envelope