rkerberos 0.2.2 → 0.3.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.
data/EXAMPLES.md ADDED
@@ -0,0 +1,1015 @@
1
+ # rkerberos Usage Examples
2
+
3
+ This page provides comprehensive examples for each class in the rkerberos library. All examples assume you have already installed the gem and have a working Kerberos environment.
4
+
5
+ ```ruby
6
+ require 'rkerberos'
7
+ ```
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ - [Kerberos::Krb5](#kerberoskrb5)
14
+ - [Kerberos::Krb5::Context](#kerberoskrb5context)
15
+ - [Kerberos::Krb5::CredentialsCache](#kerberoskrb5credentialscache)
16
+ - [Kerberos::Krb5::Keytab](#kerberoskrb5keytab)
17
+ - [Kerberos::Krb5::Keytab::Entry](#kerberoskrb5keytabentry)
18
+ - [Kerberos::Krb5::Principal](#kerberoskrb5principal)
19
+ - [Kerberos::Kadm5](#kerberoskadm5)
20
+ - [Kerberos::Kadm5::Config](#kerberoskadm5config)
21
+ - [Kerberos::Kadm5::Policy](#kerberoskadm5policy)
22
+ - [Constants](#constants)
23
+ - [Error Handling](#error-handling)
24
+
25
+ ---
26
+
27
+ ## Kerberos::Krb5
28
+
29
+ The main class for Kerberos client operations: authentication, credential acquisition, realm queries, and password management.
30
+
31
+ ### Creating an Instance
32
+
33
+ ```ruby
34
+ # Simple instantiation
35
+ krb5 = Kerberos::Krb5.new
36
+
37
+ # With a shared context (see Kerberos::Krb5::Context)
38
+ ctx = Kerberos::Krb5::Context.new
39
+ krb5 = Kerberos::Krb5.new(context: ctx)
40
+
41
+ # Block form — automatically closes when the block exits
42
+ Kerberos::Krb5.new do |krb5|
43
+ puts krb5.default_realm
44
+ end
45
+ ```
46
+
47
+ ### Querying the Default Realm
48
+
49
+ ```ruby
50
+ krb5 = Kerberos::Krb5.new
51
+ puts krb5.default_realm # => "EXAMPLE.COM"
52
+ krb5.close
53
+ ```
54
+
55
+ ### Setting the Default Realm
56
+
57
+ ```ruby
58
+ krb5 = Kerberos::Krb5.new
59
+ krb5.set_default_realm('OTHER.REALM.COM')
60
+ puts krb5.default_realm # => "OTHER.REALM.COM"
61
+
62
+ # Reset to the default from krb5.conf
63
+ krb5.set_default_realm
64
+ krb5.close
65
+ ```
66
+
67
+ ### Authenticating with a Password
68
+
69
+ ```ruby
70
+ krb5 = Kerberos::Krb5.new
71
+
72
+ # Basic password authentication
73
+ krb5.get_init_creds_password(principal: 'user@EXAMPLE.COM', password: 's3cret')
74
+
75
+ # With an explicit service
76
+ krb5.get_init_creds_password(principal: 'user@EXAMPLE.COM', password: 's3cret', service: 'krbtgt/EXAMPLE.COM')
77
+
78
+ krb5.close
79
+ ```
80
+
81
+ ### Secure Authentication (authenticate!)
82
+
83
+ The `authenticate!` method acquires credentials **and** verifies them against the KDC, protecting against KDC-forging attacks. This is the recommended method for login flows.
84
+
85
+ ```ruby
86
+ krb5 = Kerberos::Krb5.new
87
+
88
+ begin
89
+ krb5.authenticate!(principal: 'user@EXAMPLE.COM', password: 's3cret')
90
+ puts "Authentication successful"
91
+ rescue Kerberos::Krb5::Exception => e
92
+ puts "Authentication failed: #{e.message}"
93
+ end
94
+
95
+ krb5.close
96
+ ```
97
+
98
+ ### Authenticating with a Keytab
99
+
100
+ ```ruby
101
+ krb5 = Kerberos::Krb5.new
102
+
103
+ # Using the default keytab (/etc/krb5.keytab)
104
+ krb5.get_init_creds_keytab(principal: 'host/server.example.com')
105
+
106
+ # Using a specific keytab file
107
+ krb5.get_init_creds_keytab(principal: 'host/server.example.com', keytab: 'FILE:/etc/app.keytab')
108
+
109
+ # With a specific service name
110
+ krb5.get_init_creds_keytab(principal: 'host/server.example.com', service: 'host')
111
+
112
+ # Store the resulting credentials in a cache
113
+ cc = Kerberos::Krb5::CredentialsCache.new
114
+ krb5.get_init_creds_keytab(principal: 'host/server.example.com', ccache: cc)
115
+
116
+ krb5.close
117
+ ```
118
+
119
+ ### Verifying Credentials
120
+
121
+ After acquiring credentials, you can explicitly verify them against the KDC:
122
+
123
+ ```ruby
124
+ krb5 = Kerberos::Krb5.new
125
+ krb5.get_init_creds_password(principal: 'user@EXAMPLE.COM', password: 's3cret')
126
+
127
+ # Basic verification
128
+ krb5.verify_init_creds
129
+
130
+ # Verify against a specific server principal
131
+ krb5.verify_init_creds(server: 'host/server.example.com@EXAMPLE.COM')
132
+
133
+ # Verify using a specific keytab and store results in a credential cache
134
+ keytab = Kerberos::Krb5::Keytab.new
135
+ cc = Kerberos::Krb5::CredentialsCache.new
136
+ krb5.verify_init_creds(keytab: keytab, ccache: cc)
137
+ puts cc.primary_principal # => "user@EXAMPLE.COM"
138
+
139
+ krb5.close
140
+ ```
141
+
142
+ ### Changing a Password
143
+
144
+ ```ruby
145
+ krb5 = Kerberos::Krb5.new
146
+
147
+ # First authenticate the user
148
+ krb5.get_init_creds_password(principal: 'user@EXAMPLE.COM', password: 'old_password')
149
+
150
+ # Then change the password
151
+ krb5.change_password('old_password', 'new_password')
152
+
153
+ krb5.close
154
+ ```
155
+
156
+ ### Getting the Default Principal
157
+
158
+ Returns the principal from the default credentials cache:
159
+
160
+ ```ruby
161
+ krb5 = Kerberos::Krb5.new
162
+ puts krb5.default_principal # => "user@EXAMPLE.COM"
163
+ krb5.close
164
+ ```
165
+
166
+ ### Listing Permitted Encryption Types
167
+
168
+ ```ruby
169
+ krb5 = Kerberos::Krb5.new
170
+ enctypes = krb5.get_permitted_enctypes
171
+
172
+ enctypes.each do |code, description|
173
+ puts "#{code}: #{description}"
174
+ end
175
+ # Example output:
176
+ # 17: AES-128 CTS mode with 96-bit SHA-1 HMAC
177
+ # 18: AES-256 CTS mode with 96-bit SHA-1 HMAC
178
+ # 23: ArcFour with HMAC/md5
179
+
180
+ krb5.close
181
+ ```
182
+
183
+ ### Version
184
+
185
+ ```ruby
186
+ puts Kerberos::Krb5::VERSION # => "0.3.0"
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Kerberos::Krb5::Context
192
+
193
+ A lightweight Kerberos context object. Useful when you need a context for configuration queries without full credential management.
194
+
195
+ ### Standard Context
196
+
197
+ ```ruby
198
+ ctx = Kerberos::Krb5::Context.new
199
+ # ... use ctx ...
200
+ ctx.close
201
+ ```
202
+
203
+ ### Secure Context
204
+
205
+ A secure context ignores environment variables like `KRB5_CONFIG` and reads only from system configuration files. Use this in setuid programs or other security-sensitive environments.
206
+
207
+ ```ruby
208
+ ctx = Kerberos::Krb5::Context.new(secure: true)
209
+ ctx.close
210
+ ```
211
+
212
+ ### Context with a Custom Profile
213
+
214
+ Load configuration from a specific krb5.conf file:
215
+
216
+ ```ruby
217
+ ctx = Kerberos::Krb5::Context.new(profile: '/opt/custom/krb5.conf')
218
+ ctx.close
219
+ ```
220
+
221
+ ### Combining Options
222
+
223
+ ```ruby
224
+ ctx = Kerberos::Krb5::Context.new(profile: '/opt/custom/krb5.conf', secure: true)
225
+ ctx.close
226
+ ```
227
+
228
+ ### Sharing a Context
229
+
230
+ A Context can be shared with other rkerberos objects via the `context:` keyword argument. This avoids repeatedly parsing configuration files and allows multiple objects to operate under the same Kerberos configuration.
231
+
232
+ ```ruby
233
+ ctx = Kerberos::Krb5::Context.new(profile: '/opt/custom/krb5.conf')
234
+
235
+ # All of these objects will share the same underlying krb5_context
236
+ krb5 = Kerberos::Krb5.new(context: ctx)
237
+ cc = Kerberos::Krb5::CredentialsCache.new(context: ctx)
238
+ keytab = Kerberos::Krb5::Keytab.new(context: ctx)
239
+ princ = Kerberos::Krb5::Principal.new(name: 'user@EXAMPLE.COM', context: ctx)
240
+ config = Kerberos::Kadm5::Config.new(context: ctx)
241
+ kadm5 = Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass', context: ctx)
242
+
243
+ # When sharing a context, the Context object owns the underlying krb5_context.
244
+ # The objects that borrow it will NOT free the context when they are closed
245
+ # or garbage collected — only closing the Context itself will release it.
246
+ # Make sure the Context outlives all objects that reference it.
247
+
248
+ krb5.close
249
+ cc.close
250
+ keytab.close
251
+ kadm5.close
252
+ ctx.close
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Kerberos::Krb5::CredentialsCache
258
+
259
+ Encapsulates a Kerberos credentials cache (ccache). Used for storing and managing TGTs and service tickets.
260
+
261
+ ### Using the Default Cache
262
+
263
+ ```ruby
264
+ cc = Kerberos::Krb5::CredentialsCache.new
265
+ puts cc.default_name # => "FILE:/tmp/krb5cc_1000"
266
+
267
+ cc.close
268
+ ```
269
+
270
+ ### Creating a Cache with a Specific Principal
271
+
272
+ ```ruby
273
+ # Creates (or reinitializes) the default cache with this principal
274
+ cc = Kerberos::Krb5::CredentialsCache.new(principal: 'user@EXAMPLE.COM')
275
+ puts cc.primary_principal # => "user@EXAMPLE.COM"
276
+
277
+ cc.close
278
+ ```
279
+
280
+ ### Using a Named Cache
281
+
282
+ ```ruby
283
+ # Use a specific cache file
284
+ cc = Kerberos::Krb5::CredentialsCache.new(cache_name: 'FILE:/tmp/krb5cc_myapp')
285
+ puts cc.cache_name # => "/tmp/krb5cc_myapp"
286
+ puts cc.cache_type # => "FILE"
287
+
288
+ cc.close
289
+ ```
290
+
291
+ ### Querying Cache Properties
292
+
293
+ ```ruby
294
+ cc = Kerberos::Krb5::CredentialsCache.new
295
+
296
+ puts cc.default_name # Default cache path for the system
297
+ puts cc.cache_name # Actual name of this cache instance
298
+ puts cc.cache_type # Cache type, e.g. "FILE", "MEMORY", "KCM"
299
+ puts cc.primary_principal # The principal stored in this cache
300
+ puts cc.principal # Alias for primary_principal
301
+
302
+ cc.close
303
+ ```
304
+
305
+ ### Duplicating a Cache
306
+
307
+ ```ruby
308
+ cc = Kerberos::Krb5::CredentialsCache.new(principal: 'user@EXAMPLE.COM')
309
+ cc2 = cc.dup # Independent copy; closing one does not affect the other
310
+
311
+ cc.close
312
+ cc2.close
313
+ ```
314
+
315
+ ### Destroying a Cache
316
+
317
+ Destroys the cache file and invalidates the object. Returns `true` if the cache was destroyed or `false` if no cache was found.
318
+
319
+ ```ruby
320
+ cc = Kerberos::Krb5::CredentialsCache.new(principal: 'user@EXAMPLE.COM')
321
+ cc.destroy # => true (also aliased as cc.delete)
322
+ ```
323
+
324
+ ### Storing Verified Credentials in a Cache
325
+
326
+ ```ruby
327
+ krb5 = Kerberos::Krb5.new
328
+ cc = Kerberos::Krb5::CredentialsCache.new
329
+
330
+ krb5.get_init_creds_password(principal: 'user@EXAMPLE.COM', password: 's3cret')
331
+ krb5.verify_init_creds(ccache: cc)
332
+
333
+ puts cc.primary_principal # => "user@EXAMPLE.COM"
334
+
335
+ cc.close
336
+ krb5.close
337
+ ```
338
+
339
+ ---
340
+
341
+ ## Kerberos::Krb5::Keytab
342
+
343
+ Provides access to Kerberos keytab files for reading entries and performing keytab-based authentication.
344
+
345
+ ### Opening the Default Keytab
346
+
347
+ ```ruby
348
+ keytab = Kerberos::Krb5::Keytab.new
349
+ puts keytab.name # => "FILE:/etc/krb5.keytab"
350
+ puts keytab.default_name # => "FILE:/etc/krb5.keytab"
351
+ puts keytab.keytab_name # Canonical name from the library
352
+ puts keytab.keytab_type # => "FILE"
353
+
354
+ keytab.close
355
+ ```
356
+
357
+ ### Opening a Specific Keytab
358
+
359
+ ```ruby
360
+ keytab = Kerberos::Krb5::Keytab.new(name: 'FILE:/etc/app.keytab')
361
+ puts keytab.name # => "FILE:/etc/app.keytab"
362
+
363
+ keytab.close
364
+ ```
365
+
366
+ ### Iterating Over Entries
367
+
368
+ ```ruby
369
+ keytab = Kerberos::Krb5::Keytab.new
370
+
371
+ keytab.each do |entry|
372
+ puts "Principal: #{entry.principal}"
373
+ puts "Timestamp: #{entry.timestamp}"
374
+ puts "Version: #{entry.vno}"
375
+ puts "Key Type: #{entry.key}"
376
+ puts "---"
377
+ end
378
+
379
+ keytab.close
380
+ ```
381
+
382
+ ### Using the Singleton foreach Method
383
+
384
+ Iterate over keytab entries without creating an instance:
385
+
386
+ ```ruby
387
+ # Default keytab
388
+ Kerberos::Krb5::Keytab.foreach do |entry|
389
+ puts entry.principal
390
+ end
391
+
392
+ # Specific keytab
393
+ Kerberos::Krb5::Keytab.foreach('FILE:/etc/app.keytab') do |entry|
394
+ puts entry.principal
395
+ end
396
+ ```
397
+
398
+ ### Looking Up a Specific Entry
399
+
400
+ ```ruby
401
+ keytab = Kerberos::Krb5::Keytab.new
402
+
403
+ # Find the first entry matching a principal
404
+ entry = keytab.get_entry('host/server.example.com@EXAMPLE.COM')
405
+ puts entry.principal
406
+ puts entry.vno
407
+
408
+ # Find with a specific version number
409
+ entry = keytab.get_entry('host/server.example.com@EXAMPLE.COM', 2)
410
+
411
+ # Find with a specific version number and encryption type
412
+ entry = keytab.get_entry(
413
+ 'host/server.example.com@EXAMPLE.COM',
414
+ 0,
415
+ Kerberos::Krb5::ENCTYPE_AES256_CTS_HMAC_SHA1_96
416
+ )
417
+
418
+ keytab.close
419
+ ```
420
+
421
+ ### Duplicating a Keytab
422
+
423
+ ```ruby
424
+ keytab = Kerberos::Krb5::Keytab.new
425
+ keytab2 = keytab.dup # Independent handle; closing one doesn't affect the other
426
+
427
+ keytab.close
428
+ keytab2.close
429
+ ```
430
+
431
+ ---
432
+
433
+ ## Kerberos::Krb5::Keytab::Entry
434
+
435
+ Represents a single entry in a keytab. These objects are yielded by `Keytab#each` and `Keytab.foreach`, or returned by `Keytab#get_entry`.
436
+
437
+ ### Attributes
438
+
439
+ ```ruby
440
+ keytab = Kerberos::Krb5::Keytab.new
441
+
442
+ keytab.each do |entry|
443
+ entry.principal # => "host/server.example.com@EXAMPLE.COM"
444
+ entry.timestamp # => 2026-03-01 12:00:00 -0500 (Time object)
445
+ entry.vno # => 1 (key version number)
446
+ entry.key # => 18 (encryption type, e.g. AES-256)
447
+ end
448
+
449
+ keytab.close
450
+ ```
451
+
452
+ ### Inspecting an Entry
453
+
454
+ ```ruby
455
+ keytab = Kerberos::Krb5::Keytab.new
456
+
457
+ entry = keytab.get_entry('host/server.example.com@EXAMPLE.COM')
458
+ puts entry.inspect
459
+ # => #<Kerberos::Krb5::Keytab::Entry principal="host/server.example.com@EXAMPLE.COM" timestamp=2026-03-01 12:00:00 -0500 vno=1 key=18>
460
+
461
+ keytab.close
462
+ ```
463
+
464
+ ---
465
+
466
+ ## Kerberos::Krb5::Principal
467
+
468
+ Represents a Kerberos principal with associated metadata. Typically returned by `Kadm5#get_principal` or `Kadm5#find_principal`, but can also be created standalone.
469
+
470
+ ### Creating a Principal
471
+
472
+ ```ruby
473
+ principal = Kerberos::Krb5::Principal.new(name: 'user@EXAMPLE.COM')
474
+ puts principal.principal # => "user@EXAMPLE.COM"
475
+ puts principal.name # => "user@EXAMPLE.COM" (alias)
476
+ puts principal.realm # => "EXAMPLE.COM"
477
+ ```
478
+
479
+ ### Using a Block
480
+
481
+ ```ruby
482
+ principal = Kerberos::Krb5::Principal.new(name: 'user@EXAMPLE.COM') do |p|
483
+ p.expire_time = Time.now + 86400 * 365
484
+ p.max_life = 36000
485
+ end
486
+ ```
487
+
488
+ ### Changing the Realm
489
+
490
+ ```ruby
491
+ principal = Kerberos::Krb5::Principal.new(name: 'user@EXAMPLE.COM')
492
+ puts principal.realm # => "EXAMPLE.COM"
493
+
494
+ principal.realm = 'OTHER.REALM.COM'
495
+ puts principal.realm # => "OTHER.REALM.COM"
496
+ ```
497
+
498
+ ### Comparing Principals
499
+
500
+ ```ruby
501
+ p1 = Kerberos::Krb5::Principal.new(name: 'user@EXAMPLE.COM')
502
+ p2 = Kerberos::Krb5::Principal.new(name: 'user@EXAMPLE.COM')
503
+ p3 = Kerberos::Krb5::Principal.new(name: 'admin@EXAMPLE.COM')
504
+
505
+ p1 == p2 # => true
506
+ p1 == p3 # => false
507
+ ```
508
+
509
+ ### Attributes from Kadm5
510
+
511
+ When retrieved via `Kadm5#get_principal`, the Principal object is populated with additional metadata:
512
+
513
+ ```ruby
514
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
515
+ princ = kadm5.get_principal('user@EXAMPLE.COM')
516
+
517
+ puts princ.principal # => "user@EXAMPLE.COM"
518
+ puts princ.attributes # => Bitmask of principal flags
519
+ puts princ.aux_attributes # => Auxiliary attributes
520
+ puts princ.expire_time # => Time or nil
521
+ puts princ.fail_auth_count # => Integer
522
+ puts princ.kvno # => Key version number
523
+ puts princ.last_failed # => Time or nil
524
+ puts princ.last_password_change # => Time or nil
525
+ puts princ.last_success # => Time or nil
526
+ puts princ.max_life # => Max ticket life in seconds
527
+ puts princ.max_renewable_life # => Max renewable life in seconds
528
+ puts princ.mod_date # => Time or nil
529
+ puts princ.mod_name # => "admin/admin@EXAMPLE.COM"
530
+ puts princ.password_expiration # => Time or nil
531
+ puts princ.policy # => "default" or nil
532
+ end
533
+ ```
534
+
535
+ ---
536
+
537
+ ## Kerberos::Kadm5
538
+
539
+ The admin interface for managing principals, policies, and passwords. Requires admin credentials.
540
+
541
+ ### Connecting with a Password
542
+
543
+ ```ruby
544
+ # Block form (recommended) — automatically closes when done
545
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
546
+ # ... admin operations ...
547
+ end
548
+
549
+ # Manual form
550
+ kadm5 = Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass')
551
+ # ... admin operations ...
552
+ kadm5.close
553
+ ```
554
+
555
+ ### Connecting with a Keytab
556
+
557
+ ```ruby
558
+ # Using the default keytab (/etc/krb5.keytab)
559
+ Kerberos::Kadm5.new(principal: 'admin/admin', keytab: true) do |kadm5|
560
+ # ...
561
+ end
562
+
563
+ # Using a specific keytab file
564
+ Kerberos::Kadm5.new(principal: 'admin/admin', keytab: '/etc/admin.keytab') do |kadm5|
565
+ # ...
566
+ end
567
+ ```
568
+
569
+ ### Using a Shared Context
570
+
571
+ ```ruby
572
+ ctx = Kerberos::Krb5::Context.new(profile: '/opt/custom/krb5.conf')
573
+
574
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass', context: ctx) do |kadm5|
575
+ # The admin connection uses the custom Kerberos configuration
576
+ princ = kadm5.get_principal('user@EXAMPLE.COM')
577
+ puts princ.principal
578
+ end
579
+
580
+ ctx.close
581
+ ```
582
+
583
+ ### Specifying a Custom Service
584
+
585
+ ```ruby
586
+ Kerberos::Kadm5.new(
587
+ principal: 'admin/admin',
588
+ password: 'admin_pass',
589
+ service: 'kadmin/changepw'
590
+ ) do |kadm5|
591
+ # ...
592
+ end
593
+ ```
594
+
595
+ ### Using Database Arguments
596
+
597
+ ```ruby
598
+ # Single db_arg
599
+ Kerberos::Kadm5.new(
600
+ principal: 'admin/admin',
601
+ password: 'admin_pass',
602
+ db_args: 'tktpolicy=default'
603
+ ) do |kadm5|
604
+ # ...
605
+ end
606
+
607
+ # Multiple db_args
608
+ Kerberos::Kadm5.new(
609
+ principal: 'admin/admin',
610
+ password: 'admin_pass',
611
+ db_args: ['arg1=value1', 'arg2=value2']
612
+ ) do |kadm5|
613
+ # ...
614
+ end
615
+ ```
616
+
617
+ ### Creating a Principal
618
+
619
+ ```ruby
620
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
621
+ kadm5.create_principal(name: 'newuser@EXAMPLE.COM', password: 'initial_password')
622
+
623
+ # With database arguments
624
+ kadm5.create_principal(
625
+ name: 'ldapuser@EXAMPLE.COM',
626
+ password: 'password',
627
+ db_args: 'tktpolicy=default'
628
+ )
629
+ end
630
+ ```
631
+
632
+ ### Deleting a Principal
633
+
634
+ ```ruby
635
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
636
+ kadm5.delete_principal('newuser@EXAMPLE.COM')
637
+ end
638
+ ```
639
+
640
+ ### Getting Principal Information
641
+
642
+ ```ruby
643
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
644
+ # Raises PrincipalNotFoundException if not found
645
+ princ = kadm5.get_principal('user@EXAMPLE.COM')
646
+ puts princ.inspect
647
+ end
648
+ ```
649
+
650
+ ### Finding a Principal (nil if not found)
651
+
652
+ ```ruby
653
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
654
+ princ = kadm5.find_principal('user@EXAMPLE.COM')
655
+
656
+ if princ
657
+ puts princ.principal
658
+ else
659
+ puts "Principal not found"
660
+ end
661
+ end
662
+ ```
663
+
664
+ ### Listing Principals
665
+
666
+ ```ruby
667
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
668
+ # List all principals
669
+ all = kadm5.get_principals
670
+ all.each { |name| puts name }
671
+
672
+ # List principals matching a pattern
673
+ matches = kadm5.get_principals('host/*')
674
+ matches.each { |name| puts name }
675
+
676
+ # Other pattern examples
677
+ kadm5.get_principals('user*') # Starts with "user"
678
+ kadm5.get_principals('*/admin@*') # Admin instances
679
+ end
680
+ ```
681
+
682
+ ### Setting a Password
683
+
684
+ ```ruby
685
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
686
+ kadm5.set_password('user@EXAMPLE.COM', 'new_password')
687
+ end
688
+ ```
689
+
690
+ ### Setting Password Expiration
691
+
692
+ ```ruby
693
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
694
+ # Set password to expire at a specific Unix timestamp
695
+ expire_time = (Time.now + 86400 * 90).to_i # 90 days from now
696
+ kadm5.set_pwexpire('user@EXAMPLE.COM', expire_time)
697
+ end
698
+ ```
699
+
700
+ ### Generating Random Keys
701
+
702
+ ```ruby
703
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
704
+ num_keys = kadm5.generate_random_key('host/server.example.com@EXAMPLE.COM')
705
+ puts "Generated #{num_keys} random key(s)"
706
+ end
707
+ ```
708
+
709
+ ### Checking Admin Privileges
710
+
711
+ ```ruby
712
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
713
+ # Get privileges as a bitmask
714
+ privs = kadm5.get_privileges
715
+ puts privs # => 15 (all privileges)
716
+
717
+ # Get privileges as human-readable strings
718
+ privs = kadm5.get_privileges(true)
719
+ puts privs.inspect # => ["GET", "ADD", "MODIFY", "DELETE"]
720
+ end
721
+ ```
722
+
723
+ ### Complete Lifecycle Example
724
+
725
+ ```ruby
726
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
727
+ # Create a new user
728
+ kadm5.create_principal(name: 'jdoe@EXAMPLE.COM', password: 'temp_password')
729
+
730
+ # Look up the user
731
+ princ = kadm5.get_principal('jdoe@EXAMPLE.COM')
732
+ puts "Created: #{princ.principal}, kvno: #{princ.kvno}"
733
+
734
+ # Reset their password
735
+ kadm5.set_password('jdoe@EXAMPLE.COM', 'better_password')
736
+
737
+ # Set password expiration to 90 days
738
+ kadm5.set_pwexpire('jdoe@EXAMPLE.COM', (Time.now + 86400 * 90).to_i)
739
+
740
+ # Generate random keys (e.g. for a service principal)
741
+ kadm5.create_principal(name: 'HTTP/webapp.example.com@EXAMPLE.COM', password: 'temp')
742
+ kadm5.generate_random_key('HTTP/webapp.example.com@EXAMPLE.COM')
743
+
744
+ # Clean up
745
+ kadm5.delete_principal('jdoe@EXAMPLE.COM')
746
+ kadm5.delete_principal('HTTP/webapp.example.com@EXAMPLE.COM')
747
+ end
748
+ ```
749
+
750
+ ---
751
+
752
+ ## Kerberos::Kadm5::Config
753
+
754
+ A read-only snapshot of the Kerberos admin configuration. The returned object is frozen.
755
+
756
+ ### Reading Configuration
757
+
758
+ ```ruby
759
+ config = Kerberos::Kadm5::Config.new
760
+
761
+ puts config.realm # => "EXAMPLE.COM"
762
+ puts config.admin_server # => "kdc.example.com"
763
+ puts config.kadmind_port # => 749
764
+ puts config.kpasswd_port # => 464
765
+ puts config.acl_file # => "/var/kerberos/krb5kdc/kadm5.acl"
766
+ puts config.dict_file # => nil (or path to dictionary file)
767
+ puts config.stash_file # => "/var/kerberos/krb5kdc/.k5.EXAMPLE.COM"
768
+ puts config.mkey_name # => "K/M"
769
+ puts config.enctype # => 18 (AES-256)
770
+ puts config.max_life # => 36000 (seconds)
771
+ puts config.max_rlife # => 604800 (seconds)
772
+ puts config.expiration # => Time object or nil
773
+ puts config.flags # => Integer bitmask
774
+ puts config.kvno # => 1
775
+ puts config.iprop_enabled # => true or false
776
+ puts config.iprop_logfile # => path or nil
777
+ puts config.iprop_poll_time # => Integer (seconds) or nil
778
+ puts config.iprop_port # => Integer or nil
779
+ puts config.num_keysalts # => Integer
780
+ puts config.keysalts # => Array of KeySalt objects or nil
781
+
782
+ puts config.inspect
783
+ ```
784
+
785
+ ### Using a Shared Context
786
+
787
+ ```ruby
788
+ ctx = Kerberos::Krb5::Context.new(profile: '/opt/custom/krb5.conf')
789
+ config = Kerberos::Kadm5::Config.new(context: ctx)
790
+
791
+ puts config.realm
792
+ puts config.admin_server
793
+
794
+ ctx.close
795
+ ```
796
+
797
+ ### Inspecting KeySalt Entries
798
+
799
+ ```ruby
800
+ config = Kerberos::Kadm5::Config.new
801
+
802
+ if config.keysalts
803
+ config.keysalts.each do |ks|
804
+ puts "Enctype: #{ks.enctype}, Salttype: #{ks.salttype}"
805
+ end
806
+ end
807
+ ```
808
+
809
+ ---
810
+
811
+ ## Kerberos::Kadm5::Policy
812
+
813
+ Represents a Kerberos password policy. Policy objects are created in Ruby and then applied via the Kadm5 admin interface.
814
+
815
+ ### Creating a Policy Object
816
+
817
+ ```ruby
818
+ policy = Kerberos::Kadm5::Policy.new(
819
+ name: 'strict',
820
+ min_life: 3600, # Minimum password lifetime: 1 hour
821
+ max_life: 7776000, # Maximum password lifetime: 90 days
822
+ min_length: 12, # Minimum password length
823
+ min_classes: 3, # Require at least 3 character classes
824
+ history_num: 5 # Remember last 5 passwords
825
+ )
826
+
827
+ puts policy.name # => "strict" (alias for policy.policy)
828
+ puts policy.policy # => "strict"
829
+ puts policy.min_life # => 3600
830
+ puts policy.max_life # => 7776000
831
+ puts policy.min_length # => 12
832
+ puts policy.min_classes # => 3
833
+ puts policy.history_num # => 5
834
+ ```
835
+
836
+ ### Creating a Policy in Kerberos
837
+
838
+ ```ruby
839
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
840
+ # Using a Policy object
841
+ policy = Kerberos::Kadm5::Policy.new(name: 'standard', min_length: 8, min_classes: 2)
842
+ kadm5.create_policy(policy)
843
+
844
+ # Using keywords directly
845
+ kadm5.create_policy(name: 'simple', min_length: 6)
846
+ end
847
+ ```
848
+
849
+ ### Retrieving a Policy
850
+
851
+ ```ruby
852
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
853
+ # Raises an exception if not found
854
+ policy = kadm5.get_policy('standard')
855
+ puts policy.inspect
856
+
857
+ # Returns nil if not found
858
+ policy = kadm5.find_policy('nonexistent')
859
+ puts policy.nil? # => true
860
+ end
861
+ ```
862
+
863
+ ### Modifying a Policy
864
+
865
+ ```ruby
866
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
867
+ policy = kadm5.get_policy('standard')
868
+ policy.min_length = 10
869
+ policy.min_classes = 3
870
+ kadm5.modify_policy(policy)
871
+ end
872
+ ```
873
+
874
+ ### Listing Policies
875
+
876
+ ```ruby
877
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
878
+ # List all policies
879
+ kadm5.get_policies.each { |name| puts name }
880
+
881
+ # List policies matching a pattern
882
+ kadm5.get_policies('s*').each { |name| puts name }
883
+ end
884
+ ```
885
+
886
+ ### Deleting a Policy
887
+
888
+ ```ruby
889
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
890
+ kadm5.delete_policy('standard')
891
+ end
892
+ ```
893
+
894
+ ### Policy Lifecycle Example
895
+
896
+ ```ruby
897
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'admin_pass') do |kadm5|
898
+ # Create a policy
899
+ kadm5.create_policy(name: 'webusers', min_length: 10, min_classes: 2, max_life: 7776000)
900
+
901
+ # Verify it was created
902
+ policy = kadm5.get_policy('webusers')
903
+ puts "Policy '#{policy.name}' created with min_length=#{policy.min_length}"
904
+
905
+ # Modify it
906
+ policy.min_length = 12
907
+ kadm5.modify_policy(policy)
908
+
909
+ # Clean up
910
+ kadm5.delete_policy('webusers')
911
+ end
912
+ ```
913
+
914
+ ---
915
+
916
+ ## Constants
917
+
918
+ ### Encryption Type Constants
919
+
920
+ Available on `Kerberos::Krb5`:
921
+
922
+ | Constant | Value | Description |
923
+ |---|---|---|
924
+ | `ENCTYPE_NULL` | 0 | None |
925
+ | `ENCTYPE_DES_CBC_CRC` | 1 | DES cbc mode with CRC-32 |
926
+ | `ENCTYPE_DES_CBC_MD4` | 2 | DES cbc mode with RSA-MD4 |
927
+ | `ENCTYPE_DES_CBC_MD5` | 3 | DES cbc mode with RSA-MD5 |
928
+ | `ENCTYPE_DES_CBC_RAW` | 4 | DES cbc mode raw |
929
+ | `ENCTYPE_DES3_CBC_SHA` | 5 | DES-3 cbc mode with NIST-SHA |
930
+ | `ENCTYPE_DES3_CBC_RAW` | 6 | DES-3 cbc mode raw |
931
+ | `ENCTYPE_DES_HMAC_SHA1` | 8 | HMAC SHA1 |
932
+ | `ENCTYPE_DES3_CBC_SHA1` | 16 | DES3 CBC SHA1 |
933
+ | `ENCTYPE_AES128_CTS_HMAC_SHA1_96` | 17 | AES-128 CTS mode with 96-bit SHA-1 HMAC |
934
+ | `ENCTYPE_AES256_CTS_HMAC_SHA1_96` | 18 | AES-256 CTS mode with 96-bit SHA-1 HMAC |
935
+ | `ENCTYPE_ARCFOUR_HMAC` | 23 | ArcFour with HMAC/md5 |
936
+ | `ENCTYPE_ARCFOUR_HMAC_EXP` | 24 | ArcFour HMAC EXP |
937
+ | `ENCTYPE_UNKNOWN` | 511 | Unknown |
938
+
939
+ ### Kadm5 Attribute Constants
940
+
941
+ Available on `Kerberos::Kadm5`:
942
+
943
+ | Constant | Description |
944
+ |---|---|
945
+ | `DISALLOW_POSTDATED` | Disallow postdated tickets |
946
+ | `DISALLOW_FORWARDABLE` | Disallow forwardable tickets |
947
+ | `DISALLOW_TGT_BASED` | Disallow TGT-based requests |
948
+ | `DISALLOW_RENEWABLE` | Disallow renewable tickets |
949
+ | `DISALLOW_PROXIABLE` | Disallow proxiable tickets |
950
+ | `DISALLOW_DUP_SKEY` | Disallow duplicate session keys |
951
+ | `DISALLOW_ALL_TIX` | Disallow all tickets |
952
+ | `REQUIRES_PRE_AUTH` | Require pre-authentication |
953
+ | `REQUIRES_HW_AUTH` | Require hardware authentication |
954
+ | `REQUIRES_PWCHANGE` | Require password change |
955
+ | `DISALLOW_SVR` | Disallow service tickets |
956
+ | `PWCHANGE_SERVICE` | Password change service |
957
+ | `SUPPORT_DESMD5` | Support DES-MD5 |
958
+ | `NEW_PRINC` | New principal |
959
+
960
+ ---
961
+
962
+ ## Error Handling
963
+
964
+ The library raises specific exception classes depending on the component:
965
+
966
+ ```ruby
967
+ begin
968
+ krb5 = Kerberos::Krb5.new
969
+ krb5.get_init_creds_password(principal: 'user@EXAMPLE.COM', password: 'wrong_password')
970
+ rescue Kerberos::Krb5::Exception => e
971
+ puts "Krb5 error: #{e.message}"
972
+ ensure
973
+ krb5&.close
974
+ end
975
+
976
+ begin
977
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'wrong') do |kadm5|
978
+ # ...
979
+ end
980
+ rescue Kerberos::Kadm5::Exception => e
981
+ puts "Kadm5 error: #{e.message}"
982
+ end
983
+
984
+ # Principal not found (specific subclass)
985
+ begin
986
+ Kerberos::Kadm5.new(principal: 'admin/admin', password: 'pass') do |kadm5|
987
+ kadm5.get_principal('nonexistent@EXAMPLE.COM')
988
+ end
989
+ rescue Kerberos::Kadm5::PrincipalNotFoundException => e
990
+ puts "Principal not found: #{e.message}"
991
+ rescue Kerberos::Kadm5::Exception => e
992
+ puts "Other admin error: #{e.message}"
993
+ end
994
+
995
+ # Keytab-specific errors
996
+ begin
997
+ keytab = Kerberos::Krb5::Keytab.new(name: 'FILE:/nonexistent/path')
998
+ rescue Kerberos::Krb5::Keytab::Exception => e
999
+ puts "Keytab error: #{e.message}"
1000
+ end
1001
+ ```
1002
+
1003
+ ### Exception Hierarchy
1004
+
1005
+ ```
1006
+ StandardError
1007
+ ├── Kerberos::Krb5::Exception
1008
+ │ └── (general Kerberos errors)
1009
+ ├── Kerberos::Krb5::Keytab::Exception
1010
+ │ └── (keytab-specific errors)
1011
+ ├── Kerberos::Kadm5::Exception
1012
+ │ └── (admin errors)
1013
+ └── Kerberos::Kadm5::PrincipalNotFoundException
1014
+ └── (principal lookup failures)
1015
+ ```