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 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