rkerberos 0.3.0 → 0.3.1

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.
@@ -36,6 +36,17 @@ RSpec.describe Kerberos::Krb5::Keytab, :kadm5 do
36
36
 
37
37
  subject(:keytab) { described_class.new }
38
38
 
39
+ def write_keytab_with_invalid_second_record(destination)
40
+ data = File.binread(@keytab_file)
41
+ first_record_size = data.byteslice(2, 4).unpack1('N')
42
+ second_record_offset = 6 + first_record_size
43
+
44
+ # INT32_MIN is reserved as an invalid record size by the FILE keytab
45
+ # implementation, so iteration must fail after yielding the first entry.
46
+ data[second_record_offset, 4] = [0x80000000].pack('N')
47
+ File.binwrite(destination, data)
48
+ end
49
+
39
50
  describe 'constructor' do
40
51
  it 'accepts an optional name keyword' do
41
52
  expect { described_class.new(name: "FILE:/usr/local/var/keytab") }.not_to raise_error
@@ -50,7 +61,10 @@ RSpec.describe Kerberos::Krb5::Keytab, :kadm5 do
50
61
 
51
62
  it 'accepts a context keyword argument' do
52
63
  ctx = Kerberos::Krb5::Context.new
53
- expect { described_class.new(name: @keytab_name, context: ctx) }.not_to raise_error
64
+ keytab = described_class.new(name: @keytab_name, context: ctx)
65
+ expect { ctx.close }.to raise_error(Kerberos::Krb5::Exception, /dependent wrapper/)
66
+ keytab.close
67
+ expect { ctx.close }.not_to raise_error
54
68
  end
55
69
 
56
70
  it 'works with context and no name' do
@@ -123,6 +137,15 @@ RSpec.describe Kerberos::Krb5::Keytab, :kadm5 do
123
137
  kt = nil
124
138
  GC.start
125
139
  end
140
+
141
+ it 'raises from every operation that requires the closed handle' do
142
+ kt = described_class.new(name: @keytab_name)
143
+ kt.close
144
+
145
+ expect { kt.default_name }.to raise_error(Kerberos::Krb5::Exception)
146
+ expect { kt.each { |_| } }.to raise_error(Kerberos::Krb5::Exception)
147
+ expect { kt.get_entry("testuser1@#{@realm}") }.to raise_error(Kerberos::Krb5::Exception)
148
+ end
126
149
  end
127
150
 
128
151
  describe '.foreach' do
@@ -157,6 +180,19 @@ RSpec.describe Kerberos::Krb5::Keytab, :kadm5 do
157
180
  }.to raise_error(Kerberos::Krb5::Exception)
158
181
  end
159
182
 
183
+ it 'raises when an error interrupts iteration' do
184
+ corrupt_keytab = File.join(Dir.tmpdir, "invalid-foreach-#{Process.pid}.keytab")
185
+ write_keytab_with_invalid_second_record(corrupt_keytab)
186
+ entries = []
187
+
188
+ expect {
189
+ described_class.foreach("FILE:#{corrupt_keytab}") { |entry| entries << entry }
190
+ }.to raise_error(Kerberos::Krb5::Exception, /krb5_kt_next_entry/)
191
+ expect(entries).not_to be_empty
192
+ ensure
193
+ FileUtils.rm_f(corrupt_keytab)
194
+ end
195
+
160
196
  it 'raises an error for an invalid keytab type' do
161
197
  expect {
162
198
  described_class.foreach("BOGUS:/tmp/keytab") { |_| }
@@ -208,6 +244,21 @@ RSpec.describe Kerberos::Krb5::Keytab, :kadm5 do
208
244
  end
209
245
  expect(result).to eq(:escaped)
210
246
  end
247
+
248
+ it 'raises instead of returning a partial result after an iteration error' do
249
+ corrupt_keytab = File.join(Dir.tmpdir, "invalid-each-#{Process.pid}.keytab")
250
+ write_keytab_with_invalid_second_record(corrupt_keytab)
251
+ entries = []
252
+ kt = described_class.new(name: "FILE:#{corrupt_keytab}")
253
+
254
+ expect {
255
+ kt.each { |entry| entries << entry }
256
+ }.to raise_error(Kerberos::Krb5::Exception, /krb5_kt_next_entry/)
257
+ expect(entries).not_to be_empty
258
+ ensure
259
+ kt&.close
260
+ FileUtils.rm_f(corrupt_keytab)
261
+ end
211
262
  end
212
263
 
213
264
  describe '#get_entry' do
data/spec/krb5_spec.rb CHANGED
@@ -23,7 +23,7 @@ RSpec.describe Kerberos::Krb5 do
23
23
  let(:service) { 'kadmin/admin' }
24
24
 
25
25
  it 'has the correct version constant' do
26
- expect(Kerberos::Krb5::VERSION).to eq('0.3.0')
26
+ expect(Kerberos::Krb5::VERSION).to eq('0.3.1')
27
27
  end
28
28
 
29
29
  it 'accepts a block and yields itself' do
@@ -73,6 +73,17 @@ RSpec.describe Kerberos::Krb5 do
73
73
  it 'default_realm is an alias for get_default_realm' do
74
74
  expect(krb5.method(:default_realm)).to eq(krb5.method(:get_default_realm))
75
75
  end
76
+ it 'raises after the object is closed' do
77
+ krb5.close
78
+ expect { krb5.get_default_realm }.to raise_error(Kerberos::Krb5::Exception)
79
+ end
80
+ end
81
+
82
+ describe '#set_default_realm' do
83
+ it 'raises after the object is closed' do
84
+ krb5.close
85
+ expect { krb5.set_default_realm(@realm) }.to raise_error(Kerberos::Krb5::Exception)
86
+ end
76
87
  end
77
88
 
78
89
  describe '#verify_init_creds', :kadm5 do
@@ -131,6 +142,14 @@ RSpec.describe Kerberos::Krb5 do
131
142
  expect(krb5.verify_init_creds(keytab: kt)).to be true
132
143
  end
133
144
 
145
+ it 'rejects a closed Keytab object' do
146
+ krb5.get_init_creds_password(principal: user, password: 'changeme')
147
+ kt = Kerberos::Krb5::Keytab.new
148
+ kt.close
149
+ expect { krb5.verify_init_creds(keytab: kt) }
150
+ .to raise_error(Kerberos::Krb5::Exception, /keytab is closed/)
151
+ end
152
+
134
153
  it 'stores additional credentials in provided CredentialsCache' do
135
154
  ccache = Kerberos::Krb5::CredentialsCache.new
136
155
  krb5.get_init_creds_password(principal: user, password: 'changeme')
@@ -139,6 +158,22 @@ RSpec.describe Kerberos::Krb5 do
139
158
  expect(ccache.primary_principal).to include('@')
140
159
  end
141
160
 
161
+ it 'rejects a closed CredentialsCache' do
162
+ krb5.get_init_creds_password(principal: user, password: 'changeme')
163
+ ccache = Kerberos::Krb5::CredentialsCache.new
164
+ ccache.close
165
+ expect { krb5.verify_init_creds(ccache: ccache) }
166
+ .to raise_error(Kerberos::Krb5::Exception, /credentials cache is closed/)
167
+ end
168
+
169
+ it 'does not acquire credentials into a closed CredentialsCache' do
170
+ ccache = Kerberos::Krb5::CredentialsCache.new
171
+ ccache.close
172
+ expect {
173
+ krb5.get_init_creds_password(principal: user, password: 'changeme', ccache: ccache)
174
+ }.to raise_error(Kerberos::Krb5::Exception, /credentials cache is closed/)
175
+ end
176
+
142
177
  it 'provides authenticate! which acquires and verifies (Zanarotti mitigation)' do
143
178
  expect(krb5).to respond_to(:authenticate!)
144
179
  expect(krb5.authenticate!(principal: user, password: 'changeme')).to be true
@@ -147,9 +182,19 @@ RSpec.describe Kerberos::Krb5 do
147
182
 
148
183
  it 'accepts an optional service argument' do
149
184
  expect {
150
- krb5.authenticate!(principal: user, password: 'changeme', service: 'kadmin/changepw')
185
+ krb5.authenticate!(principal: user, password: 'changeme', service: 'verify/rkerberos-test')
151
186
  }.not_to raise_error
152
- expect(krb5.verify_init_creds).to be true
187
+ end
188
+
189
+ it 'fails closed when no verification keytab is available' do
190
+ original_keytab = ENV['KRB5_KTNAME']
191
+ ENV['KRB5_KTNAME'] = "FILE:/tmp/missing_verify_keytab_#{Process.pid}"
192
+
193
+ expect {
194
+ described_class.new.authenticate!(principal: user, password: 'changeme')
195
+ }.to raise_error(Kerberos::Krb5::Exception, /krb5_verify_init_creds/)
196
+ ensure
197
+ ENV['KRB5_KTNAME'] = original_keytab
153
198
  end
154
199
 
155
200
  it 'validates argument types for authenticate!' do
@@ -272,6 +317,15 @@ RSpec.describe Kerberos::Krb5 do
272
317
  expect(ccache.primary_principal).to be_a(String)
273
318
  expect(ccache.primary_principal).to include('@')
274
319
  end
320
+
321
+ it 'rejects a closed CredentialsCache' do
322
+ ccache = Kerberos::Krb5::CredentialsCache.new
323
+ ccache.close
324
+ kt_name = "FILE:#{@kt_file}"
325
+ expect {
326
+ krb5.get_init_creds_keytab(principal: user, keytab: kt_name, ccache: ccache)
327
+ }.to raise_error(Kerberos::Krb5::Exception, /credentials cache is closed/)
328
+ end
275
329
  end
276
330
 
277
331
  describe '.thread_safe?' do
@@ -63,6 +63,32 @@ RSpec.describe Kerberos::Krb5::Principal do
63
63
  end
64
64
  end
65
65
 
66
+ describe '#close' do
67
+ it 'releases a borrowed context and prevents further native operations' do
68
+ context = Kerberos::Krb5::Context.new
69
+ principal = described_class.new(name: name, context: context)
70
+
71
+ expect(principal.close).to equal(principal)
72
+ expect { principal.realm }.to raise_error(Kerberos::Krb5::Exception)
73
+ expect { context.close }.not_to raise_error
74
+ end
75
+
76
+ it 'can be called multiple times' do
77
+ expect { 2.times { princ.close } }.not_to raise_error
78
+ end
79
+ end
80
+
81
+ describe '#==' do
82
+ it 'returns true for equivalent principals' do
83
+ expect(princ).to eq(described_class.new(name: name))
84
+ end
85
+
86
+ it 'returns false for objects of another type' do
87
+ expect(princ == name).to be false
88
+ expect(princ == nil).to be false
89
+ end
90
+ end
91
+
66
92
  describe '#name' do
67
93
  it 'responds to name' do
68
94
  expect(princ).to respond_to(:name)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rkerberos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Berger