net-ssh-kerberos 0.1.3 → 0.2.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.
- 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
|