net-ssh-kerberos 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +33 -4
- data/VERSION.yml +2 -2
- data/lib/net/ssh/authentication/methods/gssapi_with_mic.rb +5 -8
- data/lib/net/ssh/kerberos.rb +7 -20
- data/lib/net/ssh/kerberos/constants.rb +3 -7
- data/lib/net/ssh/kerberos/context.rb +75 -0
- data/lib/net/ssh/kerberos/drivers.rb +57 -0
- data/lib/net/ssh/kerberos/drivers/gss.rb +263 -0
- data/lib/net/ssh/kerberos/drivers/sspi.rb +216 -0
- data/test/gss_context_test.rb +3 -4
- data/test/gss_test.rb +43 -61
- data/test/sspi_context_test.rb +2 -4
- data/test/sspi_test.rb +31 -39
- metadata +7 -20
- data/lib/net/ssh/kerberos/common/context.rb +0 -71
- data/lib/net/ssh/kerberos/gss.rb +0 -9
- data/lib/net/ssh/kerberos/gss/api.rb +0 -163
- data/lib/net/ssh/kerberos/gss/context.rb +0 -115
- data/lib/net/ssh/kerberos/sspi.rb +0 -5
- data/lib/net/ssh/kerberos/sspi/api.rb +0 -228
- data/lib/net/ssh/kerberos/sspi/context.rb +0 -76
data/Rakefile
CHANGED
@@ -7,14 +7,13 @@ begin
|
|
7
7
|
gem.name = "net-ssh-kerberos"
|
8
8
|
gem.summary = %Q{Add Kerberos support to Net::SSH}
|
9
9
|
gem.description = <<-EOTEXT
|
10
|
-
|
10
|
+
Extends Net::SSH by adding Kerberos authentication capability for password-less logins on multiple platforms.
|
11
11
|
EOTEXT
|
12
12
|
gem.email = "joe@ankhcraft.com"
|
13
13
|
gem.homepage = "http://github.com/joekhoobyar/net-ssh-kerberos"
|
14
14
|
gem.authors = ["Joe Khoobyar"]
|
15
15
|
gem.rubyforge_project = 'net-ssh-krb'
|
16
16
|
gem.add_runtime_dependency(%q<net-ssh>, [">= 2.0"])
|
17
|
-
gem.add_runtime_dependency(%q<rubysspi>, [">= 1.3"])
|
18
17
|
|
19
18
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
20
19
|
end
|
@@ -42,6 +41,33 @@ rescue LoadError
|
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
44
|
+
# These are new tasks
|
45
|
+
begin
|
46
|
+
require 'rake/contrib/sshpublisher'
|
47
|
+
namespace :rubyforge do
|
48
|
+
|
49
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
50
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
51
|
+
|
52
|
+
namespace :release do
|
53
|
+
desc "Publish RDoc to RubyForge."
|
54
|
+
task :docs => [:rdoc] do
|
55
|
+
config = YAML.load(
|
56
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
57
|
+
)
|
58
|
+
|
59
|
+
host = "#{config['username']}@rubyforge.org"
|
60
|
+
remote_dir = "/var/www/gforge-projects/net-ssh-krb/"
|
61
|
+
local_dir = 'doc'
|
62
|
+
|
63
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
rescue LoadError
|
68
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
69
|
+
end
|
70
|
+
|
45
71
|
|
46
72
|
task :default => :test
|
47
73
|
|
@@ -53,9 +79,12 @@ Rake::RDocTask.new do |rdoc|
|
|
53
79
|
else
|
54
80
|
version = ""
|
55
81
|
end
|
82
|
+
rdoc.options << '--line-numbers' << '--inline-source' <<
|
83
|
+
'--main' << 'README.rdoc' <<
|
84
|
+
'--charset' << 'utf-8'
|
56
85
|
|
57
|
-
rdoc.rdoc_dir = '
|
58
|
-
rdoc.title = "Net
|
86
|
+
rdoc.rdoc_dir = 'doc'
|
87
|
+
rdoc.title = "Net::SSH::Kerberos #{version}"
|
59
88
|
rdoc.rdoc_files.include('README*')
|
60
89
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
61
90
|
end
|
data/VERSION.yml
CHANGED
@@ -10,20 +10,17 @@ module Net
|
|
10
10
|
class GssapiWithMic < Abstract
|
11
11
|
include Net::SSH::Kerberos::Constants
|
12
12
|
|
13
|
-
# OID 1.2.840.113554.1.2.2 - Kerberos 5 (RFC 1964)
|
14
|
-
SUPPORTED_OID = "\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
|
15
|
-
|
16
13
|
# Attempts to perform gssapi-with-mic Kerberos authentication
|
17
14
|
def authenticate(next_service, username, password=nil)
|
18
|
-
gss_klass = (defined?(Net::SSH::Kerberos::SSPI::Context) ?
|
19
|
-
Net::SSH::Kerberos::SSPI::Context : Net::SSH::Kerberos::GSS::Context)
|
15
|
+
gss_klass = (defined?(Net::SSH::Kerberos::Drivers::SSPI::Context) ?
|
16
|
+
Net::SSH::Kerberos::Drivers::SSPI::Context : Net::SSH::Kerberos::Drivers::GSS::Context)
|
20
17
|
gss = nil
|
21
18
|
|
22
19
|
# Try to start gssapi-with-mic authentication.
|
23
20
|
debug { "trying kerberos authentication" }
|
24
21
|
req = userauth_request(username, next_service, "gssapi-with-mic")
|
25
|
-
req.write_long
|
26
|
-
req.write_string
|
22
|
+
req.write_long(1)
|
23
|
+
req.write_string(supported_oid = 6.chr + GSS_KRB5_MECH.length.chr + GSS_KRB5_MECH)
|
27
24
|
send_message req
|
28
25
|
message = session.next_message
|
29
26
|
case message.type
|
@@ -38,7 +35,7 @@ module Net
|
|
38
35
|
|
39
36
|
# Try to match the OID.
|
40
37
|
oid = message.read_string
|
41
|
-
if oid !=
|
38
|
+
if oid != supported_oid
|
42
39
|
info { "gssapi-with-mic failed (USERAUTH_GSSAPI_RESPONSE)" }
|
43
40
|
return false
|
44
41
|
end
|
data/lib/net/ssh/kerberos.rb
CHANGED
@@ -1,24 +1,11 @@
|
|
1
1
|
require 'net/ssh'
|
2
|
+
require 'net/ssh/errors'
|
3
|
+
|
4
|
+
module Net; module SSH; module Kerberos
|
5
|
+
end; end; end
|
6
|
+
|
2
7
|
require 'net/ssh/kerberos/constants'
|
8
|
+
require 'net/ssh/kerberos/context'
|
9
|
+
require 'net/ssh/kerberos/drivers'
|
3
10
|
#require 'net/ssh/kerberos/kex'
|
4
|
-
|
5
|
-
if RUBY_PLATFORM.include?('win') && ! RUBY_PLATFORM.include?('dar'); then
|
6
|
-
begin
|
7
|
-
require 'net/ssh/kerberos/sspi'
|
8
|
-
rescue Exception => e
|
9
|
-
if RuntimeError === e and e.message =~ /^LoadLibrary: ([^\s]+)/
|
10
|
-
$stderr.puts "error: While loading Kerberos SSPI: failed to load library: #{$1}"
|
11
|
-
else
|
12
|
-
raise e
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
require 'net/ssh/kerberos/gss'
|
17
11
|
require 'net/ssh/authentication/methods/gssapi_with_mic'
|
18
|
-
|
19
|
-
module Net
|
20
|
-
module SSH
|
21
|
-
module Kerberos
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,11 +1,7 @@
|
|
1
1
|
module Net; module SSH; module Kerberos
|
2
2
|
module Constants
|
3
3
|
|
4
|
-
|
5
|
-
#--
|
6
4
|
# GSSAPI Key exchange method specific messages
|
7
|
-
#++
|
8
|
-
|
9
5
|
KEXGSS_INIT = 30
|
10
6
|
KEXGSS_CONTINUE = 31
|
11
7
|
KEXGSS_COMPLETE = 32
|
@@ -14,10 +10,7 @@ module Net; module SSH; module Kerberos
|
|
14
10
|
KEXGSS_GROUPREQ = 40
|
15
11
|
KEXGSS_GROUP = 41
|
16
12
|
|
17
|
-
#--
|
18
13
|
# GSSAPI User authentication method specific messages
|
19
|
-
#++
|
20
|
-
|
21
14
|
USERAUTH_GSSAPI_RESPONSE = 60
|
22
15
|
USERAUTH_GSSAPI_TOKEN = 61
|
23
16
|
USERAUTH_GSSAPI_EXCHANGE_COMPLETE = 63
|
@@ -25,6 +18,9 @@ module Net; module SSH; module Kerberos
|
|
25
18
|
USERAUTH_GSSAPI_ERRTOK = 65
|
26
19
|
USERAUTH_GSSAPI_MIC = 66
|
27
20
|
|
21
|
+
# GSSAPI constant OID(s)
|
22
|
+
GSS_KRB5_MECH = "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
|
23
|
+
GSS_KRB5_MECH_USER2USER = "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x03"
|
28
24
|
end
|
29
25
|
end; end; end
|
30
26
|
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Net; module SSH; module Kerberos
|
2
|
+
|
3
|
+
class Context
|
4
|
+
|
5
|
+
class GeneralError < StandardError; end
|
6
|
+
|
7
|
+
class State < Struct.new(:handle, :result, :token, :timestamp)
|
8
|
+
def complete?; result.complete? end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :cred_name, :cred_krb_name, :server_name, :server_krb_name
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
raise "Don't create this class directly - use a subclass" if self.class == Context
|
15
|
+
end
|
16
|
+
|
17
|
+
def create(user, host)
|
18
|
+
dispose if @credentials or @target or @handle
|
19
|
+
|
20
|
+
creds, name = acquire_current_credentials
|
21
|
+
begin
|
22
|
+
@cred_name = name.to_s.sub(/^[^\\\/]*[\\\/]/, '')
|
23
|
+
@cred_krb_name = @cred_name.gsub('@', '/');
|
24
|
+
|
25
|
+
z = (user.include?('@') ? user.gsub('@','/') : user+'/')
|
26
|
+
unless z.downcase == @cred_krb_name[0,z.length].downcase
|
27
|
+
raise GeneralError, "Credentials mismatch: current is #{@cred_name}, requested is #{user}"
|
28
|
+
end
|
29
|
+
@credentials = creds
|
30
|
+
ensure
|
31
|
+
if @credentials.nil?
|
32
|
+
release_credentials creds
|
33
|
+
@cred_name = @cred_krb_name = nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
@server_name = Socket.gethostbyname(host)[0]
|
38
|
+
@target, @server_krb_name = import_server_name host
|
39
|
+
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def credentials?; ! @credentials.nil? end
|
44
|
+
|
45
|
+
def established?; ! @handle.nil? && (@state.nil? || @state.complete?) end
|
46
|
+
|
47
|
+
def init(token=nil); raise NotImplementedError, "subclasses must implement this method" end
|
48
|
+
|
49
|
+
def get_mic(token); raise NotImplementedError, "subclasses must implement this method" end
|
50
|
+
|
51
|
+
def dispose
|
52
|
+
@handle and delete_context @handle
|
53
|
+
@credentials and release_credentials @credentials
|
54
|
+
@target and release_server_name @target
|
55
|
+
ensure
|
56
|
+
@credentials = @cred_name = @cred_krb_name = nil
|
57
|
+
@target = @server_name = @server_krb_name = nil
|
58
|
+
@handle = @state = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def acquire_current_credentials; raise NotImplementedError, "subclasses must implement this method" end
|
64
|
+
|
65
|
+
def release_credentials(creds); raise NotImplementedError, "subclasses must implement this method" end
|
66
|
+
|
67
|
+
def import_server_name(host); raise NotImplementedError, "subclasses must implement this method" end
|
68
|
+
|
69
|
+
def release_server_name(target); raise NotImplementedError, "subclasses must implement this method" end
|
70
|
+
|
71
|
+
def delete_context(handle); raise NotImplementedError, "subclasses must implement this method" end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end; end; end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'dl/import'
|
2
|
+
require 'dl/struct'
|
3
|
+
|
4
|
+
module Net; module SSH; module Kerberos;
|
5
|
+
|
6
|
+
module Drivers
|
7
|
+
|
8
|
+
# Some useful DL extensions.
|
9
|
+
module DLExtensions
|
10
|
+
PTR_ENC = proc{|v| v && (DL::PtrData===v ? v : v.to_ptr) }
|
11
|
+
PTR_REF_ENC = proc{|v| (v.nil? ? DL::PtrData.new(v) : (DL::PtrData===v ? v : v.to_ptr)).ref }
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def PTR_DEC(t) proc{|v| v && t.new(v)} end
|
15
|
+
def PTR_REF_DEC(t) proc{|v| v && v.ptr && t.new(v.ptr)} end
|
16
|
+
|
17
|
+
def struct2(fields, &block)
|
18
|
+
t = struct fields
|
19
|
+
return t unless block_given?
|
20
|
+
t.instance_variable_set :@methods, Module.new(&block)
|
21
|
+
class << t
|
22
|
+
alias :new_struct :new
|
23
|
+
def new(ptr)
|
24
|
+
mem = new_struct(ptr)
|
25
|
+
mem.extend @methods
|
26
|
+
mem
|
27
|
+
end
|
28
|
+
end
|
29
|
+
t
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.included(base)
|
34
|
+
base.extend ClassMethods
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
@@available = []
|
39
|
+
def self.available; @@available end
|
40
|
+
|
41
|
+
if RUBY_PLATFORM.include?('win') && ! RUBY_PLATFORM.include?('dar'); then
|
42
|
+
begin require 'net/ssh/kerberos/drivers/sspi'; available << 'SSPI'
|
43
|
+
rescue => e
|
44
|
+
raise e unless RuntimeError === e and e.message =~ /^LoadLibrary: ([^\s]+)/
|
45
|
+
$stderr.puts "error: While loading Kerberos SSPI: failed to load library: #{$1}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
begin require 'net/ssh/kerberos/drivers/gss'; available << 'GSS'
|
49
|
+
rescue => e; raise e if available.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
if available.empty?
|
53
|
+
$stderr.puts "error: Failed to a find a supported GSS implementation on this platform (#{RUBY_PLATFORM})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end; end; end
|
@@ -0,0 +1,263 @@
|
|
1
|
+
module Net; module SSH; module Kerberos; module Drivers;
|
2
|
+
|
3
|
+
module GSS
|
4
|
+
|
5
|
+
include Net::SSH::Kerberos::Constants
|
6
|
+
|
7
|
+
GSS_C_INITIATE = 1
|
8
|
+
|
9
|
+
GSS_C_DELEG_FLAG = 1
|
10
|
+
GSS_C_MUTUAL_FLAG = 2
|
11
|
+
GSS_C_REPLAY_FLAG = 4
|
12
|
+
GSS_C_SEQUENCE_FLAG = 8
|
13
|
+
GSS_C_CONF_FLAG = 16
|
14
|
+
GSS_C_INTEG_FLAG = 32
|
15
|
+
GSS_C_ANON_FLAG = 64
|
16
|
+
GSS_C_PROT_READY_FLAG = 128
|
17
|
+
GSS_C_TRANS_FLAG = 256
|
18
|
+
|
19
|
+
GSS_C_NO_NAME = nil
|
20
|
+
GSS_C_NO_BUFFER = nil
|
21
|
+
GSS_C_NO_OID = nil
|
22
|
+
GSS_C_NO_OID_SET = nil
|
23
|
+
GSS_C_NO_CONTEXT = nil
|
24
|
+
GSS_C_NO_CREDENTIAL = nil
|
25
|
+
GSS_C_NO_CHANNEL_BINDINGS = nil
|
26
|
+
GSS_C_QOP_DEFAULT = 0
|
27
|
+
|
28
|
+
GSS_S_COMPLETE = 0
|
29
|
+
GSS_S_CONTINUE_NEEDED = 1
|
30
|
+
GSS_S_DUPLICATE_TOKEN = 2
|
31
|
+
GSS_S_OLD_TOKEN = 4
|
32
|
+
GSS_S_UNSEQ_TOKEN = 8
|
33
|
+
GSS_S_GAP_TOKEN = 16
|
34
|
+
|
35
|
+
GSS_C_GSS_CODE = 1
|
36
|
+
GSS_C_MECH_CODE = 2
|
37
|
+
|
38
|
+
module API
|
39
|
+
extend DL::Importable
|
40
|
+
include DLExtensions
|
41
|
+
|
42
|
+
def self.gss_func(sym, sig)
|
43
|
+
extern "OM_uint32 #{sym} (OM_uint32_ref, #{sig})"
|
44
|
+
module_eval <<-"EOCODE"
|
45
|
+
alias :_#{sym} :#{sym}
|
46
|
+
module_function :_#{sym}
|
47
|
+
def #{sym}(*args)
|
48
|
+
_#{sym}(*(args.unshift(0)))
|
49
|
+
@retval = GssResult.new(@retval, @args.shift)
|
50
|
+
end
|
51
|
+
module_function :#{sym}
|
52
|
+
EOCODE
|
53
|
+
end
|
54
|
+
|
55
|
+
if RUBY_PLATFORM =~ /cygwin/
|
56
|
+
dlload('cyggss-1.dll')
|
57
|
+
else
|
58
|
+
dlload('libgssapi_krb5.so')
|
59
|
+
end
|
60
|
+
|
61
|
+
typealias "void **", "p", PTR_REF_ENC, proc{|v| v.ptr}
|
62
|
+
typealias "GssResult", "L", proc{|v| v.to_i }, proc{|v| GssResult.new(v) }
|
63
|
+
typealias 'OM_uint32', 'unsigned int'
|
64
|
+
typealias "OM_uint32_ref", 'unsigned int ref'
|
65
|
+
typealias 'size_t', 'unsigned int'
|
66
|
+
typealias "gss_bytes_t", "P" #, nil, nil, "P", PTR_ENC
|
67
|
+
GssBuffer = struct2 [ "size_t length", "gss_bytes_t value" ] do
|
68
|
+
def to_s; value.to_s(length) if length > 0 end
|
69
|
+
end
|
70
|
+
typealias 'gss_buffer_desc', 'GssBuffer'
|
71
|
+
typealias 'gss_buffer_t', 'gss_buffer_desc *'
|
72
|
+
GssOID = struct2 [ "OM_uint32 length", "gss_bytes_t elements" ] do
|
73
|
+
def eql?(oid) !oid.nil? && length==oid.length && to_s==oid.to_s end
|
74
|
+
def ==(oid) !oid.nil? && length==oid.length && to_s==oid.to_s end
|
75
|
+
def to_s; elements.to_s(length) if length > 0 end
|
76
|
+
def inspect; 'OID: ' + (to_s.unpack("H2" * length).join(' ') rescue 'nil') end
|
77
|
+
end
|
78
|
+
def GssOID.create(bytes) new [bytes.length, bytes].pack("LP#{bytes.length}").to_ptr end
|
79
|
+
typealias 'gss_OID', 'P', PTR_ENC, PTR_DEC(GssOID)
|
80
|
+
typealias 'gss_OID_ref', 'p', PTR_REF_ENC, PTR_REF_DEC(GssOID)
|
81
|
+
GssOIDSet = struct2 [ "size_t count", "gss_OID elements" ] do
|
82
|
+
def oids
|
83
|
+
if @oids.nil? or elements != (@oids.first.to_ptr rescue nil)
|
84
|
+
@oids = []
|
85
|
+
0.upto(count-1) { |n| @oids[n] = GssOID.new(elements + n*GssOID.size) } unless elements.nil?
|
86
|
+
end
|
87
|
+
@oids
|
88
|
+
end
|
89
|
+
def inspect; 'OIDSet: [' + oids.map {|o| o.inspect }.join(', ') + ']' end
|
90
|
+
end
|
91
|
+
typealias 'gss_OID_set', 'P', PTR_ENC, PTR_DEC(GssOIDSet)
|
92
|
+
typealias 'gss_OID_set_ref', 'p', PTR_REF_ENC, PTR_REF_DEC(GssOIDSet)
|
93
|
+
|
94
|
+
typealias 'gss_ctx_id_t', 'void *'
|
95
|
+
typealias 'gss_ctx_id_ref', 'void **'
|
96
|
+
typealias 'gss_cred_id_t', 'void *'
|
97
|
+
typealias 'gss_cred_id_ref', 'void **'
|
98
|
+
typealias 'gss_name_t', 'void *'
|
99
|
+
typealias 'gss_name_ref', 'void **'
|
100
|
+
typealias 'gss_qop_t', 'OM_uint32'
|
101
|
+
typealias 'gss_qop_ref', 'OM_uint32_ref'
|
102
|
+
typealias 'gss_cred_usage_t', 'int'
|
103
|
+
typealias 'gss_cred_usage_ref', 'int ref'
|
104
|
+
|
105
|
+
class GssResult
|
106
|
+
def initialize(code, minor=nil) @value, @minor = code, minor end
|
107
|
+
def ok?; major.zero? end
|
108
|
+
def complete?; status.zero? end
|
109
|
+
def incomplete?; false end
|
110
|
+
def failure?; major.nonzero? end
|
111
|
+
def temporary_failure?
|
112
|
+
routine_error==GSS_S_CREDENTIALS_EXPIRED ||
|
113
|
+
routine_error==GSS_S_CONTEXT_EXPIRED ||
|
114
|
+
routine_error==GSS_S_UNAVAILABLE
|
115
|
+
end
|
116
|
+
def major; (@value >> 16) & 0x0000ffff end
|
117
|
+
def status; @value & 0x0000ffff end
|
118
|
+
def calling_error; (@value >> 24) & 0x000000ff end
|
119
|
+
def routine_error; (@value >> 16) & 0x000000ff end
|
120
|
+
def to_i; @value end
|
121
|
+
def to_s
|
122
|
+
orig_retval, orig_args = API._retval_, API._args_
|
123
|
+
begin
|
124
|
+
msgctx, msgbuff, msglist = 0, API::GssBuffer.malloc, []
|
125
|
+
begin
|
126
|
+
result = API.gss_display_status @value, GSS_C_GSS_CODE, GSS_C_NO_OID, msgctx, msgbuff
|
127
|
+
result.ok? or return 'unknown'
|
128
|
+
msgctx = API._args_[3]
|
129
|
+
msglist << msgbuff.to_s
|
130
|
+
API.gss_release_buffer msgbuff
|
131
|
+
end until msgctx.zero?
|
132
|
+
msglist.empty? ? 'ok' : msglist.join(', ')
|
133
|
+
ensure
|
134
|
+
API.instance_eval { @retval = orig_retval; @args = orig_args }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
gss_func "gss_acquire_cred", "gss_name_t, OM_uint32, gss_OID_set, gss_cred_usage_t, gss_cred_id_ref, void *, OM_uint32_ref"
|
140
|
+
gss_func "gss_inquire_cred", "gss_cred_id_t, gss_name_ref, OM_uint32_ref, gss_cred_usage_ref, gss_OID_set_ref"
|
141
|
+
gss_func "gss_import_name", "gss_buffer_t, gss_OID, gss_name_ref"
|
142
|
+
gss_func "gss_display_name", "gss_name_t, gss_buffer_t, gss_OID_ref"
|
143
|
+
gss_func "gss_release_cred", "gss_cred_id_ref"
|
144
|
+
gss_func "gss_release_oid_set", "gss_OID_set_ref"
|
145
|
+
gss_func "gss_release_name", "gss_name_ref"
|
146
|
+
gss_func "gss_release_buffer", "gss_buffer_t"
|
147
|
+
gss_func "gss_init_sec_context", "gss_cred_id_t, gss_ctx_id_ref, gss_name_t, gss_OID, OM_uint32, OM_uint32, void *, "+
|
148
|
+
"gss_buffer_t, gss_OID_ref, gss_buffer_t, OM_uint32_ref, OM_uint32_ref"
|
149
|
+
gss_func "gss_delete_sec_context", "gss_ctx_id_ref, gss_buffer_t"
|
150
|
+
gss_func "gss_get_mic", "gss_ctx_id_t, gss_qop_t, gss_buffer_t, gss_buffer_t"
|
151
|
+
gss_func "gss_display_status", "OM_uint32, int, gss_OID, OM_uint32_ref, gss_buffer_t"
|
152
|
+
gss_func "gss_indicate_mechs", "gss_OID_set_ref"
|
153
|
+
end
|
154
|
+
|
155
|
+
# GSSAPI / Kerberos 5 OID(s)
|
156
|
+
GSS_C_NT_PRINCIPAL = API::GssOID.create("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01")
|
157
|
+
GSS_C_NT_MACHINE_UID_NAME = API::GssOID.create("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02")
|
158
|
+
GSS_C_NT_STRING_UID_NAME = API::GssOID.create("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03")
|
159
|
+
GSS_C_NT_HOSTBASED_SERVICE = API::GssOID.create("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")
|
160
|
+
GSS_C_NT_ANONYMOUS = API::GssOID.create("\x2b\x06\x01\x05\x06\x03")
|
161
|
+
GSS_C_NT_EXPORT_NAME = API::GssOID.create("\x2b\x06\x01\x05\x06\x04")
|
162
|
+
GSS_C_KRB5 = API::GssOID.create(GSS_KRB5_MECH)
|
163
|
+
|
164
|
+
# GSSAPI / Kerberos 5 Deprecated / Proprietary OID(s)
|
165
|
+
GSS_C_NT_HOSTBASED_SERVICE_X = API::GssOID.create("\x2b\x06\x01\x05\x06\x02")
|
166
|
+
|
167
|
+
# GSSAPI - Kerberos 5 mechanism support.
|
168
|
+
result = API.gss_indicate_mechs nil
|
169
|
+
result.ok? or raise "gss_indicate_mechs failed: #{result}"
|
170
|
+
if API._args_[0].oids.include? GSS_C_KRB5
|
171
|
+
API.gss_release_oid_set API._args_[0]
|
172
|
+
else
|
173
|
+
raise "This GSSAPI library reports no support for Kerberos authentication"
|
174
|
+
end
|
175
|
+
|
176
|
+
class Context < Net::SSH::Kerberos::Context
|
177
|
+
|
178
|
+
GssResult = API::GssResult
|
179
|
+
|
180
|
+
def init(token=nil)
|
181
|
+
unless token.nil?
|
182
|
+
input = API::GssBuffer.malloc
|
183
|
+
input.value = token.to_ptr
|
184
|
+
input.length = token.length
|
185
|
+
end
|
186
|
+
buffer = API::GssBuffer.malloc
|
187
|
+
context = @state.handle if @state
|
188
|
+
result = API.gss_init_sec_context @credentials, context, @target, GSS_C_KRB5,
|
189
|
+
GSS_C_DELEG_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG, 60,
|
190
|
+
GSS_C_NO_CHANNEL_BINDINGS, input, nil, buffer, 0, 0
|
191
|
+
result.failure? and raise GeneralError, "Error initializing security context: #{result}"
|
192
|
+
begin
|
193
|
+
@state = State.new(API._args_[1], result, buffer.to_s, nil)
|
194
|
+
@handle = @state.handle if result.complete?
|
195
|
+
return @state.token
|
196
|
+
ensure
|
197
|
+
API.gss_release_buffer buffer if buffer.value
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def get_mic(token)
|
202
|
+
input = API::GssBuffer.malloc
|
203
|
+
input.value = token.to_ptr
|
204
|
+
input.length = token.length
|
205
|
+
@state.result = API.gss_get_mic @handle, GSS_C_QOP_DEFAULT, input, output=API::GssBuffer.malloc
|
206
|
+
unless @state.result.complete? and output
|
207
|
+
raise GeneralError, "Error creating the signature: #{@state.result}"
|
208
|
+
end
|
209
|
+
begin return output.to_s
|
210
|
+
ensure API.gss_release_buffer output
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
protected
|
215
|
+
|
216
|
+
def state; @state end
|
217
|
+
|
218
|
+
private
|
219
|
+
|
220
|
+
def acquire_current_credentials
|
221
|
+
result = API.gss_acquire_cred nil, 60, nil, GSS_C_INITIATE, nil, nil, 0
|
222
|
+
result.ok? or raise GeneralError, "Error acquiring credentials: #{result}"
|
223
|
+
result = API.gss_inquire_cred creds=API._args_[4], nil, 0, 0, nil
|
224
|
+
result.ok? or raise GeneralError, "Error inquiring credentials: #{result}"
|
225
|
+
begin
|
226
|
+
name, oids = API._args_[1], API._args_[4]
|
227
|
+
result = API.gss_display_name name, buffer=API::GssBuffer.malloc, nil
|
228
|
+
result.ok? or raise GeneralError, "Error getting display name: #{result}"
|
229
|
+
begin return [creds, buffer.to_s]
|
230
|
+
ensure API.gss_release_buffer buffer
|
231
|
+
end
|
232
|
+
ensure
|
233
|
+
API.gss_release_name name
|
234
|
+
API.gss_release_oid_set oids
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def release_credentials(creds)
|
239
|
+
creds.nil? or API.gss_release_cred creds
|
240
|
+
end
|
241
|
+
|
242
|
+
def import_server_name(host)
|
243
|
+
host = 'host@' + host
|
244
|
+
buffer = API::GssBuffer.malloc
|
245
|
+
buffer.value = host.to_ptr
|
246
|
+
buffer.length = host.length
|
247
|
+
result = API.gss_import_name buffer, GSS_C_NT_HOSTBASED_SERVICE, nil
|
248
|
+
result.failure? and raise GeneralError, "Error importing name: #{result}"
|
249
|
+
[API._args_[2], host]
|
250
|
+
end
|
251
|
+
|
252
|
+
def release_server_name(target)
|
253
|
+
target.nil? or API.gss_release_name target
|
254
|
+
end
|
255
|
+
|
256
|
+
def delete_context(handle)
|
257
|
+
handle.nil? or API.gss_delete_sec_context handle, nil
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
end; end; end; end
|