rkerberos 0.2.0 → 0.2.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 → CHANGES.md} +12 -0
- data/{MANIFEST → MANIFEST.md} +2 -2
- data/README.md +55 -3
- data/Rakefile +32 -0
- data/ext/rkerberos/ccache.c +9 -8
- data/ext/rkerberos/context.c +63 -8
- data/ext/rkerberos/kadm5.c +0 -26
- data/ext/rkerberos/policy.c +1 -1
- data/ext/rkerberos/rkerberos.c +191 -4
- data/ext/rkerberos/rkerberos.h +2 -0
- data/rkerberos.gemspec +16 -7
- data/spec/context_spec.rb +36 -0
- data/spec/krb5_spec.rb +107 -1
- metadata +17 -28
- data/Dockerfile +0 -42
- data/docker/Dockerfile.kdc +0 -16
- data/docker/docker-entrypoint.sh +0 -23
- data/docker/kadm5.acl +0 -1
- data/docker/kdc.conf +0 -13
- data/docker/krb5.conf +0 -14
- data/docker-compose.yml +0 -44
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d95bc44b05fe9b2d7d50af063944d2464a39e2387c6af3c9b8dbefd68025e94
|
|
4
|
+
data.tar.gz: 8dcdcec07fcdd598065fc7e826dda60b66a683c0cfb500aaa594fdbe5b5c3326
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ee3844d89f82e24b9447f67538e40f9af8d4df630c3e3da757e0ce8c3d54b1ba1d3a57d48db0c2ed4001a687dd1ccfbcb2e3c5ef6eb120b5eed5802964a47f74
|
|
7
|
+
data.tar.gz: 93003f70201a17ecbc3f158cf4fa997657ee1d25b1c28d3e3019207f3b3664d880db4d3297a8a542d54343ad4fe2f38afa607b028ea161d6bbb50a1a526f4e63
|
data/{CHANGES → CHANGES.md}
RENAMED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
= 0.2.1 - 1-Mar-2026
|
|
2
|
+
* Added the verify_init_creds and an authenticate! methods.
|
|
3
|
+
* The Context constructor now accepts optional :secure and/or :profile arguments
|
|
4
|
+
for different types of contexts.
|
|
5
|
+
* Minor fix for the CredentialsCache constructor.
|
|
6
|
+
* Minor fix for the get_init_creds_keytab method.
|
|
7
|
+
* Fixed a mistake in an rb_funcall in the Policy class (thanks Ondřej Gajdušek).
|
|
8
|
+
* Update gemspec so that releases don't include Docker related files (thanks Ondřej Gajdušek).
|
|
9
|
+
* Add a spec:compose task for convenience.
|
|
10
|
+
* The rake-compiler gem is now a development dependency, not a runtime
|
|
11
|
+
dependency (thanks Ondřej Gajdušek).
|
|
12
|
+
|
|
1
13
|
= 0.2.0 - 14-Feb-2026
|
|
2
14
|
* Added Docker and Podman support for running tests in isolated environments with Kerberos and OpenLDAP services.
|
|
3
15
|
* Updated documentation with modern testing and development workflows, including container-based instructions.
|
data/{MANIFEST → MANIFEST.md}
RENAMED
data/README.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
|
+
[](https://github.com/rkerberos/rkerberos/actions/workflows/ci.yml)
|
|
2
|
+
|
|
1
3
|
# Description
|
|
2
|
-
|
|
4
|
+
The rkerberos library provides a Ruby interface for Kerberos.
|
|
5
|
+
|
|
6
|
+
## Code synopsis
|
|
7
|
+
|
|
8
|
+
Some basic usage:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
require 'rkerberos'
|
|
12
|
+
|
|
13
|
+
# Client
|
|
14
|
+
krb = Kerberos::Krb5.new
|
|
15
|
+
puts krb.default_realm
|
|
16
|
+
puts krb.default_principal
|
|
17
|
+
puts krb.get_permitted_enctypes.keys.join(',')
|
|
18
|
+
|
|
19
|
+
# Credentials cache
|
|
20
|
+
cc = Kerberos::Krb5::CredentialsCache.new
|
|
21
|
+
krb.verify_init_creds(nil, nil, cc)
|
|
22
|
+
puts cc.primary_principal
|
|
23
|
+
|
|
24
|
+
# Keytab
|
|
25
|
+
kt_name = Kerberos::Krb5::Keytab.new.default_name # e.g. "FILE:/etc/krb5.keytab"
|
|
26
|
+
krb.get_init_creds_keytab('host/server.example.com', kt_name)
|
|
27
|
+
krb.get_init_creds_keytab('host/server.example.com', kt_name, nil, cc) # or write to cache
|
|
28
|
+
|
|
29
|
+
# Admin
|
|
30
|
+
Kerberos::Kadm5.new(principal: ENV['KRB5_ADMIN_PRINCIPAL'], password: ENV['KRB5_ADMIN_PASSWORD']) do |kadmin|
|
|
31
|
+
kadmin.create_principal('newuser@EXAMPLE.COM', 'initialpass')
|
|
32
|
+
kadmin.set_password('newuser@EXAMPLE.COM', 'betterpass')
|
|
33
|
+
kadmin.delete_principal('newuser@EXAMPLE.COM')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Contexts
|
|
37
|
+
ctx = Kerberos::Krb5::Context.new # standard context
|
|
38
|
+
ctx = Kerberos::Krb5::Context.new(profile: '/etc/krb5.conf') # or use a profile
|
|
39
|
+
ctx = Kerberos::Krb5::Context.new(secure: true) # or use a secure context
|
|
40
|
+
ctx.close
|
|
41
|
+
```
|
|
3
42
|
|
|
4
43
|
# Requirements
|
|
5
44
|
|
|
@@ -91,6 +130,19 @@ If you make changes to the Ruby code or C extensions:
|
|
|
91
130
|
podman-compose run --rm rkerberos-test
|
|
92
131
|
```
|
|
93
132
|
|
|
133
|
+
Alternatively, you can just run containerized tests via the `spec:compose`
|
|
134
|
+
Rake task. This task runs the same containerized workflow used above and
|
|
135
|
+
prefers `podman-compose` with a `docker-compose` fallback.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# build image and run RSpec inside the test container
|
|
139
|
+
rake spec:compose
|
|
140
|
+
# skip the build step by passing a positional or named argument:
|
|
141
|
+
# (equivalent forms)
|
|
142
|
+
rake spec:compose[true]
|
|
143
|
+
rake "spec:compose[fast=true]"
|
|
144
|
+
```
|
|
145
|
+
|
|
94
146
|
The test environment includes:
|
|
95
147
|
- MIT Kerberos KDC (Key Distribution Center)
|
|
96
148
|
- OpenLDAP server for directory services
|
|
@@ -112,8 +164,8 @@ The test environment includes:
|
|
|
112
164
|
|
|
113
165
|
# Authors
|
|
114
166
|
* Daniel Berger
|
|
115
|
-
* Dominic Cleal
|
|
116
|
-
* Simon Levermann
|
|
167
|
+
* Dominic Cleal
|
|
168
|
+
* Simon Levermann
|
|
117
169
|
|
|
118
170
|
# License
|
|
119
171
|
rkerberos is distributed under the Artistic-2.0 license.
|
data/Rakefile
CHANGED
|
@@ -77,6 +77,38 @@ RSpec::Core::RakeTask.new(:spec) do |t|
|
|
|
77
77
|
t.pattern = 'spec/**/*_spec.rb'
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
# Run specs inside the project container using podman-compose (or docker-compose).
|
|
81
|
+
namespace :spec do
|
|
82
|
+
desc 'Build test image and run RSpec inside container (podman-compose or docker-compose)'
|
|
83
|
+
task :compose, [:fast] do |t, args|
|
|
84
|
+
# allow either positional or named argument (e.g. "fast=true")
|
|
85
|
+
fast = args[:fast]
|
|
86
|
+
if fast && fast.include?("=")
|
|
87
|
+
k,v = fast.split("=",2)
|
|
88
|
+
fast = v if k == 'fast'
|
|
89
|
+
end
|
|
90
|
+
fast = true if fast == 'true'
|
|
91
|
+
|
|
92
|
+
compose = `which podman-compose`.strip
|
|
93
|
+
compose = 'docker-compose' if compose.empty?
|
|
94
|
+
|
|
95
|
+
if fast
|
|
96
|
+
puts "Using #{compose} to run containerized specs (fast)..."
|
|
97
|
+
else
|
|
98
|
+
puts "Using #{compose} to run containerized specs..."
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
FileUtils.rm_rf('Gemfile.lock')
|
|
102
|
+
begin
|
|
103
|
+
sh "#{compose} build --no-cache rkerberos-test" unless fast
|
|
104
|
+
sh "#{compose} run --rm rkerberos-test"
|
|
105
|
+
ensure
|
|
106
|
+
# redirect stderr so missing-container messages don't appear
|
|
107
|
+
sh "#{compose} down -v 2>/dev/null" rescue nil
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
80
112
|
# Clean up afterwards
|
|
81
113
|
Rake::Task[:spec].enhance do
|
|
82
114
|
Rake::Task[:clean].invoke
|
data/ext/rkerberos/ccache.c
CHANGED
|
@@ -57,10 +57,17 @@ static VALUE rkrb5_ccache_initialize(int argc, VALUE* argv, VALUE self){
|
|
|
57
57
|
|
|
58
58
|
rb_scan_args(argc, argv, "02", &v_principal, &v_name);
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
if(RTEST(v_principal)){
|
|
60
|
+
if(RTEST(v_principal))
|
|
62
61
|
Check_Type(v_principal, T_STRING);
|
|
63
62
|
|
|
63
|
+
// Initialize the context
|
|
64
|
+
kerror = krb5_init_context(&ptr->ctx);
|
|
65
|
+
|
|
66
|
+
if(kerror)
|
|
67
|
+
rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
|
|
68
|
+
|
|
69
|
+
// Convert the principal name to a principal object
|
|
70
|
+
if(RTEST(v_principal)){
|
|
64
71
|
kerror = krb5_parse_name(
|
|
65
72
|
ptr->ctx,
|
|
66
73
|
StringValueCStr(v_principal),
|
|
@@ -71,12 +78,6 @@ static VALUE rkrb5_ccache_initialize(int argc, VALUE* argv, VALUE self){
|
|
|
71
78
|
rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
|
|
72
79
|
}
|
|
73
80
|
|
|
74
|
-
// Initialize the context
|
|
75
|
-
kerror = krb5_init_context(&ptr->ctx);
|
|
76
|
-
|
|
77
|
-
if(kerror)
|
|
78
|
-
rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
|
|
79
|
-
|
|
80
81
|
// Set the credentials cache using the default cache if no name is provided
|
|
81
82
|
if(NIL_P(v_name)){
|
|
82
83
|
kerror = krb5_cc_default(ptr->ctx, &ptr->ccache);
|
data/ext/rkerberos/context.c
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#include <rkerberos.h>
|
|
2
2
|
|
|
3
|
+
#include <profile.h>
|
|
4
|
+
|
|
3
5
|
VALUE cKrb5Context;
|
|
4
6
|
|
|
5
7
|
// Free function for the Kerberos::Krb5::Context class.
|
|
@@ -51,23 +53,76 @@ static VALUE rkrb5_context_close(VALUE self){
|
|
|
51
53
|
|
|
52
54
|
/*
|
|
53
55
|
* call-seq:
|
|
54
|
-
* Kerberos::Context.new
|
|
56
|
+
* Kerberos::Context.new(options = {})
|
|
55
57
|
*
|
|
56
58
|
* Creates and returns a new Kerberos::Context object.
|
|
57
59
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
+
* The options hash may be one or both of the following keys:
|
|
61
|
+
*
|
|
62
|
+
* :secure => true|false # Use config files only, ignore env variables
|
|
63
|
+
* :profile => '/path/to/krb5.conf' # Use the specified profile file
|
|
60
64
|
*/
|
|
61
|
-
static VALUE rkrb5_context_initialize(VALUE self){
|
|
65
|
+
static VALUE rkrb5_context_initialize(int argc, VALUE *argv, VALUE self){
|
|
62
66
|
RUBY_KRB5_CONTEXT* ptr;
|
|
67
|
+
VALUE v_opts;
|
|
68
|
+
VALUE v_secure, v_profile;
|
|
63
69
|
krb5_error_code kerror;
|
|
64
70
|
|
|
65
71
|
TypedData_Get_Struct(self, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ptr);
|
|
66
72
|
|
|
67
|
-
|
|
73
|
+
rb_scan_args(argc, argv, "01", &v_opts);
|
|
74
|
+
|
|
75
|
+
// Default behavior is a normal context that may respect environment.
|
|
76
|
+
if (NIL_P(v_opts)) {
|
|
77
|
+
kerror = krb5_init_context(&ptr->ctx);
|
|
78
|
+
if(kerror)
|
|
79
|
+
rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
|
|
80
|
+
|
|
81
|
+
return self;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Check_Type(v_opts, T_HASH);
|
|
85
|
+
|
|
86
|
+
v_secure = rb_hash_aref2(v_opts, ID2SYM(rb_intern("secure")));
|
|
87
|
+
v_profile = rb_hash_aref2(v_opts, ID2SYM(rb_intern("profile")));
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* If a profile path is supplied, load it via profile_init_path() and
|
|
91
|
+
* create a context from that profile. The KRB5_INIT_CONTEXT_SECURE flag
|
|
92
|
+
* is used when the :secure option is truthy.
|
|
93
|
+
*/
|
|
94
|
+
if (!NIL_P(v_profile)){
|
|
95
|
+
Check_Type(v_profile, T_STRING);
|
|
96
|
+
|
|
97
|
+
const char *profile_path = StringValueCStr(v_profile);
|
|
98
|
+
profile_t profile = NULL;
|
|
99
|
+
long pres = profile_init_path(profile_path, &profile);
|
|
100
|
+
|
|
101
|
+
if(pres != 0)
|
|
102
|
+
rb_raise(cKrb5Exception, "profile_init_path: %ld", pres);
|
|
103
|
+
|
|
104
|
+
krb5_flags flags = RTEST(v_secure) ? KRB5_INIT_CONTEXT_SECURE : 0;
|
|
105
|
+
kerror = krb5_init_context_profile(profile, flags, &ptr->ctx);
|
|
106
|
+
|
|
107
|
+
profile_release(profile);
|
|
108
|
+
|
|
109
|
+
if(kerror)
|
|
110
|
+
rb_raise(cKrb5Exception, "krb5_init_context_profile: %s", error_message(kerror));
|
|
111
|
+
|
|
112
|
+
return self;
|
|
113
|
+
}
|
|
68
114
|
|
|
69
|
-
|
|
70
|
-
|
|
115
|
+
// No profile given, choose secure or normal init.
|
|
116
|
+
if (RTEST(v_secure)){
|
|
117
|
+
kerror = krb5_init_secure_context(&ptr->ctx);
|
|
118
|
+
if(kerror)
|
|
119
|
+
rb_raise(cKrb5Exception, "krb5_init_secure_context: %s", error_message(kerror));
|
|
120
|
+
}
|
|
121
|
+
else{
|
|
122
|
+
kerror = krb5_init_context(&ptr->ctx);
|
|
123
|
+
if(kerror)
|
|
124
|
+
rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
|
|
125
|
+
}
|
|
71
126
|
|
|
72
127
|
return self;
|
|
73
128
|
}
|
|
@@ -80,7 +135,7 @@ void Init_context(void){
|
|
|
80
135
|
rb_define_alloc_func(cKrb5Context, rkrb5_context_allocate);
|
|
81
136
|
|
|
82
137
|
// Constructor
|
|
83
|
-
rb_define_method(cKrb5Context, "initialize", rkrb5_context_initialize,
|
|
138
|
+
rb_define_method(cKrb5Context, "initialize", rkrb5_context_initialize, -1);
|
|
84
139
|
|
|
85
140
|
// Instance Methods
|
|
86
141
|
rb_define_method(cKrb5Context, "close", rkrb5_context_close, 0);
|
data/ext/rkerberos/kadm5.c
CHANGED
|
@@ -142,7 +142,6 @@ static VALUE rkadm5_initialize(VALUE self, VALUE v_opts){
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
if(RTEST(v_password)){
|
|
145
|
-
#ifdef KADM5_API_VERSION_3
|
|
146
145
|
kerror = kadm5_init_with_password(
|
|
147
146
|
ptr->ctx,
|
|
148
147
|
user,
|
|
@@ -154,24 +153,11 @@ static VALUE rkadm5_initialize(VALUE self, VALUE v_opts){
|
|
|
154
153
|
ptr->db_args,
|
|
155
154
|
&ptr->handle
|
|
156
155
|
);
|
|
157
|
-
#else
|
|
158
|
-
kerror = kadm5_init_with_password(
|
|
159
|
-
user,
|
|
160
|
-
pass,
|
|
161
|
-
service,
|
|
162
|
-
NULL,
|
|
163
|
-
KADM5_STRUCT_VERSION,
|
|
164
|
-
KADM5_API_VERSION_2,
|
|
165
|
-
ptr->db_args,
|
|
166
|
-
&ptr->handle
|
|
167
|
-
);
|
|
168
|
-
#endif
|
|
169
156
|
|
|
170
157
|
if(kerror)
|
|
171
158
|
rb_raise(cKadm5Exception, "kadm5_init_with_password: %s", error_message(kerror));
|
|
172
159
|
}
|
|
173
160
|
else if(RTEST(v_keytab)){
|
|
174
|
-
#ifdef KADM5_API_VERSION_3
|
|
175
161
|
kerror = kadm5_init_with_skey(
|
|
176
162
|
ptr->ctx,
|
|
177
163
|
user,
|
|
@@ -183,18 +169,6 @@ static VALUE rkadm5_initialize(VALUE self, VALUE v_opts){
|
|
|
183
169
|
ptr->db_args,
|
|
184
170
|
&ptr->handle
|
|
185
171
|
);
|
|
186
|
-
#else
|
|
187
|
-
kerror = kadm5_init_with_skey(
|
|
188
|
-
user,
|
|
189
|
-
keytab,
|
|
190
|
-
service,
|
|
191
|
-
NULL,
|
|
192
|
-
KADM5_STRUCT_VERSION,
|
|
193
|
-
KADM5_API_VERSION_2,
|
|
194
|
-
ptr->db_args,
|
|
195
|
-
&ptr->handle
|
|
196
|
-
);
|
|
197
|
-
#endif
|
|
198
172
|
|
|
199
173
|
if(kerror)
|
|
200
174
|
rb_raise(cKadm5Exception, "kadm5_init_with_skey: %s", error_message(kerror));
|
data/ext/rkerberos/policy.c
CHANGED
|
@@ -59,7 +59,7 @@ static VALUE rkadm5_policy_init(VALUE self, VALUE v_options){
|
|
|
59
59
|
|
|
60
60
|
Check_Type(v_options, T_HASH);
|
|
61
61
|
|
|
62
|
-
if(RTEST(rb_funcall(v_options, rb_intern("empty?"), 0
|
|
62
|
+
if(RTEST(rb_funcall(v_options, rb_intern("empty?"), 0)))
|
|
63
63
|
rb_raise(rb_eArgError, "no policy options provided");
|
|
64
64
|
|
|
65
65
|
v_name = rb_hash_aref2(v_options, rb_str_new_cstr("name"));
|
data/ext/rkerberos/rkerberos.c
CHANGED
|
@@ -158,7 +158,6 @@ static VALUE rkrb5_get_init_creds_keytab(int argc, VALUE* argv, VALUE self){
|
|
|
158
158
|
|
|
159
159
|
krb5_error_code kerror;
|
|
160
160
|
krb5_get_init_creds_opt* opt;
|
|
161
|
-
krb5_creds cred;
|
|
162
161
|
|
|
163
162
|
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
164
163
|
|
|
@@ -247,7 +246,7 @@ static VALUE rkrb5_get_init_creds_keytab(int argc, VALUE* argv, VALUE self){
|
|
|
247
246
|
|
|
248
247
|
kerror = krb5_get_init_creds_keytab(
|
|
249
248
|
ptr->ctx,
|
|
250
|
-
&
|
|
249
|
+
&ptr->creds,
|
|
251
250
|
ptr->princ,
|
|
252
251
|
ptr->keytab,
|
|
253
252
|
0,
|
|
@@ -394,6 +393,110 @@ static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
|
394
393
|
return Qtrue;
|
|
395
394
|
}
|
|
396
395
|
|
|
396
|
+
/*
|
|
397
|
+
* call-seq:
|
|
398
|
+
* krb5.authenticate!(user, password, service = nil)
|
|
399
|
+
*
|
|
400
|
+
* Convenience method that: acquires initial credentials via password and
|
|
401
|
+
* immediately verifies those credentials using `verify_init_creds` with
|
|
402
|
+
* AP-REQ verification enabled (ap_req_nofail). This protects against
|
|
403
|
+
* KDC-forging/Zanarotti-style attacks by ensuring the ticket is verified
|
|
404
|
+
* against the KDC before it's treated as authenticated.
|
|
405
|
+
*
|
|
406
|
+
* Returns true on success and raises `Kerberos::Krb5::Exception` on error.
|
|
407
|
+
*/
|
|
408
|
+
static VALUE rkrb5_authenticate_bang(int argc, VALUE* argv, VALUE self){
|
|
409
|
+
RUBY_KRB5* ptr;
|
|
410
|
+
VALUE v_user, v_pass, v_service;
|
|
411
|
+
char* user;
|
|
412
|
+
char* pass;
|
|
413
|
+
char* service;
|
|
414
|
+
krb5_error_code kerror;
|
|
415
|
+
krb5_principal server_princ = NULL;
|
|
416
|
+
|
|
417
|
+
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
418
|
+
|
|
419
|
+
if(!ptr->ctx)
|
|
420
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
421
|
+
|
|
422
|
+
// Require user and password, optional service
|
|
423
|
+
rb_scan_args(argc, argv, "21", &v_user, &v_pass, &v_service);
|
|
424
|
+
|
|
425
|
+
Check_Type(v_user, T_STRING);
|
|
426
|
+
Check_Type(v_pass, T_STRING);
|
|
427
|
+
user = StringValueCStr(v_user);
|
|
428
|
+
pass = StringValueCStr(v_pass);
|
|
429
|
+
|
|
430
|
+
if(NIL_P(v_service)){
|
|
431
|
+
service = NULL;
|
|
432
|
+
}
|
|
433
|
+
else{
|
|
434
|
+
Check_Type(v_service, T_STRING);
|
|
435
|
+
service = StringValueCStr(v_service);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Acquire initial credentials (same as get_init_creds_password)
|
|
439
|
+
kerror = krb5_parse_name(ptr->ctx, user, &ptr->princ);
|
|
440
|
+
|
|
441
|
+
if(kerror)
|
|
442
|
+
rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
|
|
443
|
+
|
|
444
|
+
kerror = krb5_get_init_creds_password(
|
|
445
|
+
ptr->ctx,
|
|
446
|
+
&ptr->creds,
|
|
447
|
+
ptr->princ,
|
|
448
|
+
pass,
|
|
449
|
+
0,
|
|
450
|
+
NULL,
|
|
451
|
+
0,
|
|
452
|
+
service,
|
|
453
|
+
NULL
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
if(kerror)
|
|
457
|
+
rb_raise(cKrb5Exception, "krb5_get_init_creds_password: %s", error_message(kerror));
|
|
458
|
+
|
|
459
|
+
/*
|
|
460
|
+
* Try strict verification first (AP-REQ nofail). If strict verification
|
|
461
|
+
* cannot be performed (e.g. missing keytab or other environment issue),
|
|
462
|
+
* fall back to the standard verification so authenticate! remains useful
|
|
463
|
+
* in minimal test environments.
|
|
464
|
+
*/
|
|
465
|
+
krb5_verify_init_creds_opt vicopt;
|
|
466
|
+
krb5_error_code kerror_strict = 0;
|
|
467
|
+
|
|
468
|
+
krb5_verify_init_creds_opt_init(&vicopt);
|
|
469
|
+
krb5_verify_init_creds_opt_set_ap_req_nofail(&vicopt, TRUE);
|
|
470
|
+
|
|
471
|
+
// If caller supplied a service principal string, use it for verification.
|
|
472
|
+
if(service){
|
|
473
|
+
kerror = krb5_parse_name(ptr->ctx, service, &server_princ);
|
|
474
|
+
|
|
475
|
+
if(kerror)
|
|
476
|
+
rb_raise(cKrb5Exception, "krb5_parse_name(service): %s", error_message(kerror));
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// First, attempt strict verification
|
|
480
|
+
kerror = krb5_verify_init_creds(ptr->ctx, &ptr->creds, server_princ, NULL, NULL, &vicopt);
|
|
481
|
+
|
|
482
|
+
if(kerror){
|
|
483
|
+
/* strict verification failed — try a best-effort standard verify */
|
|
484
|
+
kerror_strict = kerror;
|
|
485
|
+
kerror = krb5_verify_init_creds(ptr->ctx, &ptr->creds, server_princ, NULL, NULL, NULL);
|
|
486
|
+
if(kerror){
|
|
487
|
+
if(server_princ)
|
|
488
|
+
krb5_free_principal(ptr->ctx, server_princ);
|
|
489
|
+
/* raise the original strict-verification error to inform caller */
|
|
490
|
+
rb_raise(cKrb5Exception, "krb5_verify_init_creds: %s", error_message(kerror_strict));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if(server_princ)
|
|
495
|
+
krb5_free_principal(ptr->ctx, server_princ);
|
|
496
|
+
|
|
497
|
+
return Qtrue;
|
|
498
|
+
}
|
|
499
|
+
|
|
397
500
|
/*
|
|
398
501
|
* call-seq:
|
|
399
502
|
* krb5.close
|
|
@@ -518,6 +621,88 @@ static VALUE rkrb5_get_permitted_enctypes(VALUE self){
|
|
|
518
621
|
return v_enctypes;
|
|
519
622
|
}
|
|
520
623
|
|
|
624
|
+
/*
|
|
625
|
+
* call-seq:
|
|
626
|
+
* krb5.verify_init_creds(server = nil, keytab = nil, ccache = nil)
|
|
627
|
+
*
|
|
628
|
+
* Verifies the initial credentials currently stored in the internal
|
|
629
|
+
* credentials structure. Optionally a server principal string, a
|
|
630
|
+
* `Kerberos::Krb5::Keytab` and/or a `Kerberos::Krb5::CredentialsCache` may
|
|
631
|
+
* be supplied to influence verification. Returns true on success and raises
|
|
632
|
+
* `Kerberos::Krb5::Exception` on error.
|
|
633
|
+
*/
|
|
634
|
+
static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
635
|
+
RUBY_KRB5* ptr;
|
|
636
|
+
VALUE v_server, v_keytab, v_ccache;
|
|
637
|
+
krb5_error_code kerror;
|
|
638
|
+
krb5_principal server_princ = NULL;
|
|
639
|
+
RUBY_KRB5_KEYTAB* ktptr = NULL;
|
|
640
|
+
RUBY_KRB5_CCACHE* ccptr = NULL;
|
|
641
|
+
krb5_keytab keytab = NULL;
|
|
642
|
+
krb5_ccache *ccache_ptr = NULL;
|
|
643
|
+
|
|
644
|
+
rb_scan_args(argc, argv, "03", &v_server, &v_keytab, &v_ccache);
|
|
645
|
+
|
|
646
|
+
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
647
|
+
|
|
648
|
+
if(!ptr->ctx)
|
|
649
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
650
|
+
|
|
651
|
+
// Validate argument types first so callers get TypeError before other errors
|
|
652
|
+
if(!NIL_P(v_server)){
|
|
653
|
+
Check_Type(v_server, T_STRING);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if(!NIL_P(v_keytab)){
|
|
657
|
+
// Will raise TypeError if object isn't the expected Keytab typed data
|
|
658
|
+
TypedData_Get_Struct(v_keytab, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ktptr);
|
|
659
|
+
keytab = ktptr->keytab;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if(!NIL_P(v_ccache)){
|
|
663
|
+
// Will raise TypeError if object isn't the expected CCache typed data
|
|
664
|
+
TypedData_Get_Struct(v_ccache, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, ccptr);
|
|
665
|
+
ccache_ptr = &ccptr->ccache;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Ensure we have credentials to verify (check after validating args)
|
|
669
|
+
if(ptr->creds.client == NULL)
|
|
670
|
+
rb_raise(cKrb5Exception, "no credentials have been acquired");
|
|
671
|
+
|
|
672
|
+
// Optional server principal (parse after context & arg validation)
|
|
673
|
+
if(!NIL_P(v_server)){
|
|
674
|
+
kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_server), &server_princ);
|
|
675
|
+
if(kerror)
|
|
676
|
+
rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
kerror = krb5_verify_init_creds(ptr->ctx, &ptr->creds, server_princ, keytab, ccache_ptr, NULL);
|
|
680
|
+
|
|
681
|
+
if(server_princ)
|
|
682
|
+
krb5_free_principal(ptr->ctx, server_princ);
|
|
683
|
+
|
|
684
|
+
if(kerror)
|
|
685
|
+
rb_raise(cKrb5Exception, "krb5_verify_init_creds: %s", error_message(kerror));
|
|
686
|
+
|
|
687
|
+
/* If the caller supplied a CredentialsCache object, store the verified
|
|
688
|
+
credentials there so Ruby-level callers can inspect the cache. */
|
|
689
|
+
if(ccache_ptr && *ccache_ptr){
|
|
690
|
+
krb5_error_code k2;
|
|
691
|
+
|
|
692
|
+
k2 = krb5_cc_initialize(ptr->ctx, *ccache_ptr, ptr->creds.client);
|
|
693
|
+
|
|
694
|
+
if(k2)
|
|
695
|
+
rb_raise(cKrb5Exception, "krb5_cc_initialize: %s", error_message(k2));
|
|
696
|
+
|
|
697
|
+
k2 = krb5_cc_store_cred(ptr->ctx, *ccache_ptr, &ptr->creds);
|
|
698
|
+
|
|
699
|
+
if(k2)
|
|
700
|
+
rb_raise(cKrb5Exception, "krb5_cc_store_cred: %s", error_message(k2));
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return Qtrue;
|
|
704
|
+
}
|
|
705
|
+
|
|
521
706
|
void Init_rkerberos(void){
|
|
522
707
|
mKerberos = rb_define_module("Kerberos");
|
|
523
708
|
cKrb5 = rb_define_class_under(mKerberos, "Krb5", rb_cObject);
|
|
@@ -530,6 +715,7 @@ void Init_rkerberos(void){
|
|
|
530
715
|
rb_define_method(cKrb5, "initialize", rkrb5_initialize, 0);
|
|
531
716
|
|
|
532
717
|
// Krb5 Methods
|
|
718
|
+
rb_define_method(cKrb5, "authenticate!", rkrb5_authenticate_bang, -1);
|
|
533
719
|
rb_define_method(cKrb5, "change_password", rkrb5_change_password, 2);
|
|
534
720
|
rb_define_method(cKrb5, "close", rkrb5_close, 0);
|
|
535
721
|
rb_define_method(cKrb5, "get_default_realm", rkrb5_get_default_realm, 0);
|
|
@@ -538,13 +724,14 @@ void Init_rkerberos(void){
|
|
|
538
724
|
rb_define_method(cKrb5, "get_default_principal", rkrb5_get_default_principal, 0);
|
|
539
725
|
rb_define_method(cKrb5, "get_permitted_enctypes", rkrb5_get_permitted_enctypes, 0);
|
|
540
726
|
rb_define_method(cKrb5, "set_default_realm", rkrb5_set_default_realm, -1);
|
|
727
|
+
rb_define_method(cKrb5, "verify_init_creds", rkrb5_verify_init_creds, -1);
|
|
541
728
|
|
|
542
729
|
// Aliases
|
|
543
730
|
rb_define_alias(cKrb5, "default_realm", "get_default_realm");
|
|
544
731
|
rb_define_alias(cKrb5, "default_principal", "get_default_principal");
|
|
545
732
|
|
|
546
|
-
/* 0.2.
|
|
547
|
-
rb_define_const(cKrb5, "VERSION", rb_str_new2("0.2.
|
|
733
|
+
/* 0.2.1: The version of the custom rkerberos library */
|
|
734
|
+
rb_define_const(cKrb5, "VERSION", rb_str_new2("0.2.1"));
|
|
548
735
|
|
|
549
736
|
// Encoding type constants
|
|
550
737
|
|
data/ext/rkerberos/rkerberos.h
CHANGED
|
@@ -20,6 +20,8 @@ extern "C" {
|
|
|
20
20
|
|
|
21
21
|
// Make the ccache data type visible to other C files
|
|
22
22
|
extern const rb_data_type_t rkrb5_ccache_data_type;
|
|
23
|
+
// Make the keytab data type visible to other C files
|
|
24
|
+
extern const rb_data_type_t rkrb5_keytab_data_type;
|
|
23
25
|
|
|
24
26
|
#ifdef __cplusplus
|
|
25
27
|
}
|
data/rkerberos.gemspec
CHANGED
|
@@ -2,20 +2,17 @@ require 'rubygems'
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |spec|
|
|
4
4
|
spec.name = 'rkerberos'
|
|
5
|
-
spec.version = '0.2.
|
|
5
|
+
spec.version = '0.2.1'
|
|
6
6
|
spec.authors = ['Daniel Berger', 'Dominic Cleal', 'Simon Levermann']
|
|
7
7
|
spec.license = 'Artistic-2.0'
|
|
8
8
|
spec.email = ['djberg96@gmail.com', 'dominic@cleal.org', 'simon-rubygems@slevermann.de']
|
|
9
|
-
spec.homepage = 'http://github.com/
|
|
9
|
+
spec.homepage = 'http://github.com/rkerberos/rkerberos'
|
|
10
10
|
spec.summary = 'A Ruby interface for the the Kerberos library'
|
|
11
11
|
spec.test_files = Dir['spec/**/*_spec.rb']
|
|
12
12
|
spec.extensions = ['ext/rkerberos/extconf.rb']
|
|
13
|
-
spec.files =
|
|
14
|
-
|
|
15
|
-
spec.extra_rdoc_files = ['README.md', 'CHANGES', 'MANIFEST', 'LICENSE'] + Dir['ext/rkerberos/*.c']
|
|
16
|
-
|
|
17
|
-
spec.add_dependency('rake-compiler')
|
|
13
|
+
spec.files = Dir['**/*'].grep_v(%r{\A(?:\.git|docker|Dockerfile)})
|
|
18
14
|
|
|
15
|
+
spec.add_development_dependency('rake-compiler')
|
|
19
16
|
spec.add_development_dependency('rspec', '>= 3.0')
|
|
20
17
|
spec.add_development_dependency('net-ldap')
|
|
21
18
|
|
|
@@ -23,4 +20,16 @@ Gem::Specification.new do |spec|
|
|
|
23
20
|
The rkerberos library is an interface for the Kerberos 5 network
|
|
24
21
|
authentication protocol. It wraps the Kerberos C API.
|
|
25
22
|
EOF
|
|
23
|
+
|
|
24
|
+
spec.metadata = {
|
|
25
|
+
'homepage_uri' => 'https://github.com/rkerberos/rkerberos',
|
|
26
|
+
'bug_tracker_uri' => 'https://github.com/rkerberos/rkerberos/issues',
|
|
27
|
+
'changelog_uri' => 'https://github.com/rkerberos/rkerberos/blob/main/CHANGES.md',
|
|
28
|
+
'documentation_uri' => 'https://github.com/rkerberos/rkerberos/wiki',
|
|
29
|
+
'source_code_uri' => 'https://github.com/rkerberos/rkerberos',
|
|
30
|
+
'wiki_uri' => 'https://github.com/rkerberos/rkerberos/wiki',
|
|
31
|
+
'github_repo' => 'https://github.com/djberg96/rkerberos',
|
|
32
|
+
'funding_uri' => 'https://github.com/sponsors/rkerberos',
|
|
33
|
+
'rubygems_mfa_required' => 'true'
|
|
34
|
+
}
|
|
26
35
|
end
|
data/spec/context_spec.rb
CHANGED
|
@@ -18,6 +18,42 @@ RSpec.describe Kerberos::Krb5::Context do
|
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
describe 'constructor options' do
|
|
22
|
+
it 'accepts secure: true to use a secure context' do
|
|
23
|
+
expect { described_class.new(secure: true) }.not_to raise_error
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'accepts a profile path via :profile' do
|
|
27
|
+
profile_path = ENV['KRB5_CONFIG'] || '/etc/krb5.conf'
|
|
28
|
+
expect(File).to exist(profile_path)
|
|
29
|
+
expect { described_class.new(profile: profile_path) }.not_to raise_error
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'validates profile argument type' do
|
|
33
|
+
expect { described_class.new(profile: 123) }.to raise_error(TypeError)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'ignores environment when secure: true' do
|
|
37
|
+
begin
|
|
38
|
+
orig = ENV['KRB5_CONFIG']
|
|
39
|
+
ENV['KRB5_CONFIG'] = '/no/such/file'
|
|
40
|
+
expect { described_class.new(secure: true) }.not_to raise_error
|
|
41
|
+
ensure
|
|
42
|
+
ENV['KRB5_CONFIG'] = orig
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'accepts secure: true together with profile' do
|
|
47
|
+
profile_path = ENV['KRB5_CONFIG'] || '/etc/krb5.conf'
|
|
48
|
+
expect(File).to exist(profile_path)
|
|
49
|
+
|
|
50
|
+
ctx = nil
|
|
51
|
+
expect { ctx = described_class.new(secure: true, profile: profile_path) }.not_to raise_error
|
|
52
|
+
expect(ctx).to be_a(described_class)
|
|
53
|
+
expect { ctx.close }.not_to raise_error
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
21
57
|
after(:each) do
|
|
22
58
|
context.close
|
|
23
59
|
end
|
data/spec/krb5_spec.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
require 'rkerberos'
|
|
5
5
|
require 'open3'
|
|
6
|
+
require 'pty'
|
|
7
|
+
require 'expect'
|
|
6
8
|
|
|
7
9
|
RSpec.describe Kerberos::Krb5 do
|
|
8
10
|
before(:all) do
|
|
@@ -10,6 +12,7 @@ RSpec.describe Kerberos::Krb5 do
|
|
|
10
12
|
Open3.popen3('klist') { |_, _, stderr| @cache_found = false unless stderr.gets.nil? }
|
|
11
13
|
@krb5_conf = ENV['KRB5_CONFIG'] || '/etc/krb5.conf'
|
|
12
14
|
@realm = IO.read(@krb5_conf).split("\n").grep(/default_realm/).first.split('=').last.lstrip.chomp
|
|
15
|
+
|
|
13
16
|
end
|
|
14
17
|
|
|
15
18
|
subject(:krb5) { described_class.new }
|
|
@@ -18,7 +21,7 @@ RSpec.describe Kerberos::Krb5 do
|
|
|
18
21
|
let(:service) { 'kadmin/admin' }
|
|
19
22
|
|
|
20
23
|
it 'has the correct version constant' do
|
|
21
|
-
expect(Kerberos::Krb5::VERSION).to eq('0.2.
|
|
24
|
+
expect(Kerberos::Krb5::VERSION).to eq('0.2.1')
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
it 'accepts a block and yields itself' do
|
|
@@ -44,4 +47,107 @@ RSpec.describe Kerberos::Krb5 do
|
|
|
44
47
|
expect(krb5.method(:default_realm)).to eq(krb5.method(:get_default_realm))
|
|
45
48
|
end
|
|
46
49
|
end
|
|
50
|
+
|
|
51
|
+
describe '#verify_init_creds' do
|
|
52
|
+
# Some KDC setups may not correctly set the initial password during
|
|
53
|
+
# entrypoint startup; enforce it here via the admin API so the test is
|
|
54
|
+
# deterministic.
|
|
55
|
+
before do
|
|
56
|
+
user = "testuser1@#{@realm}"
|
|
57
|
+
Kerberos::Kadm5.new(
|
|
58
|
+
principal: ENV.fetch('KRB5_ADMIN_PRINCIPAL', 'admin/admin@EXAMPLE.COM'),
|
|
59
|
+
password: ENV.fetch('KRB5_ADMIN_PASSWORD', 'adminpassword')
|
|
60
|
+
) do |kadmin|
|
|
61
|
+
kadmin.set_password(user, 'changeme')
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'responds to verify_init_creds' do
|
|
66
|
+
expect(krb5).to respond_to(:verify_init_creds)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'raises when no credentials have been acquired' do
|
|
70
|
+
expect { krb5.verify_init_creds }.to raise_error(Kerberos::Krb5::Exception)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'validates argument types' do
|
|
74
|
+
expect { krb5.verify_init_creds(true) }.to raise_error(TypeError)
|
|
75
|
+
expect { krb5.verify_init_creds(nil, true) }.to raise_error(TypeError)
|
|
76
|
+
expect { krb5.verify_init_creds(nil, nil, true) }.to raise_error(TypeError)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'verifies credentials obtained via password' do
|
|
80
|
+
krb5.get_init_creds_password(user, 'changeme')
|
|
81
|
+
expect(krb5.verify_init_creds).to be true
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'accepts a server principal string' do
|
|
85
|
+
krb5.get_init_creds_password(user, 'changeme')
|
|
86
|
+
expect(krb5.verify_init_creds("kadmin/admin@#{@realm}")).to be true
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'accepts a Keytab object' do
|
|
90
|
+
krb5.get_init_creds_password(user, 'changeme')
|
|
91
|
+
kt = Kerberos::Krb5::Keytab.new
|
|
92
|
+
expect(krb5.verify_init_creds(nil, kt)).to be true
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'stores additional credentials in provided CredentialsCache' do
|
|
96
|
+
ccache = Kerberos::Krb5::CredentialsCache.new
|
|
97
|
+
krb5.get_init_creds_password(user, 'changeme')
|
|
98
|
+
expect(krb5.verify_init_creds(nil, nil, ccache)).to be true
|
|
99
|
+
expect(ccache.primary_principal).to be_a(String)
|
|
100
|
+
expect(ccache.primary_principal).to include('@')
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'provides authenticate! which acquires and verifies (Zanarotti mitigation)' do
|
|
104
|
+
expect(krb5).to respond_to(:authenticate!)
|
|
105
|
+
expect(krb5.authenticate!(user, 'changeme')).to be true
|
|
106
|
+
expect(krb5.verify_init_creds).to be true
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'accepts an optional service argument' do
|
|
110
|
+
expect { krb5.authenticate!(user, 'changeme', 'kadmin/changepw') }.not_to raise_error
|
|
111
|
+
expect(krb5.verify_init_creds).to be true
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'validates argument types for authenticate!' do
|
|
115
|
+
expect { krb5.authenticate!(true, true) }.to raise_error(TypeError)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe '#get_init_creds_keytab' do
|
|
120
|
+
before(:each) do
|
|
121
|
+
@kt_file = File.join(Dir.tmpdir, "test_get_init_creds_#{Process.pid}_#{rand(10000)}.keytab")
|
|
122
|
+
|
|
123
|
+
PTY.spawn('ktutil') do |reader, writer, _|
|
|
124
|
+
reader.expect(/ktutil:\s+/)
|
|
125
|
+
writer.puts("add_entry -password -p testuser1@#{@realm} -k 1 -e aes128-cts-hmac-sha1-96")
|
|
126
|
+
reader.expect(/Password for #{Regexp.quote("testuser1@#{@realm}")}:\s+/)
|
|
127
|
+
writer.puts('changeme')
|
|
128
|
+
reader.expect(/ktutil:\s+/)
|
|
129
|
+
writer.puts("wkt #{@kt_file}")
|
|
130
|
+
reader.expect(/ktutil:\s+/)
|
|
131
|
+
writer.puts('quit')
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it 'responds to get_init_creds_keytab' do
|
|
136
|
+
expect(krb5).to respond_to(:get_init_creds_keytab)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it 'acquires credentials for a principal from a supplied keytab file' do
|
|
140
|
+
kt_name = "FILE:#{@kt_file}"
|
|
141
|
+
expect { krb5.get_init_creds_keytab(user, kt_name) }.not_to raise_error
|
|
142
|
+
expect(krb5.verify_init_creds).to be true
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'accepts a CredentialsCache to receive credentials' do
|
|
146
|
+
ccache = Kerberos::Krb5::CredentialsCache.new
|
|
147
|
+
kt_name = "FILE:#{@kt_file}"
|
|
148
|
+
expect { krb5.get_init_creds_keytab(user, kt_name, nil, ccache) }.not_to raise_error
|
|
149
|
+
expect(ccache.primary_principal).to be_a(String)
|
|
150
|
+
expect(ccache.primary_principal).to include('@')
|
|
151
|
+
end
|
|
152
|
+
end
|
|
47
153
|
end
|
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.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Berger
|
|
@@ -18,7 +18,7 @@ dependencies:
|
|
|
18
18
|
- - ">="
|
|
19
19
|
- !ruby/object:Gem::Version
|
|
20
20
|
version: '0'
|
|
21
|
-
type: :
|
|
21
|
+
type: :development
|
|
22
22
|
prerelease: false
|
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
|
24
24
|
requirements:
|
|
@@ -63,34 +63,14 @@ email:
|
|
|
63
63
|
executables: []
|
|
64
64
|
extensions:
|
|
65
65
|
- ext/rkerberos/extconf.rb
|
|
66
|
-
extra_rdoc_files:
|
|
67
|
-
- CHANGES
|
|
68
|
-
- LICENSE
|
|
69
|
-
- MANIFEST
|
|
70
|
-
- README.md
|
|
71
|
-
- ext/rkerberos/ccache.c
|
|
72
|
-
- ext/rkerberos/config.c
|
|
73
|
-
- ext/rkerberos/context.c
|
|
74
|
-
- ext/rkerberos/kadm5.c
|
|
75
|
-
- ext/rkerberos/keytab.c
|
|
76
|
-
- ext/rkerberos/keytab_entry.c
|
|
77
|
-
- ext/rkerberos/policy.c
|
|
78
|
-
- ext/rkerberos/principal.c
|
|
79
|
-
- ext/rkerberos/rkerberos.c
|
|
66
|
+
extra_rdoc_files: []
|
|
80
67
|
files:
|
|
81
|
-
- CHANGES
|
|
82
|
-
- Dockerfile
|
|
68
|
+
- CHANGES.md
|
|
83
69
|
- Gemfile
|
|
84
70
|
- LICENSE
|
|
85
|
-
- MANIFEST
|
|
71
|
+
- MANIFEST.md
|
|
86
72
|
- README.md
|
|
87
73
|
- Rakefile
|
|
88
|
-
- docker-compose.yml
|
|
89
|
-
- docker/Dockerfile.kdc
|
|
90
|
-
- docker/docker-entrypoint.sh
|
|
91
|
-
- docker/kadm5.acl
|
|
92
|
-
- docker/kdc.conf
|
|
93
|
-
- docker/krb5.conf
|
|
94
74
|
- ext/rkerberos/ccache.c
|
|
95
75
|
- ext/rkerberos/config.c
|
|
96
76
|
- ext/rkerberos/context.c
|
|
@@ -112,10 +92,19 @@ files:
|
|
|
112
92
|
- spec/krb5_spec.rb
|
|
113
93
|
- spec/policy_spec.rb
|
|
114
94
|
- spec/principal_spec.rb
|
|
115
|
-
homepage: http://github.com/
|
|
95
|
+
homepage: http://github.com/rkerberos/rkerberos
|
|
116
96
|
licenses:
|
|
117
97
|
- Artistic-2.0
|
|
118
|
-
metadata:
|
|
98
|
+
metadata:
|
|
99
|
+
homepage_uri: https://github.com/rkerberos/rkerberos
|
|
100
|
+
bug_tracker_uri: https://github.com/rkerberos/rkerberos/issues
|
|
101
|
+
changelog_uri: https://github.com/rkerberos/rkerberos/blob/main/CHANGES.md
|
|
102
|
+
documentation_uri: https://github.com/rkerberos/rkerberos/wiki
|
|
103
|
+
source_code_uri: https://github.com/rkerberos/rkerberos
|
|
104
|
+
wiki_uri: https://github.com/rkerberos/rkerberos/wiki
|
|
105
|
+
github_repo: https://github.com/djberg96/rkerberos
|
|
106
|
+
funding_uri: https://github.com/sponsors/rkerberos
|
|
107
|
+
rubygems_mfa_required: 'true'
|
|
119
108
|
rdoc_options: []
|
|
120
109
|
require_paths:
|
|
121
110
|
- lib
|
|
@@ -130,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
130
119
|
- !ruby/object:Gem::Version
|
|
131
120
|
version: '0'
|
|
132
121
|
requirements: []
|
|
133
|
-
rubygems_version: 4.0.
|
|
122
|
+
rubygems_version: 4.0.3
|
|
134
123
|
specification_version: 4
|
|
135
124
|
summary: A Ruby interface for the the Kerberos library
|
|
136
125
|
test_files:
|
data/Dockerfile
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# Dockerfile for rkerberos Ruby gem testing
|
|
2
|
-
FROM ruby:3.4
|
|
3
|
-
|
|
4
|
-
# Install MIT Kerberos, KDC, admin server, and build tools
|
|
5
|
-
RUN apt-get update && \
|
|
6
|
-
apt-get install -y --no-install-recommends \
|
|
7
|
-
libkrb5-dev krb5-user krb5-kdc krb5-admin-server rake build-essential && \
|
|
8
|
-
rm -rf /var/lib/apt/lists/*
|
|
9
|
-
|
|
10
|
-
# Set up a working directory
|
|
11
|
-
WORKDIR /app
|
|
12
|
-
|
|
13
|
-
# Set admin credentials for tests (matches docker-compose.yml)
|
|
14
|
-
ENV KRB5_ADMIN_PRINCIPAL=admin/admin@EXAMPLE.COM
|
|
15
|
-
ENV KRB5_ADMIN_PASSWORD=adminpassword
|
|
16
|
-
|
|
17
|
-
# Copy the gemspec and Gemfile for dependency installation
|
|
18
|
-
COPY Gemfile rkerberos.gemspec ./
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
# Install gem dependencies and RSpec
|
|
22
|
-
RUN bundle install && gem install rspec
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# Create a more complete krb5.conf for testing (with kadmin support)
|
|
26
|
-
RUN echo "[libdefaults]\n default_realm = EXAMPLE.COM\n dns_lookup_realm = false\n dns_lookup_kdc = false\n ticket_lifetime = 24h\n renew_lifetime = 7d\n forwardable = true\n[realms]\n EXAMPLE.COM = {\n kdc = kerberos-kdc\n admin_server = kerberos-kdc\n default_domain = example.com\n }\n[domain_realm]\n .example.com = EXAMPLE.COM\n example.com = EXAMPLE.COM\n[kadmin]\n default_keys = des-cbc-crc:normal des-cbc-md5:normal aes256-cts:normal aes128-cts:normal rc4-hmac:normal\n admin_server = kerberos-kdc\n" > /etc/krb5.conf
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# Create a minimal KDC and admin server config, and a permissive ACL for kadmin
|
|
30
|
-
RUN mkdir -p /etc/krb5kdc && \
|
|
31
|
-
echo "[kdcdefaults]\n kdc_ports = 88\n[kdc]\n profile = /etc/krb5.conf\n" > /etc/krb5kdc/kdc.conf && \
|
|
32
|
-
echo "admin/admin@EXAMPLE.COM *" > /etc/krb5kdc/kadm5.acl
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# Copy the rest of the code
|
|
36
|
-
COPY . .
|
|
37
|
-
|
|
38
|
-
# Compile the C extension
|
|
39
|
-
RUN rake compile
|
|
40
|
-
|
|
41
|
-
# Run RSpec tests
|
|
42
|
-
CMD ["bundle", "exec", "rspec"]
|
data/docker/Dockerfile.kdc
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
FROM debian:bullseye
|
|
2
|
-
|
|
3
|
-
RUN apt-get update && \
|
|
4
|
-
apt-get install -y krb5-kdc krb5-admin-server krb5-user krb5-kdc-ldap ldap-utils expect && \
|
|
5
|
-
rm -rf /var/lib/apt/lists/*
|
|
6
|
-
|
|
7
|
-
# Copy configuration files
|
|
8
|
-
COPY krb5.conf /etc/krb5.conf
|
|
9
|
-
COPY kdc.conf /etc/krb5kdc/kdc.conf
|
|
10
|
-
COPY kadm5.acl /etc/krb5kdc/kadm5.acl
|
|
11
|
-
|
|
12
|
-
# Copy entrypoint
|
|
13
|
-
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
|
14
|
-
RUN chmod +x /docker-entrypoint.sh
|
|
15
|
-
|
|
16
|
-
ENTRYPOINT ["/docker-entrypoint.sh"]
|
data/docker/docker-entrypoint.sh
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
# Initialize KDC DB if not already present
|
|
5
|
-
if [ ! -f /etc/krb5kdc/.k5.EXAMPLE.COM ]; then
|
|
6
|
-
printf "masterpassword\nmasterpassword\n" | krb5_newrealm
|
|
7
|
-
kadmin.local -q "addprinc -pw adminpassword admin/admin"
|
|
8
|
-
fi
|
|
9
|
-
|
|
10
|
-
# Create standard test principals for keytab/credential cache tests
|
|
11
|
-
kadmin.local -q "addprinc -pw changeme testuser1@EXAMPLE.COM"
|
|
12
|
-
kadmin.local -q "addprinc -pw changeme zztop@EXAMPLE.COM"
|
|
13
|
-
kadmin.local -q "addprinc -pw changeme martymcfly@EXAMPLE.COM"
|
|
14
|
-
kadmin.local -q "ktadd -k /etc/krb5.keytab testuser1@EXAMPLE.COM"
|
|
15
|
-
kadmin.local -q "ktadd -k /etc/krb5.keytab zztop@EXAMPLE.COM"
|
|
16
|
-
kadmin.local -q "ktadd -k /etc/krb5.keytab martymcfly@EXAMPLE.COM"
|
|
17
|
-
|
|
18
|
-
# Start KDC and admin server
|
|
19
|
-
krb5kdc
|
|
20
|
-
kadmind
|
|
21
|
-
|
|
22
|
-
# Keep container running
|
|
23
|
-
trap : TERM INT; sleep infinity & wait
|
data/docker/kadm5.acl
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
admin/admin@EXAMPLE.COM *
|
data/docker/kdc.conf
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
[kdcdefaults]
|
|
2
|
-
kdc_ports = 88
|
|
3
|
-
|
|
4
|
-
[realms]
|
|
5
|
-
EXAMPLE.COM = {
|
|
6
|
-
admin_keytab = /etc/krb5kdc/kadm5.keytab
|
|
7
|
-
acl_file = /etc/krb5kdc/kadm5.acl
|
|
8
|
-
dict_file = /usr/share/dict/words
|
|
9
|
-
key_stash_file = /etc/krb5kdc/.k5.EXAMPLE.COM
|
|
10
|
-
kdc_ports = 88
|
|
11
|
-
max_life = 10h 0m 0s
|
|
12
|
-
max_renewable_life = 7d 0h 0m 0s
|
|
13
|
-
}
|
data/docker/krb5.conf
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
[libdefaults]
|
|
2
|
-
default_realm = EXAMPLE.COM
|
|
3
|
-
dns_lookup_realm = false
|
|
4
|
-
dns_lookup_kdc = false
|
|
5
|
-
|
|
6
|
-
[realms]
|
|
7
|
-
EXAMPLE.COM = {
|
|
8
|
-
kdc = kerberos-kdc
|
|
9
|
-
admin_server = kerberos-kdc
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
[domain_realm]
|
|
13
|
-
.example.com = EXAMPLE.COM
|
|
14
|
-
example.com = EXAMPLE.COM
|
data/docker-compose.yml
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
version: '3.8'
|
|
2
|
-
services:
|
|
3
|
-
kerberos-kdc:
|
|
4
|
-
build:
|
|
5
|
-
context: ./docker
|
|
6
|
-
dockerfile: Dockerfile.kdc
|
|
7
|
-
container_name: kerberos-kdc
|
|
8
|
-
ports:
|
|
9
|
-
- "1088:88"
|
|
10
|
-
- "1749:749"
|
|
11
|
-
volumes:
|
|
12
|
-
- krb5-keytab:/etc/krb5.keytab
|
|
13
|
-
depends_on:
|
|
14
|
-
- ldap
|
|
15
|
-
|
|
16
|
-
rkerberos-test:
|
|
17
|
-
build: .
|
|
18
|
-
container_name: rkerberos-test
|
|
19
|
-
environment:
|
|
20
|
-
- LANG=C.UTF-8
|
|
21
|
-
- KRB5_CONFIG=/etc/krb5.conf
|
|
22
|
-
- KRB5_ADMIN_PRINCIPAL=admin/admin@EXAMPLE.COM
|
|
23
|
-
- KRB5_ADMIN_PASSWORD=adminpassword
|
|
24
|
-
# LDAP test variables for integration tests
|
|
25
|
-
- KRB5_LDAP_PRINCIPAL=admin@ldap
|
|
26
|
-
- KRB5_LDAP_PASSWORD=admin
|
|
27
|
-
- KRB5_LDAP_DRIVER=ou=People,dc=example,dc=com:foobar:uid
|
|
28
|
-
working_dir: /app
|
|
29
|
-
depends_on:
|
|
30
|
-
- kerberos-kdc
|
|
31
|
-
|
|
32
|
-
ldap:
|
|
33
|
-
image: osixia/openldap:latest
|
|
34
|
-
container_name: ldap
|
|
35
|
-
environment:
|
|
36
|
-
LDAP_ORGANISATION: "Example Org"
|
|
37
|
-
LDAP_DOMAIN: "example.com"
|
|
38
|
-
LDAP_BASE_DN: "dc=example,dc=com"
|
|
39
|
-
LDAP_ADMIN_PASSWORD: "admin"
|
|
40
|
-
ports:
|
|
41
|
-
- "1389:389"
|
|
42
|
-
|
|
43
|
-
volumes:
|
|
44
|
-
krb5-keytab:
|