winrm-s 0.3.6 → 0.3.7.dev

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,35 +1,35 @@
1
- #
2
- # Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
3
- # Copyright:: Copyright (c) 2014 Chef Software, Inc.
4
- # License:: Apache License, Version 2.0
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
- #
18
-
19
- module WinRM
20
- module HTTP
21
-
22
- class HttpSSPINegotiate < HttpTransport
23
- def initialize(endpoint, user, pass, opts)
24
- # Override the relevant functionality in httpclient to make sspi work.
25
- require 'winrm/http/auth'
26
- super(endpoint)
27
- @httpcli.set_auth(nil, user, pass)
28
- # Remove non-sspi auths
29
- auths = @httpcli.www_auth.instance_variable_get('@authenticator')
30
- auths.delete_if {|i| not i.is_a?(HTTPClient::SSPINegotiateAuth)}
31
- end
32
- end
33
-
34
- end
35
- end # WinRM
1
+ #
2
+ # Author:: Kaustubh Deorukhkar (<kaustubh@clogeny.com>)
3
+ # Copyright:: Copyright (c) 2014 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
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
+ #
18
+
19
+ module WinRM
20
+ module HTTP
21
+
22
+ class HttpSSPINegotiate < HttpTransport
23
+ def initialize(endpoint, user, pass, opts)
24
+ # Override the relevant functionality in httpclient to make sspi work.
25
+ require 'winrm/http/auth'
26
+ super(endpoint)
27
+ @httpcli.set_auth(nil, user, pass)
28
+ # Remove non-sspi auths
29
+ auths = @httpcli.www_auth.instance_variable_get('@authenticator')
30
+ auths.delete_if {|i| not i.is_a?(HTTPClient::SSPINegotiateAuth)}
31
+ end
32
+ end
33
+
34
+ end
35
+ end # WinRM
@@ -1,256 +1,256 @@
1
-
2
- require 'winrm/helpers/assert_patch'
3
-
4
- def validate_patch
5
- # The code below patches the Win32::SSPI module from ruby core to add support
6
- # for encrypt/decrypt as described below.
7
- # Add few restrictions to make sure the patched methods are still
8
- # available, but still give a way to consciously use later versions
9
- PatchAssertions.assert_arity_of_patched_method(Win32::SSPI::NegotiateAuth, "initialize", -1)
10
- PatchAssertions.assert_arity_of_patched_method(Win32::SSPI::NegotiateAuth, "complete_authentication", 1)
11
- PatchAssertions.assert_arity_of_patched_method(Win32::SSPI::NegotiateAuth, "get_credentials", 0)
12
- end
13
-
14
- # Perform the patch validations
15
- validate_patch
16
-
17
- # Overrides and enhances the ruby core win32 sspi module to add support to
18
- # encrypt/decrypt data to be sent over channel, example using SSP Negotiate auth
19
-
20
- module Win32
21
- module SSPI
22
- # QueryContextAttributes attributes flags
23
- SECPKG_ATTR_SIZES = 0x00000000
24
-
25
- module API
26
- QueryContextAttributes = Win32API.new("secur32", "QueryContextAttributes", 'pLp', 'L')
27
- EncryptMessage = Win32API.new("secur32", "EncryptMessage", 'pLpL', 'L')
28
- DecryptMessage = Win32API.new("secur32", "DecryptMessage", 'ppLp', 'L')
29
- end
30
-
31
- class SecPkgContext_Sizes
32
- attr_accessor :cbMaxToken, :cbMaxSignature, :cbBlockSize, :cbSecurityTrailer
33
-
34
- def initialize
35
- @cbMaxToken = @cbMaxSignature = @cbBlockSize = @cbSecurityTrailer = 0
36
- end
37
-
38
- def cbMaxToken
39
- @cbMaxToken = @struct.unpack("LLLL")[0] if @struct
40
- end
41
-
42
- def cbMaxSignature
43
- @cbMaxSignature = @struct.unpack("LLLL")[1] if @struct
44
- end
45
-
46
- def cbBlockSize
47
- @cbBlockSize = @struct.unpack("LLLL")[2] if @struct
48
- end
49
-
50
- def cbSecurityTrailer
51
- @cbSecurityTrailer = @struct.unpack("LLLL")[3] if @struct
52
- end
53
-
54
- def to_p
55
- @struct ||= [@cbMaxToken, @cbMaxSignature, @cbBlockSize, @cbSecurityTrailer].pack("LLLL")
56
- end
57
- end
58
-
59
- # SecurityBuffer for data to be encrypted
60
- class EncryptedSecurityBuffer
61
-
62
- SECBUFFER_DATA = 0x1 # Security buffer data
63
- SECBUFFER_TOKEN = 0x2 # Security token
64
- SECBUFFER_VERSION = 0
65
-
66
- def initialize(data_buffer, sizes)
67
- @original_msg_len = data_buffer.length
68
- @cbSecurityTrailer = sizes.cbSecurityTrailer
69
- @data_buffer = data_buffer
70
- @security_trailer = "\0" * sizes.cbSecurityTrailer
71
- end
72
-
73
- def to_p
74
- # Assumption is that when to_p is called we are going to get a packed structure. Therefore,
75
- # set @unpacked back to nil so we know to unpack when accessors are next accessed.
76
- @unpacked = nil
77
- # Assignment of inner structure to variable is very important here. Without it,
78
- # will not be able to unpack changes to the structure. Alternative, nested unpacks,
79
- # does not work (i.e. @struct.unpack("LLP12")[2].unpack("LLP12") results in "no associated pointer")
80
- @sec_buffer = [@original_msg_len, SECBUFFER_DATA, @data_buffer, @cbSecurityTrailer, SECBUFFER_TOKEN, @security_trailer].pack("LLPLLP")
81
- @struct ||= [SECBUFFER_VERSION, 2, @sec_buffer].pack("LLP")
82
- end
83
-
84
- def buffer
85
- unpack
86
- @buffer
87
- end
88
-
89
- private
90
-
91
- # Unpacks the SecurityBufferDesc structure into member variables. We
92
- # only want to do this once per struct, so the struct is deleted
93
- # after unpacking.
94
- def unpack
95
- if ! @unpacked && @sec_buffer && @struct
96
- dataBufferSize, dType, dataBuffer, tokenBufferSize, tType, tokenBuffer = @sec_buffer.unpack("LLPLLP")
97
- dataBufferSize, dType, dataBuffer, tokenBufferSize, tType, tokenBuffer = @sec_buffer.unpack("LLP#{dataBufferSize}LLP#{tokenBufferSize}")
98
- # Form the buffer stream as required by server
99
- @buffer = [tokenBufferSize].pack("L")
100
- @buffer << tokenBuffer << dataBuffer
101
- @struct = nil
102
- @sec_buffer = nil
103
- @unpacked = true
104
- end
105
- end
106
- end
107
-
108
- class DecryptedSecurityBuffer
109
-
110
- SECBUFFER_DATA = 0x1 # Security buffer data
111
- SECBUFFER_TOKEN = 0x2 # Security token
112
- SECBUFFER_VERSION = 0
113
-
114
- def initialize(buffer)
115
- # unpack to extract the msg and token
116
- token_size, token_buffer, enc_buffer = buffer.unpack("L")
117
- @original_msg_len = buffer.length - token_size - 4
118
- @cbSecurityTrailer, @security_trailer, @data_buffer = buffer.unpack("La#{token_size}a#{@original_msg_len}")
119
- end
120
-
121
- def to_p
122
- # Assumption is that when to_p is called we are going to get a packed structure. Therefore,
123
- # set @unpacked back to nil so we know to unpack when accessors are next accessed.
124
- @unpacked = nil
125
- # Assignment of inner structure to variable is very important here. Without it,
126
- # will not be able to unpack changes to the structure. Alternative, nested unpacks,
127
- # does not work (i.e. @struct.unpack("LLP12")[2].unpack("LLP12") results in "no associated pointer")
128
- @sec_buffer = [@original_msg_len, SECBUFFER_DATA, @data_buffer, @cbSecurityTrailer, SECBUFFER_TOKEN, @security_trailer].pack("LLPLLP")
129
- @struct ||= [SECBUFFER_VERSION, 2, @sec_buffer].pack("LLP")
130
- end
131
-
132
- def buffer
133
- unpack
134
- @buffer
135
- end
136
-
137
- private
138
-
139
- # Unpacks the SecurityBufferDesc structure into member variables. We
140
- # only want to do this once per struct, so the struct is deleted
141
- # after unpacking.
142
- def unpack
143
- if ! @unpacked && @sec_buffer && @struct
144
- dataBufferSize, dType, dataBuffer, tokenBufferSize, tType, tokenBuffer = @sec_buffer.unpack("LLPLLP")
145
- dataBufferSize, dType, dataBuffer, tokenBufferSize, tType, tokenBuffer = @sec_buffer.unpack("LLP#{dataBufferSize}LLP#{tokenBufferSize}")
146
- @buffer = dataBuffer
147
- @struct = nil
148
- @sec_buffer = nil
149
- @unpacked = true
150
- end
151
- end
152
- end
153
-
154
- class NegotiateAuth
155
- # Override to remember password
156
- # Creates a new instance ready for authentication as the given user in the given domain.
157
- # Defaults to current user and domain as defined by ENV["USERDOMAIN"] and ENV["USERNAME"] if
158
- # no arguments are supplied.
159
- def initialize(user = nil, domain = nil, password = nil)
160
- if user.nil? && domain.nil? && ENV["USERNAME"].nil? && ENV["USERDOMAIN"].nil?
161
- raise "A username or domain must be supplied since they cannot be retrieved from the environment"
162
- end
163
- @user = user || ENV["USERNAME"].dup
164
- @domain = domain || ENV["USERDOMAIN"].dup
165
- @password = password
166
- end
167
-
168
- # Takes a token and gets the next token in the Negotiate authentication chain. Token can be Base64 encoded or not.
169
- # The token can include the "Negotiate" header and it will be stripped.
170
- # Does not indicate if SEC_I_CONTINUE or SEC_E_OK was returned.
171
- # Token returned is Base64 encoded w/ all new lines removed.
172
- def complete_authentication(token)
173
- raise "This object is no longer usable because its resources have been freed." if @cleaned_up
174
-
175
- # Nil token OK, just set it to empty string
176
- token = "" if token.nil?
177
-
178
- if token.include? "Negotiate"
179
- # If the Negotiate prefix is passed in, assume we are seeing "Negotiate <token>" and get the token.
180
- token = token.split(" ").last
181
- end
182
-
183
- if token.include? B64_TOKEN_PREFIX
184
- # indicates base64 encoded token
185
- token = token.strip.unpack("m")[0]
186
- end
187
-
188
- outputBuffer = SecurityBuffer.new
189
- result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, @context.to_p, nil,
190
- REQUEST_FLAGS, 0, SECURITY_NETWORK_DREP, SecurityBuffer.new(token).to_p, 0,
191
- @context.to_p,
192
- outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p))
193
-
194
- if result.ok? then
195
- @auth_successful = true
196
- return encode_token(outputBuffer.token)
197
- else
198
- raise "Error: #{result.to_s}"
199
- end
200
- ensure
201
- # need to make sure we don't clean up if we've already cleaned up.
202
- # XXX - clean up later since encrypt/decrypt needs this
203
- # clean_up unless @cleaned_up
204
- end
205
-
206
- def encrypt_payload(body)
207
- if @auth_successful
208
- # Approach - http://msdn.microsoft.com/en-us/magazine/cc301890.aspx
209
- sizes = SecPkgContext_Sizes.new
210
- result = SSPIResult.new(API::QueryContextAttributes.call(@context.to_p, SECPKG_ATTR_SIZES, sizes.to_p))
211
-
212
- outputBuffer = EncryptedSecurityBuffer.new(body, sizes)
213
- result = SSPIResult.new(API::EncryptMessage.call(@context.to_p, 0, outputBuffer.to_p, 0 ))
214
- encrypted_msg = outputBuffer.buffer
215
-
216
- body = <<-EOF
217
- --Encrypted Boundary\r
218
- Content-Type: application/HTTP-SPNEGO-session-encrypted\r
219
- OriginalContent: type=application/soap+xml;charset=UTF-8;Length=#{encrypted_msg.length - sizes.cbSecurityTrailer - 4}\r
220
- --Encrypted Boundary\r
221
- Content-Type: application/octet-stream\r
222
- #{encrypted_msg}--Encrypted Boundary\r
223
- EOF
224
- end
225
- body
226
- end
227
-
228
- def decrypt_payload(body)
229
- if @auth_successful
230
-
231
- matched_data = /--Encrypted Boundary\s+Content-Type:\s+application\/HTTP-SPNEGO-session-encrypted\s+OriginalContent:\s+type=\S+Length=(\d+)\s+--Encrypted Boundary\s+Content-Type:\s+application\/octet-stream\s+([\S\s]+)--Encrypted Boundary/.match(body)
232
- raise "Error: Unencrypted communication not supported. Please check winrm configuration winrm/config/service AllowUnencrypted flag." if matched_data.nil?
233
-
234
- encrypted_msg = matched_data[2]
235
-
236
- outputBuffer = DecryptedSecurityBuffer.new(encrypted_msg)
237
- result = SSPIResult.new(API::DecryptMessage.call(@context.to_p, outputBuffer.to_p, 0, 0 ))
238
- body = outputBuffer.buffer
239
- end
240
- body
241
- end
242
-
243
- private
244
- # ** Override to add password support
245
- # Gets credentials based on user, domain or both. If both are nil, an error occurs
246
- def get_credentials
247
- @credentials = CredHandle.new
248
- ts = TimeStamp.new
249
- @identity = Identity.new @user, @domain, @password
250
- result = SSPIResult.new(API::AcquireCredentialsHandle.call(nil, "Negotiate", SECPKG_CRED_OUTBOUND, nil, @identity.to_p,
251
- nil, nil, @credentials.to_p, ts.to_p))
252
- raise "Error acquire credentials: #{result}" unless result.ok?
253
- end
254
- end
255
- end
256
- end
1
+
2
+ require 'winrm/helpers/assert_patch'
3
+
4
+ def validate_patch
5
+ # The code below patches the Win32::SSPI module from ruby core to add support
6
+ # for encrypt/decrypt as described below.
7
+ # Add few restrictions to make sure the patched methods are still
8
+ # available, but still give a way to consciously use later versions
9
+ PatchAssertions.assert_arity_of_patched_method(Win32::SSPI::NegotiateAuth, "initialize", -1)
10
+ PatchAssertions.assert_arity_of_patched_method(Win32::SSPI::NegotiateAuth, "complete_authentication", 1)
11
+ PatchAssertions.assert_arity_of_patched_method(Win32::SSPI::NegotiateAuth, "get_credentials", 0)
12
+ end
13
+
14
+ # Perform the patch validations
15
+ validate_patch
16
+
17
+ # Overrides and enhances the ruby core win32 sspi module to add support to
18
+ # encrypt/decrypt data to be sent over channel, example using SSP Negotiate auth
19
+
20
+ module Win32
21
+ module SSPI
22
+ # QueryContextAttributes attributes flags
23
+ SECPKG_ATTR_SIZES = 0x00000000
24
+
25
+ module API
26
+ QueryContextAttributes = Win32API.new("secur32", "QueryContextAttributes", 'pLp', 'L')
27
+ EncryptMessage = Win32API.new("secur32", "EncryptMessage", 'pLpL', 'L')
28
+ DecryptMessage = Win32API.new("secur32", "DecryptMessage", 'ppLp', 'L')
29
+ end
30
+
31
+ class SecPkgContext_Sizes
32
+ attr_accessor :cbMaxToken, :cbMaxSignature, :cbBlockSize, :cbSecurityTrailer
33
+
34
+ def initialize
35
+ @cbMaxToken = @cbMaxSignature = @cbBlockSize = @cbSecurityTrailer = 0
36
+ end
37
+
38
+ def cbMaxToken
39
+ @cbMaxToken = @struct.unpack("LLLL")[0] if @struct
40
+ end
41
+
42
+ def cbMaxSignature
43
+ @cbMaxSignature = @struct.unpack("LLLL")[1] if @struct
44
+ end
45
+
46
+ def cbBlockSize
47
+ @cbBlockSize = @struct.unpack("LLLL")[2] if @struct
48
+ end
49
+
50
+ def cbSecurityTrailer
51
+ @cbSecurityTrailer = @struct.unpack("LLLL")[3] if @struct
52
+ end
53
+
54
+ def to_p
55
+ @struct ||= [@cbMaxToken, @cbMaxSignature, @cbBlockSize, @cbSecurityTrailer].pack("LLLL")
56
+ end
57
+ end
58
+
59
+ # SecurityBuffer for data to be encrypted
60
+ class EncryptedSecurityBuffer
61
+
62
+ SECBUFFER_DATA = 0x1 # Security buffer data
63
+ SECBUFFER_TOKEN = 0x2 # Security token
64
+ SECBUFFER_VERSION = 0
65
+
66
+ def initialize(data_buffer, sizes)
67
+ @original_msg_len = data_buffer.length
68
+ @cbSecurityTrailer = sizes.cbSecurityTrailer
69
+ @data_buffer = data_buffer
70
+ @security_trailer = "\0" * sizes.cbSecurityTrailer
71
+ end
72
+
73
+ def to_p
74
+ # Assumption is that when to_p is called we are going to get a packed structure. Therefore,
75
+ # set @unpacked back to nil so we know to unpack when accessors are next accessed.
76
+ @unpacked = nil
77
+ # Assignment of inner structure to variable is very important here. Without it,
78
+ # will not be able to unpack changes to the structure. Alternative, nested unpacks,
79
+ # does not work (i.e. @struct.unpack("LLP12")[2].unpack("LLP12") results in "no associated pointer")
80
+ @sec_buffer = [@original_msg_len, SECBUFFER_DATA, @data_buffer, @cbSecurityTrailer, SECBUFFER_TOKEN, @security_trailer].pack("LLPLLP")
81
+ @struct ||= [SECBUFFER_VERSION, 2, @sec_buffer].pack("LLP")
82
+ end
83
+
84
+ def buffer
85
+ unpack
86
+ @buffer
87
+ end
88
+
89
+ private
90
+
91
+ # Unpacks the SecurityBufferDesc structure into member variables. We
92
+ # only want to do this once per struct, so the struct is deleted
93
+ # after unpacking.
94
+ def unpack
95
+ if ! @unpacked && @sec_buffer && @struct
96
+ dataBufferSize, dType, dataBuffer, tokenBufferSize, tType, tokenBuffer = @sec_buffer.unpack("LLPLLP")
97
+ dataBufferSize, dType, dataBuffer, tokenBufferSize, tType, tokenBuffer = @sec_buffer.unpack("LLP#{dataBufferSize}LLP#{tokenBufferSize}")
98
+ # Form the buffer stream as required by server
99
+ @buffer = [tokenBufferSize].pack("L")
100
+ @buffer << tokenBuffer << dataBuffer
101
+ @struct = nil
102
+ @sec_buffer = nil
103
+ @unpacked = true
104
+ end
105
+ end
106
+ end
107
+
108
+ class DecryptedSecurityBuffer
109
+
110
+ SECBUFFER_DATA = 0x1 # Security buffer data
111
+ SECBUFFER_TOKEN = 0x2 # Security token
112
+ SECBUFFER_VERSION = 0
113
+
114
+ def initialize(buffer)
115
+ # unpack to extract the msg and token
116
+ token_size, token_buffer, enc_buffer = buffer.unpack("L")
117
+ @original_msg_len = buffer.length - token_size - 4
118
+ @cbSecurityTrailer, @security_trailer, @data_buffer = buffer.unpack("La#{token_size}a#{@original_msg_len}")
119
+ end
120
+
121
+ def to_p
122
+ # Assumption is that when to_p is called we are going to get a packed structure. Therefore,
123
+ # set @unpacked back to nil so we know to unpack when accessors are next accessed.
124
+ @unpacked = nil
125
+ # Assignment of inner structure to variable is very important here. Without it,
126
+ # will not be able to unpack changes to the structure. Alternative, nested unpacks,
127
+ # does not work (i.e. @struct.unpack("LLP12")[2].unpack("LLP12") results in "no associated pointer")
128
+ @sec_buffer = [@original_msg_len, SECBUFFER_DATA, @data_buffer, @cbSecurityTrailer, SECBUFFER_TOKEN, @security_trailer].pack("LLPLLP")
129
+ @struct ||= [SECBUFFER_VERSION, 2, @sec_buffer].pack("LLP")
130
+ end
131
+
132
+ def buffer
133
+ unpack
134
+ @buffer
135
+ end
136
+
137
+ private
138
+
139
+ # Unpacks the SecurityBufferDesc structure into member variables. We
140
+ # only want to do this once per struct, so the struct is deleted
141
+ # after unpacking.
142
+ def unpack
143
+ if ! @unpacked && @sec_buffer && @struct
144
+ dataBufferSize, dType, dataBuffer, tokenBufferSize, tType, tokenBuffer = @sec_buffer.unpack("LLPLLP")
145
+ dataBufferSize, dType, dataBuffer, tokenBufferSize, tType, tokenBuffer = @sec_buffer.unpack("LLP#{dataBufferSize}LLP#{tokenBufferSize}")
146
+ @buffer = dataBuffer
147
+ @struct = nil
148
+ @sec_buffer = nil
149
+ @unpacked = true
150
+ end
151
+ end
152
+ end
153
+
154
+ class NegotiateAuth
155
+ # Override to remember password
156
+ # Creates a new instance ready for authentication as the given user in the given domain.
157
+ # Defaults to current user and domain as defined by ENV["USERDOMAIN"] and ENV["USERNAME"] if
158
+ # no arguments are supplied.
159
+ def initialize(user = nil, domain = nil, password = nil)
160
+ if user.nil? && domain.nil? && ENV["USERNAME"].nil? && ENV["USERDOMAIN"].nil?
161
+ raise "A username or domain must be supplied since they cannot be retrieved from the environment"
162
+ end
163
+ @user = user || ENV["USERNAME"].dup
164
+ @domain = domain || ENV["USERDOMAIN"].dup
165
+ @password = password
166
+ end
167
+
168
+ # Takes a token and gets the next token in the Negotiate authentication chain. Token can be Base64 encoded or not.
169
+ # The token can include the "Negotiate" header and it will be stripped.
170
+ # Does not indicate if SEC_I_CONTINUE or SEC_E_OK was returned.
171
+ # Token returned is Base64 encoded w/ all new lines removed.
172
+ def complete_authentication(token)
173
+ raise "This object is no longer usable because its resources have been freed." if @cleaned_up
174
+
175
+ # Nil token OK, just set it to empty string
176
+ token = "" if token.nil?
177
+
178
+ if token.include? "Negotiate"
179
+ # If the Negotiate prefix is passed in, assume we are seeing "Negotiate <token>" and get the token.
180
+ token = token.split(" ").last
181
+ end
182
+
183
+ if token.include? B64_TOKEN_PREFIX
184
+ # indicates base64 encoded token
185
+ token = token.strip.unpack("m")[0]
186
+ end
187
+
188
+ outputBuffer = SecurityBuffer.new
189
+ result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, @context.to_p, nil,
190
+ REQUEST_FLAGS, 0, SECURITY_NETWORK_DREP, SecurityBuffer.new(token).to_p, 0,
191
+ @context.to_p,
192
+ outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p))
193
+
194
+ if result.ok? then
195
+ @auth_successful = true
196
+ return encode_token(outputBuffer.token)
197
+ else
198
+ raise "Error: #{result.to_s}"
199
+ end
200
+ ensure
201
+ # need to make sure we don't clean up if we've already cleaned up.
202
+ # XXX - clean up later since encrypt/decrypt needs this
203
+ # clean_up unless @cleaned_up
204
+ end
205
+
206
+ def encrypt_payload(body)
207
+ if @auth_successful
208
+ # Approach - http://msdn.microsoft.com/en-us/magazine/cc301890.aspx
209
+ sizes = SecPkgContext_Sizes.new
210
+ result = SSPIResult.new(API::QueryContextAttributes.call(@context.to_p, SECPKG_ATTR_SIZES, sizes.to_p))
211
+
212
+ outputBuffer = EncryptedSecurityBuffer.new(body, sizes)
213
+ result = SSPIResult.new(API::EncryptMessage.call(@context.to_p, 0, outputBuffer.to_p, 0 ))
214
+ encrypted_msg = outputBuffer.buffer
215
+
216
+ body = <<-EOF
217
+ --Encrypted Boundary\r
218
+ Content-Type: application/HTTP-SPNEGO-session-encrypted\r
219
+ OriginalContent: type=application/soap+xml;charset=UTF-8;Length=#{encrypted_msg.length - sizes.cbSecurityTrailer - 4}\r
220
+ --Encrypted Boundary\r
221
+ Content-Type: application/octet-stream\r
222
+ #{encrypted_msg}--Encrypted Boundary\r
223
+ EOF
224
+ end
225
+ body
226
+ end
227
+
228
+ def decrypt_payload(body)
229
+ if @auth_successful
230
+
231
+ matched_data = /--Encrypted Boundary\s+Content-Type:\s+application\/HTTP-SPNEGO-session-encrypted\s+OriginalContent:\s+type=\S+Length=(\d+)\s+--Encrypted Boundary\s+Content-Type:\s+application\/octet-stream\s+([\S\s]+)--Encrypted Boundary/.match(body)
232
+ raise "Error: Unencrypted communication not supported. Please check winrm configuration winrm/config/service AllowUnencrypted flag." if matched_data.nil?
233
+
234
+ encrypted_msg = matched_data[2]
235
+
236
+ outputBuffer = DecryptedSecurityBuffer.new(encrypted_msg)
237
+ result = SSPIResult.new(API::DecryptMessage.call(@context.to_p, outputBuffer.to_p, 0, 0 ))
238
+ body = outputBuffer.buffer
239
+ end
240
+ body
241
+ end
242
+
243
+ private
244
+ # ** Override to add password support
245
+ # Gets credentials based on user, domain or both. If both are nil, an error occurs
246
+ def get_credentials
247
+ @credentials = CredHandle.new
248
+ ts = TimeStamp.new
249
+ @identity = Identity.new @user, @domain, @password
250
+ result = SSPIResult.new(API::AcquireCredentialsHandle.call(nil, "Negotiate", SECPKG_CRED_OUTBOUND, nil, @identity.to_p,
251
+ nil, nil, @credentials.to_p, ts.to_p))
252
+ raise "Error acquire credentials: #{result}" unless result.ok?
253
+ end
254
+ end
255
+ end
256
+ end