rkerberos 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +15 -0
- data/EXAMPLES.md +1 -1
- data/README.md +13 -2
- data/ext/rkerberos/ccache.c +9 -11
- data/ext/rkerberos/config.c +22 -16
- data/ext/rkerberos/context.c +64 -9
- data/ext/rkerberos/kadm5.c +79 -56
- data/ext/rkerberos/keytab.c +35 -30
- data/ext/rkerberos/principal.c +42 -12
- data/ext/rkerberos/rkerberos.c +55 -39
- data/ext/rkerberos/rkerberos.h +6 -0
- data/rkerberos.gemspec +1 -1
- data/spec/config_spec.rb +8 -0
- data/spec/context_spec.rb +45 -0
- data/spec/credentials_cache_spec.rb +3 -0
- data/spec/kadm5_spec.rb +42 -1
- data/spec/krb5_keytab_spec.rb +52 -1
- data/spec/krb5_spec.rb +57 -3
- data/spec/principal_spec.rb +26 -0
- metadata +1 -1
data/ext/rkerberos/keytab.c
CHANGED
|
@@ -20,6 +20,8 @@ void rkrb5_keytab_typed_free(void *ptr) {
|
|
|
20
20
|
krb5_free_cred_contents(kt->ctx, &kt->creds);
|
|
21
21
|
if (kt->ctx && kt->rb_context == Qnil)
|
|
22
22
|
krb5_free_context(kt->ctx);
|
|
23
|
+
else if (kt->rb_context != Qnil)
|
|
24
|
+
rkrb5_context_release(kt->rb_context);
|
|
23
25
|
free(kt);
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -54,7 +56,7 @@ typedef struct {
|
|
|
54
56
|
static VALUE rkrb5_keytab_each_body(VALUE arg){
|
|
55
57
|
keytab_each_arg* ea = (keytab_each_arg*)arg;
|
|
56
58
|
krb5_keytab_entry entry;
|
|
57
|
-
krb5_error_code kerror;
|
|
59
|
+
krb5_error_code kerror, iteration_error;
|
|
58
60
|
char* principal;
|
|
59
61
|
VALUE v_kt_entry;
|
|
60
62
|
|
|
@@ -79,9 +81,13 @@ static VALUE rkrb5_keytab_each_body(VALUE arg){
|
|
|
79
81
|
rb_yield(v_kt_entry);
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
iteration_error = kerror;
|
|
83
85
|
|
|
84
86
|
kerror = krb5_kt_end_seq_get(ea->ctx, ea->keytab, &ea->cursor);
|
|
87
|
+
ea->cursor_active = 0;
|
|
88
|
+
|
|
89
|
+
if(iteration_error != KRB5_KT_END)
|
|
90
|
+
rb_raise(cKrb5Exception, "krb5_kt_next_entry: %s", error_message(iteration_error));
|
|
85
91
|
|
|
86
92
|
if(kerror)
|
|
87
93
|
rb_raise(cKrb5Exception, "krb5_kt_end_seq_get: %s", error_message(kerror));
|
|
@@ -114,6 +120,9 @@ static VALUE rkrb5_keytab_each(VALUE self){
|
|
|
114
120
|
|
|
115
121
|
TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);
|
|
116
122
|
|
|
123
|
+
if(!ptr->ctx || !ptr->keytab)
|
|
124
|
+
rb_raise(cKrb5Exception, "keytab is closed");
|
|
125
|
+
|
|
117
126
|
ea.ctx = ptr->ctx;
|
|
118
127
|
ea.keytab = ptr->keytab;
|
|
119
128
|
|
|
@@ -144,6 +153,9 @@ static VALUE rkrb5_keytab_default_name(VALUE self){
|
|
|
144
153
|
|
|
145
154
|
TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);
|
|
146
155
|
|
|
156
|
+
if(!ptr->ctx)
|
|
157
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
158
|
+
|
|
147
159
|
kerror = krb5_kt_default_name(ptr->ctx, default_name, MAX_KEYTAB_NAME_LEN);
|
|
148
160
|
|
|
149
161
|
if(kerror)
|
|
@@ -177,6 +189,8 @@ static VALUE rkrb5_keytab_close(VALUE self){
|
|
|
177
189
|
|
|
178
190
|
if(ptr->ctx && ptr->rb_context == Qnil)
|
|
179
191
|
krb5_free_context(ptr->ctx);
|
|
192
|
+
else if(ptr->rb_context != Qnil)
|
|
193
|
+
rkrb5_context_release(ptr->rb_context);
|
|
180
194
|
|
|
181
195
|
ptr->ctx = NULL;
|
|
182
196
|
ptr->rb_context = Qnil;
|
|
@@ -233,13 +247,14 @@ static VALUE rkrb5_keytab_add_entry(int argc, VALUE* argv, VALUE self){
|
|
|
233
247
|
|
|
234
248
|
memset(&entry, 0, sizeof(entry));
|
|
235
249
|
|
|
250
|
+
entry.vno = NIL_P(v_vno) ? 1 : NUM2INT(v_vno);
|
|
251
|
+
entry.key.enctype = NIL_P(v_enctype) ? ENCTYPE_AES256_CTS_HMAC_SHA1_96 : NUM2INT(v_enctype);
|
|
252
|
+
|
|
236
253
|
kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_principal), &entry.principal);
|
|
237
254
|
|
|
238
255
|
if(kerror)
|
|
239
256
|
rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
|
|
240
257
|
|
|
241
|
-
entry.vno = NIL_P(v_vno) ? 1 : NUM2INT(v_vno);
|
|
242
|
-
entry.key.enctype = NIL_P(v_enctype) ? ENCTYPE_AES256_CTS_HMAC_SHA1_96 : NUM2INT(v_enctype);
|
|
243
258
|
entry.timestamp = time(NULL);
|
|
244
259
|
|
|
245
260
|
// Derive the salt from the principal
|
|
@@ -323,14 +338,14 @@ static VALUE rkrb5_keytab_remove_entry(int argc, VALUE* argv, VALUE self){
|
|
|
323
338
|
|
|
324
339
|
Check_Type(v_principal, T_STRING);
|
|
325
340
|
|
|
341
|
+
match_vno = NIL_P(v_vno) ? 0 : NUM2INT(v_vno);
|
|
342
|
+
match_enctype = NIL_P(v_enctype) ? 0 : NUM2INT(v_enctype);
|
|
343
|
+
|
|
326
344
|
kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_principal), &match_princ);
|
|
327
345
|
|
|
328
346
|
if(kerror)
|
|
329
347
|
rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
|
|
330
348
|
|
|
331
|
-
match_vno = NIL_P(v_vno) ? 0 : NUM2INT(v_vno);
|
|
332
|
-
match_enctype = NIL_P(v_enctype) ? 0 : NUM2INT(v_enctype);
|
|
333
|
-
|
|
334
349
|
// Retrieve the full entry via krb5_kt_get_entry and then pass the
|
|
335
350
|
// complete struct to krb5_kt_remove_entry so all fields match exactly.
|
|
336
351
|
// Loop to remove every matching entry when vno/enctype are wildcards.
|
|
@@ -383,26 +398,22 @@ static VALUE rkrb5_keytab_get_entry(int argc, VALUE* argv, VALUE self){
|
|
|
383
398
|
|
|
384
399
|
TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);
|
|
385
400
|
|
|
401
|
+
if(!ptr->ctx || !ptr->keytab)
|
|
402
|
+
rb_raise(cKrb5Exception, "keytab is closed");
|
|
403
|
+
|
|
386
404
|
rb_scan_args(argc, argv, "12", &v_principal, &v_vno, &v_enctype);
|
|
387
405
|
|
|
388
406
|
Check_Type(v_principal, T_STRING);
|
|
389
407
|
name = StringValueCStr(v_principal);
|
|
390
408
|
|
|
409
|
+
vno = NIL_P(v_vno) ? 0 : NUM2INT(v_vno);
|
|
410
|
+
enctype = NIL_P(v_enctype) ? 0 : NUM2INT(v_enctype);
|
|
411
|
+
|
|
391
412
|
kerror = krb5_parse_name(ptr->ctx, name, &principal);
|
|
392
413
|
|
|
393
414
|
if(kerror)
|
|
394
415
|
rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
|
|
395
416
|
|
|
396
|
-
if(NIL_P(v_vno))
|
|
397
|
-
vno = 0;
|
|
398
|
-
else
|
|
399
|
-
vno = NUM2INT(v_vno);
|
|
400
|
-
|
|
401
|
-
if(NIL_P(v_enctype))
|
|
402
|
-
enctype = 0;
|
|
403
|
-
else
|
|
404
|
-
enctype = NUM2INT(v_enctype);
|
|
405
|
-
|
|
406
417
|
kerror = krb5_kt_get_entry(
|
|
407
418
|
ptr->ctx,
|
|
408
419
|
ptr->keytab,
|
|
@@ -566,17 +577,7 @@ static VALUE rkrb5_keytab_initialize(int argc, VALUE* argv, VALUE self){
|
|
|
566
577
|
|
|
567
578
|
// Initialize or borrow the context
|
|
568
579
|
if(!NIL_P(v_context)){
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
if(!rb_obj_is_kind_of(v_context, cKrb5Context))
|
|
572
|
-
rb_raise(rb_eTypeError, "context must be a Kerberos::Krb5::Context object");
|
|
573
|
-
|
|
574
|
-
TypedData_Get_Struct(v_context, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ctx_ptr);
|
|
575
|
-
|
|
576
|
-
if(!ctx_ptr->ctx)
|
|
577
|
-
rb_raise(cKrb5Exception, "context is closed");
|
|
578
|
-
|
|
579
|
-
ptr->ctx = ctx_ptr->ctx;
|
|
580
|
+
ptr->ctx = rkrb5_context_borrow(v_context);
|
|
580
581
|
ptr->rb_context = v_context;
|
|
581
582
|
}
|
|
582
583
|
else{
|
|
@@ -629,7 +630,7 @@ typedef struct {
|
|
|
629
630
|
static VALUE rkrb5_s_keytab_foreach_body(VALUE arg){
|
|
630
631
|
keytab_foreach_arg* fa = (keytab_foreach_arg*)arg;
|
|
631
632
|
krb5_keytab_entry entry;
|
|
632
|
-
krb5_error_code kerror;
|
|
633
|
+
krb5_error_code kerror, iteration_error;
|
|
633
634
|
char* principal;
|
|
634
635
|
VALUE v_kt_entry;
|
|
635
636
|
|
|
@@ -654,9 +655,13 @@ static VALUE rkrb5_s_keytab_foreach_body(VALUE arg){
|
|
|
654
655
|
rb_yield(v_kt_entry);
|
|
655
656
|
}
|
|
656
657
|
|
|
657
|
-
|
|
658
|
+
iteration_error = kerror;
|
|
658
659
|
|
|
659
660
|
kerror = krb5_kt_end_seq_get(fa->ctx, fa->keytab, &fa->cursor);
|
|
661
|
+
fa->cursor_active = 0;
|
|
662
|
+
|
|
663
|
+
if(iteration_error != KRB5_KT_END)
|
|
664
|
+
rb_raise(cKrb5Exception, "krb5_kt_next_entry: %s", error_message(iteration_error));
|
|
660
665
|
|
|
661
666
|
if(kerror)
|
|
662
667
|
rb_raise(cKrb5Exception, "krb5_kt_end_seq_get: %s", error_message(kerror));
|
data/ext/rkerberos/principal.c
CHANGED
|
@@ -18,6 +18,8 @@ static void rkrb5_princ_typed_free(void *ptr) {
|
|
|
18
18
|
krb5_free_principal(p->ctx, p->principal);
|
|
19
19
|
if (p->ctx && p->rb_context == Qnil)
|
|
20
20
|
krb5_free_context(p->ctx);
|
|
21
|
+
else if (p->rb_context != Qnil)
|
|
22
|
+
rkrb5_context_release(p->rb_context);
|
|
21
23
|
free(p);
|
|
22
24
|
}
|
|
23
25
|
|
|
@@ -86,17 +88,7 @@ static VALUE rkrb5_princ_initialize(int argc, VALUE* argv, VALUE self){
|
|
|
86
88
|
|
|
87
89
|
// Initialize or borrow the context
|
|
88
90
|
if(!NIL_P(v_context)){
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if(!rb_obj_is_kind_of(v_context, cKrb5Context))
|
|
92
|
-
rb_raise(rb_eTypeError, "context must be a Kerberos::Krb5::Context object");
|
|
93
|
-
|
|
94
|
-
TypedData_Get_Struct(v_context, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ctx_ptr);
|
|
95
|
-
|
|
96
|
-
if(!ctx_ptr->ctx)
|
|
97
|
-
rb_raise(cKrb5Exception, "context is closed");
|
|
98
|
-
|
|
99
|
-
ptr->ctx = ctx_ptr->ctx;
|
|
91
|
+
ptr->ctx = rkrb5_context_borrow(v_context);
|
|
100
92
|
ptr->rb_context = v_context;
|
|
101
93
|
}
|
|
102
94
|
else{
|
|
@@ -145,6 +137,36 @@ static VALUE rkrb5_princ_initialize(int argc, VALUE* argv, VALUE self){
|
|
|
145
137
|
return self;
|
|
146
138
|
}
|
|
147
139
|
|
|
140
|
+
/*
|
|
141
|
+
* call-seq:
|
|
142
|
+
* principal.close
|
|
143
|
+
*
|
|
144
|
+
* Releases the principal and its Kerberos context lease. Once closed, the
|
|
145
|
+
* principal cannot be reused.
|
|
146
|
+
*/
|
|
147
|
+
static VALUE rkrb5_princ_close(VALUE self){
|
|
148
|
+
RUBY_KRB5_PRINC* ptr;
|
|
149
|
+
|
|
150
|
+
TypedData_Get_Struct(self, RUBY_KRB5_PRINC, &rkrb5_princ_data_type, ptr);
|
|
151
|
+
|
|
152
|
+
if(!ptr->ctx)
|
|
153
|
+
return self;
|
|
154
|
+
|
|
155
|
+
if(ptr->principal)
|
|
156
|
+
krb5_free_principal(ptr->ctx, ptr->principal);
|
|
157
|
+
|
|
158
|
+
if(ptr->rb_context == Qnil)
|
|
159
|
+
krb5_free_context(ptr->ctx);
|
|
160
|
+
else
|
|
161
|
+
rkrb5_context_release(ptr->rb_context);
|
|
162
|
+
|
|
163
|
+
ptr->ctx = NULL;
|
|
164
|
+
ptr->principal = NULL;
|
|
165
|
+
ptr->rb_context = Qnil;
|
|
166
|
+
|
|
167
|
+
return self;
|
|
168
|
+
}
|
|
169
|
+
|
|
148
170
|
/*
|
|
149
171
|
* call-seq:
|
|
150
172
|
* principal.realm
|
|
@@ -153,13 +175,16 @@ static VALUE rkrb5_princ_initialize(int argc, VALUE* argv, VALUE self){
|
|
|
153
175
|
*/
|
|
154
176
|
static VALUE rkrb5_princ_get_realm(VALUE self){
|
|
155
177
|
RUBY_KRB5_PRINC* ptr;
|
|
178
|
+
krb5_data* realm;
|
|
156
179
|
|
|
157
180
|
TypedData_Get_Struct(self, RUBY_KRB5_PRINC, &rkrb5_princ_data_type, ptr);
|
|
158
181
|
|
|
159
182
|
if(!ptr->principal)
|
|
160
183
|
rb_raise(cKrb5Exception, "no principal has been established");
|
|
161
184
|
|
|
162
|
-
|
|
185
|
+
realm = krb5_princ_realm(ptr->ctx, ptr->principal);
|
|
186
|
+
|
|
187
|
+
return rb_str_new(realm->data, realm->length);
|
|
163
188
|
}
|
|
164
189
|
|
|
165
190
|
/*
|
|
@@ -195,6 +220,10 @@ static VALUE rkrb5_princ_equal(VALUE self, VALUE v_other){
|
|
|
195
220
|
VALUE v_bool = Qfalse;
|
|
196
221
|
|
|
197
222
|
TypedData_Get_Struct(self, RUBY_KRB5_PRINC, &rkrb5_princ_data_type, ptr1);
|
|
223
|
+
|
|
224
|
+
if(!rb_obj_is_kind_of(v_other, cKrb5Principal))
|
|
225
|
+
return Qfalse;
|
|
226
|
+
|
|
198
227
|
TypedData_Get_Struct(v_other, RUBY_KRB5_PRINC, &rkrb5_princ_data_type, ptr2);
|
|
199
228
|
|
|
200
229
|
if(!ptr1->principal || !ptr2->principal)
|
|
@@ -349,6 +378,7 @@ void Init_principal(void){
|
|
|
349
378
|
rb_define_method(cKrb5Principal, "==", rkrb5_princ_equal, 1);
|
|
350
379
|
rb_define_method(cKrb5Principal, "principal_type", rkrb5_princ_get_type, 0);
|
|
351
380
|
rb_define_method(cKrb5Principal, "components", rkrb5_princ_components, 0);
|
|
381
|
+
rb_define_method(cKrb5Principal, "close", rkrb5_princ_close, 0);
|
|
352
382
|
|
|
353
383
|
// Attributes
|
|
354
384
|
|
data/ext/rkerberos/rkerberos.c
CHANGED
|
@@ -43,6 +43,8 @@ static void rkrb5_typed_free(void *ptr) {
|
|
|
43
43
|
krb5_free_principal(r->ctx, r->princ);
|
|
44
44
|
if (r->ctx && r->rb_context == Qnil)
|
|
45
45
|
krb5_free_context(r->ctx);
|
|
46
|
+
else if (r->rb_context != Qnil)
|
|
47
|
+
rkrb5_context_release(r->rb_context);
|
|
46
48
|
free(r);
|
|
47
49
|
}
|
|
48
50
|
|
|
@@ -74,8 +76,8 @@ static VALUE rkrb5_allocate(VALUE klass){
|
|
|
74
76
|
* If a +context+ keyword argument is provided, it must be a
|
|
75
77
|
* Kerberos::Krb5::Context object. The context will be borrowed rather than
|
|
76
78
|
* creating a new one internally via krb5_init_context. The caller is
|
|
77
|
-
*
|
|
78
|
-
* this
|
|
79
|
+
* retained for the lifetime of this Krb5 instance. The Context cannot be
|
|
80
|
+
* closed normally until this object has been closed.
|
|
79
81
|
*
|
|
80
82
|
* A block form is also supported. If a block is given, the object is
|
|
81
83
|
* yielded to the block and automatically closed when the block returns.
|
|
@@ -98,17 +100,7 @@ static VALUE rkrb5_initialize(int argc, VALUE* argv, VALUE self){
|
|
|
98
100
|
v_context = kw_vals[0] == Qundef ? Qnil : kw_vals[0];
|
|
99
101
|
|
|
100
102
|
if(!NIL_P(v_context)){
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if(!rb_obj_is_kind_of(v_context, cKrb5Context))
|
|
104
|
-
rb_raise(rb_eTypeError, "context must be a Kerberos::Krb5::Context object");
|
|
105
|
-
|
|
106
|
-
TypedData_Get_Struct(v_context, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ctx_ptr);
|
|
107
|
-
|
|
108
|
-
if(!ctx_ptr->ctx)
|
|
109
|
-
rb_raise(cKrb5Exception, "context is closed");
|
|
110
|
-
|
|
111
|
-
ptr->ctx = ctx_ptr->ctx;
|
|
103
|
+
ptr->ctx = rkrb5_context_borrow(v_context);
|
|
112
104
|
ptr->rb_context = v_context;
|
|
113
105
|
}
|
|
114
106
|
else{
|
|
@@ -139,6 +131,9 @@ static VALUE rkrb5_get_default_realm(VALUE self){
|
|
|
139
131
|
|
|
140
132
|
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
141
133
|
|
|
134
|
+
if(!ptr->ctx)
|
|
135
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
136
|
+
|
|
142
137
|
kerror = krb5_get_default_realm(ptr->ctx, &realm);
|
|
143
138
|
|
|
144
139
|
if(kerror)
|
|
@@ -165,6 +160,9 @@ static VALUE rkrb5_set_default_realm(int argc, VALUE* argv, VALUE self){
|
|
|
165
160
|
|
|
166
161
|
TypedData_Get_Struct(self, RUBY_KRB5, &rkrb5_data_type, ptr);
|
|
167
162
|
|
|
163
|
+
if(!ptr->ctx)
|
|
164
|
+
rb_raise(cKrb5Exception, "no context has been established");
|
|
165
|
+
|
|
168
166
|
rb_scan_args(argc, argv, "01", &v_realm);
|
|
169
167
|
|
|
170
168
|
if(NIL_P(v_realm)){
|
|
@@ -317,6 +315,11 @@ static VALUE rkrb5_get_init_creds_keytab(int argc, VALUE* argv, VALUE self){
|
|
|
317
315
|
RUBY_KRB5_CCACHE* ccptr;
|
|
318
316
|
TypedData_Get_Struct(v_ccache, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, ccptr);
|
|
319
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
|
+
|
|
320
323
|
kerror = krb5_get_init_creds_opt_set_out_ccache(ptr->ctx, opt, ccptr->ccache);
|
|
321
324
|
if(kerror) {
|
|
322
325
|
krb5_get_init_creds_opt_free(ptr->ctx, opt);
|
|
@@ -384,6 +387,9 @@ static VALUE rkrb5_change_password(VALUE self, VALUE v_old, VALUE v_new){
|
|
|
384
387
|
if(!ptr->princ)
|
|
385
388
|
rb_raise(cKrb5Exception, "no principal has been established");
|
|
386
389
|
|
|
390
|
+
memset(&result_string, 0, sizeof(result_string));
|
|
391
|
+
memset(&pw_result_string, 0, sizeof(pw_result_string));
|
|
392
|
+
|
|
387
393
|
krb5_free_cred_contents(ptr->ctx, &ptr->creds);
|
|
388
394
|
memset(&ptr->creds, 0, sizeof(ptr->creds));
|
|
389
395
|
|
|
@@ -505,6 +511,9 @@ static VALUE rkrb5_get_init_creds_passwd(int argc, VALUE* argv, VALUE self){
|
|
|
505
511
|
RUBY_KRB5_CCACHE* ccptr;
|
|
506
512
|
TypedData_Get_Struct(v_ccache, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, ccptr);
|
|
507
513
|
|
|
514
|
+
if(!ccptr->ctx || !ccptr->ccache)
|
|
515
|
+
rb_raise(cKrb5Exception, "credentials cache is closed or destroyed");
|
|
516
|
+
|
|
508
517
|
kerror = krb5_get_init_creds_opt_alloc(ptr->ctx, &opt);
|
|
509
518
|
if(kerror)
|
|
510
519
|
rb_raise(cKrb5Exception, "krb5_get_init_creds_opt_alloc: %s", error_message(kerror));
|
|
@@ -618,14 +627,7 @@ static VALUE rkrb5_authenticate_bang(int argc, VALUE* argv, VALUE self){
|
|
|
618
627
|
if(kerror)
|
|
619
628
|
rb_raise(cKrb5Exception, "krb5_get_init_creds_password: %s", error_message(kerror));
|
|
620
629
|
|
|
621
|
-
/*
|
|
622
|
-
* Try strict verification first (AP-REQ nofail). If strict verification
|
|
623
|
-
* cannot be performed (e.g. missing keytab or other environment issue),
|
|
624
|
-
* fall back to the standard verification so authenticate! remains useful
|
|
625
|
-
* in minimal test environments.
|
|
626
|
-
*/
|
|
627
630
|
krb5_verify_init_creds_opt vicopt;
|
|
628
|
-
krb5_error_code kerror_strict = 0;
|
|
629
631
|
|
|
630
632
|
krb5_verify_init_creds_opt_init(&vicopt);
|
|
631
633
|
krb5_verify_init_creds_opt_set_ap_req_nofail(&vicopt, TRUE);
|
|
@@ -638,19 +640,12 @@ static VALUE rkrb5_authenticate_bang(int argc, VALUE* argv, VALUE self){
|
|
|
638
640
|
rb_raise(cKrb5Exception, "krb5_parse_name(service): %s", error_message(kerror));
|
|
639
641
|
}
|
|
640
642
|
|
|
641
|
-
// First, attempt strict verification
|
|
642
643
|
kerror = krb5_verify_init_creds(ptr->ctx, &ptr->creds, server_princ, NULL, NULL, &vicopt);
|
|
643
644
|
|
|
644
645
|
if(kerror){
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
if(kerror){
|
|
649
|
-
if(server_princ)
|
|
650
|
-
krb5_free_principal(ptr->ctx, server_princ);
|
|
651
|
-
/* raise the original strict-verification error to inform caller */
|
|
652
|
-
rb_raise(cKrb5Exception, "krb5_verify_init_creds: %s", error_message(kerror_strict));
|
|
653
|
-
}
|
|
646
|
+
if(server_princ)
|
|
647
|
+
krb5_free_principal(ptr->ctx, server_princ);
|
|
648
|
+
rb_raise(cKrb5Exception, "krb5_verify_init_creds: %s", error_message(kerror));
|
|
654
649
|
}
|
|
655
650
|
|
|
656
651
|
if(server_princ)
|
|
@@ -684,6 +679,8 @@ static VALUE rkrb5_close(VALUE self){
|
|
|
684
679
|
|
|
685
680
|
if(ptr->ctx && ptr->rb_context == Qnil)
|
|
686
681
|
krb5_free_context(ptr->ctx);
|
|
682
|
+
else if(ptr->rb_context != Qnil)
|
|
683
|
+
rkrb5_context_release(ptr->rb_context);
|
|
687
684
|
|
|
688
685
|
ptr->ctx = NULL;
|
|
689
686
|
ptr->princ = NULL;
|
|
@@ -848,12 +845,20 @@ static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
|
848
845
|
if(!NIL_P(v_keytab)){
|
|
849
846
|
// Will raise TypeError if object isn't the expected Keytab typed data
|
|
850
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
|
+
|
|
851
852
|
keytab = ktptr->keytab;
|
|
852
853
|
}
|
|
853
854
|
|
|
854
855
|
if(!NIL_P(v_ccache)){
|
|
855
856
|
// Will raise TypeError if object isn't the expected CCache typed data
|
|
856
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
|
+
|
|
857
862
|
ccache_ptr = &ccptr->ccache;
|
|
858
863
|
}
|
|
859
864
|
|
|
@@ -861,6 +866,22 @@ static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
|
861
866
|
if(ptr->creds.client == NULL)
|
|
862
867
|
rb_raise(cKrb5Exception, "no credentials have been acquired");
|
|
863
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
|
+
|
|
864
885
|
// Optional server principal (parse after context & arg validation)
|
|
865
886
|
if(!NIL_P(v_server)){
|
|
866
887
|
kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_server), &server_princ);
|
|
@@ -876,16 +897,11 @@ static VALUE rkrb5_verify_init_creds(int argc, VALUE* argv, VALUE self){
|
|
|
876
897
|
if(kerror)
|
|
877
898
|
rb_raise(cKrb5Exception, "krb5_verify_init_creds: %s", error_message(kerror));
|
|
878
899
|
|
|
879
|
-
/*
|
|
880
|
-
|
|
900
|
+
/* Include the initial credential in addition to any credentials fetched
|
|
901
|
+
and stored by krb5_verify_init_creds. */
|
|
881
902
|
if(ccache_ptr && *ccache_ptr){
|
|
882
903
|
krb5_error_code k2;
|
|
883
904
|
|
|
884
|
-
k2 = krb5_cc_initialize(ptr->ctx, *ccache_ptr, ptr->creds.client);
|
|
885
|
-
|
|
886
|
-
if(k2)
|
|
887
|
-
rb_raise(cKrb5Exception, "krb5_cc_initialize: %s", error_message(k2));
|
|
888
|
-
|
|
889
905
|
k2 = krb5_cc_store_cred(ptr->ctx, *ccache_ptr, &ptr->creds);
|
|
890
906
|
|
|
891
907
|
if(k2)
|
|
@@ -1009,8 +1025,8 @@ void Init_rkerberos(void){
|
|
|
1009
1025
|
rb_define_alias(cKrb5, "init_creds_keytab", "get_init_creds_keytab");
|
|
1010
1026
|
rb_define_alias(cKrb5, "permitted_enctypes", "get_permitted_enctypes");
|
|
1011
1027
|
|
|
1012
|
-
/* 0.3.
|
|
1013
|
-
rb_define_const(cKrb5, "VERSION", rb_str_new2("0.3.
|
|
1028
|
+
/* 0.3.1: The version of the custom rkerberos library */
|
|
1029
|
+
rb_define_const(cKrb5, "VERSION", rb_str_new2("0.3.1"));
|
|
1014
1030
|
|
|
1015
1031
|
// Encoding type constants
|
|
1016
1032
|
|
data/ext/rkerberos/rkerberos.h
CHANGED
|
@@ -49,6 +49,10 @@ void Init_ccache();
|
|
|
49
49
|
// Defined in rkerberos.c
|
|
50
50
|
VALUE rb_hash_aref2(VALUE, VALUE);
|
|
51
51
|
|
|
52
|
+
// Defined in context.c
|
|
53
|
+
krb5_context rkrb5_context_borrow(VALUE);
|
|
54
|
+
void rkrb5_context_release(VALUE);
|
|
55
|
+
|
|
52
56
|
// Variable declarations
|
|
53
57
|
extern VALUE mKerberos;
|
|
54
58
|
extern VALUE cKrb5;
|
|
@@ -76,6 +80,8 @@ typedef struct {
|
|
|
76
80
|
typedef struct {
|
|
77
81
|
krb5_context ctx;
|
|
78
82
|
krb5_enctype etypes;
|
|
83
|
+
size_t borrowers;
|
|
84
|
+
int closed;
|
|
79
85
|
} RUBY_KRB5_CONTEXT;
|
|
80
86
|
|
|
81
87
|
// Kerberos::Kadm5
|
data/rkerberos.gemspec
CHANGED
|
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
|
2
2
|
|
|
3
3
|
Gem::Specification.new do |spec|
|
|
4
4
|
spec.name = 'rkerberos'
|
|
5
|
-
spec.version = '0.3.
|
|
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']
|
data/spec/config_spec.rb
CHANGED
|
@@ -28,6 +28,14 @@ RSpec.describe 'Kerberos::Kadm5::Config', :kadm5 do
|
|
|
28
28
|
expect(c2.realm).to eq(c1.realm)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
it 'releases its context lease after copying the configuration' do
|
|
32
|
+
ctx = Kerberos::Krb5::Context.new
|
|
33
|
+
config = klass.new(context: ctx)
|
|
34
|
+
|
|
35
|
+
expect { ctx.close }.not_to raise_error
|
|
36
|
+
expect(config.realm).to be_a(String)
|
|
37
|
+
end
|
|
38
|
+
|
|
31
39
|
it 'raises TypeError for non-Context context argument' do
|
|
32
40
|
expect { klass.new(context: "bad") }.to raise_error(TypeError)
|
|
33
41
|
end
|
data/spec/context_spec.rb
CHANGED
|
@@ -16,6 +16,51 @@ RSpec.describe Kerberos::Krb5::Context do
|
|
|
16
16
|
it 'can be called multiple times without error' do
|
|
17
17
|
expect { 3.times { context.close } }.not_to raise_error
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
it 'raises while a dependent wrapper remains open' do
|
|
21
|
+
krb5 = Kerberos::Krb5.new(context: context)
|
|
22
|
+
|
|
23
|
+
expect { context.close }
|
|
24
|
+
.to raise_error(Kerberos::Krb5::Exception, /1 dependent wrapper/)
|
|
25
|
+
expect(context.default_realm).to be_a(String)
|
|
26
|
+
|
|
27
|
+
krb5.close
|
|
28
|
+
expect { context.close }.not_to raise_error
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'tracks multiple dependent wrappers independently' do
|
|
32
|
+
first = Kerberos::Krb5.new(context: context)
|
|
33
|
+
second = Kerberos::Krb5::Principal.new(name: 'user', context: context)
|
|
34
|
+
|
|
35
|
+
expect { context.close }
|
|
36
|
+
.to raise_error(Kerberos::Krb5::Exception, /2 dependent wrappers/)
|
|
37
|
+
|
|
38
|
+
first.close
|
|
39
|
+
expect { context.close }
|
|
40
|
+
.to raise_error(Kerberos::Krb5::Exception, /1 dependent wrapper/)
|
|
41
|
+
|
|
42
|
+
second.close
|
|
43
|
+
expect { context.close }.not_to raise_error
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'supports a forced logical close with deferred native cleanup' do
|
|
47
|
+
krb5 = Kerberos::Krb5.new(context: context)
|
|
48
|
+
realm = krb5.default_realm
|
|
49
|
+
|
|
50
|
+
expect(context.close(force: true)).to equal(context)
|
|
51
|
+
expect { context.default_realm }.to raise_error(Kerberos::Krb5::Exception)
|
|
52
|
+
expect { Kerberos::Krb5.new(context: context) }
|
|
53
|
+
.to raise_error(Kerberos::Krb5::Exception, /context is closed/)
|
|
54
|
+
expect(krb5.default_realm).to eq(realm)
|
|
55
|
+
|
|
56
|
+
expect { krb5.close }.not_to raise_error
|
|
57
|
+
expect { context.close }.not_to raise_error
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'requires close options to be keywords and rejects unknown keywords' do
|
|
61
|
+
expect { context.close({force: true}) }.to raise_error(ArgumentError)
|
|
62
|
+
expect { context.close(unknown: true) }.to raise_error(ArgumentError, /unknown keyword/)
|
|
63
|
+
end
|
|
19
64
|
end
|
|
20
65
|
|
|
21
66
|
describe 'constructor options' do
|
|
@@ -73,6 +73,9 @@ RSpec.describe Kerberos::Krb5::CredentialsCache do
|
|
|
73
73
|
ctx = Kerberos::Krb5::Context.new
|
|
74
74
|
c = described_class.new(principal: princ, context: ctx)
|
|
75
75
|
expect(c.primary_principal).to eq(princ)
|
|
76
|
+
expect { ctx.close }.to raise_error(Kerberos::Krb5::Exception, /dependent wrapper/)
|
|
77
|
+
c.close
|
|
78
|
+
expect { ctx.close }.not_to raise_error
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
it 'raises TypeError for non-Context context argument' do
|
data/spec/kadm5_spec.rb
CHANGED
|
@@ -66,13 +66,20 @@ RSpec.describe 'Kerberos::Kadm5', :kadm5 do
|
|
|
66
66
|
expect { subject.new(principal: user, keytab: 1) }.to raise_error(TypeError)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
it 'requires a principal when using a keytab' do
|
|
70
|
+
expect { subject.new(keytab: true) }.to raise_error(ArgumentError, /principal must be specified/)
|
|
71
|
+
end
|
|
72
|
+
|
|
69
73
|
it 'requires service to be a string' do
|
|
70
74
|
expect { subject.new(principal: user, password: pass, service: 1) }.to raise_error(TypeError)
|
|
71
75
|
end
|
|
72
76
|
|
|
73
77
|
it 'accepts a context keyword argument' do
|
|
74
78
|
ctx = Kerberos::Krb5::Context.new
|
|
75
|
-
|
|
79
|
+
kadm5 = subject.new(principal: user, password: pass, context: ctx)
|
|
80
|
+
expect { ctx.close }.to raise_error(Kerberos::Krb5::Exception, /dependent wrapper/)
|
|
81
|
+
kadm5.close
|
|
82
|
+
expect { ctx.close }.not_to raise_error
|
|
76
83
|
end
|
|
77
84
|
|
|
78
85
|
it 'raises TypeError for non-Context context argument' do
|
|
@@ -152,6 +159,27 @@ RSpec.describe 'Kerberos::Kadm5', :kadm5 do
|
|
|
152
159
|
end
|
|
153
160
|
end
|
|
154
161
|
|
|
162
|
+
describe '#close' do
|
|
163
|
+
it 'makes all administrative operations fail safely' do
|
|
164
|
+
kadm5 = subject.new(principal: user, password: pass)
|
|
165
|
+
kadm5.close
|
|
166
|
+
policy = Kerberos::Kadm5::Policy.new(name: 'closed_policy')
|
|
167
|
+
|
|
168
|
+
operations = [
|
|
169
|
+
-> { kadm5.get_privileges },
|
|
170
|
+
-> { kadm5.get_principals },
|
|
171
|
+
-> { kadm5.get_policies },
|
|
172
|
+
-> { kadm5.create_policy(policy) },
|
|
173
|
+
-> { kadm5.modify_policy(policy) },
|
|
174
|
+
-> { kadm5.delete_policy('closed_policy') }
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
operations.each do |operation|
|
|
178
|
+
expect(&operation).to raise_error(Kerberos::Kadm5::Exception, /no administrative context/)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
155
183
|
describe '#get_privileges' do
|
|
156
184
|
before(:each) do
|
|
157
185
|
@kadm5 = subject.new(principal: user, password: pass)
|
|
@@ -223,6 +251,19 @@ RSpec.describe 'Kerberos::Kadm5', :kadm5 do
|
|
|
223
251
|
@kadm5.create_policy(name: @policy_name, unknown: true)
|
|
224
252
|
}.to raise_error(ArgumentError, /unknown keyword/)
|
|
225
253
|
end
|
|
254
|
+
|
|
255
|
+
it 'modifies values assigned through Policy writers' do
|
|
256
|
+
@kadm5.create_policy(name: @policy_name, min_length: 8, history_num: 1)
|
|
257
|
+
policy = @kadm5.get_policy(@policy_name)
|
|
258
|
+
policy.min_length = 12
|
|
259
|
+
policy.history_num = 3
|
|
260
|
+
|
|
261
|
+
expect(@kadm5.modify_policy(policy)).to equal(@kadm5)
|
|
262
|
+
|
|
263
|
+
modified = @kadm5.get_policy(@policy_name)
|
|
264
|
+
expect(modified.min_length).to eq(12)
|
|
265
|
+
expect(modified.history_num).to eq(3)
|
|
266
|
+
end
|
|
226
267
|
end
|
|
227
268
|
|
|
228
269
|
describe '#create_principal' do
|