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/ext/rkerberos/rkerberos.c
CHANGED
|
@@ -25,6 +25,13 @@ VALUE rb_hash_aref2(VALUE v_hash, VALUE v_key){
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
// TypedData functions for RUBY_KRB5
|
|
28
|
+
static void rkrb5_typed_mark(void *ptr) {
|
|
29
|
+
if (!ptr) return;
|
|
30
|
+
RUBY_KRB5 *r = (RUBY_KRB5 *)ptr;
|
|
31
|
+
if (r->rb_context != Qnil)
|
|
32
|
+
rb_gc_mark(r->rb_context);
|
|
33
|
+
}
|
|
34
|
+
|
|
28
35
|
static void rkrb5_typed_free(void *ptr) {
|
|
29
36
|
if (!ptr) return;
|
|
30
37
|
RUBY_KRB5 *r = (RUBY_KRB5 *)ptr;
|
|
@@ -34,8 +41,10 @@ static void rkrb5_typed_free(void *ptr) {
|
|
|
34
41
|
krb5_free_cred_contents(r->ctx, &r->creds);
|
|
35
42
|
if (r->princ)
|
|
36
43
|
krb5_free_principal(r->ctx, r->princ);
|
|
37
|
-
if (r->ctx)
|
|
44
|
+
if (r->ctx && r->rb_context == Qnil)
|
|
38
45
|
krb5_free_context(r->ctx);
|
|
46
|
+
else if (r->rb_context != Qnil)
|
|
47
|
+
rkrb5_context_release(r->rb_context);
|
|
39
48
|
free(r);
|
|
40
49
|
}
|
|
41
50
|
|
|
@@ -43,9 +52,9 @@ static size_t rkrb5_typed_size(const void *ptr) {
|
|
|
43
52
|
return sizeof(RUBY_KRB5);
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
|
|
55
|
+
const rb_data_type_t rkrb5_data_type = {
|
|
47
56
|
"RUBY_KRB5",
|
|
48
|
-
{
|
|
57
|
+
{rkrb5_typed_mark, rkrb5_typed_free, rkrb5_typed_size,},
|
|
49
58
|
NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
|
|
50
59
|
};
|
|
51
60
|
|
|
@@ -53,26 +62,53 @@ static const rb_data_type_t rkrb5_data_type = {
|
|
|
53
62
|
static VALUE rkrb5_allocate(VALUE klass){
|
|
54
63
|
RUBY_KRB5* ptr = ALLOC(RUBY_KRB5);
|
|
55
64
|
memset(ptr, 0, sizeof(RUBY_KRB5));
|
|
65
|
+
ptr->rb_context = Qnil;
|
|
56
66
|
return TypedData_Wrap_Struct(klass, &rkrb5_data_type, ptr);
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
/*
|
|
60
70
|
* call-seq:
|
|
61
|
-
* Kerberos::Krb5.new
|
|
71
|
+
* Kerberos::Krb5.new(context: nil)
|
|
62
72
|
*
|
|
63
73
|
* Creates and returns a new Kerberos::Krb5 object. This initializes the
|
|
64
74
|
* context for future method calls on that object.
|
|
75
|
+
*
|
|
76
|
+
* If a +context+ keyword argument is provided, it must be a
|
|
77
|
+
* Kerberos::Krb5::Context object. The context will be borrowed rather than
|
|
78
|
+
* creating a new one internally via krb5_init_context. The caller is
|
|
79
|
+
* retained for the lifetime of this Krb5 instance. The Context cannot be
|
|
80
|
+
* closed normally until this object has been closed.
|
|
81
|
+
*
|
|
82
|
+
* A block form is also supported. If a block is given, the object is
|
|
83
|
+
* yielded to the block and automatically closed when the block returns.
|
|
65
84
|
*/
|
|
66
|
-
static VALUE rkrb5_initialize(VALUE self){
|
|
85
|
+
static VALUE rkrb5_initialize(int argc, VALUE* argv, VALUE self){
|
|
67
86
|
RUBY_KRB5* ptr;
|
|
87
|
+
VALUE v_opts, v_context;
|
|
88
|
+
ID kw_table[1] = { rb_intern("context") };
|
|
89
|
+
VALUE kw_vals[1];
|
|
68
90
|
krb5_error_code kerror;
|
|
69
91
|
|
|
70
92
|
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
71
93
|
|
|
72
|
-
|
|
94
|
+
rb_scan_args(argc, argv, "0:", &v_opts);
|
|
73
95
|
|
|
74
|
-
if(
|
|
75
|
-
|
|
96
|
+
if(NIL_P(v_opts))
|
|
97
|
+
v_opts = rb_hash_new();
|
|
98
|
+
|
|
99
|
+
rb_get_kwargs(v_opts, kw_table, 0, 1, kw_vals);
|
|
100
|
+
v_context = kw_vals[0] == Qundef ? Qnil : kw_vals[0];
|
|
101
|
+
|
|
102
|
+
if(!NIL_P(v_context)){
|
|
103
|
+
ptr->ctx = rkrb5_context_borrow(v_context);
|
|
104
|
+
ptr->rb_context = v_context;
|
|
105
|
+
}
|
|
106
|
+
else{
|
|
107
|
+
kerror = krb5_init_context(&ptr->ctx);
|
|
108
|
+
|
|
109
|
+
if(kerror)
|
|
110
|
+
rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
|
|
111
|
+
}
|
|
76
112
|
|
|
77
113
|
if(rb_block_given_p()){
|
|
78
114
|
rb_ensure(rb_yield, self, rkrb5_close, self);
|
|
@@ -95,6 +131,9 @@ static VALUE rkrb5_get_default_realm(VALUE self){
|
|
|
95
131
|
|
|
96
132
|
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
97
133
|
|
|
134
|
+
if(!ptr->ctx)
|
|
135
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
136
|
+
|
|
98
137
|
kerror = krb5_get_default_realm(ptr->ctx, &realm);
|
|
99
138
|
|
|
100
139
|
if(kerror)
|
|
@@ -121,6 +160,9 @@ static VALUE rkrb5_set_default_realm(int argc, VALUE* argv, VALUE self){
|
|
|
121
160
|
|
|
122
161
|
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
123
162
|
|
|
163
|
+
if(!ptr->ctx)
|
|
164
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
165
|
+
|
|
124
166
|
rb_scan_args(argc, argv, "01", &v_realm);
|
|
125
167
|
|
|
126
168
|
if(NIL_P(v_realm)){
|
|
@@ -140,7 +182,7 @@ static VALUE rkrb5_set_default_realm(int argc, VALUE* argv, VALUE self){
|
|
|
140
182
|
}
|
|
141
183
|
|
|
142
184
|
/* call-seq:
|
|
143
|
-
* krb5.get_init_creds_keytab(principal
|
|
185
|
+
* krb5.get_init_creds_keytab(principal: nil, keytab: nil, service: nil, ccache: nil)
|
|
144
186
|
*
|
|
145
187
|
* Acquire credentials for +principal+ from +keytab+ using +service+. If
|
|
146
188
|
* no principal is specified, then a principal is derived from the service
|
|
@@ -154,7 +196,10 @@ static VALUE rkrb5_set_default_realm(int argc, VALUE* argv, VALUE self){
|
|
|
154
196
|
*/
|
|
155
197
|
static VALUE rkrb5_get_init_creds_keytab(int argc, VALUE* argv, VALUE self){
|
|
156
198
|
RUBY_KRB5* ptr;
|
|
157
|
-
VALUE v_user, v_keytab_name, v_service, v_ccache;
|
|
199
|
+
VALUE v_user = Qnil, v_keytab_name = Qnil, v_service = Qnil, v_ccache = Qnil;
|
|
200
|
+
VALUE v_kwargs = Qnil;
|
|
201
|
+
ID kw_table[4] = { rb_intern("principal"), rb_intern("keytab"), rb_intern("service"), rb_intern("ccache") };
|
|
202
|
+
VALUE kw_vals[4];
|
|
158
203
|
char* user;
|
|
159
204
|
char* service;
|
|
160
205
|
char keytab_name[MAX_KEYTAB_NAME_LEN];
|
|
@@ -181,7 +226,16 @@ static VALUE rkrb5_get_init_creds_keytab(int argc, VALUE* argv, VALUE self){
|
|
|
181
226
|
krb5_free_cred_contents(ptr->ctx, &ptr->creds);
|
|
182
227
|
memset(&ptr->creds, 0, sizeof(ptr->creds));
|
|
183
228
|
|
|
184
|
-
rb_scan_args(argc, argv, "
|
|
229
|
+
rb_scan_args(argc, argv, "0:", &v_kwargs);
|
|
230
|
+
|
|
231
|
+
if(NIL_P(v_kwargs))
|
|
232
|
+
v_kwargs = rb_hash_new();
|
|
233
|
+
|
|
234
|
+
rb_get_kwargs(v_kwargs, kw_table, 0, 4, kw_vals);
|
|
235
|
+
if(kw_vals[0] != Qundef) v_user = kw_vals[0];
|
|
236
|
+
if(kw_vals[1] != Qundef) v_keytab_name = kw_vals[1];
|
|
237
|
+
if(kw_vals[2] != Qundef) v_service = kw_vals[2];
|
|
238
|
+
if(kw_vals[3] != Qundef) v_ccache = kw_vals[3];
|
|
185
239
|
|
|
186
240
|
// Validate argument types before allocating opt, so type errors don't leak it.
|
|
187
241
|
if(!NIL_P(v_user))
|
|
@@ -261,6 +315,11 @@ static VALUE rkrb5_get_init_creds_keytab(int argc, VALUE* argv, VALUE self){
|
|
|
261
315
|
RUBY_KRB5_CCACHE* ccptr;
|
|
262
316
|
TypedData_Get_Struct(v_ccache, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, ccptr);
|
|
263
317
|
|
|
318
|
+
if(!ccptr->ctx || !ccptr->ccache){
|
|
319
|
+
krb5_get_init_creds_opt_free(ptr->ctx, opt);
|
|
320
|
+
rb_raise(cKrb5Exception, "credentials cache is closed or destroyed");
|
|
321
|
+
}
|
|
322
|
+
|
|
264
323
|
kerror = krb5_get_init_creds_opt_set_out_ccache(ptr->ctx, opt, ccptr->ccache);
|
|
265
324
|
if(kerror) {
|
|
266
325
|
krb5_get_init_creds_opt_free(ptr->ctx, opt);
|
|
@@ -300,7 +359,7 @@ static VALUE rkrb5_get_init_creds_keytab(int argc, VALUE* argv, VALUE self){
|
|
|
300
359
|
*
|
|
301
360
|
* Example:
|
|
302
361
|
*
|
|
303
|
-
* krb5.get_init_creds_password('foo', 'XXXXXX') # Authenticate 'foo' user
|
|
362
|
+
* krb5.get_init_creds_password(principal: 'foo', password: 'XXXXXX') # Authenticate 'foo' user
|
|
304
363
|
* krb5.change_password('XXXXXX', 'YYYYYY') # Change password for 'foo'
|
|
305
364
|
*/
|
|
306
365
|
static VALUE rkrb5_change_password(VALUE self, VALUE v_old, VALUE v_new){
|
|
@@ -328,6 +387,9 @@ static VALUE rkrb5_change_password(VALUE self, VALUE v_old, VALUE v_new){
|
|
|
328
387
|
if(!ptr->princ)
|
|
329
388
|
rb_raise(cKrb5Exception, "no principal has been established");
|
|
330
389
|
|
|
390
|
+
memset(&result_string, 0, sizeof(result_string));
|
|
391
|
+
memset(&pw_result_string, 0, sizeof(pw_result_string));
|
|
392
|
+
|
|
331
393
|
krb5_free_cred_contents(ptr->ctx, &ptr->creds);
|
|
332
394
|
memset(&ptr->creds, 0, sizeof(ptr->creds));
|
|
333
395
|
|
|
@@ -378,19 +440,26 @@ static VALUE rkrb5_change_password(VALUE self, VALUE v_old, VALUE v_new){
|
|
|
378
440
|
|
|
379
441
|
/*
|
|
380
442
|
* call-seq:
|
|
381
|
-
* krb5.get_init_creds_password(
|
|
443
|
+
* krb5.get_init_creds_password(principal: nil, password: nil, service: nil, ccache: nil)
|
|
382
444
|
*
|
|
383
|
-
* Authenticates the credentials of +
|
|
445
|
+
* Authenticates the credentials of +principal+ using +password+ against +service+,
|
|
384
446
|
* and has the effect of setting the principal and context internally. This method
|
|
385
447
|
* must typically be called before using other methods.
|
|
448
|
+
*
|
|
449
|
+
* When +ccache+ is given, the acquired credentials will be written into the
|
|
450
|
+
* provided Kerberos::Krb5::CredentialsCache.
|
|
386
451
|
*/
|
|
387
452
|
static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
388
453
|
RUBY_KRB5* ptr;
|
|
389
|
-
VALUE v_user, v_pass, v_service;
|
|
454
|
+
VALUE v_user = Qnil, v_pass = Qnil, v_service = Qnil, v_ccache = Qnil;
|
|
455
|
+
VALUE v_kwargs = Qnil;
|
|
456
|
+
ID kw_table[4] = { rb_intern("principal"), rb_intern("password"), rb_intern("service"), rb_intern("ccache") };
|
|
457
|
+
VALUE kw_vals[4];
|
|
390
458
|
char* user;
|
|
391
459
|
char* pass;
|
|
392
460
|
char* service;
|
|
393
461
|
krb5_error_code kerror;
|
|
462
|
+
krb5_get_init_creds_opt* opt = NULL;
|
|
394
463
|
|
|
395
464
|
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
396
465
|
|
|
@@ -406,7 +475,19 @@ static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
|
406
475
|
krb5_free_cred_contents(ptr->ctx, &ptr->creds);
|
|
407
476
|
memset(&ptr->creds, 0, sizeof(ptr->creds));
|
|
408
477
|
|
|
409
|
-
rb_scan_args(argc, argv, "
|
|
478
|
+
rb_scan_args(argc, argv, "0:", &v_kwargs);
|
|
479
|
+
|
|
480
|
+
if(NIL_P(v_kwargs))
|
|
481
|
+
v_kwargs = rb_hash_new();
|
|
482
|
+
|
|
483
|
+
rb_get_kwargs(v_kwargs, kw_table, 0, 4, kw_vals);
|
|
484
|
+
if(kw_vals[0] != Qundef) v_user = kw_vals[0];
|
|
485
|
+
if(kw_vals[1] != Qundef) v_pass = kw_vals[1];
|
|
486
|
+
if(kw_vals[2] != Qundef) v_service = kw_vals[2];
|
|
487
|
+
if(kw_vals[3] != Qundef) v_ccache = kw_vals[3];
|
|
488
|
+
|
|
489
|
+
if(NIL_P(v_user) || NIL_P(v_pass))
|
|
490
|
+
rb_raise(rb_eArgError, "principal and password are required");
|
|
410
491
|
|
|
411
492
|
Check_Type(v_user, T_STRING);
|
|
412
493
|
Check_Type(v_pass, T_STRING);
|
|
@@ -426,6 +507,24 @@ static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
|
426
507
|
if(kerror)
|
|
427
508
|
rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
|
|
428
509
|
|
|
510
|
+
if(!NIL_P(v_ccache)){
|
|
511
|
+
RUBY_KRB5_CCACHE* ccptr;
|
|
512
|
+
TypedData_Get_Struct(v_ccache, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, ccptr);
|
|
513
|
+
|
|
514
|
+
if(!ccptr->ctx || !ccptr->ccache)
|
|
515
|
+
rb_raise(cKrb5Exception, "credentials cache is closed or destroyed");
|
|
516
|
+
|
|
517
|
+
kerror = krb5_get_init_creds_opt_alloc(ptr->ctx, &opt);
|
|
518
|
+
if(kerror)
|
|
519
|
+
rb_raise(cKrb5Exception, "krb5_get_init_creds_opt_alloc: %s", error_message(kerror));
|
|
520
|
+
|
|
521
|
+
kerror = krb5_get_init_creds_opt_set_out_ccache(ptr->ctx, opt, ccptr->ccache);
|
|
522
|
+
if(kerror){
|
|
523
|
+
krb5_get_init_creds_opt_free(ptr->ctx, opt);
|
|
524
|
+
rb_raise(cKrb5Exception, "krb5_get_init_creds_opt_set_out_ccache: %s", error_message(kerror));
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
429
528
|
kerror = krb5_get_init_creds_password(
|
|
430
529
|
ptr->ctx,
|
|
431
530
|
&ptr->creds,
|
|
@@ -435,9 +534,12 @@ static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
|
435
534
|
NULL,
|
|
436
535
|
0,
|
|
437
536
|
service,
|
|
438
|
-
|
|
537
|
+
opt
|
|
439
538
|
);
|
|
440
539
|
|
|
540
|
+
if(opt)
|
|
541
|
+
krb5_get_init_creds_opt_free(ptr->ctx, opt);
|
|
542
|
+
|
|
441
543
|
if(kerror)
|
|
442
544
|
rb_raise(cKrb5Exception, "krb5_get_init_creds_password: %s", error_message(kerror));
|
|
443
545
|
|
|
@@ -446,7 +548,7 @@ static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
|
446
548
|
|
|
447
549
|
/*
|
|
448
550
|
* call-seq:
|
|
449
|
-
* krb5.authenticate!(
|
|
551
|
+
* krb5.authenticate!(principal:, password:, service: nil)
|
|
450
552
|
*
|
|
451
553
|
* Convenience method that: acquires initial credentials via password and
|
|
452
554
|
* immediately verifies those credentials using `verify_init_creds` with
|
|
@@ -458,7 +560,9 @@ static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
|
458
560
|
*/
|
|
459
561
|
static VALUE rkrb5_authenticate_bang(int argc, VALUE* argv, VALUE self){
|
|
460
562
|
RUBY_KRB5* ptr;
|
|
461
|
-
VALUE v_user, v_pass, v_service;
|
|
563
|
+
VALUE v_kwargs, v_user, v_pass, v_service;
|
|
564
|
+
ID kw_table[3] = { rb_intern("principal"), rb_intern("password"), rb_intern("service") };
|
|
565
|
+
VALUE kw_vals[3];
|
|
462
566
|
char* user;
|
|
463
567
|
char* pass;
|
|
464
568
|
char* service;
|
|
@@ -479,8 +583,15 @@ static VALUE rkrb5_authenticate_bang(int argc, VALUE* argv, VALUE self){
|
|
|
479
583
|
krb5_free_cred_contents(ptr->ctx, &ptr->creds);
|
|
480
584
|
memset(&ptr->creds, 0, sizeof(ptr->creds));
|
|
481
585
|
|
|
482
|
-
|
|
483
|
-
|
|
586
|
+
rb_scan_args(argc, argv, "0:", &v_kwargs);
|
|
587
|
+
|
|
588
|
+
if(NIL_P(v_kwargs))
|
|
589
|
+
v_kwargs = rb_hash_new();
|
|
590
|
+
|
|
591
|
+
rb_get_kwargs(v_kwargs, kw_table, 2, 1, kw_vals);
|
|
592
|
+
v_user = kw_vals[0];
|
|
593
|
+
v_pass = kw_vals[1];
|
|
594
|
+
v_service = kw_vals[2] == Qundef ? Qnil : kw_vals[2];
|
|
484
595
|
|
|
485
596
|
Check_Type(v_user, T_STRING);
|
|
486
597
|
Check_Type(v_pass, T_STRING);
|
|
@@ -516,14 +627,7 @@ static VALUE rkrb5_authenticate_bang(int argc, VALUE* argv, VALUE self){
|
|
|
516
627
|
if(kerror)
|
|
517
628
|
rb_raise(cKrb5Exception, "krb5_get_init_creds_password: %s", error_message(kerror));
|
|
518
629
|
|
|
519
|
-
/*
|
|
520
|
-
* Try strict verification first (AP-REQ nofail). If strict verification
|
|
521
|
-
* cannot be performed (e.g. missing keytab or other environment issue),
|
|
522
|
-
* fall back to the standard verification so authenticate! remains useful
|
|
523
|
-
* in minimal test environments.
|
|
524
|
-
*/
|
|
525
630
|
krb5_verify_init_creds_opt vicopt;
|
|
526
|
-
krb5_error_code kerror_strict = 0;
|
|
527
631
|
|
|
528
632
|
krb5_verify_init_creds_opt_init(&vicopt);
|
|
529
633
|
krb5_verify_init_creds_opt_set_ap_req_nofail(&vicopt, TRUE);
|
|
@@ -536,19 +640,12 @@ static VALUE rkrb5_authenticate_bang(int argc, VALUE* argv, VALUE self){
|
|
|
536
640
|
rb_raise(cKrb5Exception, "krb5_parse_name(service): %s", error_message(kerror));
|
|
537
641
|
}
|
|
538
642
|
|
|
539
|
-
// First, attempt strict verification
|
|
540
643
|
kerror = krb5_verify_init_creds(ptr->ctx, &ptr->creds, server_princ, NULL, NULL, &vicopt);
|
|
541
644
|
|
|
542
645
|
if(kerror){
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
if(kerror){
|
|
547
|
-
if(server_princ)
|
|
548
|
-
krb5_free_principal(ptr->ctx, server_princ);
|
|
549
|
-
/* raise the original strict-verification error to inform caller */
|
|
550
|
-
rb_raise(cKrb5Exception, "krb5_verify_init_creds: %s", error_message(kerror_strict));
|
|
551
|
-
}
|
|
646
|
+
if(server_princ)
|
|
647
|
+
krb5_free_principal(ptr->ctx, server_princ);
|
|
648
|
+
rb_raise(cKrb5Exception, "krb5_verify_init_creds: %s", error_message(kerror));
|
|
552
649
|
}
|
|
553
650
|
|
|
554
651
|
if(server_princ)
|
|
@@ -580,11 +677,14 @@ static VALUE rkrb5_close(VALUE self){
|
|
|
580
677
|
if(ptr->princ)
|
|
581
678
|
krb5_free_principal(ptr->ctx, ptr->princ);
|
|
582
679
|
|
|
583
|
-
if(ptr->ctx)
|
|
680
|
+
if(ptr->ctx && ptr->rb_context == Qnil)
|
|
584
681
|
krb5_free_context(ptr->ctx);
|
|
682
|
+
else if(ptr->rb_context != Qnil)
|
|
683
|
+
rkrb5_context_release(ptr->rb_context);
|
|
585
684
|
|
|
586
685
|
ptr->ctx = NULL;
|
|
587
686
|
ptr->princ = NULL;
|
|
687
|
+
ptr->rb_context = Qnil;
|
|
588
688
|
|
|
589
689
|
return Qtrue;
|
|
590
690
|
}
|
|
@@ -701,7 +801,7 @@ static VALUE rkrb5_get_permitted_enctypes(VALUE self){
|
|
|
701
801
|
|
|
702
802
|
/*
|
|
703
803
|
* call-seq:
|
|
704
|
-
* krb5.verify_init_creds(server
|
|
804
|
+
* krb5.verify_init_creds(server: nil, keytab: nil, ccache: nil)
|
|
705
805
|
*
|
|
706
806
|
* Verifies the initial credentials currently stored in the internal
|
|
707
807
|
* credentials structure. Optionally a server principal string, a
|
|
@@ -711,7 +811,10 @@ static VALUE rkrb5_get_permitted_enctypes(VALUE self){
|
|
|
711
811
|
*/
|
|
712
812
|
static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
713
813
|
RUBY_KRB5* ptr;
|
|
714
|
-
VALUE v_server, v_keytab, v_ccache;
|
|
814
|
+
VALUE v_server = Qnil, v_keytab = Qnil, v_ccache = Qnil;
|
|
815
|
+
VALUE v_kwargs = Qnil;
|
|
816
|
+
ID kw_table[3] = { rb_intern("server"), rb_intern("keytab"), rb_intern("ccache") };
|
|
817
|
+
VALUE kw_vals[3];
|
|
715
818
|
krb5_error_code kerror;
|
|
716
819
|
krb5_principal server_princ = NULL;
|
|
717
820
|
RUBY_KRB5_KEYTAB* ktptr = NULL;
|
|
@@ -719,7 +822,15 @@ static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
|
719
822
|
krb5_keytab keytab = NULL;
|
|
720
823
|
krb5_ccache *ccache_ptr = NULL;
|
|
721
824
|
|
|
722
|
-
rb_scan_args(argc, argv, "
|
|
825
|
+
rb_scan_args(argc, argv, "0:", &v_kwargs);
|
|
826
|
+
|
|
827
|
+
if(NIL_P(v_kwargs))
|
|
828
|
+
v_kwargs = rb_hash_new();
|
|
829
|
+
|
|
830
|
+
rb_get_kwargs(v_kwargs, kw_table, 0, 3, kw_vals);
|
|
831
|
+
if(kw_vals[0] != Qundef) v_server = kw_vals[0];
|
|
832
|
+
if(kw_vals[1] != Qundef) v_keytab = kw_vals[1];
|
|
833
|
+
if(kw_vals[2] != Qundef) v_ccache = kw_vals[2];
|
|
723
834
|
|
|
724
835
|
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
725
836
|
|
|
@@ -734,12 +845,20 @@ static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
|
734
845
|
if(!NIL_P(v_keytab)){
|
|
735
846
|
// Will raise TypeError if object isn't the expected Keytab typed data
|
|
736
847
|
TypedData_Get_Struct(v_keytab, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ktptr);
|
|
848
|
+
|
|
849
|
+
if(!ktptr->ctx || !ktptr->keytab)
|
|
850
|
+
rb_raise(cKrb5Exception, "keytab is closed");
|
|
851
|
+
|
|
737
852
|
keytab = ktptr->keytab;
|
|
738
853
|
}
|
|
739
854
|
|
|
740
855
|
if(!NIL_P(v_ccache)){
|
|
741
856
|
// Will raise TypeError if object isn't the expected CCache typed data
|
|
742
857
|
TypedData_Get_Struct(v_ccache, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, ccptr);
|
|
858
|
+
|
|
859
|
+
if(!ccptr->ctx || !ccptr->ccache)
|
|
860
|
+
rb_raise(cKrb5Exception, "credentials cache is closed or destroyed");
|
|
861
|
+
|
|
743
862
|
ccache_ptr = &ccptr->ccache;
|
|
744
863
|
}
|
|
745
864
|
|
|
@@ -747,6 +866,22 @@ static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
|
747
866
|
if(ptr->creds.client == NULL)
|
|
748
867
|
rb_raise(cKrb5Exception, "no credentials have been acquired");
|
|
749
868
|
|
|
869
|
+
/* A resolved FILE cache may not exist on disk yet. Create it before
|
|
870
|
+
krb5_verify_init_creds attempts to store any credentials it fetches. */
|
|
871
|
+
if(ccache_ptr && *ccache_ptr){
|
|
872
|
+
krb5_principal cache_princ = NULL;
|
|
873
|
+
|
|
874
|
+
kerror = krb5_cc_get_principal(ptr->ctx, *ccache_ptr, &cache_princ);
|
|
875
|
+
|
|
876
|
+
if(kerror == KRB5_CC_NOTFOUND || kerror == KRB5_FCC_NOFILE)
|
|
877
|
+
kerror = krb5_cc_initialize(ptr->ctx, *ccache_ptr, ptr->creds.client);
|
|
878
|
+
else if(!kerror)
|
|
879
|
+
krb5_free_principal(ptr->ctx, cache_princ);
|
|
880
|
+
|
|
881
|
+
if(kerror)
|
|
882
|
+
rb_raise(cKrb5Exception, "credentials cache setup: %s", error_message(kerror));
|
|
883
|
+
}
|
|
884
|
+
|
|
750
885
|
// Optional server principal (parse after context & arg validation)
|
|
751
886
|
if(!NIL_P(v_server)){
|
|
752
887
|
kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_server), &server_princ);
|
|
@@ -762,16 +897,11 @@ static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
|
762
897
|
if(kerror)
|
|
763
898
|
rb_raise(cKrb5Exception, "krb5_verify_init_creds: %s", error_message(kerror));
|
|
764
899
|
|
|
765
|
-
/*
|
|
766
|
-
|
|
900
|
+
/* Include the initial credential in addition to any credentials fetched
|
|
901
|
+
and stored by krb5_verify_init_creds. */
|
|
767
902
|
if(ccache_ptr && *ccache_ptr){
|
|
768
903
|
krb5_error_code k2;
|
|
769
904
|
|
|
770
|
-
k2 = krb5_cc_initialize(ptr->ctx, *ccache_ptr, ptr->creds.client);
|
|
771
|
-
|
|
772
|
-
if(k2)
|
|
773
|
-
rb_raise(cKrb5Exception, "krb5_cc_initialize: %s", error_message(k2));
|
|
774
|
-
|
|
775
905
|
k2 = krb5_cc_store_cred(ptr->ctx, *ccache_ptr, &ptr->creds);
|
|
776
906
|
|
|
777
907
|
if(k2)
|
|
@@ -781,6 +911,83 @@ static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
|
781
911
|
return Qtrue;
|
|
782
912
|
}
|
|
783
913
|
|
|
914
|
+
/*
|
|
915
|
+
* call-seq:
|
|
916
|
+
* krb5.get_host_realm(hostname) -> Array
|
|
917
|
+
*
|
|
918
|
+
* Returns an array of realm names associated with +hostname+.
|
|
919
|
+
* Uses the Kerberos library's host-to-realm mapping (krb5.conf
|
|
920
|
+
* [domain_realm] section, DNS lookups, etc.).
|
|
921
|
+
*/
|
|
922
|
+
static VALUE rkrb5_get_host_realm(VALUE self, VALUE v_host){
|
|
923
|
+
RUBY_KRB5* ptr;
|
|
924
|
+
char** realms;
|
|
925
|
+
char** p;
|
|
926
|
+
krb5_error_code kerror;
|
|
927
|
+
|
|
928
|
+
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
929
|
+
|
|
930
|
+
if(!ptr->ctx)
|
|
931
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
932
|
+
|
|
933
|
+
Check_Type(v_host, T_STRING);
|
|
934
|
+
|
|
935
|
+
kerror = krb5_get_host_realm(ptr->ctx, StringValueCStr(v_host), &realms);
|
|
936
|
+
|
|
937
|
+
if(kerror)
|
|
938
|
+
rb_raise(cKrb5Exception, "krb5_get_host_realm: %s", error_message(kerror));
|
|
939
|
+
|
|
940
|
+
VALUE v_array = rb_ary_new();
|
|
941
|
+
|
|
942
|
+
for(p = realms; p && *p; p++)
|
|
943
|
+
rb_ary_push(v_array, rb_str_new2(*p));
|
|
944
|
+
|
|
945
|
+
krb5_free_host_realm(ptr->ctx, realms);
|
|
946
|
+
|
|
947
|
+
return v_array;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/*
|
|
951
|
+
* call-seq:
|
|
952
|
+
* krb5.expand_hostname(hostname) -> String
|
|
953
|
+
*
|
|
954
|
+
* Canonicalizes +hostname+ by performing a forward DNS lookup followed
|
|
955
|
+
* by a reverse lookup, returning the canonical hostname.
|
|
956
|
+
*/
|
|
957
|
+
static VALUE rkrb5_expand_hostname(VALUE self, VALUE v_host){
|
|
958
|
+
RUBY_KRB5* ptr;
|
|
959
|
+
char* canonical;
|
|
960
|
+
krb5_error_code kerror;
|
|
961
|
+
|
|
962
|
+
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
963
|
+
|
|
964
|
+
if(!ptr->ctx)
|
|
965
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
966
|
+
|
|
967
|
+
Check_Type(v_host, T_STRING);
|
|
968
|
+
|
|
969
|
+
kerror = krb5_expand_hostname(ptr->ctx, StringValueCStr(v_host), &canonical);
|
|
970
|
+
|
|
971
|
+
if(kerror)
|
|
972
|
+
rb_raise(cKrb5Exception, "krb5_expand_hostname: %s", error_message(kerror));
|
|
973
|
+
|
|
974
|
+
VALUE v_result = rb_str_new2(canonical);
|
|
975
|
+
krb5_free_string(ptr->ctx, canonical);
|
|
976
|
+
|
|
977
|
+
return v_result;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/*
|
|
981
|
+
* call-seq:
|
|
982
|
+
* Kerberos::Krb5.thread_safe?
|
|
983
|
+
*
|
|
984
|
+
* Returns true if the Kerberos library was built with multithread support,
|
|
985
|
+
* false otherwise.
|
|
986
|
+
*/
|
|
987
|
+
static VALUE rkrb5_s_thread_safe(VALUE klass){
|
|
988
|
+
return krb5_is_thread_safe() ? Qtrue : Qfalse;
|
|
989
|
+
}
|
|
990
|
+
|
|
784
991
|
void Init_rkerberos(void){
|
|
785
992
|
mKerberos = rb_define_module("Kerberos");
|
|
786
993
|
cKrb5 = rb_define_class_under(mKerberos, "Krb5", rb_cObject);
|
|
@@ -790,7 +997,10 @@ void Init_rkerberos(void){
|
|
|
790
997
|
rb_define_alloc_func(cKrb5, rkrb5_allocate);
|
|
791
998
|
|
|
792
999
|
// Initializers
|
|
793
|
-
rb_define_method(cKrb5, "initialize", rkrb5_initialize,
|
|
1000
|
+
rb_define_method(cKrb5, "initialize", rkrb5_initialize, -1);
|
|
1001
|
+
|
|
1002
|
+
// Singleton Methods
|
|
1003
|
+
rb_define_singleton_method(cKrb5, "thread_safe?", rkrb5_s_thread_safe, 0);
|
|
794
1004
|
|
|
795
1005
|
// Krb5 Methods
|
|
796
1006
|
rb_define_method(cKrb5, "authenticate!", rkrb5_authenticate_bang, -1);
|
|
@@ -803,13 +1013,20 @@ void Init_rkerberos(void){
|
|
|
803
1013
|
rb_define_method(cKrb5, "get_permitted_enctypes", rkrb5_get_permitted_enctypes, 0);
|
|
804
1014
|
rb_define_method(cKrb5, "set_default_realm", rkrb5_set_default_realm, -1);
|
|
805
1015
|
rb_define_method(cKrb5, "verify_init_creds", rkrb5_verify_init_creds, -1);
|
|
1016
|
+
rb_define_method(cKrb5, "get_host_realm", rkrb5_get_host_realm, 1);
|
|
1017
|
+
rb_define_method(cKrb5, "expand_hostname", rkrb5_expand_hostname, 1);
|
|
806
1018
|
|
|
807
1019
|
// Aliases
|
|
808
1020
|
rb_define_alias(cKrb5, "default_realm", "get_default_realm");
|
|
1021
|
+
rb_define_alias(cKrb5, "default_realm=", "set_default_realm");
|
|
809
1022
|
rb_define_alias(cKrb5, "default_principal", "get_default_principal");
|
|
1023
|
+
rb_define_alias(cKrb5, "host_realm", "get_host_realm");
|
|
1024
|
+
rb_define_alias(cKrb5, "init_creds_password", "get_init_creds_password");
|
|
1025
|
+
rb_define_alias(cKrb5, "init_creds_keytab", "get_init_creds_keytab");
|
|
1026
|
+
rb_define_alias(cKrb5, "permitted_enctypes", "get_permitted_enctypes");
|
|
810
1027
|
|
|
811
|
-
/* 0.
|
|
812
|
-
rb_define_const(cKrb5, "VERSION", rb_str_new2("0.
|
|
1028
|
+
/* 0.3.1: The version of the custom rkerberos library */
|
|
1029
|
+
rb_define_const(cKrb5, "VERSION", rb_str_new2("0.3.1"));
|
|
813
1030
|
|
|
814
1031
|
// Encoding type constants
|
|
815
1032
|
|
data/ext/rkerberos/rkerberos.h
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
// Make the context data type visible to other C files
|
|
10
10
|
extern const rb_data_type_t rkrb5_context_data_type;
|
|
11
|
+
// Make the Krb5 data type visible to other C files
|
|
12
|
+
extern const rb_data_type_t rkrb5_data_type;
|
|
11
13
|
// Make the config data type visible to other C files
|
|
12
14
|
extern const rb_data_type_t rkadm5_config_data_type;
|
|
13
15
|
|
|
@@ -47,6 +49,10 @@ void Init_ccache();
|
|
|
47
49
|
// Defined in rkerberos.c
|
|
48
50
|
VALUE rb_hash_aref2(VALUE, VALUE);
|
|
49
51
|
|
|
52
|
+
// Defined in context.c
|
|
53
|
+
krb5_context rkrb5_context_borrow(VALUE);
|
|
54
|
+
void rkrb5_context_release(VALUE);
|
|
55
|
+
|
|
50
56
|
// Variable declarations
|
|
51
57
|
extern VALUE mKerberos;
|
|
52
58
|
extern VALUE cKrb5;
|
|
@@ -67,12 +73,15 @@ typedef struct {
|
|
|
67
73
|
krb5_creds creds;
|
|
68
74
|
krb5_principal princ;
|
|
69
75
|
krb5_keytab keytab;
|
|
76
|
+
VALUE rb_context;
|
|
70
77
|
} RUBY_KRB5;
|
|
71
78
|
|
|
72
79
|
// Kerberos::Context
|
|
73
80
|
typedef struct {
|
|
74
81
|
krb5_context ctx;
|
|
75
82
|
krb5_enctype etypes;
|
|
83
|
+
size_t borrowers;
|
|
84
|
+
int closed;
|
|
76
85
|
} RUBY_KRB5_CONTEXT;
|
|
77
86
|
|
|
78
87
|
// Kerberos::Kadm5
|
|
@@ -81,6 +90,7 @@ typedef struct {
|
|
|
81
90
|
krb5_principal princ;
|
|
82
91
|
void* handle;
|
|
83
92
|
char** db_args;
|
|
93
|
+
VALUE rb_context; // Ruby Context object when borrowed, Qnil if ctx is owned
|
|
84
94
|
} RUBY_KADM5;
|
|
85
95
|
|
|
86
96
|
// Kerberos::Krb5::Keytab::Entry
|
|
@@ -96,23 +106,27 @@ typedef struct {
|
|
|
96
106
|
krb5_context ctx;
|
|
97
107
|
krb5_creds creds;
|
|
98
108
|
krb5_keytab keytab;
|
|
109
|
+
VALUE rb_context; // Ruby Context object when borrowed, Qnil if ctx is owned
|
|
99
110
|
} RUBY_KRB5_KEYTAB;
|
|
100
111
|
|
|
101
112
|
typedef struct {
|
|
102
113
|
krb5_context ctx;
|
|
103
114
|
krb5_principal principal;
|
|
115
|
+
VALUE rb_context; // Ruby Context object when borrowed, Qnil if ctx is owned
|
|
104
116
|
} RUBY_KRB5_PRINC;
|
|
105
117
|
|
|
106
118
|
typedef struct {
|
|
107
119
|
krb5_context ctx;
|
|
108
120
|
krb5_ccache ccache;
|
|
109
121
|
krb5_principal principal;
|
|
122
|
+
VALUE rb_context; // Ruby Context object when borrowed, Qnil if ctx is owned
|
|
110
123
|
} RUBY_KRB5_CCACHE;
|
|
111
124
|
|
|
112
125
|
#ifdef HAVE_KADM5_ADMIN_H
|
|
113
126
|
typedef struct {
|
|
114
127
|
krb5_context ctx;
|
|
115
128
|
kadm5_config_params config;
|
|
129
|
+
VALUE rb_context; // Ruby Context object when borrowed, Qnil if ctx is owned
|
|
116
130
|
} RUBY_KADM5_CONFIG;
|
|
117
131
|
|
|
118
132
|
typedef struct {
|
data/rkerberos.gemspec
CHANGED
|
@@ -2,15 +2,18 @@ require 'rubygems'
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |spec|
|
|
4
4
|
spec.name = 'rkerberos'
|
|
5
|
-
spec.version = '0.
|
|
5
|
+
spec.version = '0.3.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 = '
|
|
10
|
-
spec.summary = 'A Ruby interface for the
|
|
9
|
+
spec.homepage = 'https://github.com/rkerberos/rkerberos'
|
|
10
|
+
spec.summary = 'A Ruby interface for the Kerberos library'
|
|
11
|
+
spec.required_ruby_version = '>= 3.2'
|
|
11
12
|
spec.test_files = Dir['spec/**/*_spec.rb']
|
|
12
13
|
spec.extensions = ['ext/rkerberos/extconf.rb']
|
|
13
|
-
spec.files = Dir['**/*']
|
|
14
|
+
spec.files = Dir['**/*']
|
|
15
|
+
.select { |file| File.file?(file) }
|
|
16
|
+
.grep_v(%r{\A(?:\.git|docker|Dockerfile|Gemfile\.lock\z|tmp(?:/|\z))|\.gem\z})
|
|
14
17
|
|
|
15
18
|
spec.add_development_dependency('rake-compiler')
|
|
16
19
|
spec.add_development_dependency('rspec', '>= 3.0')
|
|
@@ -22,13 +25,13 @@ Gem::Specification.new do |spec|
|
|
|
22
25
|
EOF
|
|
23
26
|
|
|
24
27
|
spec.metadata = {
|
|
25
|
-
'homepage_uri' => 'https://github.com/rkerberos/rkerberos',
|
|
26
28
|
'bug_tracker_uri' => 'https://github.com/rkerberos/rkerberos/issues',
|
|
27
|
-
'changelog_uri' => 'https://github.com/rkerberos/rkerberos/blob/
|
|
29
|
+
'changelog_uri' => 'https://github.com/rkerberos/rkerberos/blob/master/CHANGES.md',
|
|
28
30
|
'documentation_uri' => 'https://github.com/rkerberos/rkerberos/wiki',
|
|
31
|
+
'homepage_uri' => 'https://github.com/rkerberos/rkerberos',
|
|
29
32
|
'source_code_uri' => 'https://github.com/rkerberos/rkerberos',
|
|
30
33
|
'wiki_uri' => 'https://github.com/rkerberos/rkerberos/wiki',
|
|
31
|
-
'github_repo' => 'https://github.com/
|
|
34
|
+
'github_repo' => 'https://github.com/rkerberos/rkerberos',
|
|
32
35
|
'funding_uri' => 'https://github.com/sponsors/rkerberos',
|
|
33
36
|
'rubygems_mfa_required' => 'true'
|
|
34
37
|
}
|