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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d21f1ea20049f7cc581df957b1b2e738b4578cab
4
- data.tar.gz: 74504aa9db7035a3935f346c2563b3ccfda586d2
3
+ metadata.gz: f345054a20a13a084ee6979f3f32d62a4ba2e898
4
+ data.tar.gz: b0909f23de117867bf9806ef63b39b3bee1e5d24
5
5
  SHA512:
6
- metadata.gz: ec9e7e068cfb22395aab8865f03f8060bee9f1e5f66196518435963f13100d7953dc9013c6dd76dd9f349f6a994eba444baea344655dc3699e2c8f4717c0a666
7
- data.tar.gz: 6a784a3b02dac99f4638d23da612918c89116eb55b79b88074176a99edeb826a523d5c0eb041ab1421227677743970fc0932708dabe052751cf49eaf6ef54ea8
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::Error.raise_if_error
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::Error.raise_if_error(args.first) { public_send(no_check_name, *args, &block) }
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::Error.raise_if_error(nil) { public_send(no_check_name, *args, &block) }
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, [:buffer_out], :krb5_error_code
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, [:buffer_out], :krb5_error_code
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 ignore environmental variables by default)
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
- @buffer = FFI::Buffer.new :pointer
54
+ pointer = FFI::MemoryPointer.new :pointer
33
55
 
34
56
  if secure
35
- Krb5::Error.raise_if_error { Krb5.init_secure_context(@buffer) }
57
+ Krb5::LibCallError.raise_if_error { Krb5.init_secure_context(pointer) }
36
58
  else
37
- Krb5::Error.raise_if_error { Krb5.init_context(@buffer) }
59
+ Krb5::LibCallError.raise_if_error { Krb5.init_context(pointer) }
38
60
  end
39
61
 
40
- ObjectSpace.define_finalizer(self, self.class.finalize(@buffer))
62
+ @ptr = FFI::AutoPointer.new pointer.get_pointer(0), self.class.method(:release)
63
+
41
64
  self
42
65
  end
43
66
 
44
- # @return [FFI::Pointer] the pointer to the krb5_context structure
45
- # @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/types/krb5_context.html krb5_context
46
- def ptr
47
- @buffer.get_pointer(0)
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
- # Builds a Proc to free the Context once its no longer in use.
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.finalize(buffer)
55
- proc { Krb5.free_context(buffer.get_pointer(0)) }
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
- attr_reader :context, :ptr
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
- Krb5.get_init_creds_password(context.ptr, ptr, principal.ptr, password.to_str, nil, nil, 0, service, nil)
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, and define its finalizer.
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(context, ptr)
47
- @context = context
48
- @ptr = ptr
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
- @ptr.autorelease = false
51
- ObjectSpace.define_finalizer(self, self.class.finalize(context, ptr))
54
+ ptr = FFI::AutoPointer.new ptr, self.class.method(:release)
52
55
 
53
- self
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
- # Attempt to verify that these Creds were obtained from a KDC with knowledge of a key in keytab.
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
- # Builds a Proc to free the credentials once they're no longer in use.
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.finalize(context, ptr)
89
- proc { Krb5.free_cred_contents(context.ptr, ptr); ptr.free }
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
- # A Kerberos library error
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 Error using an error code and the relevant Context to provide a friendly error message.
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 [Error]
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
- FFI::AutoPointer.new(error_ptr, self.class.finalize(context_ptr))
25
- super(String.new(error_message))
27
+ self.class.release(error_ptr)
28
+ super String.new(error_message).force_encoding('UTF-8')
26
29
  end
27
30
 
28
- # Build a Proc to free the error message string once it's no longer in use.
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.finalize(context_ptr)
33
- proc { |ptr| Krb5.free_error_message(context_ptr, ptr) }
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 [Error] if the krb5_error_code differed from zero
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 == 0
44
- raise Krb5::Error.new(context_ptr, err)
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, :buffer_out], :krb5_error_code
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
- attach_function :krb5_kt_get_type, [:krb5_context, :krb5_keytab], :string
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
- attr_reader :context
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
- # @return [Keytab]
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, context = Context.context)
17
- buffer = FFI::Buffer.new :pointer
18
- Krb5.kt_resolve(context.ptr, name, buffer)
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(context, buffer)
53
+ new(pointer)
21
54
  end
22
55
 
23
- # Initialize a new Keytab with a buffer containing a krb5_keytab structure, and define its finalizer.
24
- # @param context [Context]
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(context, buffer)
28
- @context = context
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
- # @return [FFI::Pointer] the pointer to the krb5_keytab structure
36
- # @see http://web.mit.edu/kerberos/krb5-1.14/doc/appdev/refs/types/krb5_keytab.html krb5_keytab
37
- def ptr
38
- @buffer.get_pointer(0)
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
- Krb5.kt_get_type(context.ptr, ptr)
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
- # Builds a Proc to close the Keytab once its no longer in use.
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.finalize(context, buffer)
52
- proc { Krb5.kt_close(context.ptr, buffer.get_pointer(0)) }
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, :krb5_principal], :krb5_error_code
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
- attr_reader :context
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, context = Context.context)
23
+ def self.new_with_name(name)
21
24
  raise ArgumentError, 'name cannot be empty' if name.empty?
22
25
 
23
- buffer = FFI::Buffer.new :pointer
24
- Krb5.parse_name(context.ptr, name, buffer)
25
- new(context, buffer)
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 buffer containing a krb5_principal structure, and define its finalizer.
29
- # @param context [Context]
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(context, buffer)
33
- @context = context
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
- # Builds a Proc to free the Principal once it's no longer in use.
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.finalize(context, buffer)
74
- proc { Krb5.free_principal(context.ptr, buffer.get_pointer(0)) }
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.6'
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.6
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-04-24 00:00:00.000000000 Z
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