rkerberos 0.2.2 → 0.3.0

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.
@@ -4,6 +4,13 @@ VALUE cKrb5Keytab, cKrb5KeytabException;
4
4
 
5
5
 
6
6
  // TypedData functions for RUBY_KRB5_KEYTAB
7
+ static void rkrb5_keytab_typed_mark(void *ptr) {
8
+ if (!ptr) return;
9
+ RUBY_KRB5_KEYTAB *kt = (RUBY_KRB5_KEYTAB *)ptr;
10
+ if (kt->rb_context != Qnil)
11
+ rb_gc_mark(kt->rb_context);
12
+ }
13
+
7
14
  void rkrb5_keytab_typed_free(void *ptr) {
8
15
  if (!ptr) return;
9
16
  RUBY_KRB5_KEYTAB *kt = (RUBY_KRB5_KEYTAB *)ptr;
@@ -11,7 +18,7 @@ void rkrb5_keytab_typed_free(void *ptr) {
11
18
  krb5_kt_close(kt->ctx, kt->keytab);
12
19
  if (kt->ctx)
13
20
  krb5_free_cred_contents(kt->ctx, &kt->creds);
14
- if (kt->ctx)
21
+ if (kt->ctx && kt->rb_context == Qnil)
15
22
  krb5_free_context(kt->ctx);
16
23
  free(kt);
17
24
  }
@@ -23,13 +30,14 @@ size_t rkrb5_keytab_typed_size(const void *ptr) {
23
30
  // Must NOT be static so it is exported
24
31
  const rb_data_type_t rkrb5_keytab_data_type = {
25
32
  "RUBY_KRB5_KEYTAB",
26
- {NULL, rkrb5_keytab_typed_free, rkrb5_keytab_typed_size,},
33
+ {rkrb5_keytab_typed_mark, rkrb5_keytab_typed_free, rkrb5_keytab_typed_size,},
27
34
  NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
28
35
  };
29
36
 
30
37
  VALUE rkrb5_keytab_allocate(VALUE klass){
31
38
  RUBY_KRB5_KEYTAB* ptr = ALLOC(RUBY_KRB5_KEYTAB);
32
39
  memset(ptr, 0, sizeof(RUBY_KRB5_KEYTAB));
40
+ ptr->rb_context = Qnil;
33
41
  return TypedData_Wrap_Struct(klass, &rkrb5_keytab_data_type, ptr);
34
42
  }
35
43
 
@@ -51,7 +59,12 @@ static VALUE rkrb5_keytab_each_body(VALUE arg){
51
59
  VALUE v_kt_entry;
52
60
 
53
61
  while((kerror = krb5_kt_next_entry(ea->ctx, ea->keytab, &entry, &ea->cursor)) == 0){
54
- krb5_unparse_name(ea->ctx, entry.principal, &principal);
62
+ kerror = krb5_unparse_name(ea->ctx, entry.principal, &principal);
63
+
64
+ if(kerror){
65
+ krb5_kt_free_entry(ea->ctx, &entry);
66
+ rb_raise(cKrb5Exception, "krb5_unparse_name: %s", error_message(kerror));
67
+ }
55
68
 
56
69
  v_kt_entry = rb_class_new_instance(0, NULL, cKrb5KtEntry);
57
70
 
@@ -162,109 +175,189 @@ static VALUE rkrb5_keytab_close(VALUE self){
162
175
  if(ptr->ctx)
163
176
  krb5_free_cred_contents(ptr->ctx, &ptr->creds);
164
177
 
165
- if(ptr->ctx)
178
+ if(ptr->ctx && ptr->rb_context == Qnil)
166
179
  krb5_free_context(ptr->ctx);
167
180
 
168
181
  ptr->ctx = NULL;
182
+ ptr->rb_context = Qnil;
169
183
 
170
184
  return Qtrue;
171
185
  }
172
186
 
173
187
  /*
174
- static VALUE rkrb5_keytab_remove_entry(int argc, VALUE* argv, VALUE self){
188
+ * call-seq:
189
+ * keytab.add_entry(principal:, password:, vno: 1, enctype: 18)
190
+ *
191
+ * Add a new entry to the keytab for the given +principal+ string.
192
+ *
193
+ * A key is derived from +password+ (and the principal-based salt) using
194
+ * +krb5_c_string_to_key+, so the resulting key will match what the KDC
195
+ * would generate for the same password.
196
+ *
197
+ * Options:
198
+ * principal:: A Kerberos principal name string (required).
199
+ * password:: The password used to derive the key (required).
200
+ * vno:: Key version number (default 1).
201
+ * enctype:: Encryption type as an integer (default 18,
202
+ * aes256-cts-hmac-sha1-96).
203
+ *
204
+ * Returns +self+.
205
+ */
206
+ static VALUE rkrb5_keytab_add_entry(int argc, VALUE* argv, VALUE self){
175
207
  RUBY_KRB5_KEYTAB* ptr;
176
208
  krb5_error_code kerror;
177
209
  krb5_keytab_entry entry;
178
- char* name;
179
- VALUE v_name, v_vno, v_enctype;
210
+ krb5_data pwd_data, salt;
211
+ VALUE v_opts, v_principal, v_password, v_vno, v_enctype;
212
+ ID kw_table[4] = { rb_intern("principal"), rb_intern("password"), rb_intern("vno"), rb_intern("enctype") };
213
+ VALUE kw_vals[4];
180
214
 
181
215
  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);
182
216
 
183
- rb_scan_args(argc, argv, "12", &v_name, &v_vno, &v_enctype);
217
+ if(!ptr->ctx)
218
+ rb_raise(cKrb5Exception, "no context has been established");
184
219
 
185
- Check_Type(v_name, T_STRING);
220
+ rb_scan_args(argc, argv, "0:", &v_opts);
186
221
 
187
- name = StringValueCStr(v_name);
222
+ if(NIL_P(v_opts))
223
+ v_opts = rb_hash_new();
188
224
 
189
- if(!ptr->ctx)
190
- rb_raise(cKrb5Exception, "no context has been established");
225
+ rb_get_kwargs(v_opts, kw_table, 2, 2, kw_vals);
226
+ v_principal = kw_vals[0];
227
+ v_password = kw_vals[1];
228
+ v_vno = kw_vals[2] == Qundef ? Qnil : kw_vals[2];
229
+ v_enctype = kw_vals[3] == Qundef ? Qnil : kw_vals[3];
230
+
231
+ Check_Type(v_principal, T_STRING);
232
+ Check_Type(v_password, T_STRING);
233
+
234
+ memset(&entry, 0, sizeof(entry));
191
235
 
192
- kerror = krb5_parse_name(ptr->ctx, name, &entry.principal);
236
+ kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_principal), &entry.principal);
193
237
 
194
238
  if(kerror)
195
239
  rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
196
240
 
197
- if(NIL_P(v_vno))
198
- entry.vno = 0;
199
- else
200
- entry.vno = NUM2INT(v_vno);
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
+ entry.timestamp = time(NULL);
201
244
 
202
- if(NIL_P(v_enctype))
203
- entry.key.enctype = 0;
204
- else
205
- entry.key.enctype = NUM2INT(v_enctype);
245
+ // Derive the salt from the principal
246
+ kerror = krb5_principal2salt(ptr->ctx, entry.principal, &salt);
206
247
 
207
- entry.key.length = 16;
248
+ if(kerror){
249
+ krb5_free_principal(ptr->ctx, entry.principal);
250
+ rb_raise(cKrb5Exception, "krb5_principal2salt: %s", error_message(kerror));
251
+ }
208
252
 
209
- kerror = krb5_kt_remove_entry(
210
- ptr->ctx,
211
- ptr->keytab,
212
- &entry
213
- );
253
+ // Derive key from password + salt
254
+ pwd_data.data = StringValuePtr(v_password);
255
+ pwd_data.length = (unsigned int)RSTRING_LEN(v_password);
256
+
257
+ kerror = krb5_c_string_to_key(ptr->ctx, entry.key.enctype, &pwd_data, &salt, &entry.key);
258
+
259
+ krb5_free_data_contents(ptr->ctx, &salt);
260
+
261
+ if(kerror){
262
+ krb5_free_principal(ptr->ctx, entry.principal);
263
+ rb_raise(cKrb5Exception, "krb5_c_string_to_key: %s", error_message(kerror));
264
+ }
265
+
266
+ kerror = krb5_kt_add_entry(ptr->ctx, ptr->keytab, &entry);
267
+
268
+ krb5_free_keyblock_contents(ptr->ctx, &entry.key);
269
+ krb5_free_principal(ptr->ctx, entry.principal);
214
270
 
215
271
  if(kerror)
216
- rb_raise(cKrb5KeytabException, "krb5_kt_remove_entry: %s", error_message(kerror));
272
+ rb_raise(cKrb5KeytabException, "krb5_kt_add_entry: %s", error_message(kerror));
217
273
 
218
274
  return self;
219
275
  }
220
276
 
221
- static VALUE rkrb5_keytab_add_entry(int argc, VALUE* argv, VALUE self){
277
+ /*
278
+ * call-seq:
279
+ * keytab.remove_entry(principal:, vno: 0, enctype: 0)
280
+ *
281
+ * Remove entries from the keytab that match the given +principal+ string,
282
+ * +vno+, and +enctype+.
283
+ *
284
+ * A +vno+ of 0 (the default) matches any version number.
285
+ * An +enctype+ of 0 (the default) matches any encryption type.
286
+ *
287
+ * When both +vno+ and +enctype+ are 0 (the defaults), all entries for
288
+ * the +principal+ are removed.
289
+ *
290
+ * Options:
291
+ * principal:: A Kerberos principal name string (required).
292
+ * vno:: Key version number to match, 0 for any (default 0).
293
+ * enctype:: Encryption type to match, 0 for any (default 0).
294
+ *
295
+ * Returns +self+.
296
+ */
297
+ static VALUE rkrb5_keytab_remove_entry(int argc, VALUE* argv, VALUE self){
222
298
  RUBY_KRB5_KEYTAB* ptr;
223
299
  krb5_error_code kerror;
224
- krb5_keytab_entry entry;
225
- char* name;
226
- VALUE v_name, v_vno, v_enctype;
300
+ krb5_keytab_entry found_entry;
301
+ krb5_principal match_princ;
302
+ krb5_kvno match_vno;
303
+ krb5_enctype match_enctype;
304
+ int removed = 0;
305
+ VALUE v_opts, v_principal, v_vno, v_enctype;
306
+ ID kw_table[3] = { rb_intern("principal"), rb_intern("vno"), rb_intern("enctype") };
307
+ VALUE kw_vals[3];
227
308
 
228
309
  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);
229
310
 
230
- rb_scan_args(argc, argv, "12", &v_name, &v_vno, &v_enctype);
311
+ if(!ptr->ctx)
312
+ rb_raise(cKrb5Exception, "no context has been established");
231
313
 
232
- Check_Type(v_name, T_STRING);
314
+ rb_scan_args(argc, argv, "0:", &v_opts);
233
315
 
234
- name = StringValueCStr(v_name);
316
+ if(NIL_P(v_opts))
317
+ v_opts = rb_hash_new();
235
318
 
236
- if(!ptr->ctx)
237
- rb_raise(cKrb5Exception, "no context has been established");
319
+ rb_get_kwargs(v_opts, kw_table, 1, 2, kw_vals);
320
+ v_principal = kw_vals[0];
321
+ v_vno = kw_vals[1] == Qundef ? Qnil : kw_vals[1];
322
+ v_enctype = kw_vals[2] == Qundef ? Qnil : kw_vals[2];
238
323
 
239
- kerror = krb5_parse_name(ptr->ctx, name, &entry.principal);
324
+ Check_Type(v_principal, T_STRING);
325
+
326
+ kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_principal), &match_princ);
240
327
 
241
328
  if(kerror)
242
329
  rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
243
330
 
244
- if(NIL_P(v_vno))
245
- entry.vno = 0;
246
- else
247
- entry.vno = NUM2INT(v_vno);
331
+ match_vno = NIL_P(v_vno) ? 0 : NUM2INT(v_vno);
332
+ match_enctype = NIL_P(v_enctype) ? 0 : NUM2INT(v_enctype);
248
333
 
249
- if(NIL_P(v_enctype))
250
- entry.key.enctype = 0;
251
- else
252
- entry.key.enctype = NUM2INT(v_enctype);
334
+ // Retrieve the full entry via krb5_kt_get_entry and then pass the
335
+ // complete struct to krb5_kt_remove_entry so all fields match exactly.
336
+ // Loop to remove every matching entry when vno/enctype are wildcards.
337
+ while(1){
338
+ kerror = krb5_kt_get_entry(
339
+ ptr->ctx, ptr->keytab, match_princ,
340
+ match_vno, match_enctype, &found_entry
341
+ );
253
342
 
254
- entry.key.length = 16;
343
+ if(kerror){
344
+ krb5_free_principal(ptr->ctx, match_princ);
345
+ if(removed)
346
+ return self;
347
+ rb_raise(cKrb5KeytabException, "krb5_kt_remove_entry: %s", error_message(kerror));
348
+ }
255
349
 
256
- kerror = krb5_kt_add_entry(
257
- ptr->ctx,
258
- ptr->keytab,
259
- &entry
260
- );
350
+ kerror = krb5_kt_remove_entry(ptr->ctx, ptr->keytab, &found_entry);
351
+ krb5_kt_free_entry(ptr->ctx, &found_entry);
261
352
 
262
- if(kerror)
263
- rb_raise(cKrb5KeytabException, "krb5_kt_add_entry: %s", error_message(kerror));
353
+ if(kerror){
354
+ krb5_free_principal(ptr->ctx, match_princ);
355
+ rb_raise(cKrb5KeytabException, "krb5_kt_remove_entry: %s", error_message(kerror));
356
+ }
264
357
 
265
- return self;
358
+ removed = 1;
359
+ }
266
360
  }
267
- */
268
361
 
269
362
  /*
270
363
  * call-seq:
@@ -425,7 +518,7 @@ static VALUE rkrb5_keytab_dup(VALUE self){
425
518
 
426
519
  /*
427
520
  * call-seq:
428
- * Kerberos::Krb5::Keytab.new(name = nil)
521
+ * Kerberos::Krb5::Keytab.new(name: nil, context: nil)
429
522
  *
430
523
  * Creates and returns a new Kerberos::Krb5::Keytab object. This initializes
431
524
  * the context and keytab for future method calls on that object.
@@ -434,28 +527,66 @@ static VALUE rkrb5_keytab_dup(VALUE self){
434
527
  * name is used. If a +name+ is provided it must be in the form 'type:residual'
435
528
  * where 'type' is a type known to the Kerberos library.
436
529
  *
530
+ * An optional +context+ keyword argument may be provided. If given, it must
531
+ * be a Kerberos::Krb5::Context object and will be used instead of creating
532
+ * a new context via krb5_init_context.
533
+ *
437
534
  * Examples:
438
535
  *
439
536
  * # Using the default keytab
440
537
  * keytab = Kerberos::Krb5::Keytab.new
441
538
  *
442
539
  * # Using an explicit keytab
443
- * keytab = Kerberos::Krb5::Keytab.new('FILE:/etc/krb5.keytab')
540
+ * keytab = Kerberos::Krb5::Keytab.new(name: 'FILE:/etc/krb5.keytab')
541
+ *
542
+ * # Using a custom context
543
+ * ctx = Kerberos::Krb5::Context.new
544
+ * keytab = Kerberos::Krb5::Keytab.new(name: 'FILE:/etc/krb5.keytab', context: ctx)
444
545
  */
445
546
  static VALUE rkrb5_keytab_initialize(int argc, VALUE* argv, VALUE self){
446
547
  RUBY_KRB5_KEYTAB* ptr;
447
548
  krb5_error_code kerror;
448
549
  char keytab_name[MAX_KEYTAB_NAME_LEN];
449
550
  VALUE v_keytab_name = Qnil;
551
+ VALUE v_opts = Qnil;
552
+ VALUE v_context = Qnil;
553
+ ID kw_table[2] = { rb_intern("name"), rb_intern("context") };
554
+ VALUE kw_vals[2];
450
555
 
451
556
  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);
452
557
 
453
- rb_scan_args(argc, argv, "01", &v_keytab_name);
558
+ rb_scan_args(argc, argv, "0:", &v_opts);
454
559
 
455
- kerror = krb5_init_context(&ptr->ctx);
560
+ if(NIL_P(v_opts))
561
+ v_opts = rb_hash_new();
456
562
 
457
- if(kerror)
458
- rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
563
+ rb_get_kwargs(v_opts, kw_table, 0, 2, kw_vals);
564
+ v_keytab_name = kw_vals[0] == Qundef ? Qnil : kw_vals[0];
565
+ v_context = kw_vals[1] == Qundef ? Qnil : kw_vals[1];
566
+
567
+ // Initialize or borrow the context
568
+ if(!NIL_P(v_context)){
569
+ RUBY_KRB5_CONTEXT* ctx_ptr;
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->rb_context = v_context;
581
+ }
582
+ else{
583
+ kerror = krb5_init_context(&ptr->ctx);
584
+
585
+ if(kerror)
586
+ rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
587
+
588
+ ptr->rb_context = Qnil;
589
+ }
459
590
 
460
591
  // Use the default keytab name if one isn't provided.
461
592
  if(NIL_P(v_keytab_name)){
@@ -503,7 +634,12 @@ static VALUE rkrb5_s_keytab_foreach_body(VALUE arg){
503
634
  VALUE v_kt_entry;
504
635
 
505
636
  while((kerror = krb5_kt_next_entry(fa->ctx, fa->keytab, &entry, &fa->cursor)) == 0){
506
- krb5_unparse_name(fa->ctx, entry.principal, &principal);
637
+ kerror = krb5_unparse_name(fa->ctx, entry.principal, &principal);
638
+
639
+ if(kerror){
640
+ krb5_kt_free_entry(fa->ctx, &entry);
641
+ rb_raise(cKrb5Exception, "krb5_unparse_name: %s", error_message(kerror));
642
+ }
507
643
 
508
644
  v_kt_entry = rb_class_new_instance(0, NULL, cKrb5KtEntry);
509
645
 
@@ -606,6 +742,26 @@ static VALUE rkrb5_s_keytab_foreach(int argc, VALUE* argv, VALUE klass){
606
742
  return Qnil;
607
743
  }
608
744
 
745
+ /*
746
+ * call-seq:
747
+ * keytab.have_content? -> true or false
748
+ *
749
+ * Returns true if the keytab exists and contains entries, false otherwise.
750
+ */
751
+ static VALUE rkrb5_keytab_have_content(VALUE self){
752
+ RUBY_KRB5_KEYTAB* ptr;
753
+ krb5_error_code kerror;
754
+
755
+ TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);
756
+
757
+ if(!ptr->ctx)
758
+ rb_raise(cKrb5Exception, "no context has been established");
759
+
760
+ kerror = krb5_kt_have_content(ptr->ctx, ptr->keytab);
761
+
762
+ return kerror ? Qfalse : Qtrue;
763
+ }
764
+
609
765
  void Init_keytab(void){
610
766
  /* The Kerberos::Krb5::Keytab class encapsulates a Kerberos keytab. */
611
767
  cKrb5Keytab = rb_define_class_under(cKrb5, "Keytab", rb_cObject);
@@ -634,11 +790,11 @@ void Init_keytab(void){
634
790
  rb_define_method(cKrb5Keytab, "keytab_name", rkrb5_keytab_get_name, 0);
635
791
  rb_define_method(cKrb5Keytab, "keytab_type", rkrb5_keytab_get_type, 0);
636
792
  rb_define_method(cKrb5Keytab, "dup", rkrb5_keytab_dup, 0);
793
+ rb_define_method(cKrb5Keytab, "have_content?", rkrb5_keytab_have_content, 0);
637
794
  rb_define_alias(cKrb5Keytab, "clone", "dup");
638
795
 
639
- // TODO: Move these into Kadm5 and/or figure out how to set the vno properly.
640
- // rb_define_method(cKrb5Keytab, "add_entry", rkrb5_keytab_add_entry, -1);
641
- // rb_define_method(cKrb5Keytab, "remove_entry", rkrb5_keytab_remove_entry, -1);
796
+ rb_define_method(cKrb5Keytab, "add_entry", rkrb5_keytab_add_entry, -1);
797
+ rb_define_method(cKrb5Keytab, "remove_entry", rkrb5_keytab_remove_entry, -1);
642
798
 
643
799
  // Accessors
644
800
 
@@ -63,7 +63,8 @@ static VALUE rkrb5_kt_entry_inspect(VALUE self){
63
63
 
64
64
  rb_str_buf_cat2(v_str, "key=");
65
65
  rb_str_buf_append(v_str, rb_inspect(rb_iv_get(self, "@key")));
66
- rb_str_buf_cat2(v_str, " ");
66
+
67
+ rb_str_buf_cat2(v_str, ">");
67
68
 
68
69
  return v_str;
69
70
  }
@@ -32,12 +32,12 @@ static VALUE rkadm5_policy_allocate(VALUE klass){
32
32
 
33
33
  /*
34
34
  * call-seq:
35
- * Kerberos::Kadm5::Policy.new(options)
35
+ * Kerberos::Kadm5::Policy.new(name:, min_life: nil, max_life: nil,
36
+ * min_length: nil, min_classes: nil, history_num: nil)
36
37
  *
37
- * Returns a new policy object using +options+ you choose to pass, where
38
- * the +options+ argument is a hash. This does NOT actually create the policy
39
- * object within Kerberos. To do that pass your Policy object to the
40
- * Kadm5.create_policy method.
38
+ * Returns a new policy object using the keyword arguments you choose to pass.
39
+ * This does NOT actually create the policy within Kerberos. To do that pass
40
+ * your Policy object to the Kadm5.create_policy method.
41
41
  *
42
42
  * The possible options are:
43
43
  *
@@ -50,32 +50,34 @@ static VALUE rkadm5_policy_allocate(VALUE klass){
50
50
  *
51
51
  * If you do not provide a :name then an ArgumentError will be raised.
52
52
  */
53
- static VALUE rkadm5_policy_init(VALUE self, VALUE v_options){
53
+ static VALUE rkadm5_policy_init(int argc, VALUE* argv, VALUE self){
54
54
  RUBY_KADM5_POLICY* ptr;
55
+ VALUE v_options;
55
56
  VALUE v_name, v_minlife, v_maxlife, v_minlength;
56
57
  VALUE v_minclasses, v_historynum;
58
+ ID kw_table[6] = {
59
+ rb_intern("name"), rb_intern("min_life"), rb_intern("max_life"),
60
+ rb_intern("min_length"), rb_intern("min_classes"), rb_intern("history_num")
61
+ };
62
+ VALUE kw_vals[6];
57
63
 
58
64
  TypedData_Get_Struct(self, RUBY_KADM5_POLICY, &rkadm5_policy_data_type, ptr);
59
65
 
60
- Check_Type(v_options, T_HASH);
66
+ rb_scan_args(argc, argv, "0:", &v_options);
61
67
 
62
- if(RTEST(rb_funcall(v_options, rb_intern("empty?"), 0)))
63
- rb_raise(rb_eArgError, "no policy options provided");
68
+ if(NIL_P(v_options))
69
+ v_options = rb_hash_new();
64
70
 
65
- v_name = rb_hash_aref2(v_options, rb_str_new_cstr("name"));
66
- v_minlife = rb_hash_aref2(v_options, rb_str_new_cstr("min_life"));
67
- v_maxlife = rb_hash_aref2(v_options, rb_str_new_cstr("max_life"));
68
- v_minlength = rb_hash_aref2(v_options, rb_str_new_cstr("min_length"));
69
- v_minclasses = rb_hash_aref2(v_options, rb_str_new_cstr("min_classes"));
70
- v_historynum = rb_hash_aref2(v_options, rb_str_new_cstr("history_num"));
71
+ rb_get_kwargs(v_options, kw_table, 1, 5, kw_vals);
72
+ v_name = kw_vals[0];
73
+ v_minlife = kw_vals[1] == Qundef ? Qnil : kw_vals[1];
74
+ v_maxlife = kw_vals[2] == Qundef ? Qnil : kw_vals[2];
75
+ v_minlength = kw_vals[3] == Qundef ? Qnil : kw_vals[3];
76
+ v_minclasses = kw_vals[4] == Qundef ? Qnil : kw_vals[4];
77
+ v_historynum = kw_vals[5] == Qundef ? Qnil : kw_vals[5];
71
78
 
72
- if(NIL_P(v_name)){
73
- rb_raise(rb_eArgError, "name policy option is mandatory");
74
- }
75
- else{
76
- ptr->policy.policy = StringValueCStr(v_name);
77
- rb_iv_set(self, "@policy", v_name);
78
- }
79
+ ptr->policy.policy = StringValueCStr(v_name);
80
+ rb_iv_set(self, "@policy", v_name);
79
81
 
80
82
  if(!NIL_P(v_minlife)){
81
83
  ptr->policy.pw_min_life = NUM2LONG(v_minlife);
@@ -171,7 +173,7 @@ void Init_policy(void){
171
173
 
172
174
  // Initialization Function
173
175
 
174
- rb_define_method(cKadm5Policy, "initialize", rkadm5_policy_init, 1);
176
+ rb_define_method(cKadm5Policy, "initialize", rkadm5_policy_init, -1);
175
177
 
176
178
  // Instance methods
177
179