rkerberos 0.2.3 → 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 +30 -0
- data/EXAMPLES.md +1015 -0
- data/MANIFEST.md +17 -12
- data/README.md +17 -6
- data/ext/rkerberos/ccache.c +99 -26
- data/ext/rkerberos/config.c +62 -10
- data/ext/rkerberos/context.c +132 -14
- data/ext/rkerberos/kadm5.c +332 -116
- data/ext/rkerberos/keytab.c +230 -79
- data/ext/rkerberos/policy.c +25 -23
- data/ext/rkerberos/principal.c +139 -11
- data/ext/rkerberos/rkerberos.c +269 -52
- data/ext/rkerberos/rkerberos.h +14 -0
- data/rkerberos.gemspec +10 -7
- data/spec/config_spec.rb +41 -0
- data/spec/context_spec.rb +96 -0
- data/spec/credentials_cache_spec.rb +82 -26
- data/spec/kadm5_spec.rb +319 -5
- data/spec/krb5_keytab_spec.rb +275 -20
- data/spec/krb5_spec.rb +186 -21
- data/spec/policy_spec.rb +4 -0
- data/spec/principal_spec.rb +116 -10
- metadata +9 -8
data/MANIFEST.md
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
* CHANGES.md
|
|
2
|
+
* EXAMPLES.md
|
|
3
|
+
* Gemfile
|
|
4
|
+
* LICENSE
|
|
2
5
|
* rkerberos.gemspec
|
|
3
6
|
* MANIFEST.md
|
|
4
7
|
* Rakefile
|
|
5
|
-
* README
|
|
6
|
-
* ext/ccache.c
|
|
7
|
-
* ext/
|
|
8
|
-
* ext/
|
|
9
|
-
* ext/
|
|
10
|
-
* ext/
|
|
11
|
-
* ext/
|
|
12
|
-
* ext/rkerberos.c
|
|
13
|
-
* ext/rkerberos.
|
|
14
|
-
* ext/
|
|
15
|
-
* ext/
|
|
8
|
+
* README.md
|
|
9
|
+
* ext/rkerberos/ccache.c
|
|
10
|
+
* ext/rkerberos/config.c
|
|
11
|
+
* ext/rkerberos/context.c
|
|
12
|
+
* ext/rkerberos/extconf.rb
|
|
13
|
+
* ext/rkerberos/kadm5.c
|
|
14
|
+
* ext/rkerberos/keytab.c
|
|
15
|
+
* ext/rkerberos/keytab_entry.c
|
|
16
|
+
* ext/rkerberos/policy.c
|
|
17
|
+
* ext/rkerberos/principal.c
|
|
18
|
+
* ext/rkerberos/rkerberos.c
|
|
19
|
+
* ext/rkerberos/rkerberos.h
|
|
16
20
|
* spec/config_spec.rb
|
|
17
21
|
* spec/context_spec.rb
|
|
18
|
-
* spec/
|
|
22
|
+
* spec/credentials_cache_spec.rb
|
|
19
23
|
* spec/kadm5_spec.rb
|
|
20
24
|
* spec/keytab_entry_spec.rb
|
|
21
25
|
* spec/krb5_keytab_spec.rb
|
|
22
26
|
* spec/krb5_spec.rb
|
|
23
27
|
* spec/policy_spec.rb
|
|
24
28
|
* spec/principal_spec.rb
|
|
29
|
+
* spec/spec_helper.rb
|
data/README.md
CHANGED
|
@@ -18,17 +18,22 @@ puts krb.get_permitted_enctypes.keys.join(',')
|
|
|
18
18
|
|
|
19
19
|
# Credentials cache
|
|
20
20
|
cc = Kerberos::Krb5::CredentialsCache.new
|
|
21
|
-
krb.
|
|
21
|
+
krb.get_init_creds_password(
|
|
22
|
+
principal: ENV['KRB5_PRINCIPAL'],
|
|
23
|
+
password: ENV['KRB5_PASSWORD'],
|
|
24
|
+
ccache: cc
|
|
25
|
+
)
|
|
26
|
+
krb.verify_init_creds
|
|
22
27
|
puts cc.primary_principal
|
|
23
28
|
|
|
24
29
|
# Keytab
|
|
25
30
|
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,
|
|
31
|
+
krb.get_init_creds_keytab(principal: 'host/server.example.com', keytab: kt_name)
|
|
32
|
+
krb.get_init_creds_keytab(principal: 'host/server.example.com', keytab: kt_name, ccache: cc) # or write to cache
|
|
28
33
|
|
|
29
34
|
# Admin
|
|
30
35
|
Kerberos::Kadm5.new(principal: ENV['KRB5_ADMIN_PRINCIPAL'], password: ENV['KRB5_ADMIN_PASSWORD']) do |kadmin|
|
|
31
|
-
kadmin.create_principal('newuser@EXAMPLE.COM', 'initialpass')
|
|
36
|
+
kadmin.create_principal(name: 'newuser@EXAMPLE.COM', password: 'initialpass')
|
|
32
37
|
kadmin.set_password('newuser@EXAMPLE.COM', 'betterpass')
|
|
33
38
|
kadmin.delete_principal('newuser@EXAMPLE.COM')
|
|
34
39
|
end
|
|
@@ -38,6 +43,13 @@ ctx = Kerberos::Krb5::Context.new # standard context
|
|
|
38
43
|
ctx = Kerberos::Krb5::Context.new(profile: '/etc/krb5.conf') # or use a profile
|
|
39
44
|
ctx = Kerberos::Krb5::Context.new(secure: true) # or use a secure context
|
|
40
45
|
ctx.close
|
|
46
|
+
|
|
47
|
+
# Contexts with active dependent wrappers reject close. A forced close stops
|
|
48
|
+
# direct context use immediately and defers native cleanup until dependents close.
|
|
49
|
+
ctx = Kerberos::Krb5::Context.new
|
|
50
|
+
krb = Kerberos::Krb5.new(context: ctx)
|
|
51
|
+
ctx.close(force: true)
|
|
52
|
+
krb.close
|
|
41
53
|
```
|
|
42
54
|
|
|
43
55
|
# Requirements
|
|
@@ -79,7 +91,7 @@ ctx.close
|
|
|
79
91
|
# Testing
|
|
80
92
|
|
|
81
93
|
## Prerequisites
|
|
82
|
-
- Ruby 3.
|
|
94
|
+
- Ruby 3.2 or later
|
|
83
95
|
- Docker or Podman
|
|
84
96
|
- docker-compose or podman-compose
|
|
85
97
|
|
|
@@ -160,7 +172,6 @@ The test environment includes:
|
|
|
160
172
|
# TODO
|
|
161
173
|
* Create a separate class for the replay cache.
|
|
162
174
|
* Better credentials cache support.
|
|
163
|
-
* Ability to add and delete keytab entries.
|
|
164
175
|
|
|
165
176
|
# Authors
|
|
166
177
|
* Daniel Berger
|
data/ext/rkerberos/ccache.c
CHANGED
|
@@ -4,6 +4,13 @@ VALUE cKrb5CCache;
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
// TypedData functions for RUBY_KRB5_CCACHE
|
|
7
|
+
static void rkrb5_ccache_typed_mark(void *ptr) {
|
|
8
|
+
if (!ptr) return;
|
|
9
|
+
RUBY_KRB5_CCACHE *c = (RUBY_KRB5_CCACHE *)ptr;
|
|
10
|
+
if (c->rb_context != Qnil)
|
|
11
|
+
rb_gc_mark(c->rb_context);
|
|
12
|
+
}
|
|
13
|
+
|
|
7
14
|
static void rkrb5_ccache_typed_free(void *ptr) {
|
|
8
15
|
if (!ptr) return;
|
|
9
16
|
RUBY_KRB5_CCACHE *c = (RUBY_KRB5_CCACHE *)ptr;
|
|
@@ -11,8 +18,10 @@ static void rkrb5_ccache_typed_free(void *ptr) {
|
|
|
11
18
|
krb5_cc_close(c->ctx, c->ccache);
|
|
12
19
|
if (c->principal)
|
|
13
20
|
krb5_free_principal(c->ctx, c->principal);
|
|
14
|
-
if (c->ctx)
|
|
21
|
+
if (c->ctx && c->rb_context == Qnil)
|
|
15
22
|
krb5_free_context(c->ctx);
|
|
23
|
+
else if (c->rb_context != Qnil)
|
|
24
|
+
rkrb5_context_release(c->rb_context);
|
|
16
25
|
free(c);
|
|
17
26
|
}
|
|
18
27
|
|
|
@@ -22,7 +31,7 @@ static size_t rkrb5_ccache_typed_size(const void *ptr) {
|
|
|
22
31
|
|
|
23
32
|
const rb_data_type_t rkrb5_ccache_data_type = {
|
|
24
33
|
"RUBY_KRB5_CCACHE",
|
|
25
|
-
{
|
|
34
|
+
{rkrb5_ccache_typed_mark, rkrb5_ccache_typed_free, rkrb5_ccache_typed_size,},
|
|
26
35
|
NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
|
|
27
36
|
};
|
|
28
37
|
|
|
@@ -30,20 +39,28 @@ const rb_data_type_t rkrb5_ccache_data_type = {
|
|
|
30
39
|
static VALUE rkrb5_ccache_allocate(VALUE klass){
|
|
31
40
|
RUBY_KRB5_CCACHE* ptr = ALLOC(RUBY_KRB5_CCACHE);
|
|
32
41
|
memset(ptr, 0, sizeof(RUBY_KRB5_CCACHE));
|
|
42
|
+
ptr->rb_context = Qnil;
|
|
33
43
|
return TypedData_Wrap_Struct(klass, &rkrb5_ccache_data_type, ptr);
|
|
34
44
|
}
|
|
35
45
|
|
|
36
46
|
/*
|
|
37
47
|
* call-seq:
|
|
38
|
-
* Kerberos::CredentialsCache.new(principal
|
|
48
|
+
* Kerberos::CredentialsCache.new(principal: nil, cache_name: nil, context: nil)
|
|
49
|
+
*
|
|
50
|
+
* Creates and returns a new Kerberos::CredentialsCache object. Accepts the
|
|
51
|
+
* following keyword arguments:
|
|
39
52
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
53
|
+
* - +principal+: A string principal name. If specified, the credentials cache
|
|
54
|
+
* is created or refreshed with this as the primary principal. If a cache
|
|
55
|
+
* already exists, its contents are destroyed. May also be an object that
|
|
56
|
+
* responds to the `.principal` method.
|
|
43
57
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
58
|
+
* - +cache_name+: The name of the credentials cache to use, which must be in
|
|
59
|
+
* "type:residual" format, where 'type' is a type known to Kerberos
|
|
60
|
+
* (typically 'FILE'). If omitted, the default cache is used.
|
|
61
|
+
*
|
|
62
|
+
* - +context+: A Kerberos::Krb5::Context object. If provided, that context is
|
|
63
|
+
* used instead of creating a new one via krb5_init_context.
|
|
47
64
|
*
|
|
48
65
|
* Note that the principal's credentials are not set via the constructor.
|
|
49
66
|
* It merely creates the cache and sets the default principal.
|
|
@@ -51,23 +68,45 @@ static VALUE rkrb5_ccache_allocate(VALUE klass){
|
|
|
51
68
|
static VALUE rkrb5_ccache_initialize(int argc, VALUE* argv, VALUE self){
|
|
52
69
|
RUBY_KRB5_CCACHE* ptr;
|
|
53
70
|
krb5_error_code kerror;
|
|
54
|
-
VALUE v_principal, v_name;
|
|
71
|
+
VALUE v_opts, v_principal, v_name, v_context;
|
|
72
|
+
ID kw_table[3] = { rb_intern("principal"), rb_intern("cache_name"), rb_intern("context") };
|
|
73
|
+
VALUE kw_vals[3];
|
|
55
74
|
|
|
56
75
|
TypedData_Get_Struct(self, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, ptr);
|
|
57
76
|
|
|
58
|
-
rb_scan_args(argc, argv, "
|
|
77
|
+
rb_scan_args(argc, argv, "0:", &v_opts);
|
|
78
|
+
|
|
79
|
+
if(NIL_P(v_opts))
|
|
80
|
+
v_opts = rb_hash_new();
|
|
81
|
+
|
|
82
|
+
rb_get_kwargs(v_opts, kw_table, 0, 3, kw_vals);
|
|
83
|
+
v_principal = kw_vals[0] == Qundef ? Qnil : kw_vals[0];
|
|
84
|
+
v_name = kw_vals[1] == Qundef ? Qnil : kw_vals[1];
|
|
85
|
+
v_context = kw_vals[2] == Qundef ? Qnil : kw_vals[2];
|
|
86
|
+
|
|
87
|
+
if(!NIL_P(v_principal)){
|
|
88
|
+
if(rb_respond_to(v_principal, rb_intern("principal")))
|
|
89
|
+
v_principal = rb_funcall(v_principal, rb_intern("principal"), 0);
|
|
59
90
|
|
|
60
|
-
if(RTEST(v_principal))
|
|
61
91
|
Check_Type(v_principal, T_STRING);
|
|
92
|
+
}
|
|
62
93
|
|
|
63
|
-
// Initialize the context
|
|
64
|
-
|
|
94
|
+
// Initialize or borrow the context
|
|
95
|
+
if(!NIL_P(v_context)){
|
|
96
|
+
ptr->ctx = rkrb5_context_borrow(v_context);
|
|
97
|
+
ptr->rb_context = v_context;
|
|
98
|
+
}
|
|
99
|
+
else{
|
|
100
|
+
kerror = krb5_init_context(&ptr->ctx);
|
|
65
101
|
|
|
66
|
-
|
|
67
|
-
|
|
102
|
+
if(kerror)
|
|
103
|
+
rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
|
|
104
|
+
|
|
105
|
+
ptr->rb_context = Qnil;
|
|
106
|
+
}
|
|
68
107
|
|
|
69
108
|
// Convert the principal name to a principal object
|
|
70
|
-
if(
|
|
109
|
+
if(!NIL_P(v_principal)){
|
|
71
110
|
kerror = krb5_parse_name(
|
|
72
111
|
ptr->ctx,
|
|
73
112
|
StringValueCStr(v_principal),
|
|
@@ -127,12 +166,15 @@ static VALUE rkrb5_ccache_close(VALUE self){
|
|
|
127
166
|
if(ptr->principal)
|
|
128
167
|
krb5_free_principal(ptr->ctx, ptr->principal);
|
|
129
168
|
|
|
130
|
-
if(ptr->ctx)
|
|
169
|
+
if(ptr->ctx && ptr->rb_context == Qnil)
|
|
131
170
|
krb5_free_context(ptr->ctx);
|
|
171
|
+
else if(ptr->rb_context != Qnil)
|
|
172
|
+
rkrb5_context_release(ptr->rb_context);
|
|
132
173
|
|
|
133
174
|
ptr->ccache = NULL;
|
|
134
175
|
ptr->ctx = NULL;
|
|
135
176
|
ptr->principal = NULL;
|
|
177
|
+
ptr->rb_context = Qnil;
|
|
136
178
|
|
|
137
179
|
return self;
|
|
138
180
|
}
|
|
@@ -228,11 +270,6 @@ static VALUE rkrb5_ccache_primary_principal(VALUE self){
|
|
|
228
270
|
return v_name;
|
|
229
271
|
}
|
|
230
272
|
|
|
231
|
-
// Simple wrapper around krb5_cc_get_principal returning a principal name string.
|
|
232
|
-
static VALUE rkrb5_ccache_principal(VALUE self){
|
|
233
|
-
return rkrb5_ccache_primary_principal(self);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
273
|
/*
|
|
237
274
|
* call-seq:
|
|
238
275
|
* ccache.destroy
|
|
@@ -264,12 +301,15 @@ static VALUE rkrb5_ccache_destroy(VALUE self){
|
|
|
264
301
|
if(ptr->principal)
|
|
265
302
|
krb5_free_principal(ptr->ctx, ptr->principal);
|
|
266
303
|
|
|
267
|
-
if(ptr->ctx)
|
|
304
|
+
if(ptr->ctx && ptr->rb_context == Qnil)
|
|
268
305
|
krb5_free_context(ptr->ctx);
|
|
306
|
+
else if(ptr->rb_context != Qnil)
|
|
307
|
+
rkrb5_context_release(ptr->rb_context);
|
|
269
308
|
|
|
270
309
|
ptr->ccache = NULL;
|
|
271
310
|
ptr->ctx = NULL;
|
|
272
311
|
ptr->principal = NULL;
|
|
312
|
+
ptr->rb_context = Qnil;
|
|
273
313
|
|
|
274
314
|
rb_raise(cKrb5Exception, "krb5_cc_destroy: %s", error_message(kerror));
|
|
275
315
|
}
|
|
@@ -278,12 +318,15 @@ static VALUE rkrb5_ccache_destroy(VALUE self){
|
|
|
278
318
|
if(ptr->principal)
|
|
279
319
|
krb5_free_principal(ptr->ctx, ptr->principal);
|
|
280
320
|
|
|
281
|
-
if(ptr->ctx)
|
|
321
|
+
if(ptr->ctx && ptr->rb_context == Qnil)
|
|
282
322
|
krb5_free_context(ptr->ctx);
|
|
323
|
+
else if(ptr->rb_context != Qnil)
|
|
324
|
+
rkrb5_context_release(ptr->rb_context);
|
|
283
325
|
|
|
284
326
|
ptr->ccache = NULL;
|
|
285
327
|
ptr->ctx = NULL;
|
|
286
328
|
ptr->principal = NULL;
|
|
329
|
+
ptr->rb_context = Qnil;
|
|
287
330
|
|
|
288
331
|
return v_bool;
|
|
289
332
|
}
|
|
@@ -329,6 +372,35 @@ static VALUE rkrb5_ccache_dup(VALUE self){
|
|
|
329
372
|
return newobj;
|
|
330
373
|
}
|
|
331
374
|
|
|
375
|
+
/*
|
|
376
|
+
* call-seq:
|
|
377
|
+
* ccache.full_name -> String
|
|
378
|
+
*
|
|
379
|
+
* Returns the full name of the credential cache, including the type prefix,
|
|
380
|
+
* e.g. "FILE:/tmp/krb5cc_1000".
|
|
381
|
+
*/
|
|
382
|
+
static VALUE rkrb5_ccache_full_name(VALUE self){
|
|
383
|
+
RUBY_KRB5_CCACHE* ptr;
|
|
384
|
+
char *full_name;
|
|
385
|
+
krb5_error_code kerror;
|
|
386
|
+
VALUE result;
|
|
387
|
+
|
|
388
|
+
TypedData_Get_Struct(self, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, ptr);
|
|
389
|
+
|
|
390
|
+
if(!ptr->ctx)
|
|
391
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
392
|
+
|
|
393
|
+
kerror = krb5_cc_get_full_name(ptr->ctx, ptr->ccache, &full_name);
|
|
394
|
+
|
|
395
|
+
if(kerror)
|
|
396
|
+
rb_raise(cKrb5Exception, "krb5_cc_get_full_name: %s", error_message(kerror));
|
|
397
|
+
|
|
398
|
+
result = rb_str_new2(full_name);
|
|
399
|
+
krb5_free_string(ptr->ctx, full_name);
|
|
400
|
+
|
|
401
|
+
return result;
|
|
402
|
+
}
|
|
403
|
+
|
|
332
404
|
void Init_ccache(void){
|
|
333
405
|
/* The Kerberos::Krb5::CredentialsCache class encapsulates a Kerberos credentials cache. */
|
|
334
406
|
cKrb5CCache = rb_define_class_under(cKrb5, "CredentialsCache", rb_cObject);
|
|
@@ -346,10 +418,11 @@ void Init_ccache(void){
|
|
|
346
418
|
rb_define_method(cKrb5CCache, "cache_type", rkrb5_ccache_get_type, 0);
|
|
347
419
|
rb_define_method(cKrb5CCache, "destroy", rkrb5_ccache_destroy, 0);
|
|
348
420
|
rb_define_method(cKrb5CCache, "primary_principal", rkrb5_ccache_primary_principal, 0);
|
|
349
|
-
rb_define_method(cKrb5CCache, "
|
|
421
|
+
rb_define_method(cKrb5CCache, "full_name", rkrb5_ccache_full_name, 0);
|
|
350
422
|
rb_define_method(cKrb5CCache, "dup", rkrb5_ccache_dup, 0);
|
|
351
423
|
rb_define_alias(cKrb5CCache, "clone", "dup");
|
|
352
424
|
|
|
353
425
|
// Aliases
|
|
354
426
|
rb_define_alias(cKrb5CCache, "delete", "destroy");
|
|
427
|
+
rb_define_alias(cKrb5CCache, "principal", "primary_principal");
|
|
355
428
|
}
|
data/ext/rkerberos/config.c
CHANGED
|
@@ -4,15 +4,35 @@
|
|
|
4
4
|
VALUE cKadm5Config;
|
|
5
5
|
VALUE cKeySalt;
|
|
6
6
|
|
|
7
|
+
static void rkadm5_config_cleanup(RUBY_KADM5_CONFIG *ptr){
|
|
8
|
+
if(!ptr->ctx)
|
|
9
|
+
return;
|
|
10
|
+
|
|
11
|
+
kadm5_free_config_params(ptr->ctx, &ptr->config);
|
|
12
|
+
|
|
13
|
+
if(ptr->rb_context == Qnil)
|
|
14
|
+
krb5_free_context(ptr->ctx);
|
|
15
|
+
else
|
|
16
|
+
rkrb5_context_release(ptr->rb_context);
|
|
17
|
+
|
|
18
|
+
ptr->ctx = NULL;
|
|
19
|
+
ptr->rb_context = Qnil;
|
|
20
|
+
memset(&ptr->config, 0, sizeof(ptr->config));
|
|
21
|
+
}
|
|
22
|
+
|
|
7
23
|
|
|
8
24
|
// TypedData functions for RUBY_KADM5_CONFIG
|
|
25
|
+
static void rkadm5_config_typed_mark(void *ptr) {
|
|
26
|
+
if (!ptr) return;
|
|
27
|
+
RUBY_KADM5_CONFIG *c = (RUBY_KADM5_CONFIG *)ptr;
|
|
28
|
+
if (c->rb_context != Qnil)
|
|
29
|
+
rb_gc_mark(c->rb_context);
|
|
30
|
+
}
|
|
31
|
+
|
|
9
32
|
static void rkadm5_config_typed_free(void *ptr) {
|
|
10
33
|
if (!ptr) return;
|
|
11
34
|
RUBY_KADM5_CONFIG *c = (RUBY_KADM5_CONFIG *)ptr;
|
|
12
|
-
|
|
13
|
-
kadm5_free_config_params(c->ctx, &c->config);
|
|
14
|
-
krb5_free_context(c->ctx);
|
|
15
|
-
}
|
|
35
|
+
rkadm5_config_cleanup(c);
|
|
16
36
|
free(c);
|
|
17
37
|
}
|
|
18
38
|
|
|
@@ -22,7 +42,7 @@ static size_t rkadm5_config_typed_size(const void *ptr) {
|
|
|
22
42
|
|
|
23
43
|
const rb_data_type_t rkadm5_config_data_type = {
|
|
24
44
|
"RUBY_KADM5_CONFIG",
|
|
25
|
-
{
|
|
45
|
+
{rkadm5_config_typed_mark, rkadm5_config_typed_free, rkadm5_config_typed_size,},
|
|
26
46
|
NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
|
|
27
47
|
};
|
|
28
48
|
|
|
@@ -30,6 +50,7 @@ const rb_data_type_t rkadm5_config_data_type = {
|
|
|
30
50
|
static VALUE rkadm5_config_allocate(VALUE klass){
|
|
31
51
|
RUBY_KADM5_CONFIG* ptr = ALLOC(RUBY_KADM5_CONFIG);
|
|
32
52
|
memset(ptr, 0, sizeof(RUBY_KADM5_CONFIG));
|
|
53
|
+
ptr->rb_context = Qnil;
|
|
33
54
|
return TypedData_Wrap_Struct(klass, &rkadm5_config_data_type, ptr);
|
|
34
55
|
}
|
|
35
56
|
|
|
@@ -43,23 +64,50 @@ static VALUE rkeysalt_new(krb5_enctype enctype, krb5_int32 salttype){
|
|
|
43
64
|
}
|
|
44
65
|
|
|
45
66
|
/*
|
|
67
|
+
* call-seq:
|
|
68
|
+
* Kerberos::Kadm5::Config.new(context: nil)
|
|
69
|
+
*
|
|
46
70
|
* Returns a Kerberos::Kadm5::Config object. This object contains Kerberos
|
|
47
71
|
* admin configuration.
|
|
48
72
|
*
|
|
73
|
+
* Accepts the following keyword argument:
|
|
74
|
+
*
|
|
75
|
+
* - +context+: A Kerberos::Krb5::Context object. If provided, that context is
|
|
76
|
+
* used instead of creating a new one via krb5_init_context.
|
|
77
|
+
*
|
|
49
78
|
* Note that the returned object is frozen. Changes made to the Kerberos
|
|
50
79
|
* admin configuration options after the call will not be reflected in this
|
|
51
80
|
* object.
|
|
52
81
|
*/
|
|
53
|
-
static VALUE rkadm5_config_initialize(VALUE self){
|
|
82
|
+
static VALUE rkadm5_config_initialize(int argc, VALUE* argv, VALUE self){
|
|
54
83
|
RUBY_KADM5_CONFIG* ptr;
|
|
55
84
|
krb5_error_code kerror;
|
|
85
|
+
VALUE v_opts, v_context;
|
|
86
|
+
ID kw_table[1] = { rb_intern("context") };
|
|
87
|
+
VALUE kw_vals[1];
|
|
56
88
|
|
|
57
89
|
TypedData_Get_Struct(self, RUBY_KADM5_CONFIG, &rkadm5_config_data_type, ptr);
|
|
58
90
|
|
|
59
|
-
|
|
91
|
+
rb_scan_args(argc, argv, "0:", &v_opts);
|
|
60
92
|
|
|
61
|
-
if(
|
|
62
|
-
|
|
93
|
+
if(NIL_P(v_opts))
|
|
94
|
+
v_opts = rb_hash_new();
|
|
95
|
+
|
|
96
|
+
rb_get_kwargs(v_opts, kw_table, 0, 1, kw_vals);
|
|
97
|
+
v_context = kw_vals[0] == Qundef ? Qnil : kw_vals[0];
|
|
98
|
+
|
|
99
|
+
if(!NIL_P(v_context)){
|
|
100
|
+
ptr->ctx = rkrb5_context_borrow(v_context);
|
|
101
|
+
ptr->rb_context = v_context;
|
|
102
|
+
}
|
|
103
|
+
else{
|
|
104
|
+
kerror = krb5_init_context(&ptr->ctx);
|
|
105
|
+
|
|
106
|
+
if(kerror)
|
|
107
|
+
rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
|
|
108
|
+
|
|
109
|
+
ptr->rb_context = Qnil;
|
|
110
|
+
}
|
|
63
111
|
|
|
64
112
|
kerror = kadm5_get_config_params(
|
|
65
113
|
ptr->ctx,
|
|
@@ -188,6 +236,10 @@ static VALUE rkadm5_config_initialize(VALUE self){
|
|
|
188
236
|
rb_iv_set(self, "@keysalts", Qnil);
|
|
189
237
|
}
|
|
190
238
|
|
|
239
|
+
// All configuration data has been copied into Ruby values, so this object
|
|
240
|
+
// no longer needs to retain its native context lease.
|
|
241
|
+
rkadm5_config_cleanup(ptr);
|
|
242
|
+
|
|
191
243
|
// This is read only data
|
|
192
244
|
rb_obj_freeze(self);
|
|
193
245
|
|
|
@@ -300,7 +352,7 @@ void Init_config(void){
|
|
|
300
352
|
|
|
301
353
|
// Initializer
|
|
302
354
|
|
|
303
|
-
rb_define_method(cKadm5Config, "initialize", rkadm5_config_initialize,
|
|
355
|
+
rb_define_method(cKadm5Config, "initialize", rkadm5_config_initialize, -1);
|
|
304
356
|
|
|
305
357
|
// Methods
|
|
306
358
|
|
data/ext/rkerberos/context.c
CHANGED
|
@@ -15,6 +15,38 @@ static void rkrb5_context_typed_free(void *ptr) {
|
|
|
15
15
|
free(c);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
krb5_context rkrb5_context_borrow(VALUE v_context){
|
|
19
|
+
RUBY_KRB5_CONTEXT* ptr;
|
|
20
|
+
|
|
21
|
+
if(!rb_obj_is_kind_of(v_context, cKrb5Context))
|
|
22
|
+
rb_raise(rb_eTypeError, "context must be a Kerberos::Krb5::Context object");
|
|
23
|
+
|
|
24
|
+
TypedData_Get_Struct(v_context, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ptr);
|
|
25
|
+
|
|
26
|
+
if(!ptr->ctx || ptr->closed)
|
|
27
|
+
rb_raise(cKrb5Exception, "context is closed");
|
|
28
|
+
|
|
29
|
+
ptr->borrowers++;
|
|
30
|
+
return ptr->ctx;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
void rkrb5_context_release(VALUE v_context){
|
|
34
|
+
RUBY_KRB5_CONTEXT* ptr;
|
|
35
|
+
|
|
36
|
+
if(NIL_P(v_context))
|
|
37
|
+
return;
|
|
38
|
+
|
|
39
|
+
ptr = (RUBY_KRB5_CONTEXT*)RTYPEDDATA_DATA(v_context);
|
|
40
|
+
|
|
41
|
+
if(ptr->borrowers > 0)
|
|
42
|
+
ptr->borrowers--;
|
|
43
|
+
|
|
44
|
+
if(ptr->closed && ptr->borrowers == 0 && ptr->ctx){
|
|
45
|
+
krb5_free_context(ptr->ctx);
|
|
46
|
+
ptr->ctx = NULL;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
18
50
|
static size_t rkrb5_context_typed_size(const void *ptr) {
|
|
19
51
|
return sizeof(RUBY_KRB5_CONTEXT);
|
|
20
52
|
}
|
|
@@ -34,30 +66,53 @@ static VALUE rkrb5_context_allocate(VALUE klass){
|
|
|
34
66
|
|
|
35
67
|
/*
|
|
36
68
|
* call-seq:
|
|
37
|
-
* context.close
|
|
69
|
+
* context.close(force: false)
|
|
38
70
|
*
|
|
39
|
-
* Closes the context object.
|
|
71
|
+
* Closes the context object. Raises if wrappers are still borrowing the
|
|
72
|
+
* context. With +force: true+, the context is closed to new operations and
|
|
73
|
+
* borrowers immediately, but its native resources are retained until the
|
|
74
|
+
* existing borrowers have closed.
|
|
40
75
|
*/
|
|
41
|
-
static VALUE rkrb5_context_close(VALUE self){
|
|
76
|
+
static VALUE rkrb5_context_close(int argc, VALUE* argv, VALUE self){
|
|
42
77
|
RUBY_KRB5_CONTEXT* ptr;
|
|
78
|
+
VALUE v_opts, v_force;
|
|
79
|
+
ID kw_table[1] = { rb_intern("force") };
|
|
80
|
+
VALUE kw_vals[1];
|
|
43
81
|
|
|
44
82
|
TypedData_Get_Struct(self, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ptr);
|
|
45
83
|
|
|
46
|
-
|
|
47
|
-
|
|
84
|
+
rb_scan_args(argc, argv, "0:", &v_opts);
|
|
85
|
+
|
|
86
|
+
if(NIL_P(v_opts))
|
|
87
|
+
v_opts = rb_hash_new();
|
|
88
|
+
|
|
89
|
+
rb_get_kwargs(v_opts, kw_table, 0, 1, kw_vals);
|
|
90
|
+
v_force = kw_vals[0] == Qundef ? Qfalse : kw_vals[0];
|
|
48
91
|
|
|
49
|
-
ptr->
|
|
92
|
+
if(ptr->closed || !ptr->ctx)
|
|
93
|
+
return self;
|
|
94
|
+
|
|
95
|
+
if(ptr->borrowers > 0 && !RTEST(v_force))
|
|
96
|
+
rb_raise(cKrb5Exception, "context is in use by %lu dependent wrapper%s",
|
|
97
|
+
(unsigned long)ptr->borrowers, ptr->borrowers == 1 ? "" : "s");
|
|
98
|
+
|
|
99
|
+
ptr->closed = 1;
|
|
100
|
+
|
|
101
|
+
if(ptr->borrowers == 0){
|
|
102
|
+
krb5_free_context(ptr->ctx);
|
|
103
|
+
ptr->ctx = NULL;
|
|
104
|
+
}
|
|
50
105
|
|
|
51
106
|
return self;
|
|
52
107
|
}
|
|
53
108
|
|
|
54
109
|
/*
|
|
55
110
|
* call-seq:
|
|
56
|
-
* Kerberos::Context.new(
|
|
111
|
+
* Kerberos::Krb5::Context.new(secure: false, profile: nil)
|
|
57
112
|
*
|
|
58
113
|
* Creates and returns a new Kerberos::Context object.
|
|
59
114
|
*
|
|
60
|
-
* The
|
|
115
|
+
* The following keyword arguments are supported:
|
|
61
116
|
*
|
|
62
117
|
* :secure => true|false # Use config files only, ignore env variables
|
|
63
118
|
* :profile => '/path/to/krb5.conf' # Use the specified profile file
|
|
@@ -68,11 +123,13 @@ static VALUE rkrb5_context_initialize(int argc, VALUE *argv, VALUE self){
|
|
|
68
123
|
RUBY_KRB5_CONTEXT* ptr;
|
|
69
124
|
VALUE v_opts;
|
|
70
125
|
VALUE v_secure, v_profile;
|
|
126
|
+
ID kw_table[2] = { rb_intern("secure"), rb_intern("profile") };
|
|
127
|
+
VALUE kw_vals[2];
|
|
71
128
|
krb5_error_code kerror;
|
|
72
129
|
|
|
73
130
|
TypedData_Get_Struct(self, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ptr);
|
|
74
131
|
|
|
75
|
-
rb_scan_args(argc, argv, "
|
|
132
|
+
rb_scan_args(argc, argv, "0:", &v_opts);
|
|
76
133
|
|
|
77
134
|
// Default behavior is a normal context that may respect environment.
|
|
78
135
|
if (NIL_P(v_opts)) {
|
|
@@ -83,10 +140,9 @@ static VALUE rkrb5_context_initialize(int argc, VALUE *argv, VALUE self){
|
|
|
83
140
|
return self;
|
|
84
141
|
}
|
|
85
142
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
v_profile = rb_hash_aref2(v_opts, ID2SYM(rb_intern("profile")));
|
|
143
|
+
rb_get_kwargs(v_opts, kw_table, 0, 2, kw_vals);
|
|
144
|
+
v_secure = kw_vals[0] == Qundef ? Qfalse : kw_vals[0];
|
|
145
|
+
v_profile = kw_vals[1] == Qundef ? Qnil : kw_vals[1];
|
|
90
146
|
|
|
91
147
|
/*
|
|
92
148
|
* If a profile path is supplied, load it via profile_init_path() and
|
|
@@ -133,6 +189,66 @@ static VALUE rkrb5_context_initialize(int argc, VALUE *argv, VALUE self){
|
|
|
133
189
|
return self;
|
|
134
190
|
}
|
|
135
191
|
|
|
192
|
+
/*
|
|
193
|
+
* call-seq:
|
|
194
|
+
* context.default_realm -> String
|
|
195
|
+
*
|
|
196
|
+
* Returns the default realm from this context.
|
|
197
|
+
*/
|
|
198
|
+
static VALUE rkrb5_context_default_realm(VALUE self){
|
|
199
|
+
RUBY_KRB5_CONTEXT* ptr;
|
|
200
|
+
char* realm;
|
|
201
|
+
krb5_error_code kerror;
|
|
202
|
+
|
|
203
|
+
TypedData_Get_Struct(self, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ptr);
|
|
204
|
+
|
|
205
|
+
if(!ptr->ctx || ptr->closed)
|
|
206
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
207
|
+
|
|
208
|
+
kerror = krb5_get_default_realm(ptr->ctx, &realm);
|
|
209
|
+
|
|
210
|
+
if(kerror)
|
|
211
|
+
rb_raise(cKrb5Exception, "krb5_get_default_realm: %s", error_message(kerror));
|
|
212
|
+
|
|
213
|
+
VALUE v_realm = rb_str_new2(realm);
|
|
214
|
+
krb5_free_default_realm(ptr->ctx, realm);
|
|
215
|
+
|
|
216
|
+
return v_realm;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/*
|
|
220
|
+
* call-seq:
|
|
221
|
+
* context.default_realm = realm
|
|
222
|
+
*
|
|
223
|
+
* Sets the default realm for this context. If +realm+ is nil the default
|
|
224
|
+
* from krb5.conf is restored.
|
|
225
|
+
*/
|
|
226
|
+
static VALUE rkrb5_context_set_default_realm(VALUE self, VALUE v_realm){
|
|
227
|
+
RUBY_KRB5_CONTEXT* ptr;
|
|
228
|
+
char* realm;
|
|
229
|
+
krb5_error_code kerror;
|
|
230
|
+
|
|
231
|
+
TypedData_Get_Struct(self, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ptr);
|
|
232
|
+
|
|
233
|
+
if(!ptr->ctx || ptr->closed)
|
|
234
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
235
|
+
|
|
236
|
+
if(NIL_P(v_realm)){
|
|
237
|
+
realm = NULL;
|
|
238
|
+
}
|
|
239
|
+
else{
|
|
240
|
+
Check_Type(v_realm, T_STRING);
|
|
241
|
+
realm = StringValueCStr(v_realm);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
kerror = krb5_set_default_realm(ptr->ctx, realm);
|
|
245
|
+
|
|
246
|
+
if(kerror)
|
|
247
|
+
rb_raise(cKrb5Exception, "krb5_set_default_realm: %s", error_message(kerror));
|
|
248
|
+
|
|
249
|
+
return v_realm;
|
|
250
|
+
}
|
|
251
|
+
|
|
136
252
|
void Init_context(void){
|
|
137
253
|
/* The Kerberos::Krb5::Context class encapsulates a Kerberos context. */
|
|
138
254
|
cKrb5Context = rb_define_class_under(cKrb5, "Context", rb_cObject);
|
|
@@ -144,5 +260,7 @@ void Init_context(void){
|
|
|
144
260
|
rb_define_method(cKrb5Context, "initialize", rkrb5_context_initialize, -1);
|
|
145
261
|
|
|
146
262
|
// Instance Methods
|
|
147
|
-
rb_define_method(cKrb5Context, "close", rkrb5_context_close,
|
|
263
|
+
rb_define_method(cKrb5Context, "close", rkrb5_context_close, -1);
|
|
264
|
+
rb_define_method(cKrb5Context, "default_realm", rkrb5_context_default_realm, 0);
|
|
265
|
+
rb_define_method(cKrb5Context, "default_realm=", rkrb5_context_set_default_realm, 1);
|
|
148
266
|
}
|