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.
- checksums.yaml +7 -0
- data/License.txt +8 -0
- data/README.md +31 -0
- data/Rakefile +49 -0
- data/examples/sspi_negotiate_client.rb +42 -0
- data/examples/sspi_negotiate_server.rb +99 -0
- data/lib/win32-sspi.rb +2 -0
- data/lib/win32/sspi/api/client.rb +17 -0
- data/lib/win32/sspi/api/common.rb +133 -0
- data/lib/win32/sspi/api/server.rb +32 -0
- data/lib/win32/sspi/negotiate/client.rb +159 -0
- data/lib/win32/sspi/negotiate/server.rb +199 -0
- data/lib/win32/sspi/windows/constants.rb +36 -0
- data/lib/win32/sspi/windows/functions.rb +31 -0
- data/lib/win32/sspi/windows/misc.rb +24 -0
- data/lib/win32/sspi/windows/structs.rb +147 -0
- data/test/all_tests.rb +2 -0
- data/test/test_win32_sspi_negotiate_client.rb +417 -0
- data/test/test_win32_sspi_negotiate_server.rb +441 -0
- data/test/test_win32_sspi_structure_creates.rb +100 -0
- data/win32-sspi.gemspec +29 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 94ed63fe1f2bf199120cb6e027ad643db0512179
|
4
|
+
data.tar.gz: 64236f4dd2ae87f6fb9f56510cf9eef7a09281a7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f47abf6eeec115b0e30fc2881b1493d106ed406b5795f49971531f1566c20e543317d2698c572d38ad51ef772437958ef596bda8341ceab2e3ba31139485d4c5
|
7
|
+
data.tar.gz: f1c4cbbcc6bf624cec854aeecf8294f1baa3b5e82ae66969cf103bfd92404ee0cec86c5c0c26a313cc8a744b069a0cb9e3dd1ff68172e4f3909f05ad2a8fc68a
|
data/License.txt
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
MIT License
|
2
|
+
Copyright (c) 2015 Gary Sick
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
5
|
+
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
7
|
+
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
This project is a fork of djbergers win32-sspi project which
|
2
|
+
provided the beginnings of a an FFI implementation of SSPI
|
3
|
+
on Windows. This project adds support for Kerberos through the
|
4
|
+
SPNEGO/Negotiate protocol.
|
5
|
+
|
6
|
+
The examples directory has working examples to illustrate
|
7
|
+
usage. The sspi_negotiate_*.rb files are client/server
|
8
|
+
examples of using Kerberos through SPNEGO/Negotiate protocol.
|
9
|
+
In order to be used successfully across multiple systems
|
10
|
+
in your domain you must define a Service Principal Name (SPN)
|
11
|
+
associated with the user account under which the server is
|
12
|
+
running unless the server is running as a Windows Service
|
13
|
+
under the LocalSystem account. To establish a SPN use the
|
14
|
+
setspn command as follows from a elevated privilege command
|
15
|
+
window:
|
16
|
+
|
17
|
+
```
|
18
|
+
setspn -S HTTP/fqdn-of-your-host USERDOMAIN\USERNAME
|
19
|
+
```
|
20
|
+
The SPN you establish must be passed to the Client constructor
|
21
|
+
with the spn option in order to succesfully connect and
|
22
|
+
authenticate with the server.
|
23
|
+
|
24
|
+
The negotiate client/server implementations are also capable
|
25
|
+
of supporting the NTLM protocol. To do so specify the auth_type
|
26
|
+
option as 'NTLM' on the construction of the client. The user name
|
27
|
+
and user domain will be taken from the environment and will use
|
28
|
+
the current logged in users security context to authenticate with
|
29
|
+
the server. Otherwise passing the options username, domain, password
|
30
|
+
to the client constructor will establish a different security
|
31
|
+
context for the given username to authenticate against.
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/clean'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'rubygems/package'
|
6
|
+
|
7
|
+
CLEAN.include('**/*.gem')
|
8
|
+
|
9
|
+
namespace :gem do
|
10
|
+
desc "Create the win32-sspi gem"
|
11
|
+
task :create => [:clean] do
|
12
|
+
spec = eval(IO.read('win32-sspi.gemspec'))
|
13
|
+
Gem::Package.build(spec)
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Install the win32-sspi gem"
|
17
|
+
task :install => [:create] do
|
18
|
+
file = Dir["*.gem"].first
|
19
|
+
sh "gem install #{file} -l --no-document"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
namespace :test do
|
24
|
+
Rake::TestTask.new(:struct) do |t|
|
25
|
+
t.test_files = FileList['test/test_win32_sspi_structure_creates.rb']
|
26
|
+
t.warning = true
|
27
|
+
t.verbose = true
|
28
|
+
end
|
29
|
+
|
30
|
+
Rake::TestTask.new(:client) do |t|
|
31
|
+
t.test_files = FileList['test/test_win32_sspi_negotiate_client.rb']
|
32
|
+
t.warning = true
|
33
|
+
t.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
Rake::TestTask.new(:server) do |t|
|
37
|
+
t.test_files = FileList['test/test_win32_sspi_negotiate_server.rb']
|
38
|
+
t.warning = true
|
39
|
+
t.verbose = true
|
40
|
+
end
|
41
|
+
|
42
|
+
Rake::TestTask.new(:all) do |t|
|
43
|
+
t.test_files = FileList['test/test_win32*']
|
44
|
+
t.warning = true
|
45
|
+
t.verbose = true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
task :default => 'test:all'
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'net/http'
|
3
|
+
unless ENV['WIN32_SSPI_TEST']
|
4
|
+
require 'win32-sspi'
|
5
|
+
require 'negotiate/client'
|
6
|
+
else
|
7
|
+
require 'win32/sspi/negotiate/client'
|
8
|
+
puts "!!!! running with test environment !!!"
|
9
|
+
end
|
10
|
+
|
11
|
+
class RubySSPIClient
|
12
|
+
def self.run(url,auth_type)
|
13
|
+
uri = URI.parse(url)
|
14
|
+
client = (Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE == auth_type) ?
|
15
|
+
Win32::SSPI::Negotiate::Client.new(spn:"HTTP/#{uri.host}") :
|
16
|
+
Win32::SSPI::Negotiate::Client.new(auth_type:auth_type)
|
17
|
+
|
18
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
19
|
+
resp = nil
|
20
|
+
client.http_authenticate do |header|
|
21
|
+
req = Net::HTTP::Get.new(uri.path)
|
22
|
+
req['Authorization'] = header
|
23
|
+
resp = http.request(req)
|
24
|
+
resp['www-authenticate']
|
25
|
+
end
|
26
|
+
|
27
|
+
puts resp.body if resp.body
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if __FILE__ == $0
|
33
|
+
if ARGV.length < 1
|
34
|
+
puts "usage: ruby sspi_negotiate_client.rb url [auth_type (Negotiate|NTLM default=Negotiate)]"
|
35
|
+
puts "where: url = http://hostname:port/path"
|
36
|
+
exit(0)
|
37
|
+
end
|
38
|
+
|
39
|
+
url = ARGV[0]
|
40
|
+
auth_type = (2 == ARGV.length) ? ARGV[1] : Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE
|
41
|
+
RubySSPIClient.run(url,auth_type)
|
42
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# Attempting to setup an example authenticating server
|
2
|
+
require 'webrick'
|
3
|
+
unless ENV['WIN32_SSPI_TEST']
|
4
|
+
require 'win32-sspi'
|
5
|
+
require 'negotiate/server'
|
6
|
+
else
|
7
|
+
require 'win32/sspi/negotiate/server'
|
8
|
+
puts "!!!! running with test environment !!!"
|
9
|
+
end
|
10
|
+
|
11
|
+
# A way to store state across multiple requests
|
12
|
+
class StateStore
|
13
|
+
def self.state
|
14
|
+
@state ||= Hash.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.store_state(key,value)
|
18
|
+
state[key] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.retrieve_state(key)
|
22
|
+
state[key]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.clear_state
|
26
|
+
state.clear
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.retrieve_server(auth_type=Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE)
|
30
|
+
state[:server] ||= Win32::SSPI::Negotiate::Server.new(auth_type: auth_type)
|
31
|
+
state[:server]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
class RubySSPIServlet < WEBrick::HTTPServlet::AbstractServlet
|
37
|
+
def initialize(server,auth_type)
|
38
|
+
super server
|
39
|
+
@auth_type = auth_type
|
40
|
+
end
|
41
|
+
|
42
|
+
def do_GET(req,resp)
|
43
|
+
if req['Authorization'].nil? || req['Authorization'].empty?
|
44
|
+
resp['www-authenticate'] = @auth_type
|
45
|
+
resp.status = 401
|
46
|
+
return
|
47
|
+
end
|
48
|
+
|
49
|
+
authenticated = false
|
50
|
+
|
51
|
+
begin
|
52
|
+
sspi_server = StateStore.retrieve_server(@auth_type)
|
53
|
+
authenticated = sspi_server.http_authenticate(req['Authorization']) do |header,complete|
|
54
|
+
resp['www-authenticate'] = header if header
|
55
|
+
if complete
|
56
|
+
resp.status = 200
|
57
|
+
resp['Remote-User'] = sspi_server.username
|
58
|
+
resp['Remote-User-Domain'] = sspi_server.domain
|
59
|
+
resp['Content-Type'] = "text/plain"
|
60
|
+
resp.body = "#{Time.now}: Hello #{sspi_server.username} at #{sspi_server.domain}"
|
61
|
+
else
|
62
|
+
resp.status = 401
|
63
|
+
end
|
64
|
+
end
|
65
|
+
rescue SecurityStatusError => e
|
66
|
+
sspi_server.free_handles
|
67
|
+
StateStore.clear_state
|
68
|
+
|
69
|
+
resp.status = 401
|
70
|
+
resp['www-authenticate'] = @auth_type
|
71
|
+
resp['Content-Type'] = "text/plain"
|
72
|
+
resp.body = e.message
|
73
|
+
puts "*** server encountered the following error ***\n #{e.message}"
|
74
|
+
return
|
75
|
+
end
|
76
|
+
|
77
|
+
StateStore.clear_state if authenticated
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.run(url,auth_type)
|
81
|
+
uri = URI.parse(url)
|
82
|
+
s = WEBrick::HTTPServer.new( :Binding=>uri.host, :Port=>uri.port)
|
83
|
+
s.mount(uri.path, RubySSPIServlet, auth_type)
|
84
|
+
trap("INT") { s.shutdown }
|
85
|
+
s.start
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
if $0 == __FILE__
|
90
|
+
if ARGV.length < 1
|
91
|
+
puts "usage: ruby sspi_negotiate_server.rb url [auth_type (Negotiate|NTLM default=Negotiate)]"
|
92
|
+
puts "where: url = http://hostname:port/path"
|
93
|
+
exit(0)
|
94
|
+
end
|
95
|
+
|
96
|
+
url = ARGV[0]
|
97
|
+
auth_type = (2 == ARGV.length) ? ARGV[1] : Win32::SSPI::API::Common::AUTH_TYPE_NEGOTIATE
|
98
|
+
RubySSPIServlet.run(url,auth_type)
|
99
|
+
end
|
data/lib/win32-sspi.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Win32
|
4
|
+
module SSPI
|
5
|
+
module API
|
6
|
+
module Client
|
7
|
+
include Common
|
8
|
+
|
9
|
+
def initialize_security_context(ph_credential,ph_context,psz_targetname,f_contextreq,reserved1,targetdatarep,p_input,reserved2,ph_newcontext,p_output,pf_contextattr,pts_expiry)
|
10
|
+
status = InitializeSecurityContext(ph_credential,ph_context,psz_targetname,f_contextreq,reserved1,targetdatarep,p_input,reserved2,ph_newcontext,p_output,pf_contextattr,pts_expiry)
|
11
|
+
return status
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require_relative '../windows/constants'
|
3
|
+
require_relative '../windows/structs'
|
4
|
+
require_relative '../windows/functions'
|
5
|
+
|
6
|
+
module Win32
|
7
|
+
module SSPI
|
8
|
+
module API
|
9
|
+
module Common
|
10
|
+
include Windows::Constants
|
11
|
+
include Windows::Structs
|
12
|
+
include Windows::Functions
|
13
|
+
|
14
|
+
AUTH_TYPE_NEGOTIATE = 'Negotiate'
|
15
|
+
AUTH_TYPE_NTLM = 'NTLM'
|
16
|
+
|
17
|
+
def create_sec_winnt_auth_identity(username,domain,password)
|
18
|
+
auth_struct = SEC_WINNT_AUTH_IDENTITY.new
|
19
|
+
auth_struct[:Flags] = SEC_WINNT_AUTH_IDENTITY_ANSI
|
20
|
+
|
21
|
+
if username
|
22
|
+
auth_struct[:User] = FFI::MemoryPointer.from_string(username.dup)
|
23
|
+
auth_struct[:UserLength] = username.size
|
24
|
+
end
|
25
|
+
|
26
|
+
if domain
|
27
|
+
auth_struct[:Domain] = FFI::MemoryPointer.from_string(domain.dup)
|
28
|
+
auth_struct[:DomainLength] = domain.size
|
29
|
+
end
|
30
|
+
|
31
|
+
if password
|
32
|
+
auth_struct[:Password] = FFI::MemoryPointer.from_string(password.dup)
|
33
|
+
auth_struct[:PasswordLength] = password.size
|
34
|
+
end
|
35
|
+
|
36
|
+
auth_struct
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_credhandle(lower=nil,upper=nil)
|
40
|
+
result = CredHandle.new
|
41
|
+
|
42
|
+
if lower && upper
|
43
|
+
result.marshal_load([lower,upper])
|
44
|
+
end
|
45
|
+
|
46
|
+
result
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_ctxhandle(lower=nil,upper=nil)
|
50
|
+
result = CtxtHandle.new
|
51
|
+
|
52
|
+
if lower && upper
|
53
|
+
result.marshal_load([lower,upper])
|
54
|
+
end
|
55
|
+
|
56
|
+
result
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_timestamp(low=nil,high=nil)
|
60
|
+
ts = TimeStamp.new
|
61
|
+
if low && high
|
62
|
+
ts[:dwLowDateTime] = low
|
63
|
+
ts[:dwHighDateTime] = high
|
64
|
+
end
|
65
|
+
ts
|
66
|
+
end
|
67
|
+
|
68
|
+
def create_secbuffer(content=nil)
|
69
|
+
SecBuffer.new.init(content)
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_secbufferdesc(sec_buffer=nil)
|
73
|
+
SecBufferDesc.new.init(sec_buffer)
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_secpkg_context_names(name=nil)
|
77
|
+
result = SecPkgContext_Names.new
|
78
|
+
if name
|
79
|
+
result[:sUserName] = FFI::MemoryPointer.from_string(name.dup)
|
80
|
+
end
|
81
|
+
result
|
82
|
+
end
|
83
|
+
|
84
|
+
def construct_http_header(auth_type, token)
|
85
|
+
b64_token = token.nil? ? nil : Base64.strict_encode64(token)
|
86
|
+
b64_token.nil? ? "#{auth_type}" : "#{auth_type} #{b64_token}"
|
87
|
+
end
|
88
|
+
|
89
|
+
def de_construct_http_header(header)
|
90
|
+
auth_type, b64_token = header.split(' ')
|
91
|
+
token = b64_token.nil? ? nil : Base64.strict_decode64(b64_token)
|
92
|
+
[auth_type, token]
|
93
|
+
end
|
94
|
+
|
95
|
+
def acquire_credentials_handle(psz_principal,psz_package,f_credentialuse,pv_logonid,p_authdata,p_getkeyfn,pv_getkeyarg,ph_credential,pts_expiry)
|
96
|
+
status = AcquireCredentialsHandle(psz_principal,psz_package,f_credentialuse,pv_logonid,p_authdata,p_getkeyfn,pv_getkeyarg,ph_credential,pts_expiry)
|
97
|
+
return status
|
98
|
+
end
|
99
|
+
|
100
|
+
def query_context_attributes(ph_context,ul_attribute,p_buffer)
|
101
|
+
status = QueryContextAttributes(ph_context, ul_attribute, p_buffer)
|
102
|
+
return status
|
103
|
+
end
|
104
|
+
|
105
|
+
def delete_security_context(ph_context)
|
106
|
+
status = DeleteSecurityContext(ph_context)
|
107
|
+
return status
|
108
|
+
end
|
109
|
+
|
110
|
+
def free_credentials_handle(ph_credential)
|
111
|
+
status = FreeCredentialsHandle(ph_credential)
|
112
|
+
return status
|
113
|
+
end
|
114
|
+
|
115
|
+
def free_context_and_credentials(context,credentials)
|
116
|
+
result = {name:'', status:SEC_E_OK, dsc_status:SEC_E_OK, fch_status:SEC_E_OK}
|
117
|
+
status = delete_security_context(context)
|
118
|
+
if SEC_E_OK != status
|
119
|
+
result[:name], result[:status], result[:dsc_status] = ["DeleteSecurityContext", status, status]
|
120
|
+
end
|
121
|
+
|
122
|
+
status = free_credentials_handle(credentials)
|
123
|
+
if SEC_E_OK != status
|
124
|
+
result[:name], result[:status], result[:fch_status] = ["FreeCredentialsHandle", status, status]
|
125
|
+
end
|
126
|
+
|
127
|
+
return result
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Win32
|
4
|
+
module SSPI
|
5
|
+
module API
|
6
|
+
module Server
|
7
|
+
include Common
|
8
|
+
|
9
|
+
def accept_security_context(ph_credential,ph_context,p_input,f_contextreq,targetdatarep,ph_newcontext,p_output,pf_contextattr,pts_timestamp)
|
10
|
+
status = AcceptSecurityContext(ph_credential,ph_context,p_input,f_contextreq,targetdatarep,ph_newcontext,p_output,pf_contextattr,pts_timestamp)
|
11
|
+
return status
|
12
|
+
end
|
13
|
+
|
14
|
+
def complete_auth_token(ph_context,p_token)
|
15
|
+
status = CompleteAuthToken(ph_context,p_token)
|
16
|
+
return status
|
17
|
+
end
|
18
|
+
|
19
|
+
def enumerate_security_packages(pc_packages,pp_packageinfo)
|
20
|
+
status = EnumerateSecurityPackages(pc_packages,pp_packageinfo)
|
21
|
+
return status
|
22
|
+
end
|
23
|
+
|
24
|
+
def free_context_buffer(pv_contextbuffer)
|
25
|
+
status = FreeContextBuffer(pv_contextbuffer)
|
26
|
+
return status
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require_relative '../windows/constants'
|
2
|
+
require_relative '../windows/misc'
|
3
|
+
require_relative '../api/client'
|
4
|
+
|
5
|
+
module Win32
|
6
|
+
module SSPI
|
7
|
+
module Negotiate
|
8
|
+
class Client
|
9
|
+
include Windows::Constants
|
10
|
+
include API::Client
|
11
|
+
|
12
|
+
attr_reader :spn
|
13
|
+
attr_reader :auth_type
|
14
|
+
attr_reader :token
|
15
|
+
|
16
|
+
def initialize(options={})
|
17
|
+
@spn = options[:spn]
|
18
|
+
@auth_type = options[:auth_type] || AUTH_TYPE_NEGOTIATE
|
19
|
+
@token = nil
|
20
|
+
@credentials_handle = nil
|
21
|
+
@context_handle = nil
|
22
|
+
@username = options[:username]
|
23
|
+
@domain = options[:domain]
|
24
|
+
@password = options[:password]
|
25
|
+
end
|
26
|
+
|
27
|
+
def http_authenticate(&block)
|
28
|
+
perform_authenticate(block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def authenticate(&block)
|
32
|
+
perform_authenticate(block,false)
|
33
|
+
end
|
34
|
+
|
35
|
+
def perform_authenticate(block,with_http_header=true)
|
36
|
+
status = acquire_handle
|
37
|
+
if SEC_E_OK == status
|
38
|
+
begin
|
39
|
+
status = initialize_context(self.token)
|
40
|
+
if SEC_I_CONTINUE_NEEDED == status
|
41
|
+
token_from_block_result(block.call(block_arg_from_token(with_http_header)),with_http_header)
|
42
|
+
end
|
43
|
+
end while( SEC_I_CONTINUE_NEEDED == status )
|
44
|
+
|
45
|
+
# if using NTLM protocol we need to complete the final leg of the authentication
|
46
|
+
if AUTH_TYPE_NTLM == self.auth_type && SEC_E_OK == status
|
47
|
+
block.call(block_arg_from_token(with_http_header))
|
48
|
+
end
|
49
|
+
|
50
|
+
if SEC_E_OK == status
|
51
|
+
free_handles
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def block_arg_from_token(with_http_header)
|
57
|
+
with_http_header ? construct_http_header(self.auth_type,self.token) : self.token
|
58
|
+
end
|
59
|
+
|
60
|
+
def token_from_block_result(block_result,with_http_header)
|
61
|
+
if block_result
|
62
|
+
if with_http_header
|
63
|
+
@auth_type, @token = de_construct_http_header(block_result)
|
64
|
+
else
|
65
|
+
@token = block_result
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def acquire_handle
|
71
|
+
return SEC_E_OK if @credentials_handle
|
72
|
+
|
73
|
+
auth_data = nil
|
74
|
+
if AUTH_TYPE_NTLM == @auth_type
|
75
|
+
@username ||= ENV['USERNAME']
|
76
|
+
@domain ||= ENV['USERDOMAIN']
|
77
|
+
auth_data = create_sec_winnt_auth_identity(@username,@domain,@password)
|
78
|
+
end
|
79
|
+
|
80
|
+
@credentials_handle = create_credhandle
|
81
|
+
expiry = create_timestamp
|
82
|
+
|
83
|
+
status = acquire_credentials_handle(
|
84
|
+
@spn,
|
85
|
+
@auth_type,
|
86
|
+
SECPKG_CRED_OUTBOUND,
|
87
|
+
nil,
|
88
|
+
auth_data,
|
89
|
+
nil,
|
90
|
+
nil,
|
91
|
+
@credentials_handle,
|
92
|
+
expiry
|
93
|
+
)
|
94
|
+
|
95
|
+
if SEC_E_OK != status
|
96
|
+
@credentials_handle = nil
|
97
|
+
raise SecurityStatusError.new('AcquireCredentialsHandle', status, FFI.errno)
|
98
|
+
end
|
99
|
+
|
100
|
+
status
|
101
|
+
end
|
102
|
+
|
103
|
+
def initialize_context(token=nil)
|
104
|
+
return SEC_E_OK if token.nil? && @context_handle
|
105
|
+
|
106
|
+
ctx = @context_handle
|
107
|
+
@context_handle ||= create_ctxhandle
|
108
|
+
context_attributes = FFI::MemoryPointer.new(:ulong)
|
109
|
+
|
110
|
+
rflags = ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION
|
111
|
+
expiry = create_timestamp
|
112
|
+
|
113
|
+
if token
|
114
|
+
input_buffer = create_secbuffer(token)
|
115
|
+
input_buffer_desc = create_secbufferdesc(input_buffer)
|
116
|
+
end
|
117
|
+
|
118
|
+
output_buffer = create_secbuffer
|
119
|
+
output_buffer_desc = create_secbufferdesc(output_buffer)
|
120
|
+
|
121
|
+
status = initialize_security_context(
|
122
|
+
@credentials_handle,
|
123
|
+
ctx,
|
124
|
+
@spn,
|
125
|
+
rflags,
|
126
|
+
0,
|
127
|
+
SECURITY_NETWORK_DREP,
|
128
|
+
(token ? input_buffer_desc : nil),
|
129
|
+
0,
|
130
|
+
@context_handle,
|
131
|
+
output_buffer_desc,
|
132
|
+
context_attributes,
|
133
|
+
expiry
|
134
|
+
)
|
135
|
+
|
136
|
+
a_success = [SEC_E_OK, SEC_I_CONTINUE_NEEDED]
|
137
|
+
if a_success.include?(status)
|
138
|
+
@token = output_buffer.to_ruby_s
|
139
|
+
else
|
140
|
+
raise SecurityStatusError.new('InitializeSecurityContext', status, FFI.errno)
|
141
|
+
end
|
142
|
+
|
143
|
+
status
|
144
|
+
end
|
145
|
+
|
146
|
+
def free_handles
|
147
|
+
result = free_context_and_credentials(@context_handle, @credentials_handle)
|
148
|
+
@context_handle, @credentials_handle = [nil,nil]
|
149
|
+
|
150
|
+
if SEC_E_OK != result[:status]
|
151
|
+
raise SecurityStatusError.new(result[:name], result[:status], FFI.errno)
|
152
|
+
end
|
153
|
+
|
154
|
+
result[:status]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|