winrm-s 0.2.4 → 0.3.0.dev.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.
@@ -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