kerberos_authenticator 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kerberos_authenticator.rb +18 -1
- data/lib/kerberos_authenticator/krb5.rb +2 -1
- data/lib/kerberos_authenticator/krb5/attach_function.rb +3 -3
- data/lib/kerberos_authenticator/krb5/context.rb +46 -15
- data/lib/kerberos_authenticator/krb5/creds.rb +45 -19
- data/lib/kerberos_authenticator/krb5/data.rb +40 -0
- data/lib/kerberos_authenticator/krb5/error.rb +40 -14
- data/lib/kerberos_authenticator/krb5/keytab.rb +107 -25
- data/lib/kerberos_authenticator/krb5/principal.rb +29 -28
- data/lib/kerberos_authenticator/version.rb +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f345054a20a13a084ee6979f3f32d62a4ba2e898
|
4
|
+
data.tar.gz: b0909f23de117867bf9806ef63b39b3bee1e5d24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 624582a12b02a838f4e8d5d29f230dc6a76904e35fbbbefd4cee22e76fa1b237f54f41e35d588235717125aa4af44b964363593cb2d72850bc083c671a4f95ee
|
7
|
+
data.tar.gz: b7045094f0fab8ab6087c191b63288a27a9e4229206b32441444a481ea8662aece5074671a6f5e872730cea2806d2b86f483e31f8fc9cfec47dca4293a1d8df1
|
@@ -6,7 +6,6 @@ require 'kerberos_authenticator/error'
|
|
6
6
|
require 'kerberos_authenticator/krb5'
|
7
7
|
|
8
8
|
module KerberosAuthenticator
|
9
|
-
|
10
9
|
# A convenience method to access the Krb5 module when using the setup method.
|
11
10
|
# @return [Krb5]
|
12
11
|
def self.krb5
|
@@ -37,6 +36,14 @@ module KerberosAuthenticator
|
|
37
36
|
true
|
38
37
|
end
|
39
38
|
|
39
|
+
# Change a user's password by authenticating with their current one.
|
40
|
+
# @raise [Error] if the attempt to change the password fails
|
41
|
+
# @return [TrueClass] always returns true if no error was raised
|
42
|
+
def self.change_password!(username, old_password, new_password)
|
43
|
+
user = Krb5::Principal.new_with_name(username)
|
44
|
+
user.change_password(old_password, new_password)
|
45
|
+
end
|
46
|
+
|
40
47
|
# @!attribute [rw] keytab_base64
|
41
48
|
# @!scope class
|
42
49
|
# @return [String] the keytab to use when verifying the identity of the KDC represented as a Base64 encoded string (overrides keytab_path)
|
@@ -113,8 +120,18 @@ module KerberosAuthenticator
|
|
113
120
|
kt = Krb5::Keytab.new_with_name("FILE:#{kt_tmp_file.path}")
|
114
121
|
elsif keytab_path
|
115
122
|
kt = Krb5::Keytab.new_with_name("FILE:#{keytab_path}")
|
123
|
+
else
|
124
|
+
kt = Krb5::Keytab.default
|
116
125
|
end
|
117
126
|
|
127
|
+
# FIXME: This seems to protect against segfaults in OS X Kerberos as of 10.9.5
|
128
|
+
# when the keytab isn't accessible or doesn't exist.
|
129
|
+
# It probably indicates an underlying memory management mistake.
|
130
|
+
#
|
131
|
+
# REVIEW: It's hard to say whether calling this or leaving it out produces
|
132
|
+
# better error messages.
|
133
|
+
kt.assert_has_content
|
134
|
+
|
118
135
|
begin
|
119
136
|
yield kt
|
120
137
|
ensure
|
@@ -8,7 +8,7 @@ module KerberosAuthenticator
|
|
8
8
|
|
9
9
|
# Version suffixes of the library to search for, in order:
|
10
10
|
# - .3: MIT as of Debian 8, RHEL 7
|
11
|
-
# - .26: Heimdal as of Debian 8, RHEL 7
|
11
|
+
# - .26: Heimdal as of Debian 8, RHEL 7
|
12
12
|
# and then no suffix (which should pickup OS X Kerberos).
|
13
13
|
PREFERRED_VERSIONS = ['.3', '.26', ''].freeze
|
14
14
|
|
@@ -38,6 +38,7 @@ end
|
|
38
38
|
require 'kerberos_authenticator/krb5/attach_function'
|
39
39
|
require 'kerberos_authenticator/krb5/error'
|
40
40
|
require 'kerberos_authenticator/krb5/context'
|
41
|
+
require 'kerberos_authenticator/krb5/data'
|
41
42
|
require 'kerberos_authenticator/krb5/principal'
|
42
43
|
require 'kerberos_authenticator/krb5/creds'
|
43
44
|
require 'kerberos_authenticator/krb5/keytab'
|
@@ -3,7 +3,7 @@ module KerberosAuthenticator
|
|
3
3
|
# Attaches a Kerberos library function to Krb5.
|
4
4
|
# Extends FFI's built-in method to:
|
5
5
|
# - drop the krb5_ prefix from function names
|
6
|
-
# - wrap any call returning a krb5_error_code with Krb5::
|
6
|
+
# - wrap any call returning a krb5_error_code with Krb5::LibCallError.raise_if_error
|
7
7
|
# @api private
|
8
8
|
# @see http://www.rubydoc.info/github/ffi/ffi/FFI/Library#attach_function-instance_method FFI::Library#attach_function
|
9
9
|
def self.attach_function(c_name, params, returns, options = {})
|
@@ -18,11 +18,11 @@ module KerberosAuthenticator
|
|
18
18
|
|
19
19
|
if params.first == :krb5_context
|
20
20
|
define_method(ruby_name) do |*args, &block|
|
21
|
-
Krb5::
|
21
|
+
Krb5::LibCallError.raise_if_error(args.first) { public_send(no_check_name, *args, &block) }
|
22
22
|
end
|
23
23
|
else
|
24
24
|
define_method(ruby_name) do |*args, &block|
|
25
|
-
Krb5::
|
25
|
+
Krb5::LibCallError.raise_if_error(nil) { public_send(no_check_name, *args, &block) }
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -2,20 +2,42 @@ module KerberosAuthenticator
|
|
2
2
|
module Krb5
|
3
3
|
typedef :pointer, :krb5_context
|
4
4
|
|
5
|
-
attach_function :krb5_init_context, [:
|
5
|
+
attach_function :krb5_init_context, [:pointer], :krb5_error_code
|
6
6
|
attach_function :krb5_free_context, [:krb5_context], :void
|
7
7
|
|
8
8
|
begin
|
9
|
-
attach_function :krb5_init_secure_context, [:
|
9
|
+
attach_function :krb5_init_secure_context, [:pointer], :krb5_error_code
|
10
10
|
rescue FFI::NotFoundError
|
11
11
|
# Then we're probably using a version of the Heimdal library
|
12
|
-
# that doesn't support init_secure_context (and
|
12
|
+
# that doesn't support init_secure_context (and ignores environmental variables by default)
|
13
13
|
alias_method(:init_secure_context, :init_context)
|
14
14
|
module_function :init_secure_context
|
15
15
|
end
|
16
16
|
|
17
|
+
attach_function :krb5_get_default_realm, [:krb5_context, :pointer], :krb5_error_code
|
18
|
+
|
19
|
+
begin
|
20
|
+
attach_function :krb5_xfree, [:pointer], :krb5_error_code
|
21
|
+
rescue FFI::NotFoundError
|
22
|
+
# MIT
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
attach_function :krb5_free_string, [:krb5_context, :pointer], :void
|
27
|
+
rescue FFI::NotFoundError
|
28
|
+
# Heimdal
|
29
|
+
define_method(:free_string) { |_ctx, pointer| Krb5.xfree(pointer) }
|
30
|
+
module_function :free_string
|
31
|
+
end
|
32
|
+
|
17
33
|
# A Kerberos context, holding all per-thread state.
|
18
34
|
class Context
|
35
|
+
# @!attribute [r] ptr
|
36
|
+
# @return [FFI::Pointer] the pointer to the wrapped krb5_context struct
|
37
|
+
|
38
|
+
|
39
|
+
attr_reader :ptr
|
40
|
+
|
19
41
|
# @return [Context] a fibre-local Context
|
20
42
|
def self.context
|
21
43
|
if Krb5.use_secure_context
|
@@ -29,30 +51,39 @@ module KerberosAuthenticator
|
|
29
51
|
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_init_secure_context.html krb5_init_secure_context
|
30
52
|
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_init_context.html krb5_init_context
|
31
53
|
def initialize(secure = false)
|
32
|
-
|
54
|
+
pointer = FFI::MemoryPointer.new :pointer
|
33
55
|
|
34
56
|
if secure
|
35
|
-
Krb5::
|
57
|
+
Krb5::LibCallError.raise_if_error { Krb5.init_secure_context(pointer) }
|
36
58
|
else
|
37
|
-
Krb5::
|
59
|
+
Krb5::LibCallError.raise_if_error { Krb5.init_context(pointer) }
|
38
60
|
end
|
39
61
|
|
40
|
-
|
62
|
+
@ptr = FFI::AutoPointer.new pointer.get_pointer(0), self.class.method(:release)
|
63
|
+
|
41
64
|
self
|
42
65
|
end
|
43
66
|
|
44
|
-
#
|
45
|
-
# @
|
46
|
-
|
47
|
-
|
67
|
+
# Retrieves the default realm
|
68
|
+
# @return [String]
|
69
|
+
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_get_default_realm.html krb5_get_default_realm
|
70
|
+
def default_realm
|
71
|
+
out_ptr = FFI::MemoryPointer.new :pointer
|
72
|
+
Krb5.get_default_realm(ptr, out_ptr)
|
73
|
+
|
74
|
+
str_ptr = out_ptr.read_pointer
|
75
|
+
copy = String.new(str_ptr.read_string).force_encoding('UTF-8')
|
76
|
+
|
77
|
+
Krb5.free_string(ptr, str_ptr)
|
78
|
+
|
79
|
+
copy
|
48
80
|
end
|
49
81
|
|
50
|
-
#
|
82
|
+
# Frees a Context
|
51
83
|
# @api private
|
52
|
-
# @return [Proc]
|
53
84
|
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_free_context.html krb5_free_context
|
54
|
-
def self.
|
55
|
-
|
85
|
+
def self.release(pointer)
|
86
|
+
Krb5.free_context pointer
|
56
87
|
end
|
57
88
|
end
|
58
89
|
end
|
@@ -11,9 +11,15 @@ module KerberosAuthenticator
|
|
11
11
|
attach_function :krb5_free_cred_contents, [:krb5_context, :krb5_creds], :void
|
12
12
|
attach_function :krb5_get_init_creds_opt_free, [:krb5_context, :pointer], :void
|
13
13
|
|
14
|
+
attach_function :krb5_set_password, [:krb5_context, :krb5_creds, :string, :krb5_principal, :pointer, :pointer, :pointer], :krb5_error_code
|
15
|
+
|
14
16
|
# Credentials, or tickets, provided by a KDC for a user.
|
15
17
|
class Creds
|
16
|
-
|
18
|
+
# @!attribute [r] ptr
|
19
|
+
# @return [FFI::Pointer] the pointer to the wrapped krb5_creds struct
|
20
|
+
|
21
|
+
|
22
|
+
attr_reader :ptr
|
17
23
|
|
18
24
|
# The size, in bytes, of the krb5_creds structure.
|
19
25
|
# This differs between implementations and architectures.
|
@@ -31,26 +37,23 @@ module KerberosAuthenticator
|
|
31
37
|
def self.initial_creds_for_principal_with_a_password(principal, password, service = nil)
|
32
38
|
raise TypeError, 'expected Principal' unless principal.is_a? Principal
|
33
39
|
|
34
|
-
context = principal.context
|
35
40
|
ptr = FFI::MemoryPointer.new :char, SIZE_OF_KRB5_CREDS
|
41
|
+
Krb5.get_init_creds_password(Context.context.ptr, ptr, principal.ptr, password.to_str, nil, nil, 0, service, nil)
|
36
42
|
|
37
|
-
|
38
|
-
|
39
|
-
new(context, ptr)
|
43
|
+
new(ptr)
|
40
44
|
end
|
41
45
|
|
42
|
-
# Initialize a new Keytab with a pointer to a krb5_keytab structure
|
43
|
-
# @param context [Context]
|
46
|
+
# Initialize a new Keytab with a pointer to a krb5_keytab structure.
|
44
47
|
# @param ptr [FFI::MemoryPointer]
|
45
48
|
# @return [Keytab]
|
46
|
-
def initialize(
|
47
|
-
|
48
|
-
|
49
|
+
def initialize(ptr)
|
50
|
+
# HACK: AutoPointer won't accept a MemoryPointer, only a Pointer
|
51
|
+
ptr.autorelease = false
|
52
|
+
ptr = FFI::Pointer.new(ptr)
|
49
53
|
|
50
|
-
|
51
|
-
ObjectSpace.define_finalizer(self, self.class.finalize(context, ptr))
|
54
|
+
ptr = FFI::AutoPointer.new ptr, self.class.method(:release)
|
52
55
|
|
53
|
-
|
56
|
+
@ptr = ptr
|
54
57
|
end
|
55
58
|
|
56
59
|
# Calls #verify with nofail as true.
|
@@ -59,7 +62,7 @@ module KerberosAuthenticator
|
|
59
62
|
verify(true, server_principal, keytab)
|
60
63
|
end
|
61
64
|
|
62
|
-
#
|
65
|
+
# Attempts to verify that these Creds were obtained from a KDC with knowledge of a key in keytab.
|
63
66
|
# @param nofail [Boolean] whether to raise an Error if no keytab information is available
|
64
67
|
# @param server_principal [Principal] the server principal to use choosing an entry in keytab
|
65
68
|
# @param keytab [Keytab] the key table containing a key that the KDC should know
|
@@ -76,17 +79,40 @@ module KerberosAuthenticator
|
|
76
79
|
server_princ_ptr = server_principal ? server_principal.ptr : nil
|
77
80
|
keytab_ptr = keytab ? keytab.ptr : nil
|
78
81
|
|
79
|
-
Krb5.verify_init_creds(context.ptr, ptr, server_princ_ptr, keytab_ptr, nil, verify_creds_opt)
|
82
|
+
Krb5.verify_init_creds(Context.context.ptr, ptr, server_princ_ptr, keytab_ptr, nil, verify_creds_opt)
|
83
|
+
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sets a password for a principal using these Creds.
|
88
|
+
# The Creds should be for the 'kadmin/changepw' service.
|
89
|
+
# @param newpw [String] the new password
|
90
|
+
# @param change_password_for [Principal] the Principal to change the password for
|
91
|
+
# @raise [Error] if there is a problem making the password change request
|
92
|
+
# @raise [Error] if server responds that the password change request failed
|
93
|
+
# @return [TrueClass] always returns true if no error was raised
|
94
|
+
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_set_password.html krb5_set_password
|
95
|
+
def set_password(newpw, change_password_for = nil)
|
96
|
+
change_password_for_ptr = change_password_for ? change_password_for.ptr : nil
|
97
|
+
|
98
|
+
result_code = FFI::MemoryPointer.new :int
|
99
|
+
result_code_string = Data.new
|
100
|
+
result_string = Data.new
|
101
|
+
|
102
|
+
Krb5.set_password(Context.context.ptr, ptr, newpw, change_password_for_ptr, result_code, result_code_string.pointer, result_string.pointer)
|
103
|
+
|
104
|
+
result_code = result_code.read_uint
|
105
|
+
result_string = result_string.read_string.force_encoding('UTF-8')
|
106
|
+
raise SetPassError.new(result_code, result_string) if result_code > 0
|
80
107
|
|
81
108
|
true
|
82
109
|
end
|
83
110
|
|
84
|
-
#
|
111
|
+
# Frees the contents of the Creds structure
|
85
112
|
# @api private
|
86
|
-
# @return [Proc]
|
87
113
|
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_free_cred_contents.html krb5_free_cred_contents
|
88
|
-
def self.
|
89
|
-
|
114
|
+
def self.release(pointer)
|
115
|
+
Krb5.free_cred_contents(Context.context.ptr, pointer)
|
90
116
|
end
|
91
117
|
end
|
92
118
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module KerberosAuthenticator
|
2
|
+
module Krb5
|
3
|
+
attach_function :krb5_free_data_contents, [:krb5_context, :pointer], :void
|
4
|
+
|
5
|
+
# Generic Kerberos library data structure.
|
6
|
+
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/types/krb5_data.html krb5_data
|
7
|
+
class Data < FFI::ManagedStruct
|
8
|
+
layout :magic, :krb5_error_code, :length, :int, :data_ptr, :pointer
|
9
|
+
|
10
|
+
# Allocates and zeroes a new krb5_data struct or cast some existing memory to one.
|
11
|
+
# @param pointer [Pointer] a pointer to existing memory to cast to a krb5_data struct
|
12
|
+
# @see https://github.com/ffi/ffi/wiki/Structs Structs
|
13
|
+
def initialize(pointer = nil)
|
14
|
+
unless pointer
|
15
|
+
pointer = FFI::MemoryPointer.new :char, self.class.size
|
16
|
+
|
17
|
+
# HACK: AutoPointer won't accept a MemoryPointer, only a Pointer
|
18
|
+
pointer.autorelease = false
|
19
|
+
pointer = FFI::Pointer.new(pointer)
|
20
|
+
end
|
21
|
+
|
22
|
+
super(pointer)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Reads the data into a string.
|
26
|
+
# @return [String]
|
27
|
+
def read_string
|
28
|
+
return '' if self[:length].zero?
|
29
|
+
self[:data_ptr].read_bytes(self[:length])
|
30
|
+
end
|
31
|
+
|
32
|
+
# Frees the contents of a Data struct
|
33
|
+
# @api private
|
34
|
+
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_free_data_contents.html krb5_free_data_contents
|
35
|
+
def self.release(pointer)
|
36
|
+
Krb5.free_data_contents(Context.context.ptr, pointer)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -4,44 +4,70 @@ module KerberosAuthenticator
|
|
4
4
|
attach_function :krb5_get_error_message, [:pointer, :krb5_error_code], :strptr
|
5
5
|
attach_function :krb5_free_error_message, [:pointer, :pointer], :void
|
6
6
|
|
7
|
-
#
|
8
|
-
class Error < StandardError
|
7
|
+
# Generic exception class
|
8
|
+
class Error < StandardError; end
|
9
|
+
|
10
|
+
# A Kerberos error returned from a library call as a `krb5_error_code`.
|
11
|
+
class LibCallError < Error
|
9
12
|
# @!attribute [r] error_code
|
10
13
|
# @return [Integer] the krb5_error_code used to convey the status of a Kerberos library operation.
|
11
|
-
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/types/krb5_error_code.html krb5_error_code
|
14
|
+
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/types/krb5_error_code.html krb5_error_code
|
12
15
|
|
13
16
|
|
14
17
|
attr_reader :error_code
|
15
18
|
|
16
|
-
# Initializes a new
|
19
|
+
# Initializes a new LibCallError using an error code and the relevant Context to provide a friendly error message.
|
17
20
|
# @param context_ptr [FFI::Pointer] A Context's pointer
|
18
21
|
# @param krb5_error_code [Integer] An integer used to convey a operation's status
|
19
|
-
# @return [
|
22
|
+
# @return [LibCallError]
|
20
23
|
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_get_error_message.html krb5_get_error_message
|
21
24
|
def initialize(context_ptr, krb5_error_code)
|
22
25
|
@error_code = krb5_error_code
|
23
26
|
error_message, error_ptr = Krb5.get_error_message(context_ptr, krb5_error_code)
|
24
|
-
|
25
|
-
super
|
27
|
+
self.class.release(error_ptr)
|
28
|
+
super String.new(error_message).force_encoding('UTF-8')
|
26
29
|
end
|
27
30
|
|
28
|
-
#
|
31
|
+
# Frees an error message
|
29
32
|
# @api private
|
30
|
-
# @return [Proc]
|
31
33
|
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_free_error_message.html krb5_free_error_message
|
32
|
-
def self.
|
33
|
-
|
34
|
+
def self.release(pointer)
|
35
|
+
Krb5.free_error_message(Context.context.ptr, pointer)
|
34
36
|
end
|
35
37
|
|
36
38
|
# Used to wrap Kerberos library functions that return a krb5_error_code.
|
37
39
|
# @return [Integer] always returns zero on success
|
38
40
|
# @yield [] A call to a Kerberos library function
|
39
41
|
# @yieldreturn [Integer] a krb5_error_code
|
40
|
-
# @raise [
|
42
|
+
# @raise [LibCallError] if the krb5_error_code differed from zero
|
41
43
|
def self.raise_if_error(context_ptr = nil)
|
42
44
|
err = yield
|
43
|
-
return 0 if err
|
44
|
-
raise
|
45
|
+
return 0 if err.zero?
|
46
|
+
raise self.new(context_ptr, err)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# An error indicating a failure response from a server
|
51
|
+
# when trying to change a password.
|
52
|
+
# @see https://www.ietf.org/rfc/rfc3244.txt RFC 3244
|
53
|
+
class SetPassError < Error
|
54
|
+
# @!attribute [r] result_code
|
55
|
+
# @return [Integer] the result code used to convey the result of a Set Password operation.
|
56
|
+
# @!attribute [r] result_string
|
57
|
+
# @return [String] the full result string used to convey the result of a Set Password operation.
|
58
|
+
|
59
|
+
|
60
|
+
attr_reader :result_code
|
61
|
+
attr_reader :result_string
|
62
|
+
|
63
|
+
# Initializes a new SetPassError using an RFC 3244 result code and result string supplied in a server response.
|
64
|
+
# @param result_code [Integer] the result code used to convey the result of a Set Password operation
|
65
|
+
# @param result_string [String] the full result string used to convey the result of a Set Password operation.
|
66
|
+
# @return [SetPassError]
|
67
|
+
def initialize(result_code, result_string)
|
68
|
+
@result_code = result_code
|
69
|
+
@result_string = result_string
|
70
|
+
super @result_string.lines.first.to_s.strip
|
45
71
|
end
|
46
72
|
end
|
47
73
|
end
|
@@ -2,54 +2,136 @@ module KerberosAuthenticator
|
|
2
2
|
module Krb5
|
3
3
|
typedef :pointer, :krb5_keytab
|
4
4
|
|
5
|
-
attach_function :krb5_kt_resolve, [:krb5_context, :string, :
|
5
|
+
attach_function :krb5_kt_resolve, [:krb5_context, :string, :pointer], :krb5_error_code
|
6
|
+
attach_function :krb5_kt_default, [:krb5_context, :pointer], :krb5_error_code
|
7
|
+
|
6
8
|
attach_function :krb5_kt_close, [:krb5_context, :krb5_keytab], :krb5_error_code
|
7
|
-
|
9
|
+
|
10
|
+
begin
|
11
|
+
# Heimdal
|
12
|
+
attach_function :krb5_kt_get_full_name, [:krb5_context, :krb5_keytab, :pointer], :krb5_error_code
|
13
|
+
rescue FFI::NotFoundError
|
14
|
+
# MIT
|
15
|
+
attach_function :krb5_kt_get_name, [:krb5_context, :krb5_keytab, :buffer_out, :int], :krb5_error_code
|
16
|
+
end
|
17
|
+
|
18
|
+
begin
|
19
|
+
attach_function :krb5_kt_have_content, [:krb5_context, :krb5_keytab], :krb5_error_code
|
20
|
+
rescue FFI::NotFoundError
|
21
|
+
# REVIEW: Then we're probably using an old version of the library that doesn't support kt_have_content.
|
22
|
+
end
|
8
23
|
|
9
24
|
# Storage for locally-stored keys.
|
10
25
|
class Keytab
|
11
|
-
|
26
|
+
# @!attribute [r] ptr
|
27
|
+
# @return [FFI::Pointer] the pointer to the wrapped krb5_keytab struct
|
28
|
+
|
12
29
|
|
30
|
+
attr_reader :ptr
|
31
|
+
|
32
|
+
# Resolves a keytab identified by name.
|
33
|
+
# The keytab is not opened and may not be accessible or contain any entries. (Use #has_content? to check.)
|
13
34
|
# @param name [String] a name of the form 'type:residual', where usually type is 'FILE' and residual the path to that file
|
14
|
-
# @
|
35
|
+
# @raises [Error] if the type is unknown
|
36
|
+
# @return [Keytab] a resolved, but not opened, keytab
|
15
37
|
# @see http://web.mit.edu/Kerberos/krb5-1.14/doc/appdev/refs/api/krb5_kt_resolve.html krb5_kt_resolve
|
16
|
-
def self.new_with_name(name
|
17
|
-
|
18
|
-
Krb5.kt_resolve(context.ptr, name,
|
38
|
+
def self.new_with_name(name)
|
39
|
+
pointer = FFI::MemoryPointer.new :pointer
|
40
|
+
Krb5.kt_resolve(Context.context.ptr, name, pointer)
|
41
|
+
|
42
|
+
new(pointer)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Resolves the default keytab, usually the file at `/etc/krb5.keytab`.
|
46
|
+
# The keytab is not opened and may not be accessible or contain any entries. (Use #has_content? to check.)
|
47
|
+
# @return [Keytab] the default keytab
|
48
|
+
# @see http://web.mit.edu/Kerberos/krb5-1.14/doc/appdev/refs/api/krb5_kt_default.html krb5_kt_default
|
49
|
+
def self.default
|
50
|
+
pointer = FFI::MemoryPointer.new :pointer
|
51
|
+
Krb5.kt_default(Context.context.ptr, pointer)
|
19
52
|
|
20
|
-
new(
|
53
|
+
new(pointer)
|
21
54
|
end
|
22
55
|
|
23
|
-
#
|
24
|
-
# @param
|
25
|
-
# @param buffer [FFI::Buffer]
|
56
|
+
# Initializes a new Keytab with a pointer to a pointer to a krb5_keytab structure.
|
57
|
+
# @param pointer [FFI::Buffer]
|
26
58
|
# @return [Keytab]
|
27
|
-
def initialize(
|
28
|
-
@
|
29
|
-
@buffer = buffer
|
59
|
+
def initialize(pointer)
|
60
|
+
@ptr = FFI::AutoPointer.new pointer.get_pointer(0), self.class.method(:release)
|
30
61
|
|
31
|
-
ObjectSpace.define_finalizer(self, self.class.finalize(context, buffer))
|
32
62
|
self
|
33
63
|
end
|
34
64
|
|
35
|
-
#
|
36
|
-
#
|
37
|
-
|
38
|
-
|
65
|
+
# Checks if the underlying keytab file or other store exists and contains entries.
|
66
|
+
# (When `krb5_kt_have_content` isn't provided by the Kerberos library, then only some very limited checks are performed.)
|
67
|
+
# @return [TrueClass] if the keytab exists and contains entries
|
68
|
+
# @raises [Error] if there is a problem finding entries in the keytab
|
69
|
+
# @see http://web.mit.edu/Kerberos/krb5-1.14/doc/appdev/refs/api/krb5_kt_have_content.html krb5_kt_have_content
|
70
|
+
def assert_has_content
|
71
|
+
if defined?(Krb5.kt_have_content)
|
72
|
+
Krb5.kt_have_content(Context.context.ptr, ptr)
|
73
|
+
else # HACK
|
74
|
+
raise Error, "Could not read #{name}" if file? and !FileTest.readable?(path)
|
75
|
+
end
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [Boolean] whether the keytab exists and contains entries
|
80
|
+
def has_content?
|
81
|
+
assert_has_content
|
82
|
+
true
|
83
|
+
rescue Error
|
84
|
+
false
|
85
|
+
end
|
86
|
+
|
87
|
+
# The maximum length, in bytes, that can be read by #name .
|
88
|
+
GET_NAME_MAX_LENGTH = 512
|
89
|
+
|
90
|
+
# The seperator between the type and the residual in a keytab's name
|
91
|
+
FULL_NAME_DELIMITER = ':'
|
92
|
+
|
93
|
+
# @return [String] the name of the key table
|
94
|
+
# @see http://web.mit.edu/Kerberos/krb5-1.14/doc/appdev/refs/api/krb5_kt_get_name.html kt_get_name
|
95
|
+
def name
|
96
|
+
if defined?(Krb5.kt_get_full_name)
|
97
|
+
pointer = FFI::MemoryPointer.new :pointer
|
98
|
+
Krb5.kt_get_full_name(Context.context.ptr, ptr, pointer)
|
99
|
+
pointer = pointer.read_pointer
|
100
|
+
copy = String.new(pointer.read_string).force_encoding('UTF-8')
|
101
|
+
Krb5.xfree(pointer)
|
102
|
+
copy
|
103
|
+
else
|
104
|
+
buffer = FFI::Buffer.new :char, GET_NAME_MAX_LENGTH
|
105
|
+
Krb5.kt_get_name(Context.context.ptr, ptr, buffer, GET_NAME_MAX_LENGTH)
|
106
|
+
buffer.read_bytes(255).force_encoding('UTF-8').split("\x00", 2)[0]
|
107
|
+
end
|
39
108
|
end
|
40
109
|
|
41
110
|
# @return [String] the type of the key table
|
42
|
-
# @see http://web.mit.edu/Kerberos/krb5-1.14/doc/appdev/refs/api/krb5_kt_get_type.html kt_get_type
|
43
111
|
def type
|
44
|
-
|
112
|
+
name.split(FULL_NAME_DELIMITER, 2).first
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [String] the residual of the key table, which means different things depending on the type
|
116
|
+
def residual
|
117
|
+
name.split(FULL_NAME_DELIMITER, 2).last
|
118
|
+
end
|
119
|
+
|
120
|
+
# @return [Boolean] if the keytab has a type of 'FILE' or 'file'
|
121
|
+
def file?
|
122
|
+
type =~ /^FILE$/i
|
123
|
+
end
|
124
|
+
|
125
|
+
# @return [String, nil] the path to the keytab file if the keytab is a file, nil otherwise
|
126
|
+
def path
|
127
|
+
file? ? residual : nil
|
45
128
|
end
|
46
129
|
|
47
|
-
#
|
130
|
+
# Closes a Keytab
|
48
131
|
# @api private
|
49
|
-
# @return [Proc]
|
50
132
|
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_kt_close.html krb5_kt_close
|
51
|
-
def self.
|
52
|
-
|
133
|
+
def self.release(pointer)
|
134
|
+
Krb5.kt_close(Context.context.ptr, pointer)
|
53
135
|
end
|
54
136
|
end
|
55
137
|
end
|
@@ -2,7 +2,7 @@ module KerberosAuthenticator
|
|
2
2
|
module Krb5
|
3
3
|
typedef :pointer, :krb5_principal
|
4
4
|
|
5
|
-
attach_function :krb5_parse_name, [:krb5_context, :string, :
|
5
|
+
attach_function :krb5_parse_name, [:krb5_context, :string, :pointer], :krb5_error_code
|
6
6
|
attach_function :krb5_free_principal, [:krb5_context, :krb5_principal], :void
|
7
7
|
|
8
8
|
attach_function :krb5_unparse_name, [:krb5_context, :krb5_principal, :pointer], :krb5_error_code
|
@@ -10,34 +10,34 @@ module KerberosAuthenticator
|
|
10
10
|
|
11
11
|
# A Kerberos principal identifying a user, service or machine.
|
12
12
|
class Principal
|
13
|
-
|
13
|
+
# @!attribute [r] ptr
|
14
|
+
# @return [FFI::Pointer] the pointer to the wrapped krb5_principal struct
|
15
|
+
|
16
|
+
|
17
|
+
attr_reader :ptr
|
14
18
|
|
15
19
|
# Convert a string representation of a principal name into a new Principal.
|
16
20
|
# @param name [String] a string representation of a principal name
|
17
|
-
# @param context [Context] a Kerberos library context
|
18
21
|
# @return [Principal]
|
19
22
|
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_parse_name.html krb5_parse_name
|
20
|
-
def self.new_with_name(name
|
23
|
+
def self.new_with_name(name)
|
21
24
|
raise ArgumentError, 'name cannot be empty' if name.empty?
|
22
25
|
|
23
|
-
|
24
|
-
Krb5.parse_name(context.ptr, name,
|
25
|
-
new(
|
26
|
+
pointer = FFI::MemoryPointer.new :pointer
|
27
|
+
Krb5.parse_name(Context.context.ptr, name, pointer)
|
28
|
+
new(pointer)
|
26
29
|
end
|
27
30
|
|
28
|
-
# Initialize a new Principal with a
|
29
|
-
# @param
|
30
|
-
# @param buffer [FFI::Buffer]
|
31
|
+
# Initialize a new Principal with a pointer to a pointer to a krb5_principal structure.
|
32
|
+
# @param pointer [FFI::Pointer]
|
31
33
|
# @return [Principal]
|
32
|
-
def initialize(
|
33
|
-
@
|
34
|
-
@buffer = buffer
|
35
|
-
|
36
|
-
ObjectSpace.define_finalizer(self, self.class.finalize(context, buffer))
|
34
|
+
def initialize(pointer)
|
35
|
+
@ptr = FFI::AutoPointer.new pointer.get_pointer(0), self.class.method(:release)
|
37
36
|
|
38
37
|
self
|
39
38
|
end
|
40
39
|
|
40
|
+
# Calls Creds.initial_creds_for_principal_with_a_password(self, password, service)
|
41
41
|
# @param password [String]
|
42
42
|
# @param service [String]
|
43
43
|
# @return [Creds]
|
@@ -46,32 +46,33 @@ module KerberosAuthenticator
|
|
46
46
|
Creds.initial_creds_for_principal_with_a_password(self, password, service)
|
47
47
|
end
|
48
48
|
|
49
|
-
# @return [FFI::Pointer] the pointer to the krb5_principal structure
|
50
|
-
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/types/krb5_principal.html krb5_principal
|
51
|
-
def ptr
|
52
|
-
@buffer.get_pointer(0)
|
53
|
-
end
|
54
|
-
|
55
49
|
# @return [String] a string representation of the principal's name
|
56
50
|
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_unparse_name.html krb5_unparse_name
|
57
51
|
def name
|
58
52
|
out_ptr = FFI::MemoryPointer.new(:pointer, 1)
|
59
|
-
Krb5.unparse_name(context.ptr, ptr, out_ptr)
|
53
|
+
Krb5.unparse_name(Context.context.ptr, ptr, out_ptr)
|
60
54
|
|
61
55
|
str_ptr = out_ptr.read_pointer
|
62
|
-
copy = String.new(str_ptr.read_string)
|
56
|
+
copy = String.new(str_ptr.read_string).force_encoding('UTF-8')
|
63
57
|
|
64
|
-
Krb5.free_unparsed_name(context.ptr, str_ptr)
|
58
|
+
Krb5.free_unparsed_name(Context.context.ptr, str_ptr)
|
65
59
|
|
66
60
|
copy
|
67
61
|
end
|
68
62
|
|
69
|
-
#
|
63
|
+
# A convenience function to allow a Principal to change a password by authenticating themselves.
|
64
|
+
# @raise [Error] if the attempt to change the password fails
|
65
|
+
# @return [TrueClass] always returns true if no error was raised
|
66
|
+
def change_password(oldpw, new_pw)
|
67
|
+
changepw_creds = self.initial_creds_with_password(oldpw, 'kadmin/changepw')
|
68
|
+
changepw_creds.set_password(new_pw, self)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Frees a Principal
|
70
72
|
# @api private
|
71
|
-
# @return [Proc]
|
72
73
|
# @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/api/krb5_free_principal.html krb5_free_principal
|
73
|
-
def self.
|
74
|
-
|
74
|
+
def self.release(pointer)
|
75
|
+
Krb5.free_principal(Context.context.ptr, pointer)
|
75
76
|
end
|
76
77
|
end
|
77
78
|
end
|
@@ -1,3 +1,3 @@
|
|
1
1
|
module KerberosAuthenticator
|
2
|
-
VERSION = '0.0.
|
3
|
-
end
|
2
|
+
VERSION = '0.0.7'.freeze
|
3
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kerberos_authenticator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Watkins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- lib/kerberos_authenticator/krb5/attach_function.rb
|
51
51
|
- lib/kerberos_authenticator/krb5/context.rb
|
52
52
|
- lib/kerberos_authenticator/krb5/creds.rb
|
53
|
+
- lib/kerberos_authenticator/krb5/data.rb
|
53
54
|
- lib/kerberos_authenticator/krb5/error.rb
|
54
55
|
- lib/kerberos_authenticator/krb5/keytab.rb
|
55
56
|
- lib/kerberos_authenticator/krb5/principal.rb
|