rkerberos 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }