rkerberos 0.1.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.
@@ -0,0 +1,566 @@
1
+ #include <rkerberos.h>
2
+
3
+ VALUE mKerberos;
4
+ VALUE cKrb5;
5
+ VALUE cKrb5Exception;
6
+
7
+ // Function prototypes
8
+ static VALUE rkrb5_close(VALUE);
9
+
10
+ VALUE rb_hash_aref2(VALUE v_hash, char* key){
11
+ VALUE v_key, v_val;
12
+
13
+ v_key = rb_str_new2(key);
14
+ v_val = rb_hash_aref(v_hash, v_key);
15
+
16
+ if(NIL_P(v_val))
17
+ v_val = rb_hash_aref(v_hash, ID2SYM(rb_intern(key)));
18
+
19
+ return v_val;
20
+ }
21
+
22
+ // Free function for the Kerberos::Krb5 class.
23
+ static void rkrb5_free(RUBY_KRB5* ptr){
24
+ if(!ptr)
25
+ return;
26
+
27
+ if(ptr->keytab)
28
+ krb5_kt_close(ptr->ctx, ptr->keytab);
29
+
30
+ if(ptr->ctx)
31
+ krb5_free_cred_contents(ptr->ctx, &ptr->creds);
32
+
33
+ if(ptr->princ)
34
+ krb5_free_principal(ptr->ctx, ptr->princ);
35
+
36
+ if(ptr->ctx)
37
+ krb5_free_context(ptr->ctx);
38
+
39
+ free(ptr);
40
+ }
41
+
42
+ // Allocation function for the Kerberos::Krb5 class.
43
+ static VALUE rkrb5_allocate(VALUE klass){
44
+ RUBY_KRB5* ptr = malloc(sizeof(RUBY_KRB5));
45
+ memset(ptr, 0, sizeof(RUBY_KRB5));
46
+ return Data_Wrap_Struct(klass, 0, rkrb5_free, ptr);
47
+ }
48
+
49
+ /*
50
+ * call-seq:
51
+ * Kerberos::Krb5.new
52
+ *
53
+ * Creates and returns a new Kerberos::Krb5 object. This initializes the
54
+ * context for future method calls on that object.
55
+ */
56
+ static VALUE rkrb5_initialize(VALUE self){
57
+ RUBY_KRB5* ptr;
58
+ krb5_error_code kerror;
59
+
60
+ Data_Get_Struct(self, RUBY_KRB5, ptr);
61
+
62
+ kerror = krb5_init_context(&ptr->ctx);
63
+
64
+ if(kerror)
65
+ rb_raise(cKrb5Exception, "krb5_init_context: %s", error_message(kerror));
66
+
67
+ if(rb_block_given_p()){
68
+ rb_ensure(rb_yield, self, rkrb5_close, self);
69
+ return Qnil;
70
+ }
71
+
72
+ return self;
73
+ }
74
+
75
+ /*
76
+ * call-seq:
77
+ * krb.get_default_realm # => 'YOUR.REALM.COM'
78
+ *
79
+ * Returns the default Kerberos realm on your system.
80
+ */
81
+ static VALUE rkrb5_get_default_realm(VALUE self){
82
+ RUBY_KRB5* ptr;
83
+ char* realm;
84
+ krb5_error_code kerror;
85
+
86
+ Data_Get_Struct(self, RUBY_KRB5, ptr);
87
+
88
+ kerror = krb5_get_default_realm(ptr->ctx, &realm);
89
+
90
+ if(kerror)
91
+ rb_raise(cKrb5Exception, "krb5_get_default_realm: %s", error_message(kerror));
92
+
93
+ return rb_str_new2(realm);
94
+ }
95
+
96
+ /*
97
+ * call-seq:
98
+ * krb.set_default_realm(realm = nil)
99
+ *
100
+ * Sets the default realm to +realm+. If no argument is provided, then the
101
+ * default realm in your krb5.conf file is used.
102
+ */
103
+ static VALUE rkrb5_set_default_realm(int argc, VALUE* argv, VALUE self){
104
+ RUBY_KRB5* ptr;
105
+ VALUE v_realm;
106
+ char* realm;
107
+ krb5_error_code kerror;
108
+
109
+ Data_Get_Struct(self, RUBY_KRB5, ptr);
110
+
111
+ rb_scan_args(argc, argv, "01", &v_realm);
112
+
113
+ if(NIL_P(v_realm)){
114
+ realm = NULL;
115
+ }
116
+ else{
117
+ Check_Type(v_realm, T_STRING);
118
+ realm = StringValuePtr(v_realm);
119
+ }
120
+
121
+ kerror = krb5_set_default_realm(ptr->ctx, realm);
122
+
123
+ if(kerror)
124
+ rb_raise(cKrb5Exception, "krb5_set_default_realm: %s", error_message(kerror));
125
+
126
+ return self;
127
+ }
128
+
129
+ /* call-seq:
130
+ * krb5.get_init_creds_keytab(principal = nil, keytab = nil, service = nil)
131
+ *
132
+ * Acquire credentials for +principal+ from +keytab+ using +service+. If
133
+ * no principal is specified, then a principal is derived from the service
134
+ * name. If no service name is specified, kerberos defaults to "host".
135
+ *
136
+ * If no keytab file is provided, the default keytab file is used. This is
137
+ * typically /etc/krb5.keytab.
138
+ */
139
+ static VALUE rkrb5_get_init_creds_keytab(int argc, VALUE* argv, VALUE self){
140
+ RUBY_KRB5* ptr;
141
+ VALUE v_user, v_keytab_name, v_service;
142
+ char* user;
143
+ char* service;
144
+ char keytab_name[MAX_KEYTAB_NAME_LEN];
145
+
146
+ krb5_error_code kerror;
147
+ krb5_get_init_creds_opt opt;
148
+ krb5_creds cred;
149
+
150
+ Data_Get_Struct(self, RUBY_KRB5, ptr);
151
+
152
+ if(!ptr->ctx)
153
+ rb_raise(cKrb5Exception, "no context has been established");
154
+
155
+ rb_scan_args(argc, argv, "03", &v_user, &v_keytab_name, &v_service);
156
+
157
+ // We need the service information for later.
158
+ if(NIL_P(v_service)){
159
+ service = NULL;
160
+ }
161
+ else{
162
+ Check_Type(v_service, T_STRING);
163
+ service = StringValuePtr(v_service);
164
+ }
165
+
166
+ // Convert the name (or service name) to a kerberos principal.
167
+ if(NIL_P(v_user)){
168
+ kerror = krb5_sname_to_principal(
169
+ ptr->ctx,
170
+ NULL,
171
+ service,
172
+ KRB5_NT_SRV_HST,
173
+ &ptr->princ
174
+ );
175
+
176
+ if(kerror)
177
+ rb_raise(cKrb5Exception, "krb5_sname_to_principal: %s", error_message(kerror));
178
+ }
179
+ else{
180
+ Check_Type(v_user, T_STRING);
181
+ user = StringValuePtr(v_user);
182
+
183
+ kerror = krb5_parse_name(ptr->ctx, user, &ptr->princ);
184
+
185
+ if(kerror)
186
+ rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
187
+ }
188
+
189
+ // Use the default keytab if none is specified.
190
+ if(NIL_P(v_keytab_name)){
191
+ kerror = krb5_kt_default_name(ptr->ctx, keytab_name, MAX_KEYTAB_NAME_LEN);
192
+
193
+ if(kerror)
194
+ rb_raise(cKrb5Exception, "krb5_kt_default_name: %s", error_message(kerror));
195
+ }
196
+ else{
197
+ Check_Type(v_keytab_name, T_STRING);
198
+ strncpy(keytab_name, StringValuePtr(v_keytab_name), MAX_KEYTAB_NAME_LEN);
199
+ }
200
+
201
+ kerror = krb5_kt_resolve(
202
+ ptr->ctx,
203
+ keytab_name,
204
+ &ptr->keytab
205
+ );
206
+
207
+ if(kerror)
208
+ rb_raise(cKrb5Exception, "krb5_kt_resolve: %s", error_message(kerror));
209
+
210
+ krb5_get_init_creds_opt_init(&opt);
211
+
212
+ kerror = krb5_get_init_creds_keytab(
213
+ ptr->ctx,
214
+ &cred,
215
+ ptr->princ,
216
+ ptr->keytab,
217
+ 0,
218
+ service,
219
+ &opt
220
+ );
221
+
222
+ if(kerror)
223
+ rb_raise(cKrb5Exception, "krb5_get_init_creds_keytab: %s", error_message(kerror));
224
+
225
+ return self;
226
+ }
227
+
228
+ /* call-seq:
229
+ * krb5.change_password(old, new)
230
+ *
231
+ * Changes the password for the principal from +old+ to +new+. The principal
232
+ * is defined as whoever the last principal was authenticated via the
233
+ * Krb5#get_init_creds_password method.
234
+ *
235
+ * Attempting to change a password before a principal has been established
236
+ * will raise an error.
237
+ *
238
+ * Example:
239
+ *
240
+ * krb5.get_init_creds_password('foo', 'XXXXXX') # Authenticate 'foo' user
241
+ * krb5.change_password('XXXXXX', 'YYYYYY') # Change password for 'foo'
242
+ */
243
+ static VALUE rkrb5_change_password(VALUE self, VALUE v_old, VALUE v_new){
244
+ Check_Type(v_old, T_STRING);
245
+ Check_Type(v_new, T_STRING);
246
+
247
+ RUBY_KRB5* ptr;
248
+ krb5_data result_string;
249
+ krb5_data pw_result_string;
250
+ krb5_error_code kerror;
251
+
252
+ int pw_result;
253
+ char* old_passwd = StringValuePtr(v_old);
254
+ char* new_passwd = StringValuePtr(v_new);
255
+
256
+ Data_Get_Struct(self, RUBY_KRB5, ptr);
257
+
258
+ if(!ptr->ctx)
259
+ rb_raise(cKrb5Exception, "no context has been established");
260
+
261
+ if(!ptr->princ)
262
+ rb_raise(cKrb5Exception, "no principal has been established");
263
+
264
+ kerror = krb5_get_init_creds_password(
265
+ ptr->ctx,
266
+ &ptr->creds,
267
+ ptr->princ,
268
+ old_passwd,
269
+ NULL,
270
+ NULL,
271
+ 0,
272
+ "kadmin/changepw",
273
+ NULL
274
+ );
275
+
276
+ if(kerror)
277
+ rb_raise(cKrb5Exception, "krb5_get_init_creds_password: %s", error_message(kerror));
278
+
279
+ kerror = krb5_change_password(
280
+ ptr->ctx,
281
+ &ptr->creds,
282
+ new_passwd,
283
+ &pw_result,
284
+ &pw_result_string,
285
+ &result_string
286
+ );
287
+
288
+ if(kerror)
289
+ rb_raise(cKrb5Exception, "krb5_change_password: %s", error_message(kerror));
290
+
291
+ return Qtrue;
292
+ }
293
+
294
+ /*
295
+ * call-seq:
296
+ * krb5.get_init_creds_password(user, password)
297
+ *
298
+ * Authenticates the credentials of +user+ using +password+, and has the effect
299
+ * of setting the principal and context internally. This method must typically
300
+ * be called before using other methods.
301
+ */
302
+ static VALUE rkrb5_get_init_creds_passwd(VALUE self, VALUE v_user, VALUE v_pass){
303
+ Check_Type(v_user, T_STRING);
304
+ Check_Type(v_pass, T_STRING);
305
+
306
+ RUBY_KRB5* ptr;
307
+ char* user = StringValuePtr(v_user);
308
+ char* pass = StringValuePtr(v_pass);
309
+ krb5_error_code kerror;
310
+
311
+ Data_Get_Struct(self, RUBY_KRB5, ptr);
312
+
313
+ if(!ptr->ctx)
314
+ rb_raise(cKrb5Exception, "no context has been established");
315
+
316
+ kerror = krb5_parse_name(ptr->ctx, user, &ptr->princ);
317
+
318
+ if(kerror)
319
+ rb_raise(cKrb5Exception, "krb5_parse_name: %s", error_message(kerror));
320
+
321
+ kerror = krb5_get_init_creds_password(
322
+ ptr->ctx,
323
+ &ptr->creds,
324
+ ptr->princ,
325
+ pass,
326
+ 0,
327
+ NULL,
328
+ 0,
329
+ NULL,
330
+ NULL
331
+ );
332
+
333
+ if(kerror)
334
+ rb_raise(cKrb5Exception, "krb5_get_init_creds_password: %s", error_message(kerror));
335
+
336
+ return Qtrue;
337
+ }
338
+
339
+ /*
340
+ * call-seq:
341
+ * krb5.close
342
+ *
343
+ * Handles cleanup of the Krb5 object, freeing any credentials, principal or
344
+ * context associated with the object.
345
+ */
346
+ static VALUE rkrb5_close(VALUE self){
347
+ RUBY_KRB5* ptr;
348
+
349
+ Data_Get_Struct(self, RUBY_KRB5, ptr);
350
+
351
+ if(ptr->ctx)
352
+ krb5_free_cred_contents(ptr->ctx, &ptr->creds);
353
+
354
+ if(ptr->princ)
355
+ krb5_free_principal(ptr->ctx, ptr->princ);
356
+
357
+ if(ptr->ctx)
358
+ krb5_free_context(ptr->ctx);
359
+
360
+ ptr->ctx = NULL;
361
+ ptr->princ = NULL;
362
+
363
+ return Qtrue;
364
+ }
365
+
366
+ /*
367
+ * call-seq:
368
+ * krb5.get_default_principal
369
+ *
370
+ * Returns the default principal for the current realm based on the current
371
+ * credentials cache.
372
+ *
373
+ * If no credentials cache is found then an error is raised.
374
+ */
375
+ static VALUE rkrb5_get_default_principal(VALUE self){
376
+ char* princ_name;
377
+ RUBY_KRB5* ptr;
378
+ krb5_ccache ccache;
379
+ krb5_error_code kerror;
380
+
381
+ Data_Get_Struct(self, RUBY_KRB5, ptr);
382
+
383
+ if(!ptr->ctx)
384
+ rb_raise(cKrb5Exception, "no context has been established");
385
+
386
+ // Get the default credentials cache
387
+ kerror = krb5_cc_default(ptr->ctx, &ccache);
388
+
389
+ if(kerror)
390
+ rb_raise(cKrb5Exception, "krb5_cc_default: %s", error_message(kerror));
391
+
392
+ kerror = krb5_cc_get_principal(ptr->ctx, ccache, &ptr->princ);
393
+
394
+ if(kerror){
395
+ krb5_cc_close(ptr->ctx, ccache);
396
+ rb_raise(cKrb5Exception, "krb5_cc_get_principal: %s", error_message(kerror));
397
+ }
398
+
399
+ krb5_cc_close(ptr->ctx, ccache);
400
+
401
+ kerror = krb5_unparse_name(ptr->ctx, ptr->princ, &princ_name);
402
+
403
+ if(kerror)
404
+ rb_raise(cKrb5Exception, "krb5_cc_default: %s", error_message(kerror));
405
+
406
+ return rb_str_new2(princ_name);
407
+ }
408
+
409
+ /*
410
+ * call-seq:
411
+ * krb5.get_permitted_enctypes
412
+ *
413
+ * Returns a hash containing the permitted encoding types. The key is the
414
+ * numeric constant, with a string description as its value.
415
+ *
416
+ * Example:
417
+ *
418
+ * krb.get_permitted_enctypes
419
+ *
420
+ * # Results:
421
+ * {
422
+ * 1 => "DES cbc mode with CRC-32",
423
+ * 2 => "DES cbc mode with RSA-MD4",
424
+ * 3 => "DES cbc mode with RSA-MD5"}
425
+ * 16 => "Triple DES cbc mode with HMAC/sha1",
426
+ * 17 => "AES-128 CTS mode with 96-bit SHA-1 HMAC",
427
+ * 18 => "AES-256 CTS mode with 96-bit SHA-1 HMAC",
428
+ * 23 => "ArcFour with HMAC/md5"
429
+ * }
430
+ */
431
+ static VALUE rkrb5_get_permitted_enctypes(VALUE self){
432
+ RUBY_KRB5* ptr;
433
+ VALUE v_enctypes;
434
+ krb5_enctype* ktypes;
435
+ krb5_error_code kerror;
436
+
437
+ Data_Get_Struct(self, RUBY_KRB5, ptr);
438
+
439
+ if(!ptr->ctx)
440
+ rb_raise(cKrb5Exception, "no context has been established");
441
+
442
+ kerror = krb5_get_permitted_enctypes(ptr->ctx, &ktypes);
443
+
444
+ if(kerror){
445
+ rb_raise(cKrb5Exception, "krb5_get_permitted_types: %s", error_message(kerror));
446
+ }
447
+ else{
448
+ int i;
449
+ char encoding[128];
450
+ v_enctypes = rb_hash_new();
451
+
452
+ for(i = 0; ktypes[i]; i++){
453
+ if(krb5_enctype_to_string(ktypes[i], encoding, 128)){
454
+ rb_raise(cKrb5Exception, "krb5_enctype_to_string: %s", error_message(kerror));
455
+ }
456
+ rb_hash_aset(v_enctypes, INT2FIX(ktypes[i]), rb_str_new2(encoding));
457
+ }
458
+ }
459
+
460
+ return v_enctypes;
461
+ }
462
+
463
+ void Init_rkerberos(){
464
+ mKerberos = rb_define_module("Kerberos");
465
+ cKrb5 = rb_define_class_under(mKerberos, "Krb5", rb_cObject);
466
+ cKrb5Exception = rb_define_class_under(cKrb5, "Exception", rb_eStandardError);
467
+
468
+ // Allocation functions
469
+ rb_define_alloc_func(cKrb5, rkrb5_allocate);
470
+
471
+ // Initializers
472
+ rb_define_method(cKrb5, "initialize", rkrb5_initialize, 0);
473
+
474
+ // Krb5 Methods
475
+ rb_define_method(cKrb5, "change_password", rkrb5_change_password, 2);
476
+ rb_define_method(cKrb5, "close", rkrb5_close, 0);
477
+ rb_define_method(cKrb5, "get_default_realm", rkrb5_get_default_realm, 0);
478
+ rb_define_method(cKrb5, "get_init_creds_password", rkrb5_get_init_creds_passwd, 2);
479
+ rb_define_method(cKrb5, "get_init_creds_keytab", rkrb5_get_init_creds_keytab, -1);
480
+ rb_define_method(cKrb5, "get_default_principal", rkrb5_get_default_principal, 0);
481
+ rb_define_method(cKrb5, "get_permitted_enctypes", rkrb5_get_permitted_enctypes, 0);
482
+ rb_define_method(cKrb5, "set_default_realm", rkrb5_set_default_realm, -1);
483
+
484
+ // Aliases
485
+ rb_define_alias(cKrb5, "default_realm", "get_default_realm");
486
+ rb_define_alias(cKrb5, "default_principal", "get_default_principal");
487
+
488
+ /* 0.1.0: The version of the custom rkerberos library */
489
+ rb_define_const(cKrb5, "VERSION", rb_str_new2("0.1.0"));
490
+
491
+ // Encoding type constants
492
+
493
+ /* 0: None */
494
+ rb_define_const(cKrb5, "ENCTYPE_NULL", INT2FIX(ENCTYPE_NULL));
495
+
496
+ /* 1: DES cbc mode with CRC-32 */
497
+ rb_define_const(cKrb5, "ENCTYPE_DES_CBC_CRC", INT2FIX(ENCTYPE_DES_CBC_CRC));
498
+
499
+ /* 2: DES cbc mode with RSA-MD4 */
500
+ rb_define_const(cKrb5, "ENCTYPE_DES_CBC_MD4", INT2FIX(ENCTYPE_DES_CBC_MD4));
501
+
502
+ /* 3: DES cbc mode with RSA-MD5 */
503
+ rb_define_const(cKrb5, "ENCTYPE_DES_CBC_MD5", INT2FIX(ENCTYPE_DES_CBC_MD5));
504
+
505
+ /* 4: DES cbc mode raw */
506
+ rb_define_const(cKrb5, "ENCTYPE_DES_CBC_RAW", INT2FIX(ENCTYPE_DES_CBC_RAW));
507
+
508
+ /* 5: DES-3 cbc mode with NIST-SHA */
509
+ rb_define_const(cKrb5, "ENCTYPE_DES3_CBC_SHA", INT2FIX(ENCTYPE_DES3_CBC_SHA));
510
+
511
+ /* 6: DES-3 cbc mode raw */
512
+ rb_define_const(cKrb5, "ENCTYPE_DES3_CBC_RAW", INT2FIX(ENCTYPE_DES3_CBC_RAW));
513
+
514
+ /* 8: HMAC SHA1 */
515
+ rb_define_const(cKrb5, "ENCTYPE_DES_HMAC_SHA1", INT2FIX(ENCTYPE_DES_HMAC_SHA1));
516
+
517
+ /* 9: DSA with SHA1, CMS signature */
518
+ rb_define_const(cKrb5, "ENCTYPE_DSA_SHA1_CMS", INT2FIX(ENCTYPE_DSA_SHA1_CMS));
519
+
520
+ /* 10: MD5 with RSA, CMS signature */
521
+ rb_define_const(cKrb5, "ENCTYPE_MD5_RSA_CMS", INT2FIX(ENCTYPE_MD5_RSA_CMS));
522
+
523
+ /* 11: SHA1 with RSA, CMS signature */
524
+ rb_define_const(cKrb5, "ENCTYPE_SHA1_RSA_CMS", INT2FIX(ENCTYPE_SHA1_RSA_CMS));
525
+
526
+ /* 12: RC2 cbc mode, CMS enveloped data */
527
+ rb_define_const(cKrb5, "ENCTYPE_RC2_CBC_ENV", INT2FIX(ENCTYPE_RC2_CBC_ENV));
528
+
529
+ /* 13: RSA encryption, CMS enveloped data */
530
+ rb_define_const(cKrb5, "ENCTYPE_RSA_ENV", INT2FIX(ENCTYPE_RSA_ENV));
531
+
532
+ /* 14: RSA w/OEAP encryption, CMS enveloped data */
533
+ rb_define_const(cKrb5, "ENCTYPE_RSA_ES_OAEP_ENV", INT2FIX(ENCTYPE_RSA_ES_OAEP_ENV));
534
+
535
+ /* 15: DES-3 cbc mode, CMS enveloped data */
536
+ rb_define_const(cKrb5, "ENCTYPE_DES3_CBC_ENV", INT2FIX(ENCTYPE_DES3_CBC_ENV));
537
+
538
+ /* 16: DES3 CBC SHA1 */
539
+ rb_define_const(cKrb5, "ENCTYPE_DES3_CBC_SHA1", INT2FIX(ENCTYPE_DES3_CBC_SHA1));
540
+
541
+ /* 17: AES128 CTS HMAC SHA1 96 */
542
+ rb_define_const(cKrb5, "ENCTYPE_AES128_CTS_HMAC_SHA1_96", INT2FIX(ENCTYPE_AES128_CTS_HMAC_SHA1_96));
543
+
544
+ /* 18: AES256 CTS HMAC SHA1 96 */
545
+ rb_define_const(cKrb5, "ENCTYPE_AES256_CTS_HMAC_SHA1_96", INT2FIX(ENCTYPE_AES256_CTS_HMAC_SHA1_96));
546
+
547
+ /* 23: ARCFOUR HMAC */
548
+ rb_define_const(cKrb5, "ENCTYPE_ARCFOUR_HMAC", INT2FIX(ENCTYPE_ARCFOUR_HMAC));
549
+
550
+ /* 24: ARCFOUR HMAC EXP */
551
+ rb_define_const(cKrb5, "ENCTYPE_ARCFOUR_HMAC_EXP", INT2FIX(ENCTYPE_ARCFOUR_HMAC_EXP));
552
+
553
+ /* 511: Unknown */
554
+ rb_define_const(cKrb5, "ENCTYPE_UNKNOWN", INT2FIX(ENCTYPE_UNKNOWN));
555
+
556
+ // Class initialization
557
+
558
+ Init_context();
559
+ Init_ccache();
560
+ Init_kadm5();
561
+ Init_config();
562
+ Init_policy();
563
+ Init_principal();
564
+ Init_keytab();
565
+ Init_keytab_entry();
566
+ }