winrm 0.0.6 → 1.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,37 +0,0 @@
1
- #############################################################################
2
- # Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
3
- #
4
- #
5
- # This file is part of WinRM.
6
- #
7
- # WinRM is free software: you can redistribute it and/or
8
- # modify it under the terms of the GNU General Public License as published
9
- # by the Free Software Foundation, either version 3 of the License, or (at
10
- # your option) any later version.
11
- #
12
- # WinRM is distributed in the hope that it will be useful,
13
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
- # Public License for more details.
16
- #
17
- # You should have received a copy of the GNU General Public License along
18
- # with WinRM. If not, see <http://www.gnu.org/licenses/>.
19
- #############################################################################
20
-
21
- # Custom extensions to the class String
22
- class String
23
- # Change CamelCased strings to ruby_cased strings
24
- # It uses the lookahead assertion ?= In this case it basically says match
25
- # anything followed by a capital letter, but not the capital letter itself.
26
- # @see http://www.pcre.org/pcre.txt The PCRE guide for more details
27
- def ruby_case
28
- self.split(/(?=[A-Z])/).join('_').downcase
29
- end
30
-
31
- # Change a ruby_cased string to CamelCased
32
- def camel_case
33
- self.split(/_/).map { |i|
34
- i.sub(/^./) { |s| s.upcase }
35
- }.join
36
- end
37
- end # String
@@ -1,36 +0,0 @@
1
- #############################################################################
2
- # Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
3
- #
4
- #
5
- # This file is part of WinRM.
6
- #
7
- # WinRM is free software: you can redistribute it and/or
8
- # modify it under the terms of the GNU General Public License as published
9
- # by the Free Software Foundation, either version 3 of the License, or (at
10
- # your option) any later version.
11
- #
12
- # WinRM is distributed in the hope that it will be useful,
13
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
- # Public License for more details.
16
- #
17
- # You should have received a copy of the GNU General Public License along
18
- # with WinRM. If not, see <http://www.gnu.org/licenses/>.
19
- #############################################################################
20
- require 'handsoap'
21
-
22
- module WinRM
23
- module SOAP
24
- NS_ADDRESSING ='a' # http://schemas.xmlsoap.org/ws/2004/08/addressing
25
- NS_CIMBINDING ='b' # http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd
26
- NS_ENUM ='n' # http://schemas.xmlsoap.org/ws/2004/09/enumeration
27
- NS_TRANSFER ='x' # http://schemas.xmlsoap.org/ws/2004/09/transfer
28
- NS_WSMAN_DMTF ='w' # http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
29
- NS_WSMAN_MSFT ='p' # http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd
30
- NS_SCHEMA_INST ='xsi' # http://www.w3.org/2001/XMLSchema-instance
31
- NS_WIN_SHELL ='rsp' # http://schemas.microsoft.com/wbem/wsman/1/windows/shell
32
- end
33
- end
34
-
35
- require 'soap/exceptions/exceptions'
36
- require 'soap/winrm_service'
@@ -1,387 +0,0 @@
1
- #############################################################################
2
- # Copyright © 2010 Dan Wanek <dan.wanek@gmail.com>
3
- #
4
- #
5
- # This file is part of WinRM.
6
- #
7
- # WinRM is free software: you can redistribute it and/or
8
- # modify it under the terms of the GNU General Public License as published
9
- # by the Free Software Foundation, either version 3 of the License, or (at
10
- # your option) any later version.
11
- #
12
- # WinRM is distributed in the hope that it will be useful,
13
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
- # Public License for more details.
16
- #
17
- # You should have received a copy of the GNU General Public License along
18
- # with WinRM. If not, see <http://www.gnu.org/licenses/>.
19
- #############################################################################
20
- Handsoap.http_driver = :http_client
21
-
22
- module WinRM
23
- module SOAP
24
- class WinRMWebService < Handsoap::Service
25
- include SOAP
26
-
27
- @@raw_soap = false
28
-
29
- def initialize()
30
- if $DEBUG
31
- @debug = File.new('winrm_debug.out', 'w')
32
- @debug.sync = true
33
- end
34
- end
35
-
36
- def self.set_auth(user,pass)
37
- @@user = user
38
- @@pass = pass
39
- true
40
- end
41
-
42
- def self.set_ca_trust_path(file_or_dir)
43
- @@ca_trust_store = file_or_dir
44
- true
45
- end
46
-
47
- # Turn off parsing and just return the soap response
48
- def self.raw_soap!
49
- @@raw_soap = true
50
- end
51
-
52
-
53
- # ********* Begin Hooks *********
54
-
55
- def on_create_document(doc)
56
- doc.alias NS_ADDRESSING, 'http://schemas.xmlsoap.org/ws/2004/08/addressing'
57
- doc.alias NS_ENUM, 'http://schemas.xmlsoap.org/ws/2004/09/enumeration'
58
- doc.alias NS_WSMAN_DMTF, 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd'
59
- doc.alias NS_WSMAN_MSFT, 'http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd'
60
- doc.alias NS_SCHEMA_INST,'http://www.w3.org/2001/XMLSchema-instance'
61
- doc.alias NS_WIN_SHELL, 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell'
62
- doc.alias NS_CIMBINDING, 'http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd'
63
-
64
- header = doc.find('Header')
65
- header.add("#{NS_ADDRESSING}:To", WinRMWebService.uri)
66
- header.add("#{NS_ADDRESSING}:ReplyTo") {|rto|
67
- rto.add("#{NS_ADDRESSING}:Address",'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous') {|addr|
68
- addr.set_attr('mustUnderstand','true')
69
- }
70
- }
71
- header.add("#{NS_WSMAN_DMTF}:MaxEnvelopeSize",'153600') {|mes|
72
- mes.set_attr('mustUnderstand','true')
73
- }
74
- header.add("#{NS_ADDRESSING}:MessageID", "uuid:#{UUID.generate.upcase}")
75
- header.add("#{NS_WSMAN_DMTF}:Locale") {|loc|
76
- loc.set_attr('xml:lang','en-US')
77
- loc.set_attr('mustUnderstand','false')
78
- }
79
- header.add("#{NS_WSMAN_MSFT}:DataLocale") {|loc|
80
- loc.set_attr('xml:lang','en-US')
81
- loc.set_attr('mustUnderstand','false')
82
- }
83
- header.add("#{NS_WSMAN_DMTF}:OperationTimeout",'PT60.000S')
84
- end
85
-
86
- # Adds knowledge of namespaces to the response object. These have to be identical to the
87
- # URIs returned in the XML response. For example, I had some issues with the 'soap'
88
- # namespace because my original URI did not end in a '/'
89
- # @example
90
- # Won't work: http://schemas.xmlsoap.org/soap/envelope
91
- # Works: http://schemas.xmlsoap.org/soap/envelope/
92
- def on_response_document(doc)
93
- doc.add_namespace NS_ADDRESSING, 'http://schemas.xmlsoap.org/ws/2004/08/addressing'
94
- doc.add_namespace NS_ENUM, 'http://schemas.xmlsoap.org/ws/2004/09/enumeration'
95
- doc.add_namespace NS_TRANSFER, 'http://schemas.xmlsoap.org/ws/2004/09/transfer'
96
- doc.add_namespace NS_WSMAN_DMTF, 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd'
97
- doc.add_namespace NS_WSMAN_MSFT, 'http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd'
98
- doc.add_namespace NS_WIN_SHELL, 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell'
99
- doc.add_namespace NS_CIMBINDING, 'http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd'
100
- end
101
-
102
- def on_after_create_http_request(req)
103
- req.set_auth @@user, @@pass
104
- req.set_header('Content-Type','application/soap+xml;charset=UTF-8')
105
- req.set_trust_ca_file(@@ca_trust_store) if defined?(@@ca_trust_store)
106
- #puts "SOAP DOCUMENT=\n#{req.body}"
107
- end
108
-
109
- def on_http_error(resp)
110
- case resp.status
111
- when 401
112
- raise WinRMAuthorizationError, "#{resp.headers}\n------\n#{resp.body}"
113
- else
114
- raise WinRMWebServiceError, "#{resp.headers}\n------\n#{resp.body}"
115
- end
116
- end
117
-
118
-
119
- # ********** End Hooks **********
120
-
121
-
122
- # Create a Shell on the destination host
123
- # @param [String<optional>] i_stream Which input stream to open. Leave this alone unless you know what you're doing
124
- # @param [String<optional>] o_stream Which output stream to open. Leave this alone unless you know what you're doing
125
- # @return [String] The ShellId from the SOAP response. This is our open shell instance on the remote machine.
126
- def open_shell(i_stream = 'stdin', o_stream = 'stdout stderr')
127
- header = {
128
- "#{NS_WSMAN_DMTF}:OptionSet" => [
129
- {"#{NS_WSMAN_DMTF}:Option" => {:name => 'WINRS_NOPROFILE', :text =>"FALSE"}},
130
- {"#{NS_WSMAN_DMTF}:Option" => {:name => 'WINRS_CODEPAGE', :text =>"437"}}
131
- ]
132
- }.merge(resource_uri_cmd).merge(action_create)
133
-
134
- resp = invoke("#{NS_WIN_SHELL}:Shell", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |shell|
135
- shell.add("#{NS_WIN_SHELL}:InputStreams", i_stream)
136
- shell.add("#{NS_WIN_SHELL}:OutputStreams",o_stream)
137
- end
138
-
139
- # Get the Shell ID from the response
140
- (resp/"//*[@Name='ShellId']").to_s
141
- end
142
-
143
- # Run a command on a machine with an open shell
144
- # @param [String] shell_id The shell id on the remote machine. See #open_shell
145
- # @param [String] command The command to run on the remote machine
146
- # @return [String] The CommandId from the SOAP response. This is the ID we need to query in order to get output.
147
- def run_command(shell_id, command)
148
- header = {
149
- "#{NS_WSMAN_DMTF}:OptionSet" => {
150
- "#{NS_WSMAN_DMTF}:Option" => {:name => 'WINRS_CONSOLEMODE_STDIN', :text =>"TRUE"},
151
- }
152
- }.merge(resource_uri_cmd).merge(action_command).merge(selector_shell_id(shell_id))
153
-
154
- # Issue the Command
155
- resp = invoke("#{NS_WIN_SHELL}:CommandLine", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |cli|
156
- cli.add("#{NS_WIN_SHELL}:Command","\"#{command}\"")
157
- end
158
-
159
- (resp/"//#{NS_WIN_SHELL}:CommandId").to_s
160
- end
161
-
162
- # Get the Output of the given shell and command
163
- # @param [String] shell_id The shell id on the remote machine. See #open_shell
164
- # @param [String] command_id The command id on the remote machine. See #run_command
165
- # @return [Hash] Returns a Hash with a key :exitcode and :data. Data is an Array of Hashes where the cooresponding key
166
- # is either :stdout or :stderr. The reason it is in an Array so so we can get the output in the order it ocurrs on
167
- # the console.
168
- def get_command_output(shell_id, command_id)
169
- header = {}.merge(resource_uri_cmd).merge(action_receive).merge(selector_shell_id(shell_id))
170
-
171
- # Get Command Output
172
- resp = invoke("#{NS_WIN_SHELL}:Receive", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |rec|
173
- rec.add("#{NS_WIN_SHELL}:DesiredStream",'stdout stderr') do |ds|
174
- ds.set_attr('CommandId', command_id)
175
- end
176
- end
177
-
178
- output = {:data => []}
179
- (resp/"//#{NS_WIN_SHELL}:Stream").each do |n|
180
- next if n.to_s.nil?
181
- output[:data] << {n['Name'].to_sym => Base64.decode64(n.to_s)}
182
- end
183
-
184
- # We may need to get additional output if the stream has not finished.
185
- # The CommandState will change from Running to Done like so:
186
- # @example
187
- # from...
188
- # <rsp:CommandState CommandId="495C3B09-E0B0-442A-9958-83B529F76C2C" State="http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Running"/>
189
- # to...
190
- # <rsp:CommandState CommandId="495C3B09-E0B0-442A-9958-83B529F76C2C" State="http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done">
191
- # <rsp:ExitCode>0</rsp:ExitCode>
192
- # </rsp:CommandState>
193
- if((resp/"//#{NS_WIN_SHELL}:ExitCode").empty?)
194
- output.merge!(get_command_output(shell_id,command_id)) do |key, old_data, new_data|
195
- old_data += new_data
196
- end
197
- else
198
- output[:exitcode] = (resp/"//#{NS_WIN_SHELL}:ExitCode").first.to_i
199
- end
200
- output
201
- end
202
-
203
- # Clean-up after a command.
204
- # @see #run_command
205
- # @param [String] shell_id The shell id on the remote machine. See #open_shell
206
- # @param [String] command_id The command id on the remote machine. See #run_command
207
- # @return [true] This should have more error checking but it just returns true for now.
208
- def cleanup_command(shell_id, command_id)
209
- header = {}.merge(resource_uri_cmd).merge(action_signal).merge(selector_shell_id(shell_id))
210
- # Signal the Command references to terminate (close stdout/stderr)
211
- resp = invoke("#{NS_WIN_SHELL}:Signal", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |sig|
212
- sig.set_attr('CommandId', command_id)
213
- sig.add("#{NS_WIN_SHELL}:Code",'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate')
214
- end
215
- true
216
- end
217
-
218
- # Close the shell
219
- # @param [String] shell_id The shell id on the remote machine. See #open_shell
220
- # @return [true] This should have more error checking but it just returns true for now.
221
- def close_shell(shell_id)
222
- header = {}.merge(resource_uri_cmd).merge(action_delete).merge(selector_shell_id(shell_id))
223
- # Delete the Shell reference
224
- resp = invoke(:nil_body, {:soap_action => nil, :soap_body => true, :http_options => nil, :soap_header => header})
225
- true
226
- end
227
-
228
- # Run a CMD command
229
- # @param [String] command The command to run on the remote system
230
- # @return [Hash] :stdout and :stderr
231
- def run_cmd(command)
232
- shell_id = open_shell
233
- command_id = run_command(shell_id, command)
234
- command_output = get_command_output(shell_id, command_id)
235
- cleanup_command(shell_id, command_id)
236
- close_shell(shell_id)
237
- command_output
238
- end
239
-
240
-
241
- # Run a Powershell script that resides on the local box.
242
- # @param [String] script_file The string representing the path to a Powershell script
243
- # @return [Hash] :stdout and :stderr
244
- def run_powershell_script(script_file)
245
- script = File.read(script_file)
246
- script = script.chars.to_a.join("\x00").chomp
247
- if(defined?(script.encode))
248
- script = script.encode('ASCII-8BIT')
249
- script = Base64.strict_encode64(script)
250
- else
251
- script = Base64.encode64(script).chomp
252
- end
253
-
254
-
255
- shell_id = open_shell
256
- command_id = run_command(shell_id, "powershell -encodedCommand #{script}")
257
- command_output = get_command_output(shell_id, command_id)
258
- cleanup_command(shell_id, command_id)
259
- close_shell(shell_id)
260
- command_output
261
- end
262
-
263
-
264
- # Run a WQL Query
265
- # @see http://msdn.microsoft.com/en-us/library/aa394606(VS.85).aspx
266
- # @param [String] wql The WQL query
267
- # @return [Array<Hash>] Returns an array of Hashes that contain the key/value pairs returned from the query.
268
- def run_wql(wql)
269
- header = {}.merge(resource_uri_wmi).merge(action_enumerate)
270
-
271
- begin
272
- resp = invoke("#{NS_ENUM}:Enumerate", {:soap_action => :auto, :http_options => nil, :soap_header => header}) do |enum|
273
- enum.add("#{NS_WSMAN_DMTF}:OptimizeEnumeration")
274
- enum.add("#{NS_WSMAN_DMTF}:MaxElements",'32000')
275
- mattr = nil
276
- enum.add("#{NS_WSMAN_DMTF}:Filter", wql) do |filt|
277
- filt.set_attr('Dialect','http://schemas.microsoft.com/wbem/wsman/1/WQL')
278
- end
279
- end
280
- rescue Handsoap::Fault => e
281
- raise WinRMWebServiceError, e.reason
282
- end
283
-
284
- query_response = []
285
- (resp/"//#{NS_ENUM}:EnumerateResponse//#{NS_WSMAN_DMTF}:Items/*").each do |i|
286
- qitem = {}
287
- (i/'*').each do |si|
288
- qitem[si.node_name] = si.to_s
289
- end
290
- query_response << qitem
291
- end
292
- query_response
293
- end
294
-
295
-
296
- # To create an empty body set :soap_body => true in the invoke options and set the action to :nil_body
297
- def iterate_hash_array(element, hash_array)
298
- add_hierarchy!(element, hash_array, nil) unless hash_array.key?(:nil_body)
299
- end
300
-
301
-
302
- protected
303
-
304
- # Add a hierarchy of elements from hash data
305
- # @example Hash to XML
306
- # {:this => {:text =>'that'},'top' => {:id => '32fss', :text => 'TestText', {'middle' => 'bottom'}}}
307
- # becomes...
308
- # <this>that</this>
309
- # <top Id='32fss'>
310
- # TestText
311
- # <middle>bottom</middle>
312
- # </top>
313
- def add_hierarchy!(node, e_hash, prefix = NS_ADDRESSING)
314
- prefix << ":" unless prefix.nil?
315
- e_hash.each_pair do |k,v|
316
- name = (k.is_a?(Symbol) && k != :text) ? k.to_s.camel_case : k
317
- if v.is_a? Hash
318
- node.add("#{prefix}#{name}", v[:text]) do |n|
319
- add_hierarchy!(n, v, prefix)
320
- end
321
- elsif v.is_a? Array
322
- node.add("#{prefix}#{name}") do |n|
323
- v.each do |i|
324
- add_hierarchy!(n, i, prefix)
325
- end
326
- end
327
- else
328
- node.set_attr(name, v) unless k == :text
329
- end
330
- end
331
- end
332
-
333
-
334
- # Private Methods (Builders and Parsers)
335
- private
336
-
337
- def build!(node, opts = {}, &block)
338
- #EwsBuilder.new(node, opts, &block)
339
- end
340
-
341
- def parse!(response, opts = {})
342
- return response if @@raw_soap
343
- #EwsParser.new(response).parse(opts)
344
- end
345
-
346
-
347
- # Helper methods for SOAP Headers
348
-
349
- def resource_uri_cmd
350
- {"#{NS_WSMAN_DMTF}:ResourceURI" => {'mustUnderstand' => 'true', :text => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd'}}
351
- end
352
-
353
- def resource_uri_wmi(namespace = 'root/cimv2/*')
354
- {"#{NS_WSMAN_DMTF}:ResourceURI" => {'mustUnderstand' => 'true', :text => "http://schemas.microsoft.com/wbem/wsman/1/wmi/#{namespace}"}}
355
- end
356
-
357
- def action_create
358
- {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.xmlsoap.org/ws/2004/09/transfer/Create'}}
359
- end
360
-
361
- def action_delete
362
- {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete'}}
363
- end
364
-
365
- def action_command
366
- {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command'}}
367
- end
368
-
369
- def action_receive
370
- {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive'}}
371
- end
372
-
373
- def action_signal
374
- {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal'}}
375
- end
376
-
377
- def action_enumerate
378
- {"#{NS_ADDRESSING}:Action" => {'mustUnderstand' => 'true', :text => 'http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate'}}
379
- end
380
-
381
- def selector_shell_id(shell_id)
382
- {"#{NS_WSMAN_DMTF}:SelectorSet" => {"#{NS_WSMAN_DMTF}:Selector" => {:name => 'ShellId', :text => shell_id}}}
383
- end
384
-
385
- end # class WinRMWebService
386
- end # module SOAP
387
- end # WinRM