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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b4449ae24587324c6b505b5b6c61fa612730a6bc1443fdb0434d8f1d0a80ce0
4
- data.tar.gz: 35b35b78ba36dadeb712e340956ae12a8f809e619d05a83bd089c295f6068937
3
+ metadata.gz: 0dc45dbb751a22370948c430c6590df2a07739638dee7d208f8050d515dfbbfc
4
+ data.tar.gz: 318bd1b2a81b3791fddf876cdc65895a84645d9f60aa0c0cef3872005b217f6b
5
5
  SHA512:
6
- metadata.gz: d70da2c89782b2cd3a04be1ac6a07417b7baaa841fec419a95f9c225d52183d6de6ee4110c7cec4a40561eaf9bc330e81d38e0a56c7a2f0edb297730ef3022e2
7
- data.tar.gz: 1ceaebf845c73143ba93c85e7e99a714a2ad4af2b6fdef881c550de0e0de6151e034f3012b7c570c75f0e5fc00555e4b4fcc578c0ed8c6b99d7ff2fbdee6ea6d
6
+ metadata.gz: 0aebb033c55950e140cbfc02abf00f37f4cedd5141228f0bb135d4b70dd93ce50dda121bda128374339259a4a58f7c6e5db09590cd41ba708130beafca734bd2
7
+ data.tar.gz: 26753f9dcecbecbf3234fc941fe42d380ac0e77829671fe4cc947dfcc081166e0faf77444fa65533bd1b26713b81f67f13f59e5be01a1f0749f3fc7b85430644
data/CHANGES.md CHANGED
@@ -1,3 +1,18 @@
1
+ # 0.3.1 - 21-Aug-2026
2
+ * Hardened initial credential verification so it fails closed when verification
3
+ cannot be performed, rejects closed keytabs and credential caches, and safely
4
+ initializes a supplied cache before storing credentials.
5
+ * Added shared context lifetime tracking. `Context#close` now raises while
6
+ dependent wrappers remain open; `close(force: true)` logically closes the
7
+ context and defers native cleanup until its dependents close.
8
+ * Hardened native handle cleanup and closed-object checks across `Krb5`, `Kadm5`,
9
+ `CredentialsCache`, `Keytab`, and `Principal`.
10
+ * Fixed `Kadm5#modify_policy` to apply values assigned through `Policy` writers,
11
+ including password history, and require an explicit principal for keytab
12
+ authentication.
13
+ * Fixed `Keytab#each` and `Keytab.foreach` to raise iteration errors instead of
14
+ silently returning partial results.
15
+
1
16
  # 0.3.0 - 20-Aug-2026
2
17
  * Added optional shared `Context` support to the `Krb5`, `Kadm5`, `Config`,
3
18
  `CredentialsCache`, `Keytab`, and `Principal` constructors.
data/EXAMPLES.md CHANGED
@@ -183,7 +183,7 @@ krb5.close
183
183
  ### Version
184
184
 
185
185
  ```ruby
186
- puts Kerberos::Krb5::VERSION # => "0.3.0"
186
+ puts Kerberos::Krb5::VERSION # => "0.3.1"
187
187
  ```
188
188
 
189
189
  ---
data/README.md CHANGED
@@ -18,7 +18,12 @@ puts krb.get_permitted_enctypes.keys.join(',')
18
18
 
19
19
  # Credentials cache
20
20
  cc = Kerberos::Krb5::CredentialsCache.new
21
- krb.verify_init_creds(ccache: cc)
21
+ krb.get_init_creds_password(
22
+ principal: ENV['KRB5_PRINCIPAL'],
23
+ password: ENV['KRB5_PASSWORD'],
24
+ ccache: cc
25
+ )
26
+ krb.verify_init_creds
22
27
  puts cc.primary_principal
23
28
 
24
29
  # Keytab
@@ -38,6 +43,13 @@ ctx = Kerberos::Krb5::Context.new # standard context
38
43
  ctx = Kerberos::Krb5::Context.new(profile: '/etc/krb5.conf') # or use a profile
39
44
  ctx = Kerberos::Krb5::Context.new(secure: true) # or use a secure context
40
45
  ctx.close
46
+
47
+ # Contexts with active dependent wrappers reject close. A forced close stops
48
+ # direct context use immediately and defers native cleanup until dependents close.
49
+ ctx = Kerberos::Krb5::Context.new
50
+ krb = Kerberos::Krb5.new(context: ctx)
51
+ ctx.close(force: true)
52
+ krb.close
41
53
  ```
42
54
 
43
55
  # Requirements
@@ -160,7 +172,6 @@ The test environment includes:
160
172
  # TODO
161
173
  * Create a separate class for the replay cache.
162
174
  * Better credentials cache support.
163
- * Ability to add and delete keytab entries.
164
175
 
165
176
  # Authors
166
177
  * Daniel Berger
@@ -20,6 +20,8 @@ static void rkrb5_ccache_typed_free(void *ptr) {
20
20
  krb5_free_principal(c->ctx, c->principal);
21
21
  if (c->ctx && c->rb_context == Qnil)
22
22
  krb5_free_context(c->ctx);
23
+ else if (c->rb_context != Qnil)
24
+ rkrb5_context_release(c->rb_context);
23
25
  free(c);
24
26
  }
25
27
 
@@ -91,17 +93,7 @@ static VALUE rkrb5_ccache_initialize(int argc, VALUE* argv, VALUE self){
91
93
 
92
94
  // Initialize or borrow the context
93
95
  if(!NIL_P(v_context)){
94
- RUBY_KRB5_CONTEXT* ctx_ptr;
95
-
96
- if(!rb_obj_is_kind_of(v_context, cKrb5Context))
97
- rb_raise(rb_eTypeError, "context must be a Kerberos::Krb5::Context object");
98
-
99
- TypedData_Get_Struct(v_context, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ctx_ptr);
100
-
101
- if(!ctx_ptr->ctx)
102
- rb_raise(cKrb5Exception, "context is closed");
103
-
104
- ptr->ctx = ctx_ptr->ctx;
96
+ ptr->ctx = rkrb5_context_borrow(v_context);
105
97
  ptr->rb_context = v_context;
106
98
  }
107
99
  else{
@@ -176,6 +168,8 @@ static VALUE rkrb5_ccache_close(VALUE self){
176
168
 
177
169
  if(ptr->ctx && ptr->rb_context == Qnil)
178
170
  krb5_free_context(ptr->ctx);
171
+ else if(ptr->rb_context != Qnil)
172
+ rkrb5_context_release(ptr->rb_context);
179
173
 
180
174
  ptr->ccache = NULL;
181
175
  ptr->ctx = NULL;
@@ -309,6 +303,8 @@ static VALUE rkrb5_ccache_destroy(VALUE self){
309
303
 
310
304
  if(ptr->ctx && ptr->rb_context == Qnil)
311
305
  krb5_free_context(ptr->ctx);
306
+ else if(ptr->rb_context != Qnil)
307
+ rkrb5_context_release(ptr->rb_context);
312
308
 
313
309
  ptr->ccache = NULL;
314
310
  ptr->ctx = NULL;
@@ -324,6 +320,8 @@ static VALUE rkrb5_ccache_destroy(VALUE self){
324
320
 
325
321
  if(ptr->ctx && ptr->rb_context == Qnil)
326
322
  krb5_free_context(ptr->ctx);
323
+ else if(ptr->rb_context != Qnil)
324
+ rkrb5_context_release(ptr->rb_context);
327
325
 
328
326
  ptr->ccache = NULL;
329
327
  ptr->ctx = NULL;
@@ -4,6 +4,22 @@
4
4
  VALUE cKadm5Config;
5
5
  VALUE cKeySalt;
6
6
 
7
+ static void rkadm5_config_cleanup(RUBY_KADM5_CONFIG *ptr){
8
+ if(!ptr->ctx)
9
+ return;
10
+
11
+ kadm5_free_config_params(ptr->ctx, &ptr->config);
12
+
13
+ if(ptr->rb_context == Qnil)
14
+ krb5_free_context(ptr->ctx);
15
+ else
16
+ rkrb5_context_release(ptr->rb_context);
17
+
18
+ ptr->ctx = NULL;
19
+ ptr->rb_context = Qnil;
20
+ memset(&ptr->config, 0, sizeof(ptr->config));
21
+ }
22
+
7
23
 
8
24
  // TypedData functions for RUBY_KADM5_CONFIG
9
25
  static void rkadm5_config_typed_mark(void *ptr) {
@@ -16,11 +32,7 @@ static void rkadm5_config_typed_mark(void *ptr) {
16
32
  static void rkadm5_config_typed_free(void *ptr) {
17
33
  if (!ptr) return;
18
34
  RUBY_KADM5_CONFIG *c = (RUBY_KADM5_CONFIG *)ptr;
19
- if (c->ctx) {
20
- kadm5_free_config_params(c->ctx, &c->config);
21
- if (c->rb_context == Qnil)
22
- krb5_free_context(c->ctx);
23
- }
35
+ rkadm5_config_cleanup(c);
24
36
  free(c);
25
37
  }
26
38
 
@@ -85,17 +97,7 @@ static VALUE rkadm5_config_initialize(int argc, VALUE* argv, VALUE self){
85
97
  v_context = kw_vals[0] == Qundef ? Qnil : kw_vals[0];
86
98
 
87
99
  if(!NIL_P(v_context)){
88
- RUBY_KRB5_CONTEXT* ctx_ptr;
89
-
90
- if(!rb_obj_is_kind_of(v_context, cKrb5Context))
91
- rb_raise(rb_eTypeError, "context must be a Kerberos::Krb5::Context object");
92
-
93
- TypedData_Get_Struct(v_context, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ctx_ptr);
94
-
95
- if(!ctx_ptr->ctx)
96
- rb_raise(cKrb5Exception, "context is closed");
97
-
98
- ptr->ctx = ctx_ptr->ctx;
100
+ ptr->ctx = rkrb5_context_borrow(v_context);
99
101
  ptr->rb_context = v_context;
100
102
  }
101
103
  else{
@@ -234,6 +236,10 @@ static VALUE rkadm5_config_initialize(int argc, VALUE* argv, VALUE self){
234
236
  rb_iv_set(self, "@keysalts", Qnil);
235
237
  }
236
238
 
239
+ // All configuration data has been copied into Ruby values, so this object
240
+ // no longer needs to retain its native context lease.
241
+ rkadm5_config_cleanup(ptr);
242
+
237
243
  // This is read only data
238
244
  rb_obj_freeze(self);
239
245
 
@@ -15,6 +15,38 @@ static void rkrb5_context_typed_free(void *ptr) {
15
15
  free(c);
16
16
  }
17
17
 
18
+ krb5_context rkrb5_context_borrow(VALUE v_context){
19
+ RUBY_KRB5_CONTEXT* ptr;
20
+
21
+ if(!rb_obj_is_kind_of(v_context, cKrb5Context))
22
+ rb_raise(rb_eTypeError, "context must be a Kerberos::Krb5::Context object");
23
+
24
+ TypedData_Get_Struct(v_context, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ptr);
25
+
26
+ if(!ptr->ctx || ptr->closed)
27
+ rb_raise(cKrb5Exception, "context is closed");
28
+
29
+ ptr->borrowers++;
30
+ return ptr->ctx;
31
+ }
32
+
33
+ void rkrb5_context_release(VALUE v_context){
34
+ RUBY_KRB5_CONTEXT* ptr;
35
+
36
+ if(NIL_P(v_context))
37
+ return;
38
+
39
+ ptr = (RUBY_KRB5_CONTEXT*)RTYPEDDATA_DATA(v_context);
40
+
41
+ if(ptr->borrowers > 0)
42
+ ptr->borrowers--;
43
+
44
+ if(ptr->closed && ptr->borrowers == 0 && ptr->ctx){
45
+ krb5_free_context(ptr->ctx);
46
+ ptr->ctx = NULL;
47
+ }
48
+ }
49
+
18
50
  static size_t rkrb5_context_typed_size(const void *ptr) {
19
51
  return sizeof(RUBY_KRB5_CONTEXT);
20
52
  }
@@ -34,19 +66,42 @@ static VALUE rkrb5_context_allocate(VALUE klass){
34
66
 
35
67
  /*
36
68
  * call-seq:
37
- * context.close
69
+ * context.close(force: false)
38
70
  *
39
- * Closes the context object.
71
+ * Closes the context object. Raises if wrappers are still borrowing the
72
+ * context. With +force: true+, the context is closed to new operations and
73
+ * borrowers immediately, but its native resources are retained until the
74
+ * existing borrowers have closed.
40
75
  */
41
- static VALUE rkrb5_context_close(VALUE self){
76
+ static VALUE rkrb5_context_close(int argc, VALUE* argv, VALUE self){
42
77
  RUBY_KRB5_CONTEXT* ptr;
78
+ VALUE v_opts, v_force;
79
+ ID kw_table[1] = { rb_intern("force") };
80
+ VALUE kw_vals[1];
43
81
 
44
82
  TypedData_Get_Struct(self, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ptr);
45
83
 
46
- if(ptr->ctx)
47
- krb5_free_context(ptr->ctx);
84
+ rb_scan_args(argc, argv, "0:", &v_opts);
85
+
86
+ if(NIL_P(v_opts))
87
+ v_opts = rb_hash_new();
88
+
89
+ rb_get_kwargs(v_opts, kw_table, 0, 1, kw_vals);
90
+ v_force = kw_vals[0] == Qundef ? Qfalse : kw_vals[0];
48
91
 
49
- ptr->ctx = NULL;
92
+ if(ptr->closed || !ptr->ctx)
93
+ return self;
94
+
95
+ if(ptr->borrowers > 0 && !RTEST(v_force))
96
+ rb_raise(cKrb5Exception, "context is in use by %lu dependent wrapper%s",
97
+ (unsigned long)ptr->borrowers, ptr->borrowers == 1 ? "" : "s");
98
+
99
+ ptr->closed = 1;
100
+
101
+ if(ptr->borrowers == 0){
102
+ krb5_free_context(ptr->ctx);
103
+ ptr->ctx = NULL;
104
+ }
50
105
 
51
106
  return self;
52
107
  }
@@ -147,7 +202,7 @@ static VALUE rkrb5_context_default_realm(VALUE self){
147
202
 
148
203
  TypedData_Get_Struct(self, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ptr);
149
204
 
150
- if(!ptr->ctx)
205
+ if(!ptr->ctx || ptr->closed)
151
206
  rb_raise(cKrb5Exception, "no context has been established");
152
207
 
153
208
  kerror = krb5_get_default_realm(ptr->ctx, &realm);
@@ -175,7 +230,7 @@ static VALUE rkrb5_context_set_default_realm(VALUE self, VALUE v_realm){
175
230
 
176
231
  TypedData_Get_Struct(self, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ptr);
177
232
 
178
- if(!ptr->ctx)
233
+ if(!ptr->ctx || ptr->closed)
179
234
  rb_raise(cKrb5Exception, "no context has been established");
180
235
 
181
236
  if(NIL_P(v_realm)){
@@ -205,7 +260,7 @@ void Init_context(void){
205
260
  rb_define_method(cKrb5Context, "initialize", rkrb5_context_initialize, -1);
206
261
 
207
262
  // Instance Methods
208
- rb_define_method(cKrb5Context, "close", rkrb5_context_close, 0);
263
+ rb_define_method(cKrb5Context, "close", rkrb5_context_close, -1);
209
264
  rb_define_method(cKrb5Context, "default_realm", rkrb5_context_default_realm, 0);
210
265
  rb_define_method(cKrb5Context, "default_realm=", rkrb5_context_set_default_realm, 1);
211
266
  }
@@ -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);
@@ -33,6 +34,8 @@ static void rkadm5_typed_free(void *ptr) {
33
34
  krb5_free_principal(k->ctx, k->princ);
34
35
  if (k->ctx && k->rb_context == Qnil)
35
36
  krb5_free_context(k->ctx);
37
+ else if (k->rb_context != Qnil)
38
+ rkrb5_context_release(k->rb_context);
36
39
  free_db_args(k->db_args);
37
40
  free(k);
38
41
  }
@@ -55,6 +58,11 @@ static VALUE rkadm5_allocate(VALUE klass){
55
58
  return TypedData_Wrap_Struct(klass, &rkadm5_data_type, ptr);
56
59
  }
57
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
+
58
66
  /*
59
67
  * call-seq:
60
68
  * Kerberos::Kadm5.new(principal: 'name', password: 'xxxxx')
@@ -140,17 +148,14 @@ static VALUE rkadm5_initialize(int argc, VALUE* argv, VALUE self){
140
148
  pass = StringValueCStr(v_password);
141
149
  }
142
150
 
143
- if(RTEST(v_ccache) && NIL_P(v_principal)){
144
- if(NIL_P(v_principal))
145
- v_principal = rb_funcall(v_ccache, rb_intern("principal"), 0);
146
- }
151
+ if(RTEST(v_keytab) && NIL_P(v_principal))
152
+ rb_raise(rb_eArgError, "principal must be specified when using a keytab");
153
+
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");
147
156
 
148
- // For a keytab use the first entry's principal
149
- if(RTEST(v_keytab) && NIL_P(v_principal)) {
150
- VALUE v_enum, v_first;
151
- v_enum = rb_funcall(v_keytab, rb_intern("each"), 0);
152
- v_first = rb_funcall(v_enum, rb_intern("first"), 0);
153
- v_principal = rb_iv_get(v_first, "@principal");
157
+ if(RTEST(v_ccache) && NIL_P(v_principal)){
158
+ v_principal = rb_funcall(v_ccache, rb_intern("principal"), 0);
154
159
  }
155
160
 
156
161
  user = StringValueCStr(v_principal);
@@ -166,17 +171,7 @@ static VALUE rkadm5_initialize(int argc, VALUE* argv, VALUE self){
166
171
 
167
172
  // Initialize or borrow the context
168
173
  if(!NIL_P(v_context)){
169
- RUBY_KRB5_CONTEXT* ctx_ptr;
170
-
171
- if(!rb_obj_is_kind_of(v_context, cKrb5Context))
172
- rb_raise(rb_eTypeError, "context must be a Kerberos::Krb5::Context object");
173
-
174
- TypedData_Get_Struct(v_context, RUBY_KRB5_CONTEXT, &rkrb5_context_data_type, ctx_ptr);
175
-
176
- if(!ctx_ptr->ctx)
177
- rb_raise(cKrb5Exception, "context is closed");
178
-
179
- ptr->ctx = ctx_ptr->ctx;
174
+ ptr->ctx = rkrb5_context_borrow(v_context);
180
175
  ptr->rb_context = v_context;
181
176
  }
182
177
  else{
@@ -239,12 +234,9 @@ static VALUE rkadm5_initialize(int argc, VALUE* argv, VALUE self){
239
234
  else if(RTEST(v_ccache)){
240
235
  RUBY_KRB5_CCACHE* cc_ptr;
241
236
 
242
- if(!rb_obj_is_kind_of(v_ccache, cKrb5CCache))
243
- rb_raise(rb_eTypeError, "ccache must be a Kerberos::Krb5::CredentialsCache object");
244
-
245
237
  TypedData_Get_Struct(v_ccache, RUBY_KRB5_CCACHE, &rkrb5_ccache_data_type, cc_ptr);
246
238
 
247
- if(!cc_ptr->ccache)
239
+ if(!cc_ptr->ctx || !cc_ptr->ccache)
248
240
  rb_raise(cKrb5Exception, "credentials cache is closed or destroyed");
249
241
 
250
242
  kerror = kadm5_init_with_creds(
@@ -289,8 +281,7 @@ static VALUE rkadm5_set_password(VALUE self, VALUE v_user, VALUE v_pass){
289
281
  user = StringValueCStr(v_user);
290
282
  pass = StringValueCStr(v_pass);
291
283
 
292
- if(!ptr->ctx)
293
- rb_raise(cKadm5Exception, "no context has been established");
284
+ rkadm5_check_open(ptr);
294
285
 
295
286
  if(ptr->princ){
296
287
  krb5_free_principal(ptr->ctx, ptr->princ);
@@ -327,8 +318,7 @@ static VALUE rkadm5_set_pwexpire(VALUE self, VALUE v_user, VALUE v_pwexpire){
327
318
 
328
319
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
329
320
 
330
- if(!ptr->ctx)
331
- rb_raise(cKadm5Exception, "no context has been established");
321
+ rkadm5_check_open(ptr);
332
322
 
333
323
  if(ptr->princ){
334
324
  krb5_free_principal(ptr->ctx, ptr->princ);
@@ -398,6 +388,8 @@ static VALUE rkadm5_create_principal(int argc, VALUE* argv, VALUE self){
398
388
 
399
389
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
400
390
 
391
+ rkadm5_check_open(ptr);
392
+
401
393
  rb_scan_args(argc, argv, "0:", &v_opts);
402
394
 
403
395
  if(NIL_P(v_opts))
@@ -429,9 +421,6 @@ static VALUE rkadm5_create_principal(int argc, VALUE* argv, VALUE self){
429
421
  add_db_args(&princ, db_args);
430
422
  free_db_args(db_args);
431
423
 
432
- if(!ptr->ctx)
433
- rb_raise(cKadm5Exception, "no context has been established");
434
-
435
424
  // Determine the principal name and populate mask from the principal object
436
425
  if(RTEST(v_principal)){
437
426
  VALUE v_princ_name, v_policy, v_expire, v_pw_expire, v_max_life, v_max_renew, v_attrs;
@@ -536,8 +525,7 @@ static VALUE rkadm5_delete_principal(VALUE self, VALUE v_user){
536
525
  Check_Type(v_user, T_STRING);
537
526
  user = StringValueCStr(v_user);
538
527
 
539
- if(!ptr->ctx)
540
- rb_raise(cKadm5Exception, "no context has been established");
528
+ rkadm5_check_open(ptr);
541
529
 
542
530
  if(ptr->princ){
543
531
  krb5_free_principal(ptr->ctx, ptr->princ);
@@ -579,6 +567,8 @@ static VALUE rkadm5_close(VALUE self){
579
567
 
580
568
  if(ptr->ctx && ptr->rb_context == Qnil)
581
569
  krb5_free_context(ptr->ctx);
570
+ else if(ptr->rb_context != Qnil)
571
+ rkrb5_context_release(ptr->rb_context);
582
572
 
583
573
  free_db_args(ptr->db_args);
584
574
 
@@ -674,8 +664,7 @@ static VALUE rkadm5_find_principal(VALUE self, VALUE v_user){
674
664
 
675
665
  memset(&ent, 0, sizeof(ent));
676
666
 
677
- if(!ptr->ctx)
678
- rb_raise(cKadm5Exception, "no context has been established");
667
+ rkadm5_check_open(ptr);
679
668
 
680
669
  if(ptr->princ){
681
670
  krb5_free_principal(ptr->ctx, ptr->princ);
@@ -735,8 +724,7 @@ static VALUE rkadm5_get_principal(VALUE self, VALUE v_user){
735
724
 
736
725
  memset(&ent, 0, sizeof(ent));
737
726
 
738
- if(!ptr->ctx)
739
- rb_raise(cKadm5Exception, "no context has been established");
727
+ rkadm5_check_open(ptr);
740
728
 
741
729
  if(ptr->princ){
742
730
  krb5_free_principal(ptr->ctx, ptr->princ);
@@ -794,6 +782,8 @@ static VALUE rkadm5_create_policy(int argc, VALUE* argv, VALUE self){
794
782
 
795
783
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
796
784
 
785
+ rkadm5_check_open(ptr);
786
+
797
787
  rb_scan_args(argc, argv, "01:", &v_policy, &v_kwargs);
798
788
 
799
789
  if(!NIL_P(v_kwargs)){
@@ -871,6 +861,8 @@ static VALUE rkadm5_delete_policy(VALUE self, VALUE v_policy){
871
861
 
872
862
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
873
863
 
864
+ rkadm5_check_open(ptr);
865
+
874
866
  policy = StringValueCStr(v_policy);
875
867
 
876
868
  kerror = kadm5_delete_policy(ptr->handle, policy);
@@ -901,8 +893,7 @@ static VALUE rkadm5_get_policy(VALUE self, VALUE v_name){
901
893
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
902
894
  memset(&ent, 0, sizeof(ent));
903
895
 
904
- if(!ptr->ctx)
905
- rb_raise(cKadm5Exception, "no context has been established");
896
+ rkadm5_check_open(ptr);
906
897
 
907
898
  policy_name = StringValueCStr(v_name);
908
899
 
@@ -955,8 +946,7 @@ static VALUE rkadm5_find_policy(VALUE self, VALUE v_name){
955
946
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
956
947
  memset(&ent, 0, sizeof(ent));
957
948
 
958
- if(!ptr->ctx)
959
- rb_raise(cKadm5Exception, "no context has been established");
949
+ rkadm5_check_open(ptr);
960
950
 
961
951
  policy_name = StringValueCStr(v_name);
962
952
 
@@ -1001,34 +991,59 @@ static VALUE rkadm5_find_policy(VALUE self, VALUE v_name){
1001
991
  * Example:
1002
992
  *
1003
993
  * policy = Kerberos::Kadm5::Policy.find('test')
1004
- * policy.max_length = 1024
994
+ * policy.max_life = 1024
1005
995
  * kadm5.modify_policy(policy)
1006
996
  */
1007
997
  static VALUE rkadm5_modify_policy(VALUE self, VALUE v_policy){
1008
998
  RUBY_KADM5* ptr;
1009
- RUBY_KADM5_POLICY* pptr;
1010
999
  kadm5_ret_t kerror;
1011
- 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;
1012
1003
 
1013
1004
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
1014
- TypedData_Get_Struct(v_policy, RUBY_KADM5_POLICY, &rkadm5_policy_data_type, pptr);
1015
1005
 
1016
- if(!ptr->ctx)
1017
- rb_raise(cKadm5Exception, "no context has been established");
1006
+ rkadm5_check_open(ptr);
1007
+
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);
1018
1020
 
1019
- if(pptr->policy.pw_min_classes)
1021
+ if(!NIL_P(v_min_classes)){
1020
1022
  mask |= KADM5_PW_MIN_CLASSES;
1023
+ ent.pw_min_classes = NUM2LONG(v_min_classes);
1024
+ }
1021
1025
 
1022
- if(pptr->policy.pw_min_length)
1026
+ if(!NIL_P(v_min_length)){
1023
1027
  mask |= KADM5_PW_MIN_LENGTH;
1028
+ ent.pw_min_length = NUM2LONG(v_min_length);
1029
+ }
1024
1030
 
1025
- if(pptr->policy.pw_min_life)
1031
+ if(!NIL_P(v_min_life)){
1026
1032
  mask |= KADM5_PW_MIN_LIFE;
1033
+ ent.pw_min_life = NUM2LONG(v_min_life);
1034
+ }
1027
1035
 
1028
- if(pptr->policy.pw_max_life)
1036
+ if(!NIL_P(v_max_life)){
1029
1037
  mask |= KADM5_PW_MAX_LIFE;
1038
+ ent.pw_max_life = NUM2LONG(v_max_life);
1039
+ }
1040
+
1041
+ if(!NIL_P(v_history_num)){
1042
+ mask |= KADM5_PW_HISTORY_NUM;
1043
+ ent.pw_history_num = NUM2LONG(v_history_num);
1044
+ }
1030
1045
 
1031
- kerror = kadm5_modify_policy(ptr->handle, &pptr->policy, mask);
1046
+ kerror = kadm5_modify_policy(ptr->handle, &ent, mask);
1032
1047
 
1033
1048
  if(kerror)
1034
1049
  rb_raise(cKadm5Exception, "kadm5_modify_policy: %s (%li)", error_message(kerror), kerror);
@@ -1059,6 +1074,8 @@ static VALUE rkadm5_get_policies(int argc, VALUE* argv, VALUE self){
1059
1074
 
1060
1075
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
1061
1076
 
1077
+ rkadm5_check_open(ptr);
1078
+
1062
1079
  rb_scan_args(argc, argv, "01", &v_expr);
1063
1080
 
1064
1081
  if(NIL_P(v_expr))
@@ -1107,6 +1124,8 @@ static VALUE rkadm5_get_principals(int argc, VALUE* argv, VALUE self){
1107
1124
 
1108
1125
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
1109
1126
 
1127
+ rkadm5_check_open(ptr);
1128
+
1110
1129
  rb_scan_args(argc, argv, "01", &v_expr);
1111
1130
 
1112
1131
  if(NIL_P(v_expr))
@@ -1155,6 +1174,8 @@ static VALUE rkadm5_get_privs(int argc, VALUE* argv, VALUE self){
1155
1174
 
1156
1175
  TypedData_Get_Struct(self, RUBY_KADM5, &rkadm5_data_type, ptr);
1157
1176
 
1177
+ rkadm5_check_open(ptr);
1178
+
1158
1179
  rb_scan_args(argc, argv, "01", &v_strings);
1159
1180
 
1160
1181
  kerror = kadm5_get_privs(ptr->handle, &privs);
@@ -1200,8 +1221,7 @@ static VALUE rkadm5_randkey_principal(VALUE self, VALUE v_user){
1200
1221
 
1201
1222
  user = StringValueCStr(v_user);
1202
1223
 
1203
- if(!ptr->ctx)
1204
- rb_raise(cKadm5Exception, "no context has been established");
1224
+ rkadm5_check_open(ptr);
1205
1225
 
1206
1226
  kerror = krb5_parse_name(ptr->ctx, user, &princ);
1207
1227
 
@@ -1240,10 +1260,13 @@ char** parse_db_args(VALUE v_db_args){
1240
1260
  case T_ARRAY:
1241
1261
  // Multiple arguments
1242
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
+
1243
1267
  db_args = (char **) malloc((array_length + 1) * sizeof(char *));
1244
1268
  for(long i = 0; i < array_length; ++i){
1245
1269
  VALUE elem = rb_ary_entry(v_db_args, i);
1246
- Check_Type(elem, T_STRING);
1247
1270
  db_args[i] = strdup(StringValueCStr(elem));
1248
1271
  }
1249
1272
  db_args[array_length] = NULL;