001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.server.kerberos.kdc.authentication;
021
022
023 import java.io.IOException;
024 import java.net.InetAddress;
025 import java.util.Date;
026 import java.util.Set;
027
028 import javax.security.auth.kerberos.KerberosKey;
029 import javax.security.auth.kerberos.KerberosPrincipal;
030
031 import org.apache.directory.server.i18n.I18n;
032 import org.apache.directory.server.kerberos.kdc.KdcContext;
033 import org.apache.directory.server.kerberos.kdc.KdcServer;
034 import org.apache.directory.server.kerberos.sam.SamException;
035 import org.apache.directory.server.kerberos.sam.SamSubsystem;
036 import org.apache.directory.server.kerberos.shared.KerberosConstants;
037 import org.apache.directory.server.kerberos.shared.KerberosUtils;
038 import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
039 import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
040 import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
041 import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
042 import org.apache.directory.server.kerberos.shared.exceptions.ErrorType;
043 import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
044 import org.apache.directory.server.kerberos.shared.io.decoder.EncryptedDataDecoder;
045 import org.apache.directory.server.kerberos.shared.io.encoder.EncryptionTypeInfoEncoder;
046 import org.apache.directory.server.kerberos.shared.io.encoder.PreAuthenticationDataEncoder;
047 import org.apache.directory.server.kerberos.shared.messages.AuthenticationReply;
048 import org.apache.directory.server.kerberos.shared.messages.KdcReply;
049 import org.apache.directory.server.kerberos.shared.messages.KdcRequest;
050 import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPart;
051 import org.apache.directory.server.kerberos.shared.messages.components.EncTicketPartModifier;
052 import org.apache.directory.server.kerberos.shared.messages.components.InvalidTicketException;
053 import org.apache.directory.server.kerberos.shared.messages.components.Ticket;
054 import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
055 import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
056 import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
057 import org.apache.directory.server.kerberos.shared.messages.value.EncryptionTypeInfoEntry;
058 import org.apache.directory.server.kerberos.shared.messages.value.KdcOptions;
059 import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
060 import org.apache.directory.server.kerberos.shared.messages.value.LastRequest;
061 import org.apache.directory.server.kerberos.shared.messages.value.PaData;
062 import org.apache.directory.server.kerberos.shared.messages.value.TransitedEncoding;
063 import org.apache.directory.server.kerberos.shared.messages.value.flags.TicketFlag;
064 import org.apache.directory.server.kerberos.shared.messages.value.types.PaDataType;
065 import org.apache.directory.server.kerberos.shared.replay.InMemoryReplayCache;
066 import org.apache.directory.server.kerberos.shared.replay.ReplayCache;
067 import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
068 import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
069 import org.slf4j.Logger;
070 import org.slf4j.LoggerFactory;
071
072
073 /**
074 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
075 * @version $Rev: 901657 $, $Date: 2010-01-21 13:27:15 +0200 (Thu, 21 Jan 2010) $
076 */
077 public class AuthenticationService
078 {
079 /** The log for this class. */
080 private static final Logger LOG = LoggerFactory.getLogger( AuthenticationService.class );
081
082 private static final ReplayCache replayCache = new InMemoryReplayCache();
083 private static final CipherTextHandler cipherTextHandler = new CipherTextHandler();
084
085 private static final String SERVICE_NAME = "Authentication Service (AS)";
086
087
088 public static void execute( AuthenticationContext authContext ) throws Exception
089 {
090 if ( LOG.isDebugEnabled() )
091 {
092 monitorRequest( authContext );
093 }
094
095 authContext.setReplayCache( replayCache );
096 authContext.setCipherTextHandler( cipherTextHandler );
097
098 if ( authContext.getRequest().getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
099 {
100 throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO );
101 }
102
103 selectEncryptionType( authContext );
104 getClientEntry( authContext );
105 verifyPolicy( authContext );
106 verifySam( authContext );
107 verifyEncryptedTimestamp( authContext );
108
109 if ( authContext.getClientKey() == null )
110 {
111 verifyEncryptedTimestamp( authContext );
112 }
113
114 getServerEntry( authContext );
115 generateTicket( authContext );
116 buildReply( authContext );
117
118 if ( LOG.isDebugEnabled() )
119 {
120 monitorContext( authContext );
121 monitorReply( ( KdcContext ) authContext );
122 }
123
124 sealReply( authContext );
125 }
126
127
128 private static void selectEncryptionType( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
129 {
130 KdcContext kdcContext = ( KdcContext ) authContext;
131 KdcServer config = kdcContext.getConfig();
132
133 Set<EncryptionType> requestedTypes = kdcContext.getRequest().getEType();
134
135 EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() );
136
137 LOG.debug( "Session will use encryption type {}.", bestType );
138
139 if ( bestType == null )
140 {
141 throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
142 }
143
144 kdcContext.setEncryptionType( bestType );
145 }
146
147
148 private static void getClientEntry( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
149 {
150 KerberosPrincipal principal = authContext.getRequest().getClientPrincipal();
151 PrincipalStore store = authContext.getStore();
152
153 PrincipalStoreEntry storeEntry = getEntry( principal, store, ErrorType.KDC_ERR_C_PRINCIPAL_UNKNOWN );
154 authContext.setClientEntry( storeEntry );
155 }
156
157
158 private static void verifyPolicy( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
159 {
160 PrincipalStoreEntry entry = authContext.getClientEntry();
161
162 if ( entry.isDisabled() )
163 {
164 throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
165 }
166
167 if ( entry.isLockedOut() )
168 {
169 throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
170 }
171
172 if ( entry.getExpiration().getTime() < new Date().getTime() )
173 {
174 throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
175 }
176 }
177
178
179 private static void verifySam( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
180 {
181 LOG.debug( "Verifying using SAM subsystem." );
182 KdcRequest request = authContext.getRequest();
183 KdcServer config = authContext.getConfig();
184
185 PrincipalStoreEntry clientEntry = authContext.getClientEntry();
186 String clientName = clientEntry.getPrincipal().getName();
187
188 EncryptionKey clientKey = null;
189
190 if ( clientEntry.getSamType() != null )
191 {
192 if ( LOG.isDebugEnabled() )
193 {
194 LOG.debug( "Entry for client principal {} has a valid SAM type. Invoking SAM subsystem for pre-authentication.", clientName );
195 }
196
197 PaData[] preAuthData = request.getPreAuthData();
198
199 if ( preAuthData == null || preAuthData.length == 0 )
200 {
201 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, preparePreAuthenticationError( config
202 .getEncryptionTypes() ) );
203 }
204
205 try
206 {
207 for ( int ii = 0; ii < preAuthData.length; ii++ )
208 {
209 if ( preAuthData[ii].getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) )
210 {
211 KerberosKey samKey = SamSubsystem.getInstance().verify( clientEntry,
212 preAuthData[ii].getPaDataValue() );
213 clientKey = new EncryptionKey( EncryptionType.getTypeByOrdinal( samKey.getKeyType() ), samKey
214 .getEncoded() );
215 }
216 }
217 }
218 catch ( SamException se )
219 {
220 throw new KerberosException( ErrorType.KRB_ERR_GENERIC, se );
221 }
222
223 authContext.setClientKey( clientKey );
224 authContext.setPreAuthenticated( true );
225
226 if ( LOG.isDebugEnabled() )
227 {
228 LOG.debug( "Pre-authentication using SAM subsystem successful for {}.", clientName );
229 }
230 }
231 }
232
233
234 private static void verifyEncryptedTimestamp( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
235 {
236 LOG.debug( "Verifying using encrypted timestamp." );
237
238 KdcServer config = authContext.getConfig();
239 KdcRequest request = authContext.getRequest();
240 CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
241 PrincipalStoreEntry clientEntry = authContext.getClientEntry();
242 String clientName = clientEntry.getPrincipal().getName();
243
244 EncryptionKey clientKey = null;
245
246 if ( clientEntry.getSamType() == null )
247 {
248 if ( LOG.isDebugEnabled() )
249 {
250 LOG.debug(
251 "Entry for client principal {} has no SAM type. Proceeding with standard pre-authentication.",
252 clientName );
253 }
254
255 EncryptionType encryptionType = authContext.getEncryptionType();
256 clientKey = clientEntry.getKeyMap().get( encryptionType );
257
258 if ( clientKey == null )
259 {
260 throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
261 }
262
263 if ( config.isPaEncTimestampRequired() )
264 {
265 PaData[] preAuthData = request.getPreAuthData();
266
267 if ( preAuthData == null )
268 {
269 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED,
270 preparePreAuthenticationError( config.getEncryptionTypes() ) );
271 }
272
273 EncryptedTimeStamp timestamp = null;
274
275 for ( int ii = 0; ii < preAuthData.length; ii++ )
276 {
277 if ( preAuthData[ii].getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) )
278 {
279 EncryptedData dataValue;
280
281 try
282 {
283 dataValue = EncryptedDataDecoder.decode( preAuthData[ii].getPaDataValue() );
284 }
285 catch ( IOException ioe )
286 {
287 throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, ioe );
288 }
289 catch ( ClassCastException cce )
290 {
291 throw new KerberosException( ErrorType.KRB_AP_ERR_BAD_INTEGRITY, cce );
292 }
293
294 timestamp = ( EncryptedTimeStamp ) cipherTextHandler.unseal( EncryptedTimeStamp.class,
295 clientKey, dataValue, KeyUsage.NUMBER1 );
296 }
297 }
298
299 if ( preAuthData.length > 0 && timestamp == null )
300 {
301 throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
302 }
303
304 if ( timestamp == null )
305 {
306 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED,
307 preparePreAuthenticationError( config.getEncryptionTypes() ) );
308 }
309
310 if ( !timestamp.getTimeStamp().isInClockSkew( config.getAllowableClockSkew() ) )
311 {
312 throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_FAILED );
313 }
314
315 /*
316 * if(decrypted_enc_timestamp and usec is replay)
317 * error_out(KDC_ERR_PREAUTH_FAILED);
318 * endif
319 *
320 * add decrypted_enc_timestamp and usec to replay cache;
321 */
322 }
323 }
324
325 authContext.setClientKey( clientKey );
326 authContext.setPreAuthenticated( true );
327
328 if ( LOG.isDebugEnabled() )
329 {
330 LOG.debug( "Pre-authentication by encrypted timestamp successful for {}.", clientName );
331 }
332 }
333
334
335 private static void getServerEntry( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
336 {
337 KerberosPrincipal principal = authContext.getRequest().getServerPrincipal();
338 PrincipalStore store = authContext.getStore();
339
340 authContext.setServerEntry( getEntry( principal, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ) );
341 }
342
343
344 private static void generateTicket( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
345 {
346 KdcRequest request = authContext.getRequest();
347 CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
348 KerberosPrincipal serverPrincipal = request.getServerPrincipal();
349
350 EncryptionType encryptionType = authContext.getEncryptionType();
351 EncryptionKey serverKey = authContext.getServerEntry().getKeyMap().get( encryptionType );
352
353 KerberosPrincipal ticketPrincipal = request.getServerPrincipal();
354 EncTicketPartModifier newTicketBody = new EncTicketPartModifier();
355 KdcServer config = authContext.getConfig();
356
357 // The INITIAL flag indicates that a ticket was issued using the AS protocol.
358 newTicketBody.setFlag( TicketFlag.INITIAL );
359
360 // The PRE-AUTHENT flag indicates that the client used pre-authentication.
361 if ( authContext.isPreAuthenticated() )
362 {
363 newTicketBody.setFlag( TicketFlag.PRE_AUTHENT );
364 }
365
366 if ( request.getOption( KdcOptions.FORWARDABLE ) )
367 {
368 if ( !config.isForwardableAllowed() )
369 {
370 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
371 }
372
373 newTicketBody.setFlag( TicketFlag.FORWARDABLE );
374 }
375
376 if ( request.getOption( KdcOptions.PROXIABLE ) )
377 {
378 if ( !config.isProxiableAllowed() )
379 {
380 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
381 }
382
383 newTicketBody.setFlag( TicketFlag.PROXIABLE );
384 }
385
386 if ( request.getOption( KdcOptions.ALLOW_POSTDATE ) )
387 {
388 if ( !config.isPostdatedAllowed() )
389 {
390 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
391 }
392
393 newTicketBody.setFlag( TicketFlag.MAY_POSTDATE );
394 }
395
396 if ( request.getOption( KdcOptions.RENEW ) || request.getOption( KdcOptions.VALIDATE )
397 || request.getOption( KdcOptions.PROXY ) || request.getOption( KdcOptions.FORWARDED )
398 || request.getOption( KdcOptions.ENC_TKT_IN_SKEY ) )
399 {
400 throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
401 }
402
403 EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( authContext.getEncryptionType() );
404 newTicketBody.setSessionKey( sessionKey );
405
406 newTicketBody.setClientPrincipal( request.getClientPrincipal() );
407 newTicketBody.setTransitedEncoding( new TransitedEncoding() );
408
409 KerberosTime now = new KerberosTime();
410
411 newTicketBody.setAuthTime( now );
412
413 KerberosTime startTime = request.getFrom();
414
415 /*
416 * "If the requested starttime is absent, indicates a time in the past,
417 * or is within the window of acceptable clock skew for the KDC and the
418 * POSTDATE option has not been specified, then the starttime of the
419 * ticket is set to the authentication server's current time."
420 */
421 if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() )
422 && !request.getOption( KdcOptions.POSTDATED ) )
423 {
424 startTime = now;
425 }
426
427 /*
428 * "If it indicates a time in the future beyond the acceptable clock skew,
429 * but the POSTDATED option has not been specified, then the error
430 * KDC_ERR_CANNOT_POSTDATE is returned."
431 */
432 if ( startTime != null && startTime.greaterThan( now )
433 && !startTime.isInClockSkew( config.getAllowableClockSkew() ) && !request.getOption( KdcOptions.POSTDATED ) )
434 {
435 throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE );
436 }
437
438 /*
439 * "Otherwise the requested starttime is checked against the policy of the
440 * local realm and if the ticket's starttime is acceptable, it is set as
441 * requested, and the INVALID flag is set in the new ticket."
442 */
443 if ( request.getOption( KdcOptions.POSTDATED ) )
444 {
445 if ( !config.isPostdatedAllowed() )
446 {
447 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
448 }
449
450 newTicketBody.setFlag( TicketFlag.POSTDATED );
451 newTicketBody.setFlag( TicketFlag.INVALID );
452 newTicketBody.setStartTime( startTime );
453 }
454
455 long till = 0;
456
457 if ( request.getTill().getTime() == 0 )
458 {
459 till = Long.MAX_VALUE;
460 }
461 else
462 {
463 till = request.getTill().getTime();
464 }
465
466 /*
467 * The end time is the minimum of (a) the requested till time or (b)
468 * the start time plus maximum lifetime as configured in policy.
469 */
470 long endTime = Math.min( till, startTime.getTime() + config.getMaximumTicketLifetime() );
471 KerberosTime kerberosEndTime = new KerberosTime( endTime );
472 newTicketBody.setEndTime( kerberosEndTime );
473
474 /*
475 * "If the requested expiration time minus the starttime (as determined
476 * above) is less than a site-determined minimum lifetime, an error
477 * message with code KDC_ERR_NEVER_VALID is returned."
478 */
479 if ( kerberosEndTime.lessThan( startTime ) )
480 {
481 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
482 }
483
484 long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() );
485
486 if ( ticketLifeTime < config.getAllowableClockSkew() )
487 {
488 throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
489 }
490
491 /*
492 * "If the requested expiration time for the ticket exceeds what was determined
493 * as above, and if the 'RENEWABLE-OK' option was requested, then the 'RENEWABLE'
494 * flag is set in the new ticket, and the renew-till value is set as if the
495 * 'RENEWABLE' option were requested."
496 */
497 KerberosTime tempRtime = request.getRtime();
498
499 if ( request.getOption( KdcOptions.RENEWABLE_OK ) && request.getTill().greaterThan( kerberosEndTime ) )
500 {
501 if ( !config.isRenewableAllowed() )
502 {
503 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
504 }
505
506 request.setOption( KdcOptions.RENEWABLE );
507 tempRtime = request.getTill();
508 }
509
510 if ( request.getOption( KdcOptions.RENEWABLE ) )
511 {
512 if ( !config.isRenewableAllowed() )
513 {
514 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
515 }
516
517 newTicketBody.setFlag( TicketFlag.RENEWABLE );
518
519 if ( tempRtime == null || tempRtime.isZero() )
520 {
521 tempRtime = KerberosTime.INFINITY;
522 }
523
524 /*
525 * The renew-till time is the minimum of (a) the requested renew-till
526 * time or (b) the start time plus maximum renewable lifetime as
527 * configured in policy.
528 */
529 long renewTill = Math.min( tempRtime.getTime(), startTime.getTime() + config.getMaximumRenewableLifetime() );
530 newTicketBody.setRenewTill( new KerberosTime( renewTill ) );
531 }
532
533 if ( request.getAddresses() != null && request.getAddresses().getAddresses() != null
534 && request.getAddresses().getAddresses().length > 0 )
535 {
536 newTicketBody.setClientAddresses( request.getAddresses() );
537 }
538 else
539 {
540 if ( !config.isEmptyAddressesAllowed() )
541 {
542 throw new KerberosException( ErrorType.KDC_ERR_POLICY );
543 }
544 }
545
546 EncTicketPart ticketPart = newTicketBody.getEncTicketPart();
547
548 EncryptedData encryptedData = cipherTextHandler.seal( serverKey, ticketPart, KeyUsage.NUMBER2 );
549
550 Ticket newTicket = new Ticket( ticketPrincipal, encryptedData );
551 newTicket.setEncTicketPart( ticketPart );
552
553 if ( LOG.isDebugEnabled() )
554 {
555 LOG.debug( "Ticket will be issued for access to {}.", serverPrincipal.toString() );
556 }
557
558 authContext.setTicket( newTicket );
559 }
560
561
562 private static void buildReply( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
563 {
564 KdcRequest request = authContext.getRequest();
565 Ticket ticket = authContext.getTicket();
566
567 AuthenticationReply reply = new AuthenticationReply();
568
569 reply.setClientPrincipal( request.getClientPrincipal() );
570 reply.setTicket( ticket );
571 reply.setKey( ticket.getEncTicketPart().getSessionKey() );
572
573 // TODO - fetch lastReq for this client; requires store
574 reply.setLastRequest( new LastRequest() );
575 // TODO - resp.key-expiration := client.expiration; requires store
576
577 reply.setNonce( request.getNonce() );
578
579 reply.setFlags( ticket.getEncTicketPart().getFlags() );
580 reply.setAuthTime( ticket.getEncTicketPart().getAuthTime() );
581 reply.setStartTime( ticket.getEncTicketPart().getStartTime() );
582 reply.setEndTime( ticket.getEncTicketPart().getEndTime() );
583
584 if ( ticket.getEncTicketPart().getFlags().isRenewable() )
585 {
586 reply.setRenewTill( ticket.getEncTicketPart().getRenewTill() );
587 }
588
589 reply.setServerPrincipal( ticket.getServerPrincipal() );
590 reply.setClientAddresses( ticket.getEncTicketPart().getClientAddresses() );
591
592 authContext.setReply( reply );
593 }
594
595
596 private static void sealReply( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
597 {
598 AuthenticationReply reply = ( AuthenticationReply ) authContext.getReply();
599 EncryptionKey clientKey = authContext.getClientKey();
600 CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
601
602 EncryptedData encryptedData = cipherTextHandler.seal( clientKey, reply, KeyUsage.NUMBER3 );
603 reply.setEncPart( encryptedData );
604 }
605
606
607 private static void monitorRequest( KdcContext kdcContext )
608 {
609 KdcRequest request = kdcContext.getRequest();
610
611 if ( LOG.isDebugEnabled() )
612 {
613 try
614 {
615 String clientAddress = kdcContext.getClientAddress().getHostAddress();
616
617 StringBuffer sb = new StringBuffer();
618
619 sb.append( "Received " + SERVICE_NAME + " request:" );
620 sb.append( "\n\t" + "messageType: " + request.getMessageType() );
621 sb.append( "\n\t" + "protocolVersionNumber: " + request.getProtocolVersionNumber() );
622 sb.append( "\n\t" + "clientAddress: " + clientAddress );
623 sb.append( "\n\t" + "nonce: " + request.getNonce() );
624 sb.append( "\n\t" + "kdcOptions: " + request.getKdcOptions() );
625 sb.append( "\n\t" + "clientPrincipal: " + request.getClientPrincipal() );
626 sb.append( "\n\t" + "serverPrincipal: " + request.getServerPrincipal() );
627 sb.append( "\n\t" + "encryptionType: " + KerberosUtils.getEncryptionTypesString( request.getEType() ) );
628 sb.append( "\n\t" + "realm: " + request.getRealm() );
629 sb.append( "\n\t" + "from time: " + request.getFrom() );
630 sb.append( "\n\t" + "till time: " + request.getTill() );
631 sb.append( "\n\t" + "renew-till time: " + request.getRtime() );
632 sb.append( "\n\t" + "hostAddresses: " + request.getAddresses() );
633
634 LOG.debug( sb.toString() );
635 }
636 catch ( Exception e )
637 {
638 // This is a monitor. No exceptions should bubble up.
639 LOG.error( I18n.err( I18n.ERR_153 ), e );
640 }
641 }
642 }
643
644 private static void monitorContext( AuthenticationContext authContext )
645 {
646 try
647 {
648 long clockSkew = authContext.getConfig().getAllowableClockSkew();
649 InetAddress clientAddress = authContext.getClientAddress();
650
651 StringBuilder sb = new StringBuilder();
652
653 sb.append( "Monitoring " + SERVICE_NAME + " context:" );
654
655 sb.append( "\n\t" + "clockSkew " + clockSkew );
656 sb.append( "\n\t" + "clientAddress " + clientAddress );
657
658 KerberosPrincipal clientPrincipal = authContext.getClientEntry().getPrincipal();
659 PrincipalStoreEntry clientEntry = authContext.getClientEntry();
660
661 sb.append( "\n\t" + "principal " + clientPrincipal );
662 sb.append( "\n\t" + "cn " + clientEntry.getCommonName() );
663 sb.append( "\n\t" + "realm " + clientEntry.getRealmName() );
664 sb.append( "\n\t" + "principal " + clientEntry.getPrincipal() );
665 sb.append( "\n\t" + "SAM type " + clientEntry.getSamType() );
666
667 KerberosPrincipal serverPrincipal = authContext.getRequest().getServerPrincipal();
668 PrincipalStoreEntry serverEntry = authContext.getServerEntry();
669
670 sb.append( "\n\t" + "principal " + serverPrincipal );
671 sb.append( "\n\t" + "cn " + serverEntry.getCommonName() );
672 sb.append( "\n\t" + "realm " + serverEntry.getRealmName() );
673 sb.append( "\n\t" + "principal " + serverEntry.getPrincipal() );
674 sb.append( "\n\t" + "SAM type " + serverEntry.getSamType() );
675
676 EncryptionType encryptionType = authContext.getEncryptionType();
677 int clientKeyVersion = clientEntry.getKeyMap().get( encryptionType ).getKeyVersion();
678 int serverKeyVersion = serverEntry.getKeyMap().get( encryptionType ).getKeyVersion();
679 sb.append( "\n\t" + "Request key type " + encryptionType );
680 sb.append( "\n\t" + "Client key version " + clientKeyVersion );
681 sb.append( "\n\t" + "Server key version " + serverKeyVersion );
682
683 LOG.debug( sb.toString() );
684 }
685 catch ( Exception e )
686 {
687 // This is a monitor. No exceptions should bubble up.
688 LOG.error( I18n.err( I18n.ERR_154 ), e );
689 }
690 }
691
692
693 private static void monitorReply( KdcContext kdcContext )
694 {
695 Object reply = kdcContext.getReply();
696
697 if ( LOG.isDebugEnabled() )
698 {
699 if ( reply instanceof KdcReply )
700 {
701 KdcReply success = ( KdcReply ) reply;
702
703 try
704 {
705 StringBuffer sb = new StringBuffer();
706
707 sb.append( "Responding with " + SERVICE_NAME + " reply:" );
708 sb.append( "\n\t" + "messageType: " + success.getMessageType() );
709 sb.append( "\n\t" + "protocolVersionNumber: " + success.getProtocolVersionNumber() );
710 sb.append( "\n\t" + "nonce: " + success.getNonce() );
711 sb.append( "\n\t" + "clientPrincipal: " + success.getClientPrincipal() );
712 sb.append( "\n\t" + "client realm: " + success.getClientRealm() );
713 sb.append( "\n\t" + "serverPrincipal: " + success.getServerPrincipal() );
714 sb.append( "\n\t" + "server realm: " + success.getServerRealm() );
715 sb.append( "\n\t" + "auth time: " + success.getAuthTime() );
716 sb.append( "\n\t" + "start time: " + success.getStartTime() );
717 sb.append( "\n\t" + "end time: " + success.getEndTime() );
718 sb.append( "\n\t" + "renew-till time: " + success.getRenewTill() );
719 sb.append( "\n\t" + "hostAddresses: " + success.getClientAddresses() );
720
721 LOG.debug( sb.toString() );
722 }
723 catch ( Exception e )
724 {
725 // This is a monitor. No exceptions should bubble up.
726 LOG.error( I18n.err( I18n.ERR_155 ), e );
727 }
728 }
729 }
730 }
731
732
733 /**
734 * Get a PrincipalStoreEntry given a principal. The ErrorType is used to indicate
735 * whether any resulting error pertains to a server or client.
736 */
737 private static PrincipalStoreEntry getEntry( KerberosPrincipal principal, PrincipalStore store, ErrorType errorType )
738 throws KerberosException
739 {
740 PrincipalStoreEntry entry = null;
741
742 try
743 {
744 entry = store.getPrincipal( principal );
745 }
746 catch ( Exception e )
747 {
748 throw new KerberosException( errorType, e );
749 }
750
751 if ( entry == null )
752 {
753 throw new KerberosException( errorType );
754 }
755
756 if ( entry.getKeyMap() == null || entry.getKeyMap().isEmpty() )
757 {
758 throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
759 }
760
761 return entry;
762 }
763
764
765 /**
766 * Prepares a pre-authentication error message containing required
767 * encryption types.
768 *
769 * @param encryptionTypes
770 * @return The error message as bytes.
771 */
772 private static byte[] preparePreAuthenticationError( Set<EncryptionType> encryptionTypes )
773 {
774 PaData[] paDataSequence = new PaData[2];
775
776 PaData paData = new PaData();
777 paData.setPaDataType( PaDataType.PA_ENC_TIMESTAMP );
778 paData.setPaDataValue( new byte[0] );
779
780 paDataSequence[0] = paData;
781
782 EncryptionTypeInfoEntry[] entries = new EncryptionTypeInfoEntry[ encryptionTypes.size() ];
783 int i = 0;
784
785 for ( EncryptionType encryptionType:encryptionTypes )
786 {
787 entries[i++] = new EncryptionTypeInfoEntry( encryptionType, null );
788 }
789
790 byte[] encTypeInfo = null;
791
792 try
793 {
794 encTypeInfo = EncryptionTypeInfoEncoder.encode( entries );
795 }
796 catch ( IOException ioe )
797 {
798 return null;
799 }
800
801 PaData encType = new PaData();
802 encType.setPaDataType( PaDataType.PA_ENCTYPE_INFO );
803 encType.setPaDataValue( encTypeInfo );
804
805 paDataSequence[1] = encType;
806
807 try
808 {
809 return PreAuthenticationDataEncoder.encode( paDataSequence );
810 }
811 catch ( IOException ioe )
812 {
813 return null;
814 }
815 }
816 }