Class: Kerberos::Krb5::Keytab

Inherits:
Object
  • Object
show all
Defined in:
ext/rkerberos/keytab.c

Defined Under Namespace

Classes: Exception

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#Kerberos::Krb5::Keytab.new(name: nil, context: nil) ⇒ Object

Creates and returns a new Kerberos::Krb5::Keytab object. This initializes the context and keytab for future method calls on that object.

A keytab file name may be provided. If not, the system's default keytab name is used. If a name is provided it must be in the form 'type:residual' where 'type' is a type known to the Kerberos library.

An optional context keyword argument may be provided. If given, it must be a Kerberos::Krb5::Context object and will be used instead of creating a new context via krb5_init_context.

Examples:

# Using the default keytab
keytab = Kerberos::Krb5::Keytab.new

# Using an explicit keytab
keytab = Kerberos::Krb5::Keytab.new(name: 'FILE:/etc/krb5.keytab')

# Using a custom context
ctx = Kerberos::Krb5::Context.new
keytab = Kerberos::Krb5::Keytab.new(name: 'FILE:/etc/krb5.keytab', context: ctx)


557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'ext/rkerberos/keytab.c', line 557

static VALUE rkrb5_keytab_initialize(int argc, VALUE* argv, VALUE self){
  RUBY_KRB5_KEYTAB* ptr;
  krb5_error_code kerror;
  char keytab_name[MAX_KEYTAB_NAME_LEN];
  VALUE v_keytab_name = Qnil;
  VALUE v_opts = Qnil;
  VALUE v_context = Qnil;
  ID kw_table[2] = { rb_intern("name"), rb_intern("context") };
  VALUE kw_vals[2];

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  rb_scan_args(argc, argv, "0:", &v_opts);

  if(NIL_P(v_opts))
    v_opts = rb_hash_new();

  rb_get_kwargs(v_opts, kw_table, 0, 2, kw_vals);
  v_keytab_name = kw_vals[0] == Qundef ? Qnil : kw_vals[0];
  v_context = kw_vals[1] == Qundef ? Qnil : kw_vals[1];

  // Initialize or borrow the context
  if(!NIL_P(v_context)){
    ptr->ctx = rkrb5_context_borrow(v_context);
    ptr->rb_context = v_context;
  }
  else{
    kerror = krb5_init_context(&ptr->ctx);

    if(kerror)
      rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));

    ptr->rb_context = Qnil;
  }

  // Use the default keytab name if one isn't provided.
  if(NIL_P(v_keytab_name)){
    kerror = krb5_kt_default_name(ptr->ctx, keytab_name, MAX_KEYTAB_NAME_LEN);

    if(kerror)
      rb_raise(cKrb5Exception, "krb5_kt_default_name: %s", error_message(kerror));

    rb_iv_set(self, "@name", rb_str_new2(keytab_name));
  }
  else{
    Check_Type(v_keytab_name, T_STRING);
    strncpy(keytab_name, StringValueCStr(v_keytab_name), MAX_KEYTAB_NAME_LEN - 1);
    keytab_name[MAX_KEYTAB_NAME_LEN - 1] = '\0';
    rb_iv_set(self, "@name", v_keytab_name);
  }

  kerror = krb5_kt_resolve(
    ptr->ctx,
    keytab_name,
    &ptr->keytab
  );

  if(kerror)
    rb_raise(cKrb5KeytabException, "krb5_kt_resolve: %s", error_message(kerror));

  return self;
}

Instance Attribute Details

#nameObject (readonly)

The name of the keytab associated with the current keytab object.

Class Method Details

.Kerberos::Krb5::Keytab.foreach(keytab = nil) ⇒ Object .entry.inspectObject

}

Iterate over each entry in the keytab and yield a Krb5::Keytab::Entry object for each entry found.

If no keytab is provided, then the default keytab is used.



698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
# File 'ext/rkerberos/keytab.c', line 698

static VALUE rkrb5_s_keytab_foreach(int argc, VALUE* argv, VALUE klass){
  VALUE v_keytab_name;
  krb5_error_code kerror;
  keytab_foreach_arg fa;
  char keytab_name[MAX_KEYTAB_NAME_LEN];

  memset(&fa, 0, sizeof(fa));

  rb_scan_args(argc, argv, "01", &v_keytab_name);

  kerror = krb5_init_context(&fa.ctx);

  if(kerror)
    rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));

  // Use the default keytab name if one isn't provided.
  if(NIL_P(v_keytab_name)){
    kerror = krb5_kt_default_name(fa.ctx, keytab_name, MAX_KEYTAB_NAME_LEN);

    if(kerror){
      krb5_free_context(fa.ctx);
      rb_raise(cKrb5Exception, "krb5_kt_default_name: %s", error_message(kerror));
    }
  }
  else{
    Check_Type(v_keytab_name, T_STRING);
    strncpy(keytab_name, StringValueCStr(v_keytab_name), MAX_KEYTAB_NAME_LEN - 1);
    keytab_name[MAX_KEYTAB_NAME_LEN - 1] = '\0';
  }

  kerror = krb5_kt_resolve(fa.ctx, keytab_name, &fa.keytab);

  if(kerror){
    krb5_free_context(fa.ctx);
    rb_raise(cKrb5Exception, "krb5_kt_resolve: %s", error_message(kerror));
  }

  kerror = krb5_kt_start_seq_get(fa.ctx, fa.keytab, &fa.cursor);

  if(kerror){
    krb5_kt_close(fa.ctx, fa.keytab);
    krb5_free_context(fa.ctx);
    rb_raise(cKrb5Exception, "krb5_kt_start_seq_get: %s", error_message(kerror));
  }

  fa.cursor_active = 1;

  rb_ensure(rkrb5_s_keytab_foreach_body, (VALUE)&fa, rkrb5_s_keytab_foreach_ensure, (VALUE)&fa);

  return Qnil;
}

Instance Method Details

#add_entry(*args) ⇒ Object



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'ext/rkerberos/keytab.c', line 220

static VALUE rkrb5_keytab_add_entry(int argc, VALUE* argv, VALUE self){
  RUBY_KRB5_KEYTAB* ptr;
  krb5_error_code kerror;
  krb5_keytab_entry entry;
  krb5_data pwd_data, salt;
  VALUE v_opts, v_principal, v_password, v_vno, v_enctype;
  ID kw_table[4] = { rb_intern("principal"), rb_intern("password"), rb_intern("vno"), rb_intern("enctype") };
  VALUE kw_vals[4];

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  if(!ptr->ctx)
    rb_raise(cKrb5Exception, "no context has been established");

  rb_scan_args(argc, argv, "0:", &v_opts);

  if(NIL_P(v_opts))
    v_opts = rb_hash_new();

  rb_get_kwargs(v_opts, kw_table, 2, 2, kw_vals);
  v_principal = kw_vals[0];
  v_password = kw_vals[1];
  v_vno = kw_vals[2] == Qundef ? Qnil : kw_vals[2];
  v_enctype = kw_vals[3] == Qundef ? Qnil : kw_vals[3];

  Check_Type(v_principal, T_STRING);
  Check_Type(v_password, T_STRING);

  memset(&entry, 0, sizeof(entry));

  entry.vno = NIL_P(v_vno) ? 1 : NUM2INT(v_vno);
  entry.key.enctype = NIL_P(v_enctype) ? ENCTYPE_AES256_CTS_HMAC_SHA1_96 : NUM2INT(v_enctype);

  kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_principal), &entry.principal);

  if(kerror)
    rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));

  entry.timestamp = time(NULL);

  // Derive the salt from the principal
  kerror = krb5_principal2salt(ptr->ctx, entry.principal, &salt);

  if(kerror){
    krb5_free_principal(ptr->ctx, entry.principal);
    rb_raise(cKrb5Exception, "krb5_principal2salt: %s", error_message(kerror));
  }

  // Derive key from password + salt
  pwd_data.data   = StringValuePtr(v_password);
  pwd_data.length = (unsigned int)RSTRING_LEN(v_password);

  kerror = krb5_c_string_to_key(ptr->ctx, entry.key.enctype, &pwd_data, &salt, &entry.key);

  krb5_free_data_contents(ptr->ctx, &salt);

  if(kerror){
    krb5_free_principal(ptr->ctx, entry.principal);
    rb_raise(cKrb5Exception, "krb5_c_string_to_key: %s", error_message(kerror));
  }

  kerror = krb5_kt_add_entry(ptr->ctx, ptr->keytab, &entry);

  krb5_free_keyblock_contents(ptr->ctx, &entry.key);
  krb5_free_principal(ptr->ctx, entry.principal);

  if(kerror)
    rb_raise(cKrb5KeytabException, "krb5_kt_add_entry: %s", error_message(kerror));

  return self;
}

#closeObject



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'ext/rkerberos/keytab.c', line 177

static VALUE rkrb5_keytab_close(VALUE self){
  RUBY_KRB5_KEYTAB* ptr;

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  if(ptr->keytab && ptr->ctx){
    krb5_kt_close(ptr->ctx, ptr->keytab);
    ptr->keytab = NULL;
  }

  if(ptr->ctx)
    krb5_free_cred_contents(ptr->ctx, &ptr->creds);

  if(ptr->ctx && ptr->rb_context == Qnil)
    krb5_free_context(ptr->ctx);
  else if(ptr->rb_context != Qnil)
    rkrb5_context_release(ptr->rb_context);

  ptr->ctx = NULL;
  ptr->rb_context = Qnil;

  return Qtrue;
}

#default_nameObject

Returns the default keytab name.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'ext/rkerberos/keytab.c', line 148

static VALUE rkrb5_keytab_default_name(VALUE self){
  char default_name[MAX_KEYTAB_NAME_LEN];
  krb5_error_code kerror;
  RUBY_KRB5_KEYTAB* ptr;
  VALUE v_default_name;

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  if(!ptr->ctx)
    rb_raise(cKrb5Exception, "no context has been established");

  kerror = krb5_kt_default_name(ptr->ctx, default_name, MAX_KEYTAB_NAME_LEN);

  if(kerror)
    rb_raise(cKrb5Exception, "krb5_kt_default_name: %s", error_message(kerror));

  v_default_name = rb_str_new2(default_name);

  return v_default_name;
}

#dupObject Also known as: clone



500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
# File 'ext/rkerberos/keytab.c', line 500

static VALUE rkrb5_keytab_dup(VALUE self){
  RUBY_KRB5_KEYTAB *ptr, *newptr;
  krb5_error_code kerror;
  VALUE newobj;

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  if(!ptr->ctx)
    rb_raise(cKrb5Exception, "no context has been established");

  newobj = rkrb5_keytab_allocate(CLASS_OF(self));
  TypedData_Get_Struct(newobj, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, newptr);

  kerror = krb5_init_context(&newptr->ctx);
  if(kerror){
    rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
  }

  kerror = krb5_kt_dup(newptr->ctx, ptr->keytab, &newptr->keytab);
  if(kerror){
    krb5_free_context(newptr->ctx);
    newptr->ctx = NULL;
    rb_raise(cKrb5Exception, "krb5_kt_dup: %s", error_message(kerror));
  }

  rb_iv_set(newobj, "@name", rb_iv_get(self, "@name"));

  return newobj;
}

#eachObject



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'ext/rkerberos/keytab.c', line 116

static VALUE rkrb5_keytab_each(VALUE self){
  RUBY_KRB5_KEYTAB* ptr;
  krb5_error_code kerror;
  keytab_each_arg ea;

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  if(!ptr->ctx || !ptr->keytab)
    rb_raise(cKrb5Exception, "keytab is closed");

  ea.ctx = ptr->ctx;
  ea.keytab = ptr->keytab;

  kerror = krb5_kt_start_seq_get(ea.ctx, ea.keytab, &ea.cursor);

  if(kerror)
    rb_raise(cKrb5Exception, "krb5_kt_start_seq_get: %s", error_message(kerror));

  ea.cursor_active = 1;

  rb_ensure(rkrb5_keytab_each_body, (VALUE)&ea, rkrb5_keytab_each_ensure, (VALUE)&ea);

  return self;
}

#get_entry(*args) ⇒ Object Also known as: find



389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'ext/rkerberos/keytab.c', line 389

static VALUE rkrb5_keytab_get_entry(int argc, VALUE* argv, VALUE self){
  RUBY_KRB5_KEYTAB* ptr;
  krb5_error_code kerror;
  krb5_principal principal;
  krb5_kvno vno;
  krb5_enctype enctype;
  krb5_keytab_entry entry;
  char* name;
  VALUE v_principal, v_vno, v_enctype, v_entry;

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  if(!ptr->ctx || !ptr->keytab)
    rb_raise(cKrb5Exception, "keytab is closed");

  rb_scan_args(argc, argv, "12", &v_principal, &v_vno, &v_enctype);

  Check_Type(v_principal, T_STRING);
  name = StringValueCStr(v_principal);

  vno = NIL_P(v_vno) ? 0 : NUM2INT(v_vno);
  enctype = NIL_P(v_enctype) ? 0 : NUM2INT(v_enctype);

  kerror = krb5_parse_name(ptr->ctx, name, &principal);

  if(kerror)
    rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));

  kerror = krb5_kt_get_entry(
    ptr->ctx,
    ptr->keytab,
    principal,
    vno,
    enctype,
    &entry
  );

  krb5_free_principal(ptr->ctx, principal);

  if(kerror)
    rb_raise(cKrb5Exception, "krb5_kt_get_entry: %s", error_message(kerror));

  v_entry = rb_class_new_instance(0, NULL, cKrb5KtEntry);

  rb_iv_set(v_entry, "@principal", rb_str_new2(name));
  rb_iv_set(v_entry, "@timestamp", rb_time_new(entry.timestamp, 0));
  rb_iv_set(v_entry, "@vno", INT2FIX(entry.vno));
  rb_iv_set(v_entry, "@key", INT2FIX(entry.key.enctype));

  krb5_kt_free_entry(ptr->ctx, &entry);

  return v_entry;
}

#have_content?Object



756
757
758
759
760
761
762
763
764
765
766
767
768
# File 'ext/rkerberos/keytab.c', line 756

static VALUE rkrb5_keytab_have_content(VALUE self){
  RUBY_KRB5_KEYTAB* ptr;
  krb5_error_code kerror;

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  if(!ptr->ctx)
    rb_raise(cKrb5Exception, "no context has been established");

  kerror = krb5_kt_have_content(ptr->ctx, ptr->keytab);

  return kerror ? Qfalse : Qtrue;
}

#keytab_nameObject



451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'ext/rkerberos/keytab.c', line 451

static VALUE rkrb5_keytab_get_name(VALUE self){
  RUBY_KRB5_KEYTAB* ptr;
  krb5_error_code kerror;
  char name[MAX_KEYTAB_NAME_LEN];

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  if(!ptr->ctx)
    rb_raise(cKrb5Exception, "no context has been established");

  kerror = krb5_kt_get_name(ptr->ctx, ptr->keytab, name, MAX_KEYTAB_NAME_LEN);

  if(kerror)
    rb_raise(cKrb5Exception, "krb5_kt_get_name: %s", error_message(kerror));

  return rb_str_new2(name);
}

#keytab_typeObject



475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
# File 'ext/rkerberos/keytab.c', line 475

static VALUE rkrb5_keytab_get_type(VALUE self){
  RUBY_KRB5_KEYTAB* ptr;
  const char *type;

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  if(!ptr->ctx)
    rb_raise(cKrb5Exception, "no context has been established");

  type = krb5_kt_get_type(ptr->ctx, ptr->keytab);

  if(!type)
    rb_raise(cKrb5Exception, "krb5_kt_get_type returned NULL");

  return rb_str_new2(type);
}

#remove_entry(*args) ⇒ Object



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'ext/rkerberos/keytab.c', line 312

static VALUE rkrb5_keytab_remove_entry(int argc, VALUE* argv, VALUE self){
  RUBY_KRB5_KEYTAB* ptr;
  krb5_error_code kerror;
  krb5_keytab_entry found_entry;
  krb5_principal match_princ;
  krb5_kvno match_vno;
  krb5_enctype match_enctype;
  int removed = 0;
  VALUE v_opts, v_principal, v_vno, v_enctype;
  ID kw_table[3] = { rb_intern("principal"), rb_intern("vno"), rb_intern("enctype") };
  VALUE kw_vals[3];

  TypedData_Get_Struct(self, RUBY_KRB5_KEYTAB, &rkrb5_keytab_data_type, ptr);

  if(!ptr->ctx)
    rb_raise(cKrb5Exception, "no context has been established");

  rb_scan_args(argc, argv, "0:", &v_opts);

  if(NIL_P(v_opts))
    v_opts = rb_hash_new();

  rb_get_kwargs(v_opts, kw_table, 1, 2, kw_vals);
  v_principal = kw_vals[0];
  v_vno = kw_vals[1] == Qundef ? Qnil : kw_vals[1];
  v_enctype = kw_vals[2] == Qundef ? Qnil : kw_vals[2];

  Check_Type(v_principal, T_STRING);

  match_vno = NIL_P(v_vno) ? 0 : NUM2INT(v_vno);
  match_enctype = NIL_P(v_enctype) ? 0 : NUM2INT(v_enctype);

  kerror = krb5_parse_name(ptr->ctx, StringValueCStr(v_principal), &match_princ);

  if(kerror)
    rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));

  // Retrieve the full entry via krb5_kt_get_entry and then pass the
  // complete struct to krb5_kt_remove_entry so all fields match exactly.
  // Loop to remove every matching entry when vno/enctype are wildcards.
  while(1){
    kerror = krb5_kt_get_entry(
      ptr->ctx, ptr->keytab, match_princ,
      match_vno, match_enctype, &found_entry
    );

    if(kerror){
      krb5_free_principal(ptr->ctx, match_princ);
      if(removed)
        return self;
      rb_raise(cKrb5KeytabException, "krb5_kt_remove_entry: %s", error_message(kerror));
    }

    kerror = krb5_kt_remove_entry(ptr->ctx, ptr->keytab, &found_entry);
    krb5_kt_free_entry(ptr->ctx, &found_entry);

    if(kerror){
      krb5_free_principal(ptr->ctx, match_princ);
      rb_raise(cKrb5KeytabException, "krb5_kt_remove_entry: %s", error_message(kerror));
    }

    removed = 1;
  }
}