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.
- checksums.yaml +4 -4
- data/CHANGES.md +15 -0
- data/EXAMPLES.md +1 -1
- data/README.md +13 -2
- data/ext/rkerberos/ccache.c +9 -11
- data/ext/rkerberos/config.c +22 -16
- data/ext/rkerberos/context.c +64 -9
- data/ext/rkerberos/kadm5.c +79 -56
- data/ext/rkerberos/keytab.c +35 -30
- data/ext/rkerberos/principal.c +42 -12
- data/ext/rkerberos/rkerberos.c +55 -39
- data/ext/rkerberos/rkerberos.h +6 -0
- data/rkerberos.gemspec +1 -1
- data/spec/config_spec.rb +8 -0
- data/spec/context_spec.rb +45 -0
- data/spec/credentials_cache_spec.rb +3 -0
- data/spec/kadm5_spec.rb +42 -1
- data/spec/krb5_keytab_spec.rb +52 -1
- data/spec/krb5_spec.rb +57 -3
- data/spec/principal_spec.rb +26 -0
- metadata +1 -1
data/spec/krb5_keytab_spec.rb
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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: '
|
|
185
|
+
krb5.authenticate!(principal: user, password: 'changeme', service: 'verify/rkerberos-test')
|
|
151
186
|
}.not_to raise_error
|
|
152
|
-
|
|
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
|
data/spec/principal_spec.rb
CHANGED
|
@@ -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)
|