1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.kerberos.shared;
21
22 import java.net.InetAddress;
23 import java.text.ParseException;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Set;
27
28 import javax.security.auth.kerberos.KerberosPrincipal;
29
30 import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
31 import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
32 import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
33 import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
34 import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
35 import org.apache.directory.server.kerberos.shared.messages.ApplicationRequest;
36 import org.apache.directory.server.kerberos.shared.messages.components.Authenticator;
37 import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
38 import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
39 import org.apache.directory.server.kerberos.shared.messages.value.ApOptions;
40 import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
41 import org.apache.directory.server.kerberos.shared.messages.value.HostAddress;
42 import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
43 import org.apache.directory.server.kerberos.shared.messages.value.PrincipalName;
44 import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
45 import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
46 import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
47 import org.apache.directory.shared.ldap.util.StringTools;
48
49
50
51
52
53
54 public class KerberosUtils
55 {
56
57 public static final int NULL = -1;
58
59
60 public static final List<String> EMPTY_PRINCIPAL_NAME = new ArrayList<String>();
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108 public static List<String> getNames( KerberosPrincipal principal ) throws ParseException
109 {
110 if ( principal == null )
111 {
112 return EMPTY_PRINCIPAL_NAME;
113 }
114
115 String names = principal.getName();
116
117 if ( StringTools.isEmpty( names ) )
118 {
119
120 return EMPTY_PRINCIPAL_NAME;
121 }
122
123 return getNames( names );
124 }
125
126
127
128
129 public static List<String> getNames( String principalNames ) throws ParseException
130 {
131 if ( principalNames == null )
132 {
133 return EMPTY_PRINCIPAL_NAME;
134 }
135
136 List<String> nameComponents = new ArrayList<String>();
137
138
139 char[] chars = principalNames.toCharArray();
140
141 boolean escaped = false;
142 boolean done = false;
143 int start = 0;
144 int pos = 0;
145
146 for ( int i = 0; i < chars.length; i++ )
147 {
148 pos = i;
149
150 switch ( chars[i] )
151 {
152 case '\\' :
153 escaped = !escaped;
154 break;
155
156 case '/' :
157 if ( escaped )
158 {
159 escaped = false;
160 }
161 else
162 {
163
164 if ( i - start > 0 )
165 {
166 String nameComponent = new String( chars, start, i - start );
167 nameComponents.add( nameComponent );
168 start = i + 1;
169 }
170 else
171 {
172 throw new ParseException( "An empty name is not valid in a kerberos name", i );
173 }
174 }
175
176 break;
177
178 case '@' :
179 if ( escaped )
180 {
181 escaped = false;
182 }
183 else
184 {
185
186 done = true;
187
188
189 if ( i - start > 0 )
190 {
191 String nameComponent = new String( chars, start, i - start );
192 nameComponents.add( nameComponent );
193 start = i + 1;
194 }
195 else
196 {
197 throw new ParseException( "An empty name is not valid in a kerberos name", i );
198 }
199 }
200
201 break;
202
203 default :
204 }
205
206 if ( done )
207 {
208 break;
209 }
210 }
211
212 if ( escaped )
213 {
214 throw new ParseException( "A '/' at the end of a Kerberos Name is not valid.", pos );
215 }
216
217 return nameComponents;
218 }
219
220
221
222
223
224
225
226
227
228
229
230 public static KerberosPrincipal getKerberosPrincipal( PrincipalName principal, String realm )
231 {
232 String name = principal.getNameString();
233
234 if ( !StringTools.isEmpty( realm ) )
235 {
236 name += '@' + realm;
237 }
238
239 return new KerberosPrincipal( name, principal.getNameType().getOrdinal() );
240 }
241
242
243
244
245
246
247
248
249
250
251 public static EncryptionType getBestEncryptionType( Set<EncryptionType> requestedTypes, Set<EncryptionType> configuredTypes )
252 {
253 for ( EncryptionType encryptionType:requestedTypes )
254 {
255 if ( configuredTypes.contains( encryptionType ) )
256 {
257 return encryptionType;
258 }
259 }
260
261 return null;
262 }
263
264
265
266
267
268
269
270
271 public static String getEncryptionTypesString( Set<EncryptionType> encryptionTypes )
272 {
273 StringBuilder sb = new StringBuilder();
274 boolean isFirst = true;
275
276 for ( EncryptionType etype:encryptionTypes )
277 {
278 if ( isFirst )
279 {
280 isFirst = false;
281 }
282 else
283 {
284 sb.append( ", " );
285 }
286
287 sb.append( etype );
288 }
289
290 return sb.toString();
291 }
292
293
294
295
296
297
298
299
300
301
302
303
304 public static PrincipalStoreEntry getEntry( KerberosPrincipal principal, PrincipalStore store, ErrorType errorType )
305 throws KerberosException
306 {
307 PrincipalStoreEntry entry = null;
308
309 try
310 {
311 entry = store.getPrincipal( principal );
312 }
313 catch ( Exception e )
314 {
315 throw new KerberosException( errorType, e );
316 }
317
318 if ( entry == null )
319 {
320 throw new KerberosException( errorType );
321 }
322
323 if ( entry.getKeyMap() == null || entry.getKeyMap().isEmpty() )
324 {
325 throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
326 }
327
328 return entry;
329 }
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348 public static Authenticator verifyAuthHeader( ApplicationRequest authHeader, Ticket ticket, EncryptionKey serverKey,
349 long clockSkew, ReplayCache replayCache, boolean emptyAddressesAllowed, InetAddress clientAddress,
350 CipherTextHandler lockBox, KeyUsage authenticatorKeyUsage, boolean isValidate ) throws KerberosException
351 {
352 if ( authHeader.getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
353 {
354 throw new KerberosException( ErrorType.KRB_AP_ERR_BADVERSION );
355 }
356
357 if ( authHeader.getMessageType() != KerberosMessageType.AP_REQ )
358 {
359 throw new KerberosException( ErrorType.KRB_AP_ERR_MSG_TYPE );
360 }
361
362 if ( authHeader.getTicket().getTktVno() != KerberosConstants.KERBEROS_V5 )
363 {
364 throw new KerberosException( ErrorType.KRB_AP_ERR_BADVERSION );
365 }
366
367 EncryptionKey ticketKey = null;
368
369 if ( authHeader.getOption( ApOptions.USE_SESSION_KEY ) )
370 {
371 ticketKey = authHeader.getTicket().getEncTicketPart().getSessionKey();
372 }
373 else
374 {
375 ticketKey = serverKey;
376 }
377
378 if ( ticketKey == null )
379 {
380
381 if ( false )
382 {
383 throw new KerberosException( ErrorType.KRB_AP_ERR_BADKEYVER );
384 }
385
386 throw new KerberosException( ErrorType.KRB_AP_ERR_NOKEY );
387 }
388
389 EncTicketPart encPart = ( EncTicketPart ) lockBox.unseal( EncTicketPart.class, ticketKey, ticket.getEncPart(),
390 KeyUsage.NUMBER2 );
391 ticket.setEncTicketPart( encPart );
392
393 Authenticator authenticator = ( Authenticator ) lockBox.unseal( Authenticator.class, ticket.getEncTicketPart().getSessionKey(),
394 authHeader.getEncPart(), authenticatorKeyUsage );
395
396 if ( !authenticator.getClientPrincipal().getName().equals( ticket.getEncTicketPart().getClientPrincipal().getName() ) )
397 {
398 throw new KerberosException( ErrorType.KRB_AP_ERR_BADMATCH );
399 }
400
401 if ( ticket.getEncTicketPart().getClientAddresses() != null )
402 {
403 if ( !ticket.getEncTicketPart().getClientAddresses().contains( new HostAddress( clientAddress ) ) )
404 {
405 throw new KerberosException( ErrorType.KRB_AP_ERR_BADADDR );
406 }
407 }
408 else
409 {
410 if ( !emptyAddressesAllowed )
411 {
412 throw new KerberosException( ErrorType.KRB_AP_ERR_BADADDR );
413 }
414 }
415
416 KerberosPrincipal serverPrincipal = ticket.getServerPrincipal();
417 KerberosPrincipal clientPrincipal = authenticator.getClientPrincipal();
418 KerberosTime clientTime = authenticator.getClientTime();
419 int clientMicroSeconds = authenticator.getClientMicroSecond();
420
421 if ( replayCache.isReplay( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds ) )
422 {
423 throw new KerberosException( ErrorType.KRB_AP_ERR_REPEAT );
424 }
425
426 replayCache.save( serverPrincipal, clientPrincipal, clientTime, clientMicroSeconds );
427
428 if ( !authenticator.getClientTime().isInClockSkew( clockSkew ) )
429 {
430 throw new KerberosException( ErrorType.KRB_AP_ERR_SKEW );
431 }
432
433
434
435
436
437
438
439 KerberosTime startTime = ( ticket.getEncTicketPart().getStartTime() != null ) ? ticket.getEncTicketPart().getStartTime() : ticket.getEncTicketPart().getAuthTime();
440
441 KerberosTime now = new KerberosTime();
442 boolean isValidStartTime = startTime.lessThan( now );
443
444 if ( !isValidStartTime || ( ticket.getEncTicketPart().getFlags().isInvalid() && !isValidate ) )
445 {
446
447 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_NYV );
448 }
449
450
451 if ( !ticket.getEncTicketPart().getEndTime().greaterThan( now ) )
452 {
453 throw new KerberosException( ErrorType.KRB_AP_ERR_TKT_EXPIRED );
454 }
455
456 authHeader.setOption( ApOptions.MUTUAL_REQUIRED );
457
458 return authenticator;
459 }
460 }