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.
@@ -8,6 +8,7 @@ VALUE cKadm5PrincipalNotFoundException;
8
8
 
9
9
  // Prototype
10
10
  static VALUE rkadm5_close(VALUE);
11
+ static void rkadm5_check_open(RUBY_KADM5 *);
11
12
  static void free_tl_data(krb5_tl_data *);
12
13
  static void free_db_args(char**);
13
14
  char** parse_db_args(VALUE v_db_args);
@@ -17,6 +18,13 @@ void add_tl_data(krb5_int16 *, krb5_tl_data **,
17
18
 
18
19
 
19
20
  // TypedData functions for RUBY_KADM5
21
+ static void rkadm5_typed_mark(void *ptr) {
22
+ if (!ptr) return;
23
+ RUBY_KADM5 *k = (RUBY_KADM5 *)ptr;
24
+ if (k->rb_context != Qnil)
25
+ rb_gc_mark(k->rb_context);
26
+ }
27
+
20
28
  static void rkadm5_typed_free(void *ptr) {
21
29
  if (!ptr) return;
22
30
  RUBY_KADM5 *k = (RUBY_KADM5 *)ptr;
@@ -24,8 +32,10 @@ static void rkadm5_typed_free(void *ptr) {
24
32
  kadm5_destroy(k->handle);
25
33
  if (k->princ)
26
34
  krb5_free_principal(k->ctx, k->princ);
27
- if (k->ctx)
35
+ if (k->ctx && k->rb_context == Qnil)
28
36
  krb5_free_context(k->ctx);
37
+ else if (k->rb_context != Qnil)
38
+ rkrb5_context_release(k->rb_context);
29
39
  free_db_args(k->db_args);
30
40
  free(k);
31
41
  }
@@ -36,7 +46,7 @@ static size_t rkadm5_typed_size(const void *ptr) {
36
46
 
37
47
  static const rb_data_type_t rkadm5_data_type = {
38
48
  "RUBY_KADM5",
39
- {NULL, rkadm5_typed_free, rkadm5_typed_size,},
49
+ {rkadm5_typed_mark, rkadm5_typed_free, rkadm5_typed_size,},
40
50
  NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
41
51
  };
42
52
 
@@ -44,34 +54,53 @@ static const rb_data_type_t rkadm5_data_type = {
44
54
  static VALUE rkadm5_allocate(VALUE klass){
45
55
  RUBY_KADM5* ptr = ALLOC(RUBY_KADM5);
46
56
  memset(ptr, 0, sizeof(RUBY_KADM5));
57
+ ptr->rb_context = Qnil;
47
58
  return TypedData_Wrap_Struct(klass, &rkadm5_data_type, ptr);
48
59
  }
49
60
 
61
+ static void rkadm5_check_open(RUBY_KADM5 *ptr){
62
+ if(!ptr->ctx || !ptr->handle)
63
+ rb_raise(cKadm5Exception, "no administrative context has been established");
64
+ }
65
+
50
66
  /*
51
67
  * call-seq:
52
- * Kerberos::Kadm5.new(:principal => 'name', :password => 'xxxxx')
53
- * Kerberos::Kadm5.new(:principal => 'name', :keytab => '/path/to/your/keytab')
54
- * Kerberos::Kadm5.new(:principal => 'name', :keytab => true)
68
+ * Kerberos::Kadm5.new(principal: 'name', password: 'xxxxx')
69
+ * Kerberos::Kadm5.new(principal: 'name', keytab: '/path/to/your/keytab')
70
+ * Kerberos::Kadm5.new(principal: 'name', keytab: true)
71
+ * Kerberos::Kadm5.new(principal: 'name', ccache: ccache_object)
55
72
  *
56
- * Creates and returns a new Kerberos::Kadm5 object. A hash argument is
57
- * accepted that allows you to specify a principal and a password, or
58
- * a keytab file.
73
+ * Creates and returns a new Kerberos::Kadm5 object. Keyword arguments allow
74
+ * you to specify a principal and a password, a keytab file, or a credentials
75
+ * cache.
59
76
  *
60
77
  * If you pass a string as the :keytab value it will attempt to use that file
61
78
  * for the keytab. If you pass true as the value it will attempt to use the
62
79
  * default keytab file, typically /etc/krb5.keytab.
63
80
  *
81
+ * If you pass a Kerberos::Krb5::CredentialsCache object as the :ccache value,
82
+ * it will authenticate using the credentials stored in that cache via
83
+ * kadm5_init_with_creds.
84
+ *
64
85
  * You may also pass the :service option to specify the service name. The
65
86
  * default is kadmin/admin.
66
87
  *
67
88
  * There is also a :db_args option, which is a single string or array of strings
68
89
  * containing options usually passed to kadmin with the -x switch. For a list of
69
- * available options, see the kadmin manpage
90
+ * available options, see the kadmin manpage.
91
+ *
92
+ * Only one of :password, :keytab, or :ccache may be specified.
70
93
  *
71
94
  */
72
- static VALUE rkadm5_initialize(VALUE self, VALUE v_opts){
95
+ static VALUE rkadm5_initialize(int argc, VALUE* argv, VALUE self){
73
96
  RUBY_KADM5* ptr;
74
- VALUE v_principal, v_password, v_keytab, v_service, v_db_args;
97
+ VALUE v_opts, v_principal, v_password, v_keytab, v_service, v_db_args, v_context, v_ccache;
98
+ ID kw_table[7] = {
99
+ rb_intern("principal"), rb_intern("password"), rb_intern("keytab"),
100
+ rb_intern("ccache"), rb_intern("service"), rb_intern("db_args"),
101
+ rb_intern("context")
102
+ };
103
+ VALUE kw_vals[7];
75
104
  char* user;
76
105
  char* pass = NULL;
77
106
  char* keytab = NULL;
@@ -80,35 +109,56 @@ static VALUE rkadm5_initialize(VALUE self, VALUE v_opts){
80
109
  krb5_error_code kerror;
81
110
 
82
111
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
83
- Check_Type(v_opts, T_HASH);
112
+ rb_scan_args(argc, argv, "0:", &v_opts);
113
+
114
+ if(NIL_P(v_opts))
115
+ v_opts = rb_hash_new();
116
+
117
+ rb_get_kwargs(v_opts, kw_table, 0, 7, kw_vals);
118
+ v_principal = kw_vals[0] == Qundef ? Qnil : kw_vals[0];
119
+ v_password = kw_vals[1] == Qundef ? Qnil : kw_vals[1];
120
+ v_keytab = kw_vals[2] == Qundef ? Qnil : kw_vals[2];
121
+ v_ccache = kw_vals[3] == Qundef ? Qnil : kw_vals[3];
122
+ v_service = kw_vals[4] == Qundef ? Qnil : kw_vals[4];
123
+ v_db_args = kw_vals[5] == Qundef ? Qnil : kw_vals[5];
124
+ v_context = kw_vals[6] == Qundef ? Qnil : kw_vals[6];
125
+
126
+ // Validate mutual exclusivity
127
+ {
128
+ int auth_count = 0;
129
+ if(RTEST(v_password)) auth_count++;
130
+ if(RTEST(v_keytab)) auth_count++;
131
+ if(RTEST(v_ccache)) auth_count++;
132
+
133
+ if(auth_count > 1)
134
+ rb_raise(rb_eArgError, "only one of password, keytab, or ccache may be specified");
135
+
136
+ if(auth_count == 0)
137
+ rb_raise(rb_eArgError, "one of password, keytab, or ccache must be specified");
138
+ }
84
139
 
85
- // Accept both string and symbol keys
86
- v_principal = rb_hash_aref2(v_opts, rb_str_new_cstr("principal"));
87
- if (NIL_P(v_principal)) v_principal = rb_hash_aref2(v_opts, ID2SYM(rb_intern("principal")));
140
+ // Principal must be specified if using a password
141
+ if(RTEST(v_password)){
142
+ if(NIL_P(v_principal))
143
+ rb_raise(rb_eArgError, "principal must be specified");
88
144
 
89
- // Principal must be specified
90
- if(NIL_P(v_principal))
91
- rb_raise(rb_eArgError, "principal must be specified");
145
+ Check_Type(v_password, T_STRING);
146
+ Check_Type(v_principal, T_STRING);
92
147
 
93
- Check_Type(v_principal, T_STRING);
94
- user = StringValueCStr(v_principal);
148
+ pass = StringValueCStr(v_password);
149
+ }
95
150
 
96
- v_password = rb_hash_aref2(v_opts, rb_str_new_cstr("password"));
97
- if (NIL_P(v_password)) v_password = rb_hash_aref2(v_opts, ID2SYM(rb_intern("password")));
98
- v_keytab = rb_hash_aref2(v_opts, rb_str_new_cstr("keytab"));
99
- if (NIL_P(v_keytab)) v_keytab = rb_hash_aref2(v_opts, ID2SYM(rb_intern("keytab")));
151
+ if(RTEST(v_keytab) && NIL_P(v_principal))
152
+ rb_raise(rb_eArgError, "principal must be specified when using a keytab");
100
153
 
101
- if(RTEST(v_password) && RTEST(v_keytab))
102
- rb_raise(rb_eArgError, "cannot use both a password and a keytab");
154
+ if(RTEST(v_ccache) && !rb_obj_is_kind_of(v_ccache, cKrb5CCache))
155
+ rb_raise(rb_eTypeError, "ccache must be a Kerberos::Krb5::CredentialsCache object");
103
156
 
104
- if(RTEST(v_password)){
105
- Check_Type(v_password, T_STRING);
106
- pass = StringValueCStr(v_password);
157
+ if(RTEST(v_ccache) && NIL_P(v_principal)){
158
+ v_principal = rb_funcall(v_ccache, rb_intern("principal"), 0);
107
159
  }
108
160
 
109
- v_service = rb_hash_aref2(v_opts, rb_str_new_cstr("service"));
110
- if (NIL_P(v_service)) v_service = rb_hash_aref2(v_opts, ID2SYM(rb_intern("service")));
111
-
161
+ user = StringValueCStr(v_principal);
112
162
  if(NIL_P(v_service)){
113
163
  service = (char *) "kadmin/admin";
114
164
  }
@@ -117,16 +167,21 @@ static VALUE rkadm5_initialize(VALUE self, VALUE v_opts){
117
167
  service = StringValueCStr(v_service);
118
168
  }
119
169
 
120
- v_db_args = rb_hash_aref2(v_opts, rb_str_new_cstr("db_args"));
121
- if (NIL_P(v_db_args)) v_db_args = rb_hash_aref2(v_opts, ID2SYM(rb_intern("db_args")));
122
170
  ptr->db_args = parse_db_args(v_db_args);
123
171
 
124
- // Normally I would wait to initialize the context, but we might need it
125
- // to get the default keytab file name.
126
- kerror = krb5_init_context(&ptr->ctx);
172
+ // Initialize or borrow the context
173
+ if(!NIL_P(v_context)){
174
+ ptr->ctx = rkrb5_context_borrow(v_context);
175
+ ptr->rb_context = v_context;
176
+ }
177
+ else{
178
+ kerror = krb5_init_context(&ptr->ctx);
127
179
 
128
- if(kerror)
129
- rb_raise(cKadm5Exception, "krb5_init_context: %s", error_message(kerror));
180
+ if(kerror)
181
+ rb_raise(cKadm5Exception, "krb5_init_context: %s", error_message(kerror));
182
+
183
+ ptr->rb_context = Qnil;
184
+ }
130
185
 
131
186
  // The docs say I can use NULL to get the default, but reality appears to be otherwise.
132
187
  if(RTEST(v_keytab)){
@@ -176,8 +231,28 @@ static VALUE rkadm5_initialize(VALUE self, VALUE v_opts){
176
231
  if(kerror)
177
232
  rb_raise(cKadm5Exception, "kadm5_init_with_skey: %s", error_message(kerror));
178
233
  }
179
- else{
180
- // TODO: Credentials cache.
234
+ else if(RTEST(v_ccache)){
235
+ RUBY_KRB5_CCACHE* cc_ptr;
236
+
237
+ TypedData_Get_Struct(v_ccache, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, cc_ptr);
238
+
239
+ if(!cc_ptr->ctx || !cc_ptr->ccache)
240
+ rb_raise(cKrb5Exception, "credentials cache is closed or destroyed");
241
+
242
+ kerror = kadm5_init_with_creds(
243
+ ptr->ctx,
244
+ user,
245
+ cc_ptr->ccache,
246
+ service,
247
+ NULL,
248
+ KADM5_STRUCT_VERSION,
249
+ KADM5_API_VERSION_3,
250
+ ptr->db_args,
251
+ &ptr->handle
252
+ );
253
+
254
+ if(kerror)
255
+ rb_raise(cKadm5Exception, "kadm5_init_with_creds: %s", error_message(kerror));
181
256
  }
182
257
 
183
258
  if(rb_block_given_p()){
@@ -206,8 +281,7 @@ static VALUE rkadm5_set_password(VALUE self, VALUE v_user, VALUE v_pass){
206
281
  user = StringValueCStr(v_user);
207
282
  pass = StringValueCStr(v_pass);
208
283
 
209
- if(!ptr->ctx)
210
- rb_raise(cKadm5Exception, "no context has been established");
284
+ rkadm5_check_open(ptr);
211
285
 
212
286
  if(ptr->princ){
213
287
  krb5_free_principal(ptr->ctx, ptr->princ);
@@ -244,8 +318,7 @@ static VALUE rkadm5_set_pwexpire(VALUE self, VALUE v_user, VALUE v_pwexpire){
244
318
 
245
319
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
246
320
 
247
- if(!ptr->ctx)
248
- rb_raise(cKadm5Exception, "no context has been established");
321
+ rkadm5_check_open(ptr);
249
322
 
250
323
  if(ptr->princ){
251
324
  krb5_free_principal(ptr->ctx, ptr->princ);
@@ -281,50 +354,147 @@ static VALUE rkadm5_set_pwexpire(VALUE self, VALUE v_user, VALUE v_pwexpire){
281
354
 
282
355
  /*
283
356
  * call-seq:
284
- * kadm5.create_principal(name, password, db_args=nil)
285
- * kadm5.create_principal(principal)
357
+ * kadm5.create_principal(name:, password:, db_args: nil)
358
+ * kadm5.create_principal(principal:, password:, db_args: nil)
359
+ *
360
+ * Creates a new principal with an initial password of +password+.
361
+ *
362
+ * The principal may be specified either as a +name+ string or as a
363
+ * +principal+ object (a Kerberos::Krb5::Principal). When a Principal
364
+ * object is provided, any non-nil writable attributes on that object
365
+ * are forwarded to the KDC:
366
+ *
367
+ * * +policy+
368
+ * * +expire_time+
369
+ * * +password_expiration+
370
+ * * +max_life+
371
+ * * +max_renewable_life+
372
+ * * +attributes+
286
373
  *
287
- * Creates a new principal +name+ with an initial password of +password+.
288
- * +db_args+ is an optional string or array of strings containing options that are usually
289
- * passed to add_principal with the -x option. For a list of options, see the kadmin manpage,
290
- * in the add_principal section.
291
- *--
292
- * TODO: Allow a Principal object to be passed in as an argument.
374
+ * +db_args+ is an optional string or array of strings containing options
375
+ * that are usually passed to add_principal with the -x option. For a
376
+ * list of options, see the kadmin manpage, in the add_principal section.
293
377
  */
294
378
  static VALUE rkadm5_create_principal(int argc, VALUE* argv, VALUE self){
295
379
  RUBY_KADM5* ptr;
296
- char* user;
297
380
  char* pass;
298
381
  char** db_args;
299
382
  int mask;
300
383
  kadm5_principal_ent_rec princ;
301
384
  krb5_error_code kerror;
302
- VALUE v_user, v_pass, v_db_args;
385
+ VALUE v_opts, v_name, v_principal, v_pass, v_db_args;
386
+ ID kw_table[4] = { rb_intern("name"), rb_intern("principal"), rb_intern("password"), rb_intern("db_args") };
387
+ VALUE kw_vals[4];
303
388
 
304
389
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
305
390
 
306
- rb_scan_args(argc, argv, "21", &v_user, &v_pass, &v_db_args);
307
- Check_Type(v_user, T_STRING);
391
+ rkadm5_check_open(ptr);
392
+
393
+ rb_scan_args(argc, argv, "0:", &v_opts);
394
+
395
+ if(NIL_P(v_opts))
396
+ v_opts = rb_hash_new();
397
+
398
+ rb_get_kwargs(v_opts, kw_table, 0, 4, kw_vals);
399
+ v_name = kw_vals[0] == Qundef ? Qnil : kw_vals[0];
400
+ v_principal = kw_vals[1] == Qundef ? Qnil : kw_vals[1];
401
+ v_pass = kw_vals[2] == Qundef ? Qnil : kw_vals[2];
402
+ v_db_args = kw_vals[3] == Qundef ? Qnil : kw_vals[3];
403
+
404
+ if(NIL_P(v_pass))
405
+ rb_raise(rb_eArgError, "password: is required");
406
+
308
407
  Check_Type(v_pass, T_STRING);
309
408
 
409
+ if(NIL_P(v_name) && NIL_P(v_principal))
410
+ rb_raise(rb_eArgError, "name: or principal: is required");
411
+
412
+ if(RTEST(v_name) && RTEST(v_principal))
413
+ rb_raise(rb_eArgError, "name: and principal: are mutually exclusive");
414
+
310
415
  memset(&princ, 0, sizeof(princ));
311
416
 
312
417
  mask = KADM5_PRINCIPAL | KADM5_TL_DATA;
313
- user = StringValueCStr(v_user);
314
418
  pass = StringValueCStr(v_pass);
315
419
 
316
420
  db_args = parse_db_args(v_db_args);
317
421
  add_db_args(&princ, db_args);
318
422
  free_db_args(db_args);
319
423
 
320
- if(!ptr->ctx)
321
- rb_raise(cKadm5Exception, "no context has been established");
424
+ // Determine the principal name and populate mask from the principal object
425
+ if(RTEST(v_principal)){
426
+ VALUE v_princ_name, v_policy, v_expire, v_pw_expire, v_max_life, v_max_renew, v_attrs;
322
427
 
323
- kerror = krb5_parse_name(ptr->ctx, user, &princ.principal);
428
+ if(!rb_obj_is_kind_of(v_principal, cKrb5Principal))
429
+ rb_raise(rb_eTypeError, "principal: must be a Kerberos::Krb5::Principal object");
324
430
 
325
- if(kerror){
326
- free_tl_data(princ.tl_data);
327
- rb_raise(cKadm5Exception, "krb5_parse_name: %s", error_message(kerror));
431
+ v_princ_name = rb_iv_get(v_principal, "@principal");
432
+
433
+ if(NIL_P(v_princ_name))
434
+ rb_raise(rb_eArgError, "principal object has no name set");
435
+
436
+ Check_Type(v_princ_name, T_STRING);
437
+
438
+ kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_princ_name), &princ.principal);
439
+
440
+ if(kerror){
441
+ free_tl_data(princ.tl_data);
442
+ rb_raise(cKadm5Exception, "krb5_parse_name: %s", error_message(kerror));
443
+ }
444
+
445
+ // Forward optional attributes from the Principal object
446
+ v_policy = rb_iv_get(v_principal, "@policy");
447
+
448
+ if(RTEST(v_policy)){
449
+ Check_Type(v_policy, T_STRING);
450
+ princ.policy = StringValueCStr(v_policy);
451
+ mask |= KADM5_POLICY;
452
+ }
453
+
454
+ v_expire = rb_iv_get(v_principal, "@expire_time");
455
+
456
+ if(RTEST(v_expire)){
457
+ princ.princ_expire_time = (krb5_timestamp)NUM2LONG(rb_funcall(v_expire, rb_intern("to_i"), 0));
458
+ mask |= KADM5_PRINC_EXPIRE_TIME;
459
+ }
460
+
461
+ v_pw_expire = rb_iv_get(v_principal, "@password_expiration");
462
+
463
+ if(RTEST(v_pw_expire)){
464
+ princ.pw_expiration = (krb5_timestamp)NUM2LONG(rb_funcall(v_pw_expire, rb_intern("to_i"), 0));
465
+ mask |= KADM5_PW_EXPIRATION;
466
+ }
467
+
468
+ v_max_life = rb_iv_get(v_principal, "@max_life");
469
+
470
+ if(RTEST(v_max_life)){
471
+ princ.max_life = NUM2LONG(v_max_life);
472
+ mask |= KADM5_MAX_LIFE;
473
+ }
474
+
475
+ v_max_renew = rb_iv_get(v_principal, "@max_renewable_life");
476
+
477
+ if(RTEST(v_max_renew)){
478
+ princ.max_renewable_life = NUM2LONG(v_max_renew);
479
+ mask |= KADM5_MAX_RLIFE;
480
+ }
481
+
482
+ v_attrs = rb_iv_get(v_principal, "@attributes");
483
+
484
+ if(RTEST(v_attrs)){
485
+ princ.attributes = NUM2LONG(v_attrs);
486
+ mask |= KADM5_ATTRIBUTES;
487
+ }
488
+ }
489
+ else{
490
+ Check_Type(v_name, T_STRING);
491
+
492
+ kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_name), &princ.principal);
493
+
494
+ if(kerror){
495
+ free_tl_data(princ.tl_data);
496
+ rb_raise(cKadm5Exception, "krb5_parse_name: %s", error_message(kerror));
497
+ }
328
498
  }
329
499
 
330
500
  kerror = kadm5_create_principal(ptr->handle, &princ, mask, pass);
@@ -355,8 +525,7 @@ static VALUE rkadm5_delete_principal(VALUE self, VALUE v_user){
355
525
  Check_Type(v_user, T_STRING);
356
526
  user = StringValueCStr(v_user);
357
527
 
358
- if(!ptr->ctx)
359
- rb_raise(cKadm5Exception, "no context has been established");
528
+ rkadm5_check_open(ptr);
360
529
 
361
530
  if(ptr->princ){
362
531
  krb5_free_principal(ptr->ctx, ptr->princ);
@@ -396,8 +565,10 @@ static VALUE rkadm5_close(VALUE self){
396
565
  if(ptr->princ)
397
566
  krb5_free_principal(ptr->ctx, ptr->princ);
398
567
 
399
- if(ptr->ctx)
568
+ if(ptr->ctx && ptr->rb_context == Qnil)
400
569
  krb5_free_context(ptr->ctx);
570
+ else if(ptr->rb_context != Qnil)
571
+ rkrb5_context_release(ptr->rb_context);
401
572
 
402
573
  free_db_args(ptr->db_args);
403
574
 
@@ -405,6 +576,7 @@ static VALUE rkadm5_close(VALUE self){
405
576
  ptr->ctx = NULL;
406
577
  ptr->princ = NULL;
407
578
  ptr->handle = NULL;
579
+ ptr->rb_context = Qnil;
408
580
 
409
581
  return self;
410
582
  }
@@ -413,11 +585,11 @@ static VALUE rkadm5_close(VALUE self){
413
585
  static VALUE create_principal_from_entry(VALUE v_name, RUBY_KADM5* ptr, kadm5_principal_ent_rec* ent){
414
586
  krb5_error_code kerror;
415
587
  VALUE v_principal;
416
- VALUE v_args[1];
588
+ VALUE v_opts = rb_hash_new();
417
589
 
418
- v_args[0] = v_name;
590
+ rb_hash_aset(v_opts, ID2SYM(rb_intern("name")), v_name);
419
591
 
420
- v_principal = rb_class_new_instance(1, v_args, cKrb5Principal);
592
+ v_principal = rb_class_new_instance_kw(1, &v_opts, cKrb5Principal, RB_PASS_KEYWORDS);
421
593
 
422
594
  rb_iv_set(v_principal, "@attributes", LONG2FIX(ent->attributes));
423
595
  rb_iv_set(v_principal, "@aux_attributes", INT2FIX(ent->aux_attributes));
@@ -492,8 +664,7 @@ static VALUE rkadm5_find_principal(VALUE self, VALUE v_user){
492
664
 
493
665
  memset(&ent, 0, sizeof(ent));
494
666
 
495
- if(!ptr->ctx)
496
- rb_raise(cKadm5Exception, "no context has been established");
667
+ rkadm5_check_open(ptr);
497
668
 
498
669
  if(ptr->princ){
499
670
  krb5_free_principal(ptr->ctx, ptr->princ);
@@ -553,8 +724,7 @@ static VALUE rkadm5_get_principal(VALUE self, VALUE v_user){
553
724
 
554
725
  memset(&ent, 0, sizeof(ent));
555
726
 
556
- if(!ptr->ctx)
557
- rb_raise(cKadm5Exception, "no context has been established");
727
+ rkadm5_check_open(ptr);
558
728
 
559
729
  if(ptr->princ){
560
730
  krb5_free_principal(ptr->ctx, ptr->princ);
@@ -596,28 +766,41 @@ static VALUE rkadm5_get_principal(VALUE self, VALUE v_user){
596
766
  * Example:
597
767
  *
598
768
  * # Using a Policy object
599
- * policy = Kerberos::Kadm5::Policy.new(:name => 'test', :min_length => 5)
769
+ * policy = Kerberos::Kadm5::Policy.new(name: 'test', min_length: 5)
600
770
  * kadm5.create_policy(policy)
601
771
  *
602
- * # Using a hash
603
- * kadm5.create_policy(:name => 'test', :min_length => 5)
772
+ * # Using keywords
773
+ * kadm5.create_policy(name: 'test', min_length: 5)
604
774
  */
605
- static VALUE rkadm5_create_policy(VALUE self, VALUE v_policy){
775
+ static VALUE rkadm5_create_policy(int argc, VALUE* argv, VALUE self){
606
776
  RUBY_KADM5* ptr;
607
777
  kadm5_ret_t kerror;
608
778
  kadm5_policy_ent_rec ent;
609
779
  long mask = KADM5_POLICY;
780
+ VALUE v_policy, v_kwargs;
610
781
  VALUE v_name, v_min_classes, v_min_life, v_max_life, v_min_length, v_history_num;
611
782
 
612
783
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
613
784
 
614
- // Allow a hash or a Policy object
615
- if(rb_obj_is_kind_of(v_policy, rb_cHash)){
785
+ rkadm5_check_open(ptr);
786
+
787
+ rb_scan_args(argc, argv, "01:", &v_policy, &v_kwargs);
788
+
789
+ if(!NIL_P(v_kwargs)){
616
790
  VALUE v_args[1];
617
- v_args[0] = v_policy;
618
- v_policy = rb_class_new_instance(1, v_args, cKadm5Policy);
791
+ if(!NIL_P(v_policy))
792
+ rb_raise(rb_eArgError, "provide a Policy object or keyword arguments, not both");
793
+
794
+ v_args[0] = v_kwargs;
795
+ v_policy = rb_class_new_instance_kw(1, v_args, cKadm5Policy, RB_PASS_KEYWORDS);
619
796
  }
620
797
 
798
+ if(NIL_P(v_policy))
799
+ rb_raise(rb_eArgError, "a Policy object or policy keywords are required");
800
+
801
+ if(!rb_obj_is_kind_of(v_policy, cKadm5Policy))
802
+ rb_raise(rb_eTypeError, "expected a Kerberos::Kadm5::Policy object or policy keywords");
803
+
621
804
  v_name = rb_iv_get(v_policy, "@policy");
622
805
  v_min_classes = rb_iv_get(v_policy, "@min_classes");
623
806
  v_min_length = rb_iv_get(v_policy, "@min_length");
@@ -678,6 +861,8 @@ static VALUE rkadm5_delete_policy(VALUE self, VALUE v_policy){
678
861
 
679
862
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
680
863
 
864
+ rkadm5_check_open(ptr);
865
+
681
866
  policy = StringValueCStr(v_policy);
682
867
 
683
868
  kerror = kadm5_delete_policy(ptr->handle, policy);
@@ -708,8 +893,7 @@ static VALUE rkadm5_get_policy(VALUE self, VALUE v_name){
708
893
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
709
894
  memset(&ent, 0, sizeof(ent));
710
895
 
711
- if(!ptr->ctx)
712
- rb_raise(cKadm5Exception, "no context has been established");
896
+ rkadm5_check_open(ptr);
713
897
 
714
898
  policy_name = StringValueCStr(v_name);
715
899
 
@@ -725,16 +909,16 @@ static VALUE rkadm5_get_policy(VALUE self, VALUE v_name){
725
909
  VALUE v_arg[1];
726
910
  VALUE v_hash = rb_hash_new();
727
911
 
728
- rb_hash_aset(v_hash, rb_str_new2("name"), rb_str_new2(ent.policy));
729
- rb_hash_aset(v_hash, rb_str_new2("min_life"), LONG2FIX(ent.pw_min_life));
730
- rb_hash_aset(v_hash, rb_str_new2("max_life"), LONG2FIX(ent.pw_max_life));
731
- rb_hash_aset(v_hash, rb_str_new2("min_length"), LONG2FIX(ent.pw_min_length));
732
- rb_hash_aset(v_hash, rb_str_new2("min_classes"), LONG2FIX(ent.pw_min_classes));
733
- rb_hash_aset(v_hash, rb_str_new2("history_num"), LONG2FIX(ent.pw_history_num));
912
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("name")), rb_str_new2(ent.policy));
913
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("min_life")), LONG2FIX(ent.pw_min_life));
914
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("max_life")), LONG2FIX(ent.pw_max_life));
915
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("min_length")), LONG2FIX(ent.pw_min_length));
916
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("min_classes")), LONG2FIX(ent.pw_min_classes));
917
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("history_num")), LONG2FIX(ent.pw_history_num));
734
918
 
735
919
  v_arg[0] = v_hash;
736
920
 
737
- v_policy = rb_class_new_instance(1, v_arg, cKadm5Policy);
921
+ v_policy = rb_class_new_instance_kw(1, v_arg, cKadm5Policy, RB_PASS_KEYWORDS);
738
922
 
739
923
  kadm5_free_policy_ent(ptr->handle, &ent);
740
924
  }
@@ -762,8 +946,7 @@ static VALUE rkadm5_find_policy(VALUE self, VALUE v_name){
762
946
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
763
947
  memset(&ent, 0, sizeof(ent));
764
948
 
765
- if(!ptr->ctx)
766
- rb_raise(cKadm5Exception, "no context has been established");
949
+ rkadm5_check_open(ptr);
767
950
 
768
951
  policy_name = StringValueCStr(v_name);
769
952
 
@@ -782,16 +965,16 @@ static VALUE rkadm5_find_policy(VALUE self, VALUE v_name){
782
965
  VALUE v_arg[1];
783
966
  VALUE v_hash = rb_hash_new();
784
967
 
785
- rb_hash_aset(v_hash, rb_str_new2("name"), rb_str_new2(ent.policy));
786
- rb_hash_aset(v_hash, rb_str_new2("min_life"), LONG2FIX(ent.pw_min_life));
787
- rb_hash_aset(v_hash, rb_str_new2("max_life"), LONG2FIX(ent.pw_max_life));
788
- rb_hash_aset(v_hash, rb_str_new2("min_length"), LONG2FIX(ent.pw_min_length));
789
- rb_hash_aset(v_hash, rb_str_new2("min_classes"), LONG2FIX(ent.pw_min_classes));
790
- rb_hash_aset(v_hash, rb_str_new2("history_num"), LONG2FIX(ent.pw_history_num));
968
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("name")), rb_str_new2(ent.policy));
969
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("min_life")), LONG2FIX(ent.pw_min_life));
970
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("max_life")), LONG2FIX(ent.pw_max_life));
971
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("min_length")), LONG2FIX(ent.pw_min_length));
972
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("min_classes")), LONG2FIX(ent.pw_min_classes));
973
+ rb_hash_aset(v_hash, ID2SYM(rb_intern("history_num")), LONG2FIX(ent.pw_history_num));
791
974
 
792
975
  v_arg[0] = v_hash;
793
976
 
794
- v_policy = rb_class_new_instance(1, v_arg, cKadm5Policy);
977
+ v_policy = rb_class_new_instance_kw(1, v_arg, cKadm5Policy, RB_PASS_KEYWORDS);
795
978
 
796
979
  kadm5_free_policy_ent(ptr->handle, &ent);
797
980
  }
@@ -808,34 +991,59 @@ static VALUE rkadm5_find_policy(VALUE self, VALUE v_name){
808
991
  * Example:
809
992
  *
810
993
  * policy = Kerberos::Kadm5::Policy.find('test')
811
- * policy.max_length = 1024
994
+ * policy.max_life = 1024
812
995
  * kadm5.modify_policy(policy)
813
996
  */
814
997
  static VALUE rkadm5_modify_policy(VALUE self, VALUE v_policy){
815
998
  RUBY_KADM5* ptr;
816
- RUBY_KADM5_POLICY* pptr;
817
999
  kadm5_ret_t kerror;
818
- long mask = KADM5_POLICY;
1000
+ kadm5_policy_ent_rec ent;
1001
+ long mask = 0;
1002
+ VALUE v_name, v_min_classes, v_min_life, v_max_life, v_min_length, v_history_num;
819
1003
 
820
1004
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
821
- TypedData_Get_Struct(v_policy, RUBY_KADM5_POLICY, &rkadm5_policy_data_type, pptr);
822
1005
 
823
- if(!ptr->ctx)
824
- rb_raise(cKadm5Exception, "no context has been established");
1006
+ rkadm5_check_open(ptr);
825
1007
 
826
- if(pptr->policy.pw_min_classes)
1008
+ if(!rb_obj_is_kind_of(v_policy, cKadm5Policy))
1009
+ rb_raise(rb_eTypeError, "expected a Kerberos::Kadm5::Policy object");
1010
+
1011
+ v_name = rb_iv_get(v_policy, "@policy");
1012
+ v_min_classes = rb_iv_get(v_policy, "@min_classes");
1013
+ v_min_length = rb_iv_get(v_policy, "@min_length");
1014
+ v_min_life = rb_iv_get(v_policy, "@min_life");
1015
+ v_max_life = rb_iv_get(v_policy, "@max_life");
1016
+ v_history_num = rb_iv_get(v_policy, "@history_num");
1017
+
1018
+ memset(&ent, 0, sizeof(ent));
1019
+ ent.policy = StringValueCStr(v_name);
1020
+
1021
+ if(!NIL_P(v_min_classes)){
827
1022
  mask |= KADM5_PW_MIN_CLASSES;
1023
+ ent.pw_min_classes = NUM2LONG(v_min_classes);
1024
+ }
828
1025
 
829
- if(pptr->policy.pw_min_length)
1026
+ if(!NIL_P(v_min_length)){
830
1027
  mask |= KADM5_PW_MIN_LENGTH;
1028
+ ent.pw_min_length = NUM2LONG(v_min_length);
1029
+ }
831
1030
 
832
- if(pptr->policy.pw_min_life)
1031
+ if(!NIL_P(v_min_life)){
833
1032
  mask |= KADM5_PW_MIN_LIFE;
1033
+ ent.pw_min_life = NUM2LONG(v_min_life);
1034
+ }
834
1035
 
835
- if(pptr->policy.pw_max_life)
1036
+ if(!NIL_P(v_max_life)){
836
1037
  mask |= KADM5_PW_MAX_LIFE;
1038
+ ent.pw_max_life = NUM2LONG(v_max_life);
1039
+ }
837
1040
 
838
- kerror = kadm5_modify_policy(ptr->handle, &pptr->policy, mask);
1041
+ if(!NIL_P(v_history_num)){
1042
+ mask |= KADM5_PW_HISTORY_NUM;
1043
+ ent.pw_history_num = NUM2LONG(v_history_num);
1044
+ }
1045
+
1046
+ kerror = kadm5_modify_policy(ptr->handle, &ent, mask);
839
1047
 
840
1048
  if(kerror)
841
1049
  rb_raise(cKadm5Exception, "kadm5_modify_policy: %s (%li)", error_message(kerror), kerror);
@@ -866,6 +1074,8 @@ static VALUE rkadm5_get_policies(int argc, VALUE* argv, VALUE self){
866
1074
 
867
1075
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
868
1076
 
1077
+ rkadm5_check_open(ptr);
1078
+
869
1079
  rb_scan_args(argc, argv, "01", &v_expr);
870
1080
 
871
1081
  if(NIL_P(v_expr))
@@ -914,6 +1124,8 @@ static VALUE rkadm5_get_principals(int argc, VALUE* argv, VALUE self){
914
1124
 
915
1125
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
916
1126
 
1127
+ rkadm5_check_open(ptr);
1128
+
917
1129
  rb_scan_args(argc, argv, "01", &v_expr);
918
1130
 
919
1131
  if(NIL_P(v_expr))
@@ -962,6 +1174,8 @@ static VALUE rkadm5_get_privs(int argc, VALUE* argv, VALUE self){
962
1174
 
963
1175
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
964
1176
 
1177
+ rkadm5_check_open(ptr);
1178
+
965
1179
  rb_scan_args(argc, argv, "01", &v_strings);
966
1180
 
967
1181
  kerror = kadm5_get_privs(ptr->handle, &privs);
@@ -1007,8 +1221,7 @@ static VALUE rkadm5_randkey_principal(VALUE self, VALUE v_user){
1007
1221
 
1008
1222
  user = StringValueCStr(v_user);
1009
1223
 
1010
- if(!ptr->ctx)
1011
- rb_raise(cKadm5Exception, "no context has been established");
1224
+ rkadm5_check_open(ptr);
1012
1225
 
1013
1226
  kerror = krb5_parse_name(ptr->ctx, user, &princ);
1014
1227
 
@@ -1047,10 +1260,13 @@ char** parse_db_args(VALUE v_db_args){
1047
1260
  case T_ARRAY:
1048
1261
  // Multiple arguments
1049
1262
  array_length = RARRAY_LEN(v_db_args);
1263
+
1264
+ for(long i = 0; i < array_length; ++i)
1265
+ Check_Type(rb_ary_entry(v_db_args, i), T_STRING);
1266
+
1050
1267
  db_args = (char **) malloc((array_length + 1) * sizeof(char *));
1051
1268
  for(long i = 0; i < array_length; ++i){
1052
1269
  VALUE elem = rb_ary_entry(v_db_args, i);
1053
- Check_Type(elem, T_STRING);
1054
1270
  db_args[i] = strdup(StringValueCStr(elem));
1055
1271
  }
1056
1272
  db_args[array_length] = NULL;
@@ -1137,12 +1353,12 @@ void Init_kadm5(void){
1137
1353
 
1138
1354
  // Initialization Method
1139
1355
 
1140
- rb_define_method(cKadm5, "initialize", rkadm5_initialize, 1);
1356
+ rb_define_method(cKadm5, "initialize", rkadm5_initialize, -1);
1141
1357
 
1142
1358
  // Instance Methods
1143
1359
 
1144
1360
  rb_define_method(cKadm5, "close", rkadm5_close, 0);
1145
- rb_define_method(cKadm5, "create_policy", rkadm5_create_policy, 1);
1361
+ rb_define_method(cKadm5, "create_policy", rkadm5_create_policy, -1);
1146
1362
  rb_define_method(cKadm5, "create_principal", rkadm5_create_principal, -1);
1147
1363
  rb_define_method(cKadm5, "delete_policy", rkadm5_delete_policy, 1);
1148
1364
  rb_define_method(cKadm5, "delete_principal", rkadm5_delete_principal, 1);