win32-sspi 0.0.1.rc1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,441 @@
1
+ ########################################################################
2
+ # Tests for the Win32::SSPI::Negotiate::Server class.
3
+ ########################################################################
4
+ require 'test-unit'
5
+ require 'win32/sspi/negotiate/server'
6
+
7
+ class TC_Win32_SSPI_Negotiate_Server < Test::Unit::TestCase
8
+ MockSpnegoToken = Random.new.bytes(128)
9
+ MockCredentialHandle = [777,888]
10
+ MockTimeStamp = [0x000000FF,0xFF000000]
11
+ MockContextHandle = [123,987]
12
+ MockSecBufferContent = Random.new.bytes(128)
13
+ ContextAttr = Windows::Constants::ISC_REQ_CONFIDENTIALITY |
14
+ Windows::Constants::ISC_REQ_REPLAY_DETECT |
15
+ Windows::Constants::ISC_REQ_CONNECTION
16
+
17
+ def setup
18
+ @server = Win32::SSPI::Negotiate::Server.new
19
+ end
20
+
21
+ # assert helper to help remove test duplication
22
+ def assert_server_call_state(server)
23
+ ach_args = server.retrieve_state(:ach)
24
+ refute_nil ach_args
25
+ assert_equal 9, ach_args.length
26
+
27
+ asc_args = server.retrieve_state(:asc)
28
+ refute_nil asc_args
29
+ assert_equal 9, asc_args.length
30
+
31
+ cat_args = server.retrieve_state(:cat)
32
+ refute_nil cat_args
33
+ assert_equal 2, cat_args.length
34
+
35
+ dsc_args = server.retrieve_state(:dsc)
36
+ refute_nil dsc_args
37
+ assert_equal 1, dsc_args.length
38
+
39
+ fch_args = server.retrieve_state(:fch)
40
+ refute_nil fch_args
41
+ assert_equal 1, fch_args.length
42
+
43
+ fcb_args = server.retrieve_state(:fcb)
44
+ refute_nil fcb_args
45
+ assert_equal 1, fcb_args.length
46
+
47
+ assert_nil server.instance_variable_get(:@credentials_handle)
48
+ assert_nil server.instance_variable_get(:@context_handle)
49
+ end
50
+
51
+ def assert_base64_http_header(header,auth_type)
52
+ if Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE == auth_type
53
+ assert_match( /\ANegotiate \p{Print}+={,2}\z/,header)
54
+ end
55
+ if Win32::SSPI::API::Common::AUTH_TYPE_NTLM == auth_type
56
+ assert_match( /\ANTLM \p{Print}+={,2}\z/,header)
57
+ end
58
+ end
59
+
60
+ def test_auth_type_basic_functionality
61
+ assert_respond_to(@server, :auth_type)
62
+ assert_respond_to(@server, :auth_type=)
63
+ assert_nothing_raised{ @server.auth_type }
64
+ assert_kind_of(String, @server.auth_type)
65
+ assert_equal Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE, @server.auth_type
66
+
67
+ server = Win32::SSPI::Negotiate::Server.new(auth_type: "Kerberos")
68
+ assert_equal "Kerberos", server.auth_type
69
+ end
70
+
71
+ def test_token_basic_functionality
72
+ assert_respond_to(@server, :token)
73
+ assert_nothing_raised{ @server.token }
74
+ assert_kind_of(String, @server.token)
75
+ assert_equal "", @server.token
76
+ end
77
+
78
+ def test_username_and_domain_basic_functionality
79
+ assert_respond_to(@server, :username)
80
+ assert_nothing_raised{ @server.username }
81
+ assert_kind_of(String, @server.username)
82
+ assert_equal "", @server.username
83
+ assert_respond_to(@server, :domain)
84
+ assert_nothing_raised{ @server.domain }
85
+ assert_kind_of(String, @server.domain)
86
+ assert_equal "", @server.domain
87
+ end
88
+
89
+ def test_acquire_handle_basic_functionality
90
+ assert_respond_to(@server, :acquire_handle)
91
+ assert_equal 0, @server.method(:acquire_handle).arity
92
+ assert_respond_to(@server, :acquire_credentials_handle)
93
+ assert_equal 9, @server.method(:acquire_credentials_handle).arity
94
+ end
95
+
96
+ def test_accept_context_basic_functionality
97
+ assert_respond_to(@server, :accept_context)
98
+ assert_equal( -1, @server.method(:accept_context).arity)
99
+ assert_respond_to(@server, :accept_security_context)
100
+ assert_equal 9, @server.method(:accept_security_context).arity
101
+ assert_respond_to(@server, :complete_auth_token)
102
+ assert_equal 2, @server.method(:complete_auth_token).arity
103
+ end
104
+
105
+ def test_query_attributes_basic_functionality
106
+ assert_respond_to(@server, :query_attributes)
107
+ assert_equal 0, @server.method(:query_attributes).arity
108
+ assert_respond_to(@server, :query_context_attributes)
109
+ assert_equal 3, @server.method(:query_context_attributes).arity
110
+ assert_respond_to(@server, :free_credentials_handle)
111
+ assert_equal 1, @server.method(:free_credentials_handle).arity
112
+ end
113
+
114
+ def test_acquire_handle_invokes_windows_api_as_expected
115
+ server = Class.new(MockNegotiateServer).new
116
+ assert_nothing_raised{ @status = server.acquire_handle }
117
+ assert_equal Windows::Constants::SEC_E_OK, @status
118
+
119
+ # test acquire_credentials_handle
120
+ args = server.retrieve_state(:ach)
121
+ assert_equal 9, args.length, "unexpected args"
122
+ assert_nil args[0], "unexpected psz_principal"
123
+ assert_equal Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE, args[1], "unexpected psz_package"
124
+ assert_equal Windows::Constants::SECPKG_CRED_INBOUND, args[2], "unexpected f_credentialuse"
125
+ assert_nil args[3], "unexpected pv_logonid"
126
+ assert_nil args[4], "unexpected p_authdata"
127
+ assert_nil args[5], "unexpected p_getkeyfn"
128
+ assert_nil args[6], "unexpected p_getkeyarg"
129
+ assert_kind_of Windows::Structs::CredHandle, args[7], "unexpected ph_newcredentials"
130
+ assert_equal MockCredentialHandle, args[7].marshal_dump
131
+ assert_kind_of Windows::Structs::TimeStamp, args[8], "unexpected pts_expiry"
132
+ assert_equal MockTimeStamp, args[8].marshal_dump
133
+ end
134
+
135
+ def test_acquire_handle_memoizes_handle
136
+ server = Class.new(MockNegotiateServer).new
137
+ assert_nothing_raised{ server.acquire_handle }
138
+ assert_nothing_raised{ @status = server.acquire_handle }
139
+ assert_equal Windows::Constants::SEC_E_OK, @status
140
+ assert_equal 9, server.retrieve_state(:ach).length
141
+ end
142
+
143
+ def test_acquire_handle_raises_when_windows_api_returns_failed_status
144
+ server = Class.new(MockNegotiateServer) do
145
+ def acquire_credentials_handle(*args)
146
+ capture_state(:ach, args)
147
+ return Windows::Constants::SEC_E_SECPKG_NOT_FOUND
148
+ end
149
+ end.new
150
+ assert_raises(SecurityStatusError){ server.acquire_handle }
151
+ end
152
+
153
+ def test_accept_context_invokes_windows_api_as_expected
154
+ server = Class.new(MockNegotiateServer).new
155
+ assert_nothing_raised{ server.acquire_handle }
156
+ assert_nothing_raised{ @status = server.accept_context(MockSpnegoToken) }
157
+ assert_equal Windows::Constants::SEC_E_OK, @status
158
+
159
+ args = server.retrieve_state(:asc)
160
+ assert_equal 9, args.length, "unexpected args"
161
+ assert_kind_of Windows::Structs::CredHandle, args[0], "unexpected ph_credentials"
162
+ assert_equal MockCredentialHandle, args[0].marshal_dump
163
+ assert_nil args[1], "unexpected ph_context"
164
+ assert_kind_of Windows::Structs::SecBufferDesc, args[2], "unexpected p_input"
165
+ assert_equal ContextAttr, args[3], "unexpected f_contextreq"
166
+ assert_equal Windows::Constants::SECURITY_NATIVE_DREP, args[4], "unexpected targetdatarep"
167
+ assert_kind_of Windows::Structs::CtxtHandle, args[5], "unexpected ph_newcontext"
168
+ assert_equal MockContextHandle, args[5].marshal_dump
169
+ assert_kind_of Windows::Structs::SecBufferDesc, args[6], "unexpected p_output"
170
+ assert_equal MockSecBufferContent, server.token
171
+ assert_kind_of FFI::MemoryPointer, args[7], "unexpected pf_contextattr"
172
+ assert_equal ContextAttr, args[7].read_ulong
173
+ assert_kind_of Windows::Structs::TimeStamp, args[8], "unexpected pts_expiry"
174
+ assert_equal MockTimeStamp, args[8].marshal_dump
175
+ end
176
+
177
+ def test_accept_context_raises_when_windows_api_returns_failed_status
178
+ server = Class.new(MockNegotiateServer) do
179
+ def accept_security_context(*args)
180
+ capture_state(:asc, args)
181
+ return Windows::Constants::SEC_E_SECPKG_NOT_FOUND
182
+ end
183
+ end.new
184
+ assert_raises(SecurityStatusError){ server.accept_context }
185
+ end
186
+
187
+ def test_complet_auth_invokes_windows_api_as_expected
188
+ server = Class.new(MockNegotiateServer) do
189
+ def accept_security_context(*args)
190
+ super
191
+ return Windows::Constants::SEC_I_COMPLETE_NEEDED
192
+ end
193
+ end.new
194
+
195
+ assert_nothing_raised{ server.acquire_handle }
196
+ assert_nothing_raised{ server.accept_context(MockSpnegoToken) }
197
+ assert_nothing_raised{ @status=server.complete_authentication }
198
+ assert_equal Windows::Constants::SEC_E_OK, @status
199
+
200
+ args = server.retrieve_state(:cat)
201
+ assert_equal 2, args.length
202
+ assert_kind_of Windows::Structs::CtxtHandle, args[0], "unexpected ph_context"
203
+ assert_equal MockContextHandle, args[0].marshal_dump
204
+ assert_kind_of Windows::Structs::SecBufferDesc, args[1], "unexpected p_output"
205
+ assert_equal MockSecBufferContent, args[1].marshal_dump
206
+ end
207
+
208
+ def test_complet_auth_raises_when_windows_api_returns_failed_status
209
+ server = Class.new(MockNegotiateServer) do
210
+ def accept_security_context(*args)
211
+ super
212
+ return Windows::Constants::SEC_I_COMPLETE_NEEDED
213
+ end
214
+ def complete_auth_token(*args)
215
+ capture_state(:cat,args)
216
+ return Windows::Constants::SEC_E_INVALID_HANDLE
217
+ end
218
+ end.new
219
+
220
+ assert_nothing_raised{ server.acquire_handle }
221
+ assert_nothing_raised{ server.accept_context(MockSpnegoToken) }
222
+ assert_raises(SecurityStatusError){ server.complete_authentication }
223
+ end
224
+
225
+ def test_query_attributes_invokes_windows_api_as_expected
226
+ server = Class.new(MockNegotiateServer).new
227
+ assert_nothing_raised{ server.acquire_handle }
228
+ assert_nothing_raised{ server.accept_context(MockSpnegoToken) }
229
+ assert_nothing_raised{ @status = server.query_attributes }
230
+ assert_equal Windows::Constants::SEC_E_OK, @status
231
+
232
+ args = server.retrieve_state(:qca)
233
+ assert_equal 3, args.length
234
+ assert_kind_of Windows::Structs::CtxtHandle, args[0], "unexpected ph_context"
235
+ assert_equal MockContextHandle, args[0].marshal_dump
236
+ assert_equal Windows::Constants::SECPKG_ATTR_NAMES, args[1], "unexpected ul_attribute"
237
+ assert_kind_of Windows::Structs::SecPkgContext_Names, args[2], "unexpected p_buffer"
238
+
239
+ assert_equal "jimmy", server.username
240
+ assert_equal "jes.local", server.domain
241
+
242
+ # Need to grap this address for test below
243
+ expected_fcb_ptr = args[2].to_username_ptr
244
+
245
+ args = server.retrieve_state(:fcb)
246
+ assert_equal 1, args.length
247
+ assert_equal expected_fcb_ptr, args[0], "unexpected pv_contextbuffer"
248
+ end
249
+
250
+ def test_query_attributes_without_domain_in_user_string
251
+ server = Class.new(MockNegotiateServer) do
252
+ def query_context_attributes(*args)
253
+ super
254
+ args[2].marshal_load("jimmy")
255
+ return Windows::Constants::SEC_E_OK
256
+ end
257
+ end.new
258
+ assert_nothing_raised{ server.acquire_handle }
259
+ assert_nothing_raised{ server.accept_context(MockSpnegoToken) }
260
+ assert_nothing_raised{ @status = server.query_attributes }
261
+ assert_equal Windows::Constants::SEC_E_OK, @status
262
+ assert_equal "jimmy", server.username
263
+ assert_equal "", server.domain
264
+ end
265
+
266
+ def test_query_attributes_raises_when_windows_api_returns_failed_status
267
+ server = Class.new(MockNegotiateServer) do
268
+ def query_context_attributes(*args)
269
+ capture_state(:asc, args)
270
+ return Windows::Constants::SEC_E_INVALID_HANDLE
271
+ end
272
+ end.new
273
+ assert_raises(SecurityStatusError){ server.query_attributes }
274
+ end
275
+
276
+ def test_free_handles_raises_when_free_handle_returns_failed_status
277
+ server = Class.new(MockNegotiateServer) do
278
+ def free_credentials_handle(*args)
279
+ capture_state(:fch,args)
280
+ return Windows::Constants::SEC_E_INVALID_HANDLE
281
+ end
282
+ end.new
283
+
284
+ assert_nothing_raised{ server.acquire_handle }
285
+ assert_nothing_raised{ server.accept_context(MockSpnegoToken) }
286
+ assert_nothing_raised{ server.query_attributes }
287
+ assert_raises(SecurityStatusError){ server.free_handles }
288
+ end
289
+
290
+ def test_both_handles_freed_when_free_handles_raises
291
+ server = Class.new(MockNegotiateServer) do
292
+ def delete_security_context(*args)
293
+ capture_state(:dsc, args)
294
+ return Windows::Constants::SEC_E_INVALID_TOKEN
295
+ end
296
+ end.new
297
+
298
+ assert_nothing_raised{ server.acquire_handle }
299
+ assert_nothing_raised{ server.accept_context(MockSpnegoToken) }
300
+
301
+ refute_nil server.instance_variable_get(:@context_handle)
302
+ refute_nil server.instance_variable_get(:@credentials_handle)
303
+
304
+ assert_raises{ server.free_handles }
305
+
306
+ assert_nil server.instance_variable_get(:@context_handle)
307
+ assert_nil server.instance_variable_get(:@credentials_handle)
308
+ end
309
+
310
+ def test_http_authenticate
311
+ server = Class.new(MockNegotiateServer) do
312
+ def accept_security_context(*args)
313
+ status = self.retrieve_state(:asc) ?
314
+ Windows::Constants::SEC_I_COMPLETE_NEEDED :
315
+ Windows::Constants::SEC_I_CONTINUE_NEEDED
316
+ super
317
+ return status
318
+ end
319
+ end.new
320
+
321
+ counter = 0
322
+ authenticated = false
323
+ until( authenticated )
324
+ header = "#{Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE} #{Base64.strict_encode64(MockSpnegoToken)}"
325
+ authenticated = server.http_authenticate(header) do |hdr|
326
+ counter += 1
327
+ fail "loop failed to complete in a reasonable iteration count" if counter > 3
328
+ assert_base64_http_header(hdr,Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE)
329
+ hdr
330
+ end
331
+ end
332
+
333
+ assert_equal 2, counter
334
+
335
+ assert_server_call_state(server)
336
+ end
337
+
338
+ def test_authenticate
339
+ server = Class.new(MockNegotiateServer) do
340
+ def accept_security_context(*args)
341
+ status = self.retrieve_state(:asc) ?
342
+ Windows::Constants::SEC_I_COMPLETE_NEEDED :
343
+ Windows::Constants::SEC_I_CONTINUE_NEEDED
344
+ super
345
+ return status
346
+ end
347
+ end.new
348
+
349
+ counter = 0
350
+ authenticated = false
351
+ until( authenticated )
352
+ client_msg = MockSpnegoToken
353
+ authenticated = server.authenticate(client_msg) do |msg|
354
+ counter += 1
355
+ fail "loop failed to complete in a reasonable iteration count" if counter > 3
356
+ assert_equal MockSecBufferContent,msg
357
+ msg
358
+ end
359
+ end
360
+
361
+ assert_equal 2, counter
362
+
363
+ assert_server_call_state(server)
364
+ end
365
+
366
+ def teardown
367
+ @server = nil
368
+ end
369
+ end
370
+
371
+ class MockNegotiateServer < Win32::SSPI::Negotiate::Server
372
+ def acquire_credentials_handle(*args)
373
+ s_args = retrieve_state(:ach) || Array.new
374
+ s_args << args
375
+ capture_state(:ach,s_args.flatten)
376
+ # this api should return a credential handle in arg[7] and a timestamp in arg[8]
377
+ args[7].marshal_load(TC_Win32_SSPI_Negotiate_Server::MockCredentialHandle)
378
+ args[8].marshal_load(TC_Win32_SSPI_Negotiate_Server::MockTimeStamp)
379
+ return Windows::Constants::SEC_E_OK
380
+ end
381
+
382
+ def accept_security_context(*args)
383
+ capture_state(:asc, args)
384
+ # this api should return a new context, p_output, context attr and timestamp
385
+ args[5].marshal_load(TC_Win32_SSPI_Negotiate_Server::MockContextHandle)
386
+ args[6].marshal_load(TC_Win32_SSPI_Negotiate_Server::MockSecBufferContent)
387
+ args[7].write_ulong(TC_Win32_SSPI_Negotiate_Server::ContextAttr)
388
+ args[8].marshal_load(TC_Win32_SSPI_Negotiate_Server::MockTimeStamp)
389
+ return Windows::Constants::SEC_E_OK
390
+ end
391
+
392
+ def complete_auth_token(*args)
393
+ capture_state(:cat,args)
394
+ return Windows::Constants::SEC_E_OK
395
+ end
396
+
397
+ def query_context_attributes(*args)
398
+ capture_state(:qca,args)
399
+ args[2].marshal_load("jes.local\\jimmy")
400
+ return Windows::Constants::SEC_E_OK
401
+ end
402
+
403
+ def delete_security_context(*args)
404
+ capture_state(:dsc,args)
405
+ return Windows::Constants::SEC_E_OK
406
+ end
407
+
408
+ def free_credentials_handle(*args)
409
+ capture_state(:fch,args)
410
+ return Windows::Constants::SEC_E_OK
411
+ end
412
+
413
+ def free_context_buffer(*args)
414
+ capture_state(:fcb,args)
415
+ return Windows::Constants::SEC_E_OK
416
+ end
417
+
418
+ def capture_state(key,value)
419
+ self.class.capture_state(key,value)
420
+ end
421
+
422
+ def retrieve_state(key)
423
+ self.class.retrieve_state(key)
424
+ end
425
+
426
+ def self.state
427
+ @state ||= Hash.new
428
+ end
429
+
430
+ def self.capture_state(key,value)
431
+ state[key] = value
432
+ end
433
+
434
+ def self.retrieve_state(key)
435
+ state[key]
436
+ end
437
+
438
+ def self.clear_state
439
+ state.clear
440
+ end
441
+ end
@@ -0,0 +1,100 @@
1
+ ########################################################################
2
+ # Tests for the Windows::API::create_xxx methods
3
+ ########################################################################
4
+ require 'test-unit'
5
+ require 'win32/sspi/api/common'
6
+
7
+ class TC_Win32_SSPI_Negotiate_Client < Test::Unit::TestCase
8
+ include Win32::SSPI::API::Common
9
+
10
+ def test_create_sec_winnt_auth_identity
11
+ user,domain,password = %w[tom gas yadayadayada]
12
+ identity = create_sec_winnt_auth_identity(user,domain,password)
13
+ assert_equal user, identity[:User].read_string
14
+ assert_equal user.length, identity[:UserLength]
15
+ assert_equal user, identity.user_to_ruby_s
16
+ assert_equal domain, identity[:Domain].read_string
17
+ assert_equal domain.length, identity[:DomainLength]
18
+ assert_equal domain, identity.domain_to_ruby_s
19
+ assert_equal password, identity[:Password].read_string
20
+ assert_equal password.length, identity[:PasswordLength]
21
+ assert_equal SEC_WINNT_AUTH_IDENTITY_ANSI, identity[:Flags]
22
+ end
23
+
24
+ def test_create_credhandle
25
+ h_credential = create_credhandle(777,888)
26
+ assert_equal 777, h_credential[:dwLower].read_ulong
27
+ assert_equal 888, h_credential[:dwUpper].read_ulong
28
+ assert_equal [777,888], h_credential.marshal_dump
29
+ end
30
+
31
+ def test_create_ctxhandle
32
+ h_ctx = create_ctxhandle(777,888)
33
+ assert_equal 777, h_ctx[:dwLower].read_ulong
34
+ assert_equal 888, h_ctx[:dwUpper].read_ulong
35
+ assert_equal [777,888], h_ctx.marshal_dump
36
+ end
37
+
38
+ def test_create_timestamp
39
+ ts = create_timestamp(0xFFAA8811,0x00000044)
40
+ assert_equal 0xFFAA8811, ts[:dwLowDateTime]
41
+ assert_equal 0x00000044, ts[:dwHighDateTime]
42
+ end
43
+
44
+ def test_create_secbuffer
45
+ content = "test content"
46
+ buffer = create_secbuffer(content)
47
+ assert_equal content, buffer[:pvBuffer].read_string
48
+ assert_equal content.length, buffer[:cbBuffer]
49
+ assert_equal SECBUFFER_TOKEN, buffer[:BufferType]
50
+
51
+ buffer = create_secbuffer
52
+ assert_not_nil buffer[:pvBuffer]
53
+ assert_equal TOKENBUFSIZE, buffer[:cbBuffer]
54
+ assert_equal SECBUFFER_TOKEN, buffer[:BufferType]
55
+ end
56
+
57
+ def test_create_secbufferdesc
58
+ content = "test content"
59
+ buffer = create_secbuffer(content)
60
+ bufdesc = create_secbufferdesc(buffer)
61
+ assert_equal FFI::Pointer.new(buffer.pointer), bufdesc[:pBuffers]
62
+ assert_equal 1, bufdesc[:cBuffers]
63
+ assert_equal SECBUFFER_VERSION, bufdesc[:ulVersion]
64
+ end
65
+
66
+ def test_create_secpkg_context_names
67
+ name ="test"
68
+ spkg_names = create_secpkg_context_names(name)
69
+ assert_equal name, spkg_names[:sUserName].read_string
70
+ end
71
+
72
+ def test_to_ruby_s_accessors
73
+ content = "test content"
74
+ buffer = create_secbuffer(content)
75
+ assert_equal content, buffer.to_ruby_s
76
+
77
+ # FIXME: something un-intuitive about this ...
78
+ buffer = create_secbuffer
79
+ assert_equal TOKENBUFSIZE, buffer.to_ruby_s.length
80
+
81
+ name ="test"
82
+ spkg_names = create_secpkg_context_names(name)
83
+ assert_equal name, spkg_names.to_ruby_s
84
+
85
+ spkg_names = create_secpkg_context_names
86
+ assert_nil spkg_names.to_ruby_s
87
+ end
88
+
89
+ def test_construct_de_construct_http_header
90
+ srand(Time.now.to_i)
91
+ tokenA = (1..40).inject([]) {|m,r| m << rand(255)}.join('')
92
+ header = construct_http_header(Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE, tokenA)
93
+ assert_equal Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE, header[0,9]
94
+ assert_match( /\A\p{Print}+\Z/, header)
95
+
96
+ auth_type, tokenB = de_construct_http_header(header)
97
+ assert_equal Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE, auth_type
98
+ assert_equal tokenA, tokenB
99
+ end
100
+ end