kerberos_authenticator 0.0.6 → 0.0.7
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.
- 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
|