1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.shared.kerberos;
21
22
23 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES3_CBC_MD5;
24 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES3_CBC_SHA1;
25 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES3_CBC_SHA1_KD;
26 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES_CBC_CRC;
27 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES_CBC_MD4;
28 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES_CBC_MD5;
29 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DES_EDE3_CBC_ENV_OID;
30 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.DSAWITHSHA1_CMSOID;
31 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.MD5WITHRSAENCRYPTION_CMSOID;
32 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.RC2CBC_ENVOID;
33 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.RC4_HMAC;
34 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.RSAENCRYPTION_ENVOID;
35 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.RSAES_OAEP_ENV_OID;
36 import static org.apache.directory.shared.kerberos.codec.types.EncryptionType.SHA1WITHRSAENCRYPTION_CMSOID;
37
38 import java.net.InetAddress;
39 import java.text.ParseException;
40 import java.text.SimpleDateFormat;
41 import java.util.ArrayList;
42 import java.util.HashSet;
43 import java.util.LinkedHashMap;
44 import java.util.LinkedHashSet;
45 import java.util.List;
46 import java.util.Locale;
47 import java.util.Map;
48 import java.util.Set;
49 import java.util.TimeZone;
50
51 import javax.security.auth.kerberos.KerberosPrincipal;
52
53 import org.apache.directory.api.util.Strings;
54 import org.apache.directory.server.i18n.I18n;
55 import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
56 import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
57 import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
58 import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
59 import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
60 import org.apache.directory.shared.kerberos.codec.KerberosDecoder;
61 import org.apache.directory.shared.kerberos.codec.options.ApOptions;
62 import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
63 import org.apache.directory.shared.kerberos.components.EncTicketPart;
64 import org.apache.directory.shared.kerberos.components.EncryptionKey;
65 import org.apache.directory.shared.kerberos.components.HostAddress;
66 import org.apache.directory.shared.kerberos.components.PrincipalName;
67 import org.apache.directory.shared.kerberos.exceptions.ErrorType;
68 import org.apache.directory.shared.kerberos.exceptions.KerberosException;
69 import org.apache.directory.shared.kerberos.messages.ApReq;
70 import org.apache.directory.shared.kerberos.messages.Authenticator;
71 import org.apache.directory.shared.kerberos.messages.Ticket;
72
73
74
75
76
77
78
79 public class KerberosUtils
80 {
81
82 public static final int NULL = -1;
83
84
85 public static final List<String> EMPTY_PRINCIPAL_NAME = new ArrayList<>();
86
87
88
89
90
91 private static final Map<String, String> cipherAlgoMap = new LinkedHashMap<>();
92
93 public static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone( "UTC" );
94
95
96 public static final SimpleDateFormat UTC_DATE_FORMAT = new SimpleDateFormat( "yyyyMMddHHmmss'Z'", Locale.ROOT );
97
98 private static final Set<EncryptionType> oldEncTypes = new HashSet<>();
99
100 static
101 {
102 UTC_DATE_FORMAT.setTimeZone( UTC_TIME_ZONE );
103
104 cipherAlgoMap.put( "rc4", "ArcFourHmac" );
105 cipherAlgoMap.put( "aes256", "AES256" );
106 cipherAlgoMap.put( "aes128", "AES128" );
107 cipherAlgoMap.put( "des3", "DESede" );
108 cipherAlgoMap.put( "des", "DES" );
109
110 oldEncTypes.add( DES_CBC_CRC );
111 oldEncTypes.add( DES_CBC_MD4 );
112 oldEncTypes.add( DES_CBC_MD5 );
113 oldEncTypes.add( DES_EDE3_CBC_ENV_OID );
114 oldEncTypes.add( DES3_CBC_MD5 );
115 oldEncTypes.add( DES3_CBC_SHA1 );
116 oldEncTypes.add( DES3_CBC_SHA1_KD );
117 oldEncTypes.add( DSAWITHSHA1_CMSOID );
118 oldEncTypes.add( MD5WITHRSAENCRYPTION_CMSOID );
119 oldEncTypes.add( SHA1WITHRSAENCRYPTION_CMSOID );
120 oldEncTypes.add( RC2CBC_ENVOID );
121 oldEncTypes.add( RSAENCRYPTION_ENVOID );
122 oldEncTypes.add( RSAES_OAEP_ENV_OID );
123 oldEncTypes.add( RC4_HMAC );
124 }
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173 public static List<String> getNames( KerberosPrincipal principal ) throws ParseException
174 {
175 if ( principal == null )
176 {
177 return EMPTY_PRINCIPAL_NAME;
178 }
179
180 String names = principal.getName();
181
182 if ( Strings.isEmpty( names ) )
183 {
184
185 return EMPTY_PRINCIPAL_NAME;
186 }
187
188 return getNames( names );
189 }
190
191
192
193
194
195 public static List<String> getNames( String principalNames ) throws ParseException
196 {
197 if ( principalNames == null )
198 {
199 return EMPTY_PRINCIPAL_NAME;
200 }
201
202 List<String> nameComponents = new ArrayList<>();
203
204
205 char[] chars = principalNames.toCharArray();
206
207 boolean escaped = false;
208 boolean done = false;
209 int start = 0;
210 int pos = 0;
211
212 for ( int i = 0; i < chars.length; i++ )
213 {
214 pos = i;
215
216 switch ( chars[i] )
217 {
218 case '\\':
219 escaped = !escaped;
220 break;
221
222 case '/':
223 if ( escaped )
224 {
225 escaped = false;
226 }
227 else
228 {
229
230 if ( i - start > 0 )
231 {
232 String nameComponent = new String( chars, start, i - start );
233 nameComponents.add( nameComponent );
234 start = i + 1;
235 }
236 else
237 {
238 throw new ParseException( I18n.err( I18n.ERR_628 ), i );
239 }
240 }
241
242 break;
243
244 case '@':
245 if ( escaped )
246 {
247 escaped = false;
248 }
249 else
250 {
251
252 done = true;
253 }
254
255 break;
256
257 default:
258 }
259
260 if ( done )
261 {
262
263 if ( i - start > 0 )
264 {
265 String nameComponent = new String( chars, start, i - start );
266 nameComponents.add( nameComponent );
267 start = i + 1;
268 }
269 else
270 {
271 throw new ParseException( I18n.err( I18n.ERR_628 ), i );
272 }
273
274 break;
275 }
276 else if ( i + 1 == chars.length )
277 {
278
279 String nameComponent = new String( chars, start, i - start + 1 );
280 nameComponents.add( nameComponent );
281
282 break;
283 }
284 }
285
286 if ( escaped )
287 {
288 throw new ParseException( I18n.err( I18n.ERR_629 ), pos );
289 }
290
291 return nameComponents;
292 }
293
294
295
296
297
298
299
300
301
302
303
304 public static KerberosPrincipal getKerberosPrincipal( PrincipalName principal, String realm )
305 {
306 String name = principal.getNameString();
307
308 if ( !Strings.isEmpty( realm ) )
309 {
310 name += '@' + realm;
311 }
312
313 return new KerberosPrincipal( name, principal.getNameType().getValue() );
314 }
315
316
317
318
319
320
321
322
323
324
325 public static EncryptionType getBestEncryptionType( Set<EncryptionType> requestedTypes,
326 Set<EncryptionType> configuredTypes )
327 {
328 for ( EncryptionType encryptionType : configuredTypes )
329 {
330 if ( requestedTypes.contains( encryptionType ) )
331 {
332 return encryptionType;
333 }
334 }
335
336 return null;
337 }
338
339
340
341
342
343
344
345
346 public static String getEncryptionTypesString( Set<EncryptionType> encryptionTypes )
347 {
348 StringBuilder sb = new StringBuilder();
349 boolean isFirst = true;
350
351 for ( EncryptionType etype : encryptionTypes )
352 {
353 if ( isFirst )
354 {
355 isFirst = false;
356 }
357 else
358 {
359 sb.append( ", " );
360 }
361
362 sb.append( etype );
363 }
364
365 return sb.toString();
366 }
367
368
369 public static boolean isKerberosString( byte[] value )
370 {
371 if ( value == null )
372 {
373 return false;
374 }
375
376 for ( byte b : value )
377 {
378 if ( ( b < 0x20 ) || ( b > 0x7E ) )
379 {
380 return false;
381 }
382 }
383
384 return true;
385 }
386
387
388 public static String getAlgoNameFromEncType( EncryptionType encType )
389 {
390 String cipherName = Strings.toLowerCaseAscii( encType.getName() );
391
392 for ( String c : cipherAlgoMap.keySet() )
393 {
394 if ( cipherName.startsWith( c ) )
395 {
396 return cipherAlgoMap.get( c );
397 }
398 }
399
400 throw new IllegalArgumentException( "Unknown algorithm name for the encryption type " + encType );
401 }
402
403
404
405
406
407
408
409
410 public static Set<EncryptionType> orderEtypesByStrength( Set<EncryptionType> etypes )
411 {
412 Set<EncryptionType> ordered = new LinkedHashSet<>( etypes.size() );
413
414 for ( String algo : cipherAlgoMap.values() )
415 {
416 for ( EncryptionType encType : etypes )
417 {
418 String foundAlgo = getAlgoNameFromEncType( encType );
419
420 if ( algo.equals( foundAlgo ) )
421 {
422 ordered.add( encType );
423 }
424 }
425 }
426
427 return ordered;
428 }
429
430
431
432
433
434
435 public static PrincipalStoreEntry getEntry( KerberosPrincipal principal, PrincipalStore store, ErrorType errorType )
436 throws KerberosException
437 {
438 PrincipalStoreEntry entry = null;
439
440 try
441 {
442 entry = store.getPrincipal( principal );
443 }
444 catch ( Exception e )
445 {
446 throw new KerberosException( errorType, e );
447 }
448
449 if ( entry == null )
450 {
451 throw new KerberosException( errorType );
452 }
453
454 if ( entry.getKeyMap() == null || entry.getKeyMap().isEmpty() )
455 {
456 throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
457 }
458
459 return entry;
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479 public static Authenticator verifyAuthHeader( ApReq authHeader, Ticket ticket, EncryptionKey serverKey,
480 long clockSkew, ReplayCache replayCache, boolean emptyAddressesAllowed, InetAddress clientAddress,
481 CipherTextHandler lockBox, KeyUsage authenticatorKeyUsage, boolean isValidate ) throws KerberosException
482 {
483 if ( authHeader.getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
484 {
485 throw new KerberosException( ErrorType.KRB_AP_ERR_BADVERSION );
486 }
487
488 if ( authHeader.getMessageType() != KerberosMessageType.AP_REQ )
489 {
490 throw new KerberosException( ErrorType.KRB_AP_ERR_MSG_TYPE );
491 }
492
493 if ( authHeader.getTicket().getTktVno() != KerberosConstants.KERBEROS_V5 )
494 {
495 throw new KerberosException( ErrorType.KRB_AP_ERR_BADVERSION );
496 }
497
498 EncryptionKey ticketKey = null;
499
500 if ( authHeader.getOption( ApOptions.USE_SESSION_KEY ) )
501 {
502 ticketKey = authHeader.getTicket().getEncTicketPart().getKey();
503 }
504 else
505 {
506 ticketKey = serverKey;
507 }
508
509 if ( ticketKey == null )
510 {
511
512
513
514
515
516
517 throw new KerberosException( ErrorType.KRB_AP_ERR_NOKEY );
518 }
519
520 byte[] encTicketPartData = lockBox.decrypt( ticketKey, ticket.getEncPart(),
521 KeyUsage.AS_OR_TGS_REP_TICKET_WITH_SRVKEY );
522 EncTicketPart encPart = KerberosDecoder.decodeEncTicketPart( encTicketPartData );
523 ticket.setEncTicketPart( encPart );
524
525 byte[] authenticatorData = lockBox.decrypt( ticket.getEncTicketPart().getKey(), authHeader.getAuthenticator(),
526 authenticatorKeyUsage );
527
528 Authenticator authenticator = KerberosDecoder.decodeAuthenticator( authenticatorData );
529
530 if ( !authenticator.getCName().getNameString().equals( ticket.getEncTicketPart().getCName().getNameString() ) )
531 {
532 throw new KerberosException( ErrorType.KRB_AP_ERR_BADMATCH );
533 }
534
535 if ( ticket.getEncTicketPart().getClientAddresses() != null )
536 {
537 if ( !ticket.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) ) )
538 {
539 throw new KerberosException( ErrorType.KRB_AP_ERR_BADADDR );
540 }
541 }
542 else
543 {
544 if ( !emptyAddressesAllowed )
545 {
546 throw new KerberosException( ErrorType.KRB_AP_ERR_BADADDR );
547 }
548 }
549
550 KerberosPrincipal serverPrincipal = getKerberosPrincipal( ticket.getSName(), ticket.getRealm() );
551 KerberosPrincipal clientPrincipal = getKerberosPrincipal( authenticator.getCName(), authenticator.getCRealm() );
552 KerberosTime clientTime = authenticator.getCtime();
553 int clientMicroSeconds = authenticator.getCusec();
554
555 if ( replayCache != null )
556 {
557 if ( replayCache.isReplay( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds ) )
558 {
559 throw new KerberosException( ErrorType.KRB_AP_ERR_REPEAT );
560 }
561
562 replayCache.save( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds );
563 }
564
565 if ( !authenticator.getCtime().isInClockSkew( clockSkew ) )
566 {
567 throw new KerberosException( ErrorType.KRB_AP_ERR_SKEW );
568 }
569
570
571
572
573
574
575
576 KerberosTime startTime = ( ticket.getEncTicketPart().getStartTime() != null ) ? ticket.getEncTicketPart()
577 .getStartTime() : ticket.getEncTicketPart().getAuthTime();
578
579 KerberosTimekerberos/KerberosTime.html#KerberosTime">KerberosTime now = new KerberosTime();
580 boolean isValidStartTime = startTime.lessThan( now );
581
582 if ( !isValidStartTime || ( ticket.getEncTicketPart().getFlags().isInvalid() && !isValidate ) )
583 {
584
585 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_NYV );
586 }
587
588
589 if ( !ticket.getEncTicketPart().getEndTime().greaterThan( now ) )
590 {
591 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_EXPIRED );
592 }
593
594 authHeader.getApOptions().set( ApOptions.MUTUAL_REQUIRED );
595
596 return authenticator;
597 }
598
599
600
601
602
603
604
605
606 public static boolean isNewEncryptionType( EncryptionType eType )
607 {
608 return !oldEncTypes.contains( eType );
609 }
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
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 }