rkerberos 0.2.0 → 0.2.2
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 +59 -0
- data/MANIFEST.md +24 -0
- data/README.md +55 -3
- data/Rakefile +35 -0
- data/ext/rkerberos/ccache.c +103 -9
- data/ext/rkerberos/config.c +4 -4
- data/ext/rkerberos/context.c +63 -8
- data/ext/rkerberos/kadm5.c +79 -65
- data/ext/rkerberos/keytab.c +237 -106
- data/ext/rkerberos/policy.c +1 -1
- data/ext/rkerberos/rkerberos.c +264 -8
- data/ext/rkerberos/rkerberos.h +2 -0
- data/rkerberos.gemspec +16 -7
- data/spec/config_spec.rb +4 -4
- data/spec/context_spec.rb +36 -0
- data/spec/credentials_cache_spec.rb +53 -0
- data/spec/kadm5_spec.rb +40 -1
- data/spec/krb5_keytab_spec.rb +195 -0
- data/spec/krb5_spec.rb +177 -1
- metadata +16 -27
- data/CHANGES +0 -34
- data/Dockerfile +0 -42
- data/MANIFEST +0 -16
- 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
data/ext/rkerberos/rkerberos.c
CHANGED
|
@@ -100,7 +100,10 @@ static VALUE rkrb5_get_default_realm(VALUE self){
|
|
|
100
100
|
if(kerror)
|
|
101
101
|
rb_raise(cKrb5Exception, "krb5_get_default_realm: %s", error_message(kerror));
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
VALUE v_realm = rb_str_new2(realm);
|
|
104
|
+
krb5_free_default_realm(ptr->ctx, realm);
|
|
105
|
+
|
|
106
|
+
return v_realm;
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
/*
|
|
@@ -158,13 +161,26 @@ static VALUE rkrb5_get_init_creds_keytab(int argc, VALUE* argv, VALUE self){
|
|
|
158
161
|
|
|
159
162
|
krb5_error_code kerror;
|
|
160
163
|
krb5_get_init_creds_opt* opt;
|
|
161
|
-
krb5_creds cred;
|
|
162
164
|
|
|
163
165
|
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
164
166
|
|
|
165
167
|
if(!ptr->ctx)
|
|
166
168
|
rb_raise(cKrb5Exception, "no context has been established");
|
|
167
169
|
|
|
170
|
+
// Free resources from a previous call to avoid leaks on repeated use.
|
|
171
|
+
if(ptr->keytab){
|
|
172
|
+
krb5_kt_close(ptr->ctx, ptr->keytab);
|
|
173
|
+
ptr->keytab = NULL;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if(ptr->princ){
|
|
177
|
+
krb5_free_principal(ptr->ctx, ptr->princ);
|
|
178
|
+
ptr->princ = NULL;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
krb5_free_cred_contents(ptr->ctx, &ptr->creds);
|
|
182
|
+
memset(&ptr->creds, 0, sizeof(ptr->creds));
|
|
183
|
+
|
|
168
184
|
kerror = krb5_get_init_creds_opt_alloc(ptr->ctx, &opt);
|
|
169
185
|
if(kerror)
|
|
170
186
|
rb_raise(cKrb5Exception, "krb5_get_init_creds_opt_alloc: %s", error_message(kerror));
|
|
@@ -247,7 +263,7 @@ static VALUE rkrb5_get_init_creds_keytab(int argc, VALUE* argv, VALUE self){
|
|
|
247
263
|
|
|
248
264
|
kerror = krb5_get_init_creds_keytab(
|
|
249
265
|
ptr->ctx,
|
|
250
|
-
&
|
|
266
|
+
&ptr->creds,
|
|
251
267
|
ptr->princ,
|
|
252
268
|
ptr->keytab,
|
|
253
269
|
0,
|
|
@@ -305,6 +321,9 @@ static VALUE rkrb5_change_password(VALUE self, VALUE v_old, VALUE v_new){
|
|
|
305
321
|
if(!ptr->princ)
|
|
306
322
|
rb_raise(cKrb5Exception, "no principal has been established");
|
|
307
323
|
|
|
324
|
+
krb5_free_cred_contents(ptr->ctx, &ptr->creds);
|
|
325
|
+
memset(&ptr->creds, 0, sizeof(ptr->creds));
|
|
326
|
+
|
|
308
327
|
kerror = krb5_get_init_creds_password(
|
|
309
328
|
ptr->ctx,
|
|
310
329
|
&ptr->creds,
|
|
@@ -329,8 +348,23 @@ static VALUE rkrb5_change_password(VALUE self, VALUE v_old, VALUE v_new){
|
|
|
329
348
|
&result_string
|
|
330
349
|
);
|
|
331
350
|
|
|
332
|
-
if(kerror)
|
|
351
|
+
if(kerror){
|
|
352
|
+
krb5_free_data_contents(ptr->ctx, &result_string);
|
|
353
|
+
krb5_free_data_contents(ptr->ctx, &pw_result_string);
|
|
333
354
|
rb_raise(cKrb5Exception, "krb5_change_password: %s", error_message(kerror));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if(pw_result){
|
|
358
|
+
VALUE v_msg = (result_string.length > 0)
|
|
359
|
+
? rb_str_new(result_string.data, result_string.length)
|
|
360
|
+
: rb_str_new_cstr("password change rejected");
|
|
361
|
+
krb5_free_data_contents(ptr->ctx, &result_string);
|
|
362
|
+
krb5_free_data_contents(ptr->ctx, &pw_result_string);
|
|
363
|
+
rb_raise(cKrb5Exception, "krb5_change_password: %s", StringValueCStr(v_msg));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
krb5_free_data_contents(ptr->ctx, &result_string);
|
|
367
|
+
krb5_free_data_contents(ptr->ctx, &pw_result_string);
|
|
334
368
|
|
|
335
369
|
return Qtrue;
|
|
336
370
|
}
|
|
@@ -356,6 +390,89 @@ static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
|
356
390
|
if(!ptr->ctx)
|
|
357
391
|
rb_raise(cKrb5Exception, "no context has been established");
|
|
358
392
|
|
|
393
|
+
// Free resources from a previous call to avoid leaks on repeated use.
|
|
394
|
+
if(ptr->princ){
|
|
395
|
+
krb5_free_principal(ptr->ctx, ptr->princ);
|
|
396
|
+
ptr->princ = NULL;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
krb5_free_cred_contents(ptr->ctx, &ptr->creds);
|
|
400
|
+
memset(&ptr->creds, 0, sizeof(ptr->creds));
|
|
401
|
+
|
|
402
|
+
rb_scan_args(argc, argv, "21", &v_user, &v_pass, &v_service);
|
|
403
|
+
|
|
404
|
+
Check_Type(v_user, T_STRING);
|
|
405
|
+
Check_Type(v_pass, T_STRING);
|
|
406
|
+
user = StringValueCStr(v_user);
|
|
407
|
+
pass = StringValueCStr(v_pass);
|
|
408
|
+
|
|
409
|
+
if(NIL_P(v_service)){
|
|
410
|
+
service = NULL;
|
|
411
|
+
}
|
|
412
|
+
else{
|
|
413
|
+
Check_Type(v_service, T_STRING);
|
|
414
|
+
service = StringValueCStr(v_service);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
kerror = krb5_parse_name(ptr->ctx, user, &ptr->princ);
|
|
418
|
+
|
|
419
|
+
if(kerror)
|
|
420
|
+
rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
|
|
421
|
+
|
|
422
|
+
kerror = krb5_get_init_creds_password(
|
|
423
|
+
ptr->ctx,
|
|
424
|
+
&ptr->creds,
|
|
425
|
+
ptr->princ,
|
|
426
|
+
pass,
|
|
427
|
+
0,
|
|
428
|
+
NULL,
|
|
429
|
+
0,
|
|
430
|
+
service,
|
|
431
|
+
NULL
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
if(kerror)
|
|
435
|
+
rb_raise(cKrb5Exception, "krb5_get_init_creds_password: %s", error_message(kerror));
|
|
436
|
+
|
|
437
|
+
return Qtrue;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/*
|
|
441
|
+
* call-seq:
|
|
442
|
+
* krb5.authenticate!(user, password, service = nil)
|
|
443
|
+
*
|
|
444
|
+
* Convenience method that: acquires initial credentials via password and
|
|
445
|
+
* immediately verifies those credentials using `verify_init_creds` with
|
|
446
|
+
* AP-REQ verification enabled (ap_req_nofail). This protects against
|
|
447
|
+
* KDC-forging/Zanarotti-style attacks by ensuring the ticket is verified
|
|
448
|
+
* against the KDC before it's treated as authenticated.
|
|
449
|
+
*
|
|
450
|
+
* Returns true on success and raises `Kerberos::Krb5::Exception` on error.
|
|
451
|
+
*/
|
|
452
|
+
static VALUE rkrb5_authenticate_bang(int argc, VALUE* argv, VALUE self){
|
|
453
|
+
RUBY_KRB5* ptr;
|
|
454
|
+
VALUE v_user, v_pass, v_service;
|
|
455
|
+
char* user;
|
|
456
|
+
char* pass;
|
|
457
|
+
char* service;
|
|
458
|
+
krb5_error_code kerror;
|
|
459
|
+
krb5_principal server_princ = NULL;
|
|
460
|
+
|
|
461
|
+
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
462
|
+
|
|
463
|
+
if(!ptr->ctx)
|
|
464
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
465
|
+
|
|
466
|
+
// Free resources from a previous call to avoid leaks on repeated use.
|
|
467
|
+
if(ptr->princ){
|
|
468
|
+
krb5_free_principal(ptr->ctx, ptr->princ);
|
|
469
|
+
ptr->princ = NULL;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
krb5_free_cred_contents(ptr->ctx, &ptr->creds);
|
|
473
|
+
memset(&ptr->creds, 0, sizeof(ptr->creds));
|
|
474
|
+
|
|
475
|
+
// Require user and password, optional service
|
|
359
476
|
rb_scan_args(argc, argv, "21", &v_user, &v_pass, &v_service);
|
|
360
477
|
|
|
361
478
|
Check_Type(v_user, T_STRING);
|
|
@@ -371,6 +488,7 @@ static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
|
371
488
|
service = StringValueCStr(v_service);
|
|
372
489
|
}
|
|
373
490
|
|
|
491
|
+
// Acquire initial credentials (same as get_init_creds_password)
|
|
374
492
|
kerror = krb5_parse_name(ptr->ctx, user, &ptr->princ);
|
|
375
493
|
|
|
376
494
|
if(kerror)
|
|
@@ -391,6 +509,44 @@ static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
|
391
509
|
if(kerror)
|
|
392
510
|
rb_raise(cKrb5Exception, "krb5_get_init_creds_password: %s", error_message(kerror));
|
|
393
511
|
|
|
512
|
+
/*
|
|
513
|
+
* Try strict verification first (AP-REQ nofail). If strict verification
|
|
514
|
+
* cannot be performed (e.g. missing keytab or other environment issue),
|
|
515
|
+
* fall back to the standard verification so authenticate! remains useful
|
|
516
|
+
* in minimal test environments.
|
|
517
|
+
*/
|
|
518
|
+
krb5_verify_init_creds_opt vicopt;
|
|
519
|
+
krb5_error_code kerror_strict = 0;
|
|
520
|
+
|
|
521
|
+
krb5_verify_init_creds_opt_init(&vicopt);
|
|
522
|
+
krb5_verify_init_creds_opt_set_ap_req_nofail(&vicopt, TRUE);
|
|
523
|
+
|
|
524
|
+
// If caller supplied a service principal string, use it for verification.
|
|
525
|
+
if(service){
|
|
526
|
+
kerror = krb5_parse_name(ptr->ctx, service, &server_princ);
|
|
527
|
+
|
|
528
|
+
if(kerror)
|
|
529
|
+
rb_raise(cKrb5Exception, "krb5_parse_name(service): %s", error_message(kerror));
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// First, attempt strict verification
|
|
533
|
+
kerror = krb5_verify_init_creds(ptr->ctx, &ptr->creds, server_princ, NULL, NULL, &vicopt);
|
|
534
|
+
|
|
535
|
+
if(kerror){
|
|
536
|
+
/* strict verification failed — try a best-effort standard verify */
|
|
537
|
+
kerror_strict = kerror;
|
|
538
|
+
kerror = krb5_verify_init_creds(ptr->ctx, &ptr->creds, server_princ, NULL, NULL, NULL);
|
|
539
|
+
if(kerror){
|
|
540
|
+
if(server_princ)
|
|
541
|
+
krb5_free_principal(ptr->ctx, server_princ);
|
|
542
|
+
/* raise the original strict-verification error to inform caller */
|
|
543
|
+
rb_raise(cKrb5Exception, "krb5_verify_init_creds: %s", error_message(kerror_strict));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if(server_princ)
|
|
548
|
+
krb5_free_principal(ptr->ctx, server_princ);
|
|
549
|
+
|
|
394
550
|
return Qtrue;
|
|
395
551
|
}
|
|
396
552
|
|
|
@@ -406,6 +562,11 @@ static VALUE rkrb5_close(VALUE self){
|
|
|
406
562
|
|
|
407
563
|
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
408
564
|
|
|
565
|
+
if(ptr->keytab){
|
|
566
|
+
krb5_kt_close(ptr->ctx, ptr->keytab);
|
|
567
|
+
ptr->keytab = NULL;
|
|
568
|
+
}
|
|
569
|
+
|
|
409
570
|
if(ptr->ctx)
|
|
410
571
|
krb5_free_cred_contents(ptr->ctx, &ptr->creds);
|
|
411
572
|
|
|
@@ -441,6 +602,12 @@ static VALUE rkrb5_get_default_principal(VALUE self){
|
|
|
441
602
|
if(!ptr->ctx)
|
|
442
603
|
rb_raise(cKrb5Exception, "no context has been established");
|
|
443
604
|
|
|
605
|
+
// Free previous principal to avoid leaks on repeated calls.
|
|
606
|
+
if(ptr->princ){
|
|
607
|
+
krb5_free_principal(ptr->ctx, ptr->princ);
|
|
608
|
+
ptr->princ = NULL;
|
|
609
|
+
}
|
|
610
|
+
|
|
444
611
|
// Get the default credentials cache
|
|
445
612
|
kerror = krb5_cc_default(ptr->ctx, &ccache);
|
|
446
613
|
|
|
@@ -459,9 +626,12 @@ static VALUE rkrb5_get_default_principal(VALUE self){
|
|
|
459
626
|
kerror = krb5_unparse_name(ptr->ctx, ptr->princ, &princ_name);
|
|
460
627
|
|
|
461
628
|
if(kerror)
|
|
462
|
-
rb_raise(cKrb5Exception, "
|
|
629
|
+
rb_raise(cKrb5Exception, "krb5_unparse_name: %s", error_message(kerror));
|
|
630
|
+
|
|
631
|
+
VALUE v_name = rb_str_new2(princ_name);
|
|
632
|
+
krb5_free_unparsed_name(ptr->ctx, princ_name);
|
|
463
633
|
|
|
464
|
-
return
|
|
634
|
+
return v_name;
|
|
465
635
|
}
|
|
466
636
|
|
|
467
637
|
/*
|
|
@@ -513,11 +683,95 @@ static VALUE rkrb5_get_permitted_enctypes(VALUE self){
|
|
|
513
683
|
}
|
|
514
684
|
rb_hash_aset(v_enctypes, INT2FIX(ktypes[i]), rb_str_new2(encoding));
|
|
515
685
|
}
|
|
686
|
+
|
|
687
|
+
krb5_free_enctypes(ptr->ctx, ktypes);
|
|
516
688
|
}
|
|
517
689
|
|
|
518
690
|
return v_enctypes;
|
|
519
691
|
}
|
|
520
692
|
|
|
693
|
+
/*
|
|
694
|
+
* call-seq:
|
|
695
|
+
* krb5.verify_init_creds(server = nil, keytab = nil, ccache = nil)
|
|
696
|
+
*
|
|
697
|
+
* Verifies the initial credentials currently stored in the internal
|
|
698
|
+
* credentials structure. Optionally a server principal string, a
|
|
699
|
+
* `Kerberos::Krb5::Keytab` and/or a `Kerberos::Krb5::CredentialsCache` may
|
|
700
|
+
* be supplied to influence verification. Returns true on success and raises
|
|
701
|
+
* `Kerberos::Krb5::Exception` on error.
|
|
702
|
+
*/
|
|
703
|
+
static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
704
|
+
RUBY_KRB5* ptr;
|
|
705
|
+
VALUE v_server, v_keytab, v_ccache;
|
|
706
|
+
krb5_error_code kerror;
|
|
707
|
+
krb5_principal server_princ = NULL;
|
|
708
|
+
RUBY_KRB5_KEYTAB* ktptr = NULL;
|
|
709
|
+
RUBY_KRB5_CCACHE* ccptr = NULL;
|
|
710
|
+
krb5_keytab keytab = NULL;
|
|
711
|
+
krb5_ccache *ccache_ptr = NULL;
|
|
712
|
+
|
|
713
|
+
rb_scan_args(argc, argv, "03", &v_server, &v_keytab, &v_ccache);
|
|
714
|
+
|
|
715
|
+
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
716
|
+
|
|
717
|
+
if(!ptr->ctx)
|
|
718
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
719
|
+
|
|
720
|
+
// Validate argument types first so callers get TypeError before other errors
|
|
721
|
+
if(!NIL_P(v_server)){
|
|
722
|
+
Check_Type(v_server, T_STRING);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if(!NIL_P(v_keytab)){
|
|
726
|
+
// Will raise TypeError if object isn't the expected Keytab typed data
|
|
727
|
+
TypedData_Get_Struct(v_keytab, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ktptr);
|
|
728
|
+
keytab = ktptr->keytab;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if(!NIL_P(v_ccache)){
|
|
732
|
+
// Will raise TypeError if object isn't the expected CCache typed data
|
|
733
|
+
TypedData_Get_Struct(v_ccache, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, ccptr);
|
|
734
|
+
ccache_ptr = &ccptr->ccache;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Ensure we have credentials to verify (check after validating args)
|
|
738
|
+
if(ptr->creds.client == NULL)
|
|
739
|
+
rb_raise(cKrb5Exception, "no credentials have been acquired");
|
|
740
|
+
|
|
741
|
+
// Optional server principal (parse after context & arg validation)
|
|
742
|
+
if(!NIL_P(v_server)){
|
|
743
|
+
kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_server), &server_princ);
|
|
744
|
+
if(kerror)
|
|
745
|
+
rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
kerror = krb5_verify_init_creds(ptr->ctx, &ptr->creds, server_princ, keytab, ccache_ptr, NULL);
|
|
749
|
+
|
|
750
|
+
if(server_princ)
|
|
751
|
+
krb5_free_principal(ptr->ctx, server_princ);
|
|
752
|
+
|
|
753
|
+
if(kerror)
|
|
754
|
+
rb_raise(cKrb5Exception, "krb5_verify_init_creds: %s", error_message(kerror));
|
|
755
|
+
|
|
756
|
+
/* If the caller supplied a CredentialsCache object, store the verified
|
|
757
|
+
credentials there so Ruby-level callers can inspect the cache. */
|
|
758
|
+
if(ccache_ptr && *ccache_ptr){
|
|
759
|
+
krb5_error_code k2;
|
|
760
|
+
|
|
761
|
+
k2 = krb5_cc_initialize(ptr->ctx, *ccache_ptr, ptr->creds.client);
|
|
762
|
+
|
|
763
|
+
if(k2)
|
|
764
|
+
rb_raise(cKrb5Exception, "krb5_cc_initialize: %s", error_message(k2));
|
|
765
|
+
|
|
766
|
+
k2 = krb5_cc_store_cred(ptr->ctx, *ccache_ptr, &ptr->creds);
|
|
767
|
+
|
|
768
|
+
if(k2)
|
|
769
|
+
rb_raise(cKrb5Exception, "krb5_cc_store_cred: %s", error_message(k2));
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return Qtrue;
|
|
773
|
+
}
|
|
774
|
+
|
|
521
775
|
void Init_rkerberos(void){
|
|
522
776
|
mKerberos = rb_define_module("Kerberos");
|
|
523
777
|
cKrb5 = rb_define_class_under(mKerberos, "Krb5", rb_cObject);
|
|
@@ -530,6 +784,7 @@ void Init_rkerberos(void){
|
|
|
530
784
|
rb_define_method(cKrb5, "initialize", rkrb5_initialize, 0);
|
|
531
785
|
|
|
532
786
|
// Krb5 Methods
|
|
787
|
+
rb_define_method(cKrb5, "authenticate!", rkrb5_authenticate_bang, -1);
|
|
533
788
|
rb_define_method(cKrb5, "change_password", rkrb5_change_password, 2);
|
|
534
789
|
rb_define_method(cKrb5, "close", rkrb5_close, 0);
|
|
535
790
|
rb_define_method(cKrb5, "get_default_realm", rkrb5_get_default_realm, 0);
|
|
@@ -538,13 +793,14 @@ void Init_rkerberos(void){
|
|
|
538
793
|
rb_define_method(cKrb5, "get_default_principal", rkrb5_get_default_principal, 0);
|
|
539
794
|
rb_define_method(cKrb5, "get_permitted_enctypes", rkrb5_get_permitted_enctypes, 0);
|
|
540
795
|
rb_define_method(cKrb5, "set_default_realm", rkrb5_set_default_realm, -1);
|
|
796
|
+
rb_define_method(cKrb5, "verify_init_creds", rkrb5_verify_init_creds, -1);
|
|
541
797
|
|
|
542
798
|
// Aliases
|
|
543
799
|
rb_define_alias(cKrb5, "default_realm", "get_default_realm");
|
|
544
800
|
rb_define_alias(cKrb5, "default_principal", "get_default_principal");
|
|
545
801
|
|
|
546
|
-
/* 0.2.
|
|
547
|
-
rb_define_const(cKrb5, "VERSION", rb_str_new2("0.2.
|
|
802
|
+
/* 0.2.1: The version of the custom rkerberos library */
|
|
803
|
+
rb_define_const(cKrb5, "VERSION", rb_str_new2("0.2.2"));
|
|
548
804
|
|
|
549
805
|
// Encoding type constants
|
|
550
806
|
|
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.2'
|
|
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/config_spec.rb
CHANGED
|
@@ -104,8 +104,8 @@ RSpec.describe Kerberos::Kadm5::Config do
|
|
|
104
104
|
it 'responds to max_life' do
|
|
105
105
|
expect(config).to respond_to(:max_life)
|
|
106
106
|
end
|
|
107
|
-
it 'returns an Integer
|
|
108
|
-
expect(
|
|
107
|
+
it 'returns an Integer' do
|
|
108
|
+
expect(config.max_life).to be_a(Integer)
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
111
|
|
|
@@ -113,8 +113,8 @@ RSpec.describe Kerberos::Kadm5::Config do
|
|
|
113
113
|
it 'responds to max_rlife' do
|
|
114
114
|
expect(config).to respond_to(:max_rlife)
|
|
115
115
|
end
|
|
116
|
-
it 'returns an Integer
|
|
117
|
-
expect(
|
|
116
|
+
it 'returns an Integer' do
|
|
117
|
+
expect(config.max_rlife).to be_a(Integer)
|
|
118
118
|
end
|
|
119
119
|
end
|
|
120
120
|
|
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
|
|
@@ -83,6 +83,29 @@ RSpec.describe Kerberos::Krb5::CredentialsCache do
|
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
+
describe '#cache_name and #cache_type' do
|
|
87
|
+
it 'returns the ccache name and type' do
|
|
88
|
+
c = described_class.new(princ)
|
|
89
|
+
expect(c).to respond_to(:cache_name)
|
|
90
|
+
expect(c).to respond_to(:cache_type)
|
|
91
|
+
|
|
92
|
+
expect(c.cache_name).to be_a(String)
|
|
93
|
+
expect(c.cache_type).to be_a(String)
|
|
94
|
+
|
|
95
|
+
# cache_name returns the residual portion of the cache name; default_name
|
|
96
|
+
# may include the type prefix (e.g. "FILE:"). ensure the suffix matches.
|
|
97
|
+
expect(c.cache_name).to eq(c.default_name.split(':').last)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe '#principal' do
|
|
102
|
+
it 'is an alias for primary_principal' do
|
|
103
|
+
c = described_class.new(princ)
|
|
104
|
+
expect(c).to respond_to(:principal)
|
|
105
|
+
expect(c.principal).to eq(c.primary_principal)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
86
109
|
describe '#primary_principal' do
|
|
87
110
|
it 'responds to primary_principal' do
|
|
88
111
|
c = described_class.new(princ)
|
|
@@ -126,4 +149,34 @@ RSpec.describe Kerberos::Krb5::CredentialsCache do
|
|
|
126
149
|
expect { c.destroy(true) }.to raise_error(ArgumentError)
|
|
127
150
|
end
|
|
128
151
|
end
|
|
152
|
+
|
|
153
|
+
describe '#dup' do
|
|
154
|
+
it 'returns a new cache object with the same properties' do
|
|
155
|
+
c = described_class.new(princ)
|
|
156
|
+
c2 = c.dup
|
|
157
|
+
expect(c2).to be_a(described_class)
|
|
158
|
+
expect(c2.default_name).to eq(c.default_name)
|
|
159
|
+
expect(c2.primary_principal).to eq(c.primary_principal)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'closing original does not affect duplicate' do
|
|
163
|
+
c = described_class.new(princ)
|
|
164
|
+
c2 = c.dup
|
|
165
|
+
c.close
|
|
166
|
+
expect { c2.default_name }.not_to raise_error
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
it 'closing duplicate does not affect original' do
|
|
170
|
+
c = described_class.new(princ)
|
|
171
|
+
c2 = c.dup
|
|
172
|
+
c2.close
|
|
173
|
+
expect { c.default_name }.not_to raise_error
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it 'raises when duping closed cache' do
|
|
177
|
+
c = described_class.new(princ)
|
|
178
|
+
c.close
|
|
179
|
+
expect { c.dup }.to raise_error(Kerberos::Krb5::Exception)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
129
182
|
end
|
data/spec/kadm5_spec.rb
CHANGED
|
@@ -58,5 +58,44 @@ RSpec.describe Kerberos::Kadm5 do
|
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
#
|
|
61
|
+
describe '#get_privileges' do
|
|
62
|
+
before(:each) do
|
|
63
|
+
@kadm5 = described_class.new(principal: user, password: pass)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
after(:each) do
|
|
67
|
+
@kadm5.close
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'returns an integer bitmask by default' do
|
|
71
|
+
result = @kadm5.get_privileges
|
|
72
|
+
expect(result).to be_a(Integer)
|
|
73
|
+
expect(result).not_to eq(0)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'returns an array of strings when passed a truthy argument' do
|
|
77
|
+
result = @kadm5.get_privileges(true)
|
|
78
|
+
expect(result).to be_a(Array)
|
|
79
|
+
expect(result).not_to be_empty
|
|
80
|
+
expect(result).to all(be_a(String))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'only contains valid privilege names' do
|
|
84
|
+
result = @kadm5.get_privileges(true)
|
|
85
|
+
valid = %w[GET ADD MODIFY DELETE]
|
|
86
|
+
result.each do |priv|
|
|
87
|
+
expect(valid).to include(priv)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'does not contain UNKNOWN entries' do
|
|
92
|
+
result = @kadm5.get_privileges(true)
|
|
93
|
+
expect(result).not_to include('UNKNOWN')
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'includes GET for an admin principal' do
|
|
97
|
+
result = @kadm5.get_privileges(true)
|
|
98
|
+
expect(result).to include('GET')
|
|
99
|
+
end
|
|
100
|
+
end
|
|
62
101
|
end
|