blob: 4891f53bf87375bcc5ddb1c7215f8ca1f3a7ee46 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.server.kerberos.kdc.authentication;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.List;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KerberosPrincipal;
import org.apache.directory.server.i18n.I18n;
import org.apache.directory.server.kerberos.kdc.KdcContext;
import org.apache.directory.server.kerberos.kdc.KdcServer;
import org.apache.directory.server.kerberos.protocol.codec.KerberosDecoder;
import org.apache.directory.server.kerberos.sam.SamException;
import org.apache.directory.server.kerberos.sam.SamSubsystem;
import org.apache.directory.server.kerberos.shared.crypto.encryption.CipherTextHandler;
import org.apache.directory.server.kerberos.shared.crypto.encryption.KeyUsage;
import org.apache.directory.server.kerberos.shared.crypto.encryption.RandomKeyFactory;
import org.apache.directory.server.kerberos.shared.store.PrincipalStore;
import org.apache.directory.server.kerberos.shared.store.PrincipalStoreEntry;
import org.apache.directory.shared.asn1.EncoderException;
import org.apache.directory.shared.kerberos.KerberosConstants;
import org.apache.directory.shared.kerberos.KerberosTime;
import org.apache.directory.shared.kerberos.KerberosUtils;
import org.apache.directory.shared.kerberos.codec.options.KdcOptions;
import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
import org.apache.directory.shared.kerberos.codec.types.LastReqType;
import org.apache.directory.shared.kerberos.codec.types.PaDataType;
import org.apache.directory.shared.kerberos.components.ETypeInfo;
import org.apache.directory.shared.kerberos.components.ETypeInfoEntry;
import org.apache.directory.shared.kerberos.components.EncKdcRepPart;
import org.apache.directory.shared.kerberos.components.EncTicketPart;
import org.apache.directory.shared.kerberos.components.EncryptedData;
import org.apache.directory.shared.kerberos.components.EncryptionKey;
import org.apache.directory.shared.kerberos.components.KdcReq;
import org.apache.directory.shared.kerberos.components.LastReq;
import org.apache.directory.shared.kerberos.components.LastReqEntry;
import org.apache.directory.shared.kerberos.components.MethodData;
import org.apache.directory.shared.kerberos.components.PaData;
import org.apache.directory.shared.kerberos.components.PaEncTsEnc;
import org.apache.directory.shared.kerberos.components.PrincipalName;
import org.apache.directory.shared.kerberos.components.TransitedEncoding;
import org.apache.directory.shared.kerberos.exceptions.ErrorType;
import org.apache.directory.shared.kerberos.exceptions.InvalidTicketException;
import org.apache.directory.shared.kerberos.exceptions.KerberosException;
import org.apache.directory.shared.kerberos.flags.TicketFlag;
import org.apache.directory.shared.kerberos.flags.TicketFlags;
import org.apache.directory.shared.kerberos.messages.AsRep;
import org.apache.directory.shared.kerberos.messages.EncAsRepPart;
import org.apache.directory.shared.kerberos.messages.Ticket;
import org.apache.directory.shared.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Subsystem in charge of authenticating the incoming users.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class AuthenticationService
{
/** The log for this class. */
private static final Logger LOG = LoggerFactory.getLogger( AuthenticationService.class );
/** The module responsible for encryption and decryption */
private static final CipherTextHandler cipherTextHandler = new CipherTextHandler();
/** The service name */
private static final String SERVICE_NAME = "Authentication Service (AS)";
/**
* Handle the authentication, given a specific context
*
* @param authContext The authentication context
* @throws Exception If the authentication failed
*/
public static void execute( AuthenticationContext authContext ) throws Exception
{
if ( LOG.isDebugEnabled() )
{
monitorRequest( authContext );
}
authContext.setCipherTextHandler( cipherTextHandler );
if ( authContext.getRequest().getProtocolVersionNumber() != KerberosConstants.KERBEROS_V5 )
{
throw new KerberosException( ErrorType.KDC_ERR_BAD_PVNO );
}
selectEncryptionType( authContext );
getClientEntry( authContext );
verifyPolicy( authContext );
verifySam( authContext );
verifyEncryptedTimestamp( authContext );
if ( authContext.getClientKey() == null )
{
verifyEncryptedTimestamp( authContext );
}
getServerEntry( authContext );
generateTicket( authContext );
buildReply( authContext );
}
private static void selectEncryptionType( AuthenticationContext authContext ) throws KerberosException,
InvalidTicketException
{
KdcContext kdcContext = authContext;
KdcServer config = kdcContext.getConfig();
List<EncryptionType> requestedTypes = kdcContext.getRequest().getKdcReqBody().getEType();
EncryptionType bestType = KerberosUtils.getBestEncryptionType( requestedTypes, config.getEncryptionTypes() );
LOG.debug( "Session will use encryption type {}.", bestType );
if ( bestType == null )
{
throw new KerberosException( ErrorType.KDC_ERR_ETYPE_NOSUPP );
}
kdcContext.setEncryptionType( bestType );
}
private static void getClientEntry( AuthenticationContext authContext ) throws KerberosException,
InvalidTicketException
{
KerberosPrincipal principal = KerberosUtils.getKerberosPrincipal(
authContext.getRequest().getKdcReqBody().getCName(), authContext.getRequest().getKdcReqBody().getRealm() );
PrincipalStore store = authContext.getStore();
PrincipalStoreEntry storeEntry = getEntry( principal, store, ErrorType.KDC_ERR_C_PRINCIPAL_UNKNOWN );
authContext.setClientEntry( storeEntry );
}
private static void verifyPolicy( AuthenticationContext authContext ) throws KerberosException,
InvalidTicketException
{
PrincipalStoreEntry entry = authContext.getClientEntry();
if ( entry.isDisabled() )
{
throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
}
if ( entry.isLockedOut() )
{
throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
}
if ( entry.getExpiration().getTime() < new Date().getTime() )
{
throw new KerberosException( ErrorType.KDC_ERR_CLIENT_REVOKED );
}
}
private static void verifySam( AuthenticationContext authContext ) throws KerberosException, InvalidTicketException
{
LOG.debug( "Verifying using SAM subsystem." );
KdcReq request = authContext.getRequest();
KdcServer config = authContext.getConfig();
PrincipalStoreEntry clientEntry = authContext.getClientEntry();
String clientName = clientEntry.getPrincipal().getName();
EncryptionKey clientKey = null;
if ( clientEntry.getSamType() != null )
{
if ( LOG.isDebugEnabled() )
{
LOG.debug(
"Entry for client principal {} has a valid SAM type. Invoking SAM subsystem for pre-authentication.",
clientName );
}
List<PaData> preAuthData = request.getPaData();
if ( preAuthData == null || preAuthData.size() == 0 )
{
throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED, preparePreAuthenticationError(
request.getKdcReqBody().getEType(), config.getEncryptionTypes() ) );
}
try
{
for ( PaData paData : preAuthData )
{
if ( paData.getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) )
{
KerberosKey samKey = SamSubsystem.getInstance().verify( clientEntry,
paData.getPaDataValue() );
clientKey = new EncryptionKey( EncryptionType.getTypeByValue( samKey.getKeyType() ), samKey
.getEncoded() );
}
}
}
catch ( SamException se )
{
throw new KerberosException( ErrorType.KRB_ERR_GENERIC, se );
}
authContext.setClientKey( clientKey );
authContext.setPreAuthenticated( true );
if ( LOG.isDebugEnabled() )
{
LOG.debug( "Pre-authentication using SAM subsystem successful for {}.", clientName );
}
}
}
private static void verifyEncryptedTimestamp( AuthenticationContext authContext ) throws KerberosException,
InvalidTicketException
{
LOG.debug( "Verifying using encrypted timestamp." );
KdcServer config = authContext.getConfig();
KdcReq request = authContext.getRequest();
CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
PrincipalStoreEntry clientEntry = authContext.getClientEntry();
String clientName = clientEntry.getPrincipal().getName();
EncryptionKey clientKey = null;
if ( clientEntry.getSamType() == null )
{
if ( LOG.isDebugEnabled() )
{
LOG.debug(
"Entry for client principal {} has no SAM type. Proceeding with standard pre-authentication.",
clientName );
}
EncryptionType encryptionType = authContext.getEncryptionType();
clientKey = clientEntry.getKeyMap().get( encryptionType );
if ( clientKey == null )
{
throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
}
if ( config.isPaEncTimestampRequired() )
{
List<PaData> preAuthData = request.getPaData();
if ( preAuthData == null )
{
throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED,
preparePreAuthenticationError( authContext.getRequest().getKdcReqBody().getEType(),
config.getEncryptionTypes() ) );
}
PaEncTsEnc timestamp = null;
for ( PaData paData : preAuthData )
{
if ( paData.getPaDataType().equals( PaDataType.PA_ENC_TIMESTAMP ) )
{
EncryptedData dataValue = KerberosDecoder.decodeEncryptedData( paData.getPaDataValue() );
paData.getPaDataType();
byte[] decryptedData = cipherTextHandler.decrypt( clientKey, dataValue,
KeyUsage.AS_REQ_PA_ENC_TIMESTAMP_WITH_CKEY );
timestamp = KerberosDecoder.decodePaEncTsEnc( decryptedData );
}
}
if ( ( preAuthData.size() > 0 ) && ( timestamp == null ) )
{
throw new KerberosException( ErrorType.KDC_ERR_PADATA_TYPE_NOSUPP );
}
if ( timestamp == null )
{
throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_REQUIRED,
preparePreAuthenticationError( authContext.getRequest().getKdcReqBody().getEType(),
config.getEncryptionTypes() ) );
}
if ( !timestamp.getPaTimestamp().isInClockSkew( config.getAllowableClockSkew() ) )
{
throw new KerberosException( ErrorType.KDC_ERR_PREAUTH_FAILED );
}
/*
* if(decrypted_enc_timestamp and usec is replay)
* error_out(KDC_ERR_PREAUTH_FAILED);
* endif
*
* add decrypted_enc_timestamp and usec to replay cache;
*/
}
}
authContext.setClientKey( clientKey );
authContext.setPreAuthenticated( true );
if ( LOG.isDebugEnabled() )
{
LOG.debug( "Pre-authentication by encrypted timestamp successful for {}.", clientName );
}
}
private static void getServerEntry( AuthenticationContext authContext ) throws KerberosException,
InvalidTicketException
{
PrincipalName principal = authContext.getRequest().getKdcReqBody().getSName();
PrincipalStore store = authContext.getStore();
KerberosPrincipal principalWithRealm = new KerberosPrincipal( principal.getNameString() + "@"
+ authContext.getRequest().getKdcReqBody().getRealm() );
authContext.setServerEntry( getEntry( principalWithRealm, store, ErrorType.KDC_ERR_S_PRINCIPAL_UNKNOWN ) );
}
private static void generateTicket( AuthenticationContext authContext ) throws KerberosException,
InvalidTicketException
{
KdcReq request = authContext.getRequest();
CipherTextHandler cipherTextHandler = authContext.getCipherTextHandler();
PrincipalName serverPrincipal = request.getKdcReqBody().getSName();
EncryptionType encryptionType = authContext.getEncryptionType();
EncryptionKey serverKey = authContext.getServerEntry().getKeyMap().get( encryptionType );
PrincipalName ticketPrincipal = request.getKdcReqBody().getSName();
EncTicketPart encTicketPart = new EncTicketPart();
KdcServer config = authContext.getConfig();
// The INITIAL flag indicates that a ticket was issued using the AS protocol.
TicketFlags ticketFlags = new TicketFlags();
encTicketPart.setFlags( ticketFlags );
ticketFlags.setFlag( TicketFlag.INITIAL );
// The PRE-AUTHENT flag indicates that the client used pre-authentication.
if ( authContext.isPreAuthenticated() )
{
ticketFlags.setFlag( TicketFlag.PRE_AUTHENT );
}
if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.FORWARDABLE ) )
{
if ( !config.isForwardableAllowed() )
{
throw new KerberosException( ErrorType.KDC_ERR_POLICY );
}
ticketFlags.setFlag( TicketFlag.FORWARDABLE );
}
if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.PROXIABLE ) )
{
if ( !config.isProxiableAllowed() )
{
throw new KerberosException( ErrorType.KDC_ERR_POLICY );
}
ticketFlags.setFlag( TicketFlag.PROXIABLE );
}
if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.ALLOW_POSTDATE ) )
{
if ( !config.isPostdatedAllowed() )
{
throw new KerberosException( ErrorType.KDC_ERR_POLICY );
}
ticketFlags.setFlag( TicketFlag.MAY_POSTDATE );
}
if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RENEW )
|| request.getKdcReqBody().getKdcOptions().get( KdcOptions.VALIDATE )
|| request.getKdcReqBody().getKdcOptions().get( KdcOptions.PROXY )
|| request.getKdcReqBody().getKdcOptions().get( KdcOptions.FORWARDED )
|| request.getKdcReqBody().getKdcOptions().get( KdcOptions.ENC_TKT_IN_SKEY ) )
{
throw new KerberosException( ErrorType.KDC_ERR_BADOPTION );
}
EncryptionKey sessionKey = RandomKeyFactory.getRandomKey( authContext.getEncryptionType() );
encTicketPart.setKey( sessionKey );
encTicketPart.setCName( request.getKdcReqBody().getCName() );
encTicketPart.setCRealm( request.getKdcReqBody().getRealm() );
encTicketPart.setTransited( new TransitedEncoding() );
String serverRealm = request.getKdcReqBody().getRealm();
KerberosTime now = new KerberosTime();
encTicketPart.setAuthTime( now );
KerberosTime startTime = request.getKdcReqBody().getFrom();
/*
* "If the requested starttime is absent, indicates a time in the past,
* or is within the window of acceptable clock skew for the KDC and the
* POSTDATE option has not been specified, then the starttime of the
* ticket is set to the authentication server's current time."
*/
if ( startTime == null || startTime.lessThan( now ) || startTime.isInClockSkew( config.getAllowableClockSkew() )
&& !request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) )
{
startTime = now;
}
/*
* "If it indicates a time in the future beyond the acceptable clock skew,
* but the POSTDATED option has not been specified, then the error
* KDC_ERR_CANNOT_POSTDATE is returned."
*/
if ( startTime != null && startTime.greaterThan( now )
&& !startTime.isInClockSkew( config.getAllowableClockSkew() )
&& !request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) )
{
throw new KerberosException( ErrorType.KDC_ERR_CANNOT_POSTDATE );
}
/*
* "Otherwise the requested starttime is checked against the policy of the
* local realm and if the ticket's starttime is acceptable, it is set as
* requested, and the INVALID flag is set in the new ticket."
*/
if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.POSTDATED ) )
{
if ( !config.isPostdatedAllowed() )
{
throw new KerberosException( ErrorType.KDC_ERR_POLICY );
}
ticketFlags.setFlag( TicketFlag.POSTDATED );
ticketFlags.setFlag( TicketFlag.INVALID );
encTicketPart.setStartTime( startTime );
}
long till = 0;
if ( request.getKdcReqBody().getTill().getTime() == 0 )
{
till = Long.MAX_VALUE;
}
else
{
till = request.getKdcReqBody().getTill().getTime();
}
/*
* The end time is the minimum of (a) the requested till time or (b)
* the start time plus maximum lifetime as configured in policy.
*/
long endTime = Math.min( till, startTime.getTime() + config.getMaximumTicketLifetime() );
KerberosTime kerberosEndTime = new KerberosTime( endTime );
encTicketPart.setEndTime( kerberosEndTime );
/*
* "If the requested expiration time minus the starttime (as determined
* above) is less than a site-determined minimum lifetime, an error
* message with code KDC_ERR_NEVER_VALID is returned."
*/
if ( kerberosEndTime.lessThan( startTime ) )
{
throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
}
long ticketLifeTime = Math.abs( startTime.getTime() - kerberosEndTime.getTime() );
if ( ticketLifeTime < config.getAllowableClockSkew() )
{
throw new KerberosException( ErrorType.KDC_ERR_NEVER_VALID );
}
/*
* "If the requested expiration time for the ticket exceeds what was determined
* as above, and if the 'RENEWABLE-OK' option was requested, then the 'RENEWABLE'
* flag is set in the new ticket, and the renew-till value is set as if the
* 'RENEWABLE' option were requested."
*/
KerberosTime tempRtime = request.getKdcReqBody().getRTime();
if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RENEWABLE_OK )
&& request.getKdcReqBody().getTill().greaterThan( kerberosEndTime ) )
{
if ( !config.isRenewableAllowed() )
{
throw new KerberosException( ErrorType.KDC_ERR_POLICY );
}
request.getKdcReqBody().getKdcOptions().set( KdcOptions.RENEWABLE );
tempRtime = request.getKdcReqBody().getTill();
}
if ( request.getKdcReqBody().getKdcOptions().get( KdcOptions.RENEWABLE ) )
{
if ( !config.isRenewableAllowed() )
{
throw new KerberosException( ErrorType.KDC_ERR_POLICY );
}
ticketFlags.setFlag( TicketFlag.RENEWABLE );
if ( tempRtime == null || tempRtime.isZero() )
{
tempRtime = KerberosTime.INFINITY;
}
/*
* The renew-till time is the minimum of (a) the requested renew-till
* time or (b) the start time plus maximum renewable lifetime as
* configured in policy.
*/
long renewTill = Math.min( tempRtime.getTime(), startTime.getTime() + config.getMaximumRenewableLifetime() );
encTicketPart.setRenewTill( new KerberosTime( renewTill ) );
}
if ( request.getKdcReqBody().getAddresses() != null
&& request.getKdcReqBody().getAddresses().getAddresses() != null
&& request.getKdcReqBody().getAddresses().getAddresses().length > 0 )
{
encTicketPart.setClientAddresses( request.getKdcReqBody().getAddresses() );
}
else
{
if ( !config.isEmptyAddressesAllowed() )
{
throw new KerberosException( ErrorType.KDC_ERR_POLICY );
}
}
EncryptedData encryptedData = cipherTextHandler.seal( serverKey, encTicketPart,
KeyUsage.AS_OR_TGS_REP_TICKET_WITH_SRVKEY );
Ticket newTicket = new Ticket( ticketPrincipal, encryptedData );
newTicket.setRealm( serverRealm );
newTicket.setEncTicketPart( encTicketPart );
if ( LOG.isDebugEnabled() )
{
LOG.debug( "Ticket will be issued for access to {}.", serverPrincipal.toString() );
}
authContext.setTicket( newTicket );
}
private static void buildReply( AuthenticationContext authContext ) throws KerberosException,
InvalidTicketException
{
KdcReq request = authContext.getRequest();
Ticket ticket = authContext.getTicket();
AsRep reply = new AsRep();
reply.setCName( request.getKdcReqBody().getCName() );
reply.setCRealm( request.getKdcReqBody().getRealm() );
reply.setTicket( ticket );
EncKdcRepPart encKdcRepPart = new EncKdcRepPart();
encKdcRepPart.setKey( ticket.getEncTicketPart().getKey() );
// TODO - fetch lastReq for this client; requires store
// FIXME temporary fix, IMO we should create some new ATs to store this info in DIT
LastReq lastReq = new LastReq();
lastReq.addEntry( new LastReqEntry( LastReqType.TIME_OF_INITIAL_REQ, new KerberosTime() ) );
encKdcRepPart.setLastReq( lastReq );
// TODO - resp.key-expiration := client.expiration; requires store
encKdcRepPart.setNonce( request.getKdcReqBody().getNonce() );
encKdcRepPart.setFlags( ticket.getEncTicketPart().getFlags() );
encKdcRepPart.setAuthTime( ticket.getEncTicketPart().getAuthTime() );
encKdcRepPart.setStartTime( ticket.getEncTicketPart().getStartTime() );
encKdcRepPart.setEndTime( ticket.getEncTicketPart().getEndTime() );
if ( ticket.getEncTicketPart().getFlags().isRenewable() )
{
encKdcRepPart.setRenewTill( ticket.getEncTicketPart().getRenewTill() );
}
encKdcRepPart.setSName( ticket.getSName() );
encKdcRepPart.setSRealm( ticket.getRealm() );
encKdcRepPart.setClientAddresses( ticket.getEncTicketPart().getClientAddresses() );
EncAsRepPart encAsRepPart = new EncAsRepPart();
encAsRepPart.setEncKdcRepPart( encKdcRepPart );
if ( LOG.isDebugEnabled() )
{
monitorContext( authContext );
monitorReply( reply, encKdcRepPart );
}
EncryptionKey clientKey = authContext.getClientKey();
EncryptedData encryptedData = cipherTextHandler.seal( clientKey, encAsRepPart,
KeyUsage.AS_REP_ENC_PART_WITH_CKEY );
reply.setEncPart( encryptedData );
reply.setEncKdcRepPart( encKdcRepPart );
authContext.setReply( reply );
}
private static void monitorRequest( KdcContext kdcContext )
{
KdcReq request = kdcContext.getRequest();
if ( LOG.isDebugEnabled() )
{
try
{
String clientAddress = kdcContext.getClientAddress().getHostAddress();
StringBuffer sb = new StringBuffer();
sb.append( "Received " + SERVICE_NAME + " request:" );
sb.append( "\n\t" + "messageType: " + request.getMessageType() );
sb.append( "\n\t" + "protocolVersionNumber: " + request.getProtocolVersionNumber() );
sb.append( "\n\t" + "clientAddress: " + clientAddress );
sb.append( "\n\t" + "nonce: " + request.getKdcReqBody().getNonce() );
sb.append( "\n\t" + "kdcOptions: " + request.getKdcReqBody().getKdcOptions() );
sb.append( "\n\t" + "clientPrincipal: " + request.getKdcReqBody().getCName() );
sb.append( "\n\t" + "serverPrincipal: " + request.getKdcReqBody().getSName() );
sb.append( "\n\t" + "encryptionType: "
+ KerberosUtils.getEncryptionTypesString( request.getKdcReqBody().getEType() ) );
sb.append( "\n\t" + "realm: " + request.getKdcReqBody().getRealm() );
sb.append( "\n\t" + "from time: " + request.getKdcReqBody().getFrom() );
sb.append( "\n\t" + "till time: " + request.getKdcReqBody().getTill() );
sb.append( "\n\t" + "renew-till time: " + request.getKdcReqBody().getRTime() );
sb.append( "\n\t" + "hostAddresses: " + request.getKdcReqBody().getAddresses() );
LOG.debug( sb.toString() );
}
catch ( Exception e )
{
// This is a monitor. No exceptions should bubble up.
LOG.error( I18n.err( I18n.ERR_153 ), e );
}
}
}
private static void monitorContext( AuthenticationContext authContext )
{
try
{
long clockSkew = authContext.getConfig().getAllowableClockSkew();
InetAddress clientAddress = authContext.getClientAddress();
StringBuilder sb = new StringBuilder();
sb.append( "Monitoring " + SERVICE_NAME + " context:" );
sb.append( "\n\t" + "clockSkew " + clockSkew );
sb.append( "\n\t" + "clientAddress " + clientAddress );
KerberosPrincipal clientPrincipal = authContext.getClientEntry().getPrincipal();
PrincipalStoreEntry clientEntry = authContext.getClientEntry();
sb.append( "\n\t" + "principal " + clientPrincipal );
sb.append( "\n\t" + "cn " + clientEntry.getCommonName() );
sb.append( "\n\t" + "realm " + clientEntry.getRealmName() );
sb.append( "\n\t" + "principal " + clientEntry.getPrincipal() );
sb.append( "\n\t" + "SAM type " + clientEntry.getSamType() );
PrincipalName serverPrincipal = authContext.getRequest().getKdcReqBody().getSName();
PrincipalStoreEntry serverEntry = authContext.getServerEntry();
sb.append( "\n\t" + "principal " + serverPrincipal );
sb.append( "\n\t" + "cn " + serverEntry.getCommonName() );
sb.append( "\n\t" + "realm " + serverEntry.getRealmName() );
sb.append( "\n\t" + "principal " + serverEntry.getPrincipal() );
sb.append( "\n\t" + "SAM type " + serverEntry.getSamType() );
EncryptionType encryptionType = authContext.getEncryptionType();
int clientKeyVersion = clientEntry.getKeyMap().get( encryptionType ).getKeyVersion();
int serverKeyVersion = serverEntry.getKeyMap().get( encryptionType ).getKeyVersion();
sb.append( "\n\t" + "Request key type " + encryptionType );
sb.append( "\n\t" + "Client key version " + clientKeyVersion );
sb.append( "\n\t" + "Server key version " + serverKeyVersion );
LOG.debug( sb.toString() );
}
catch ( Exception e )
{
// This is a monitor. No exceptions should bubble up.
LOG.error( I18n.err( I18n.ERR_154 ), e );
}
}
private static void monitorReply( AsRep reply, EncKdcRepPart part )
{
if ( LOG.isDebugEnabled() )
{
try
{
StringBuffer sb = new StringBuffer();
sb.append( "Responding with " + SERVICE_NAME + " reply:" );
sb.append( "\n\t" + "messageType: " + reply.getMessageType() );
sb.append( "\n\t" + "protocolVersionNumber: " + reply.getProtocolVersionNumber() );
sb.append( "\n\t" + "nonce: " + part.getNonce() );
sb.append( "\n\t" + "clientPrincipal: " + reply.getCName() );
sb.append( "\n\t" + "client realm: " + reply.getCRealm() );
sb.append( "\n\t" + "serverPrincipal: " + part.getSName() );
sb.append( "\n\t" + "server realm: " + part.getSRealm() );
sb.append( "\n\t" + "auth time: " + part.getAuthTime() );
sb.append( "\n\t" + "start time: " + part.getStartTime() );
sb.append( "\n\t" + "end time: " + part.getEndTime() );
sb.append( "\n\t" + "renew-till time: " + part.getRenewTill() );
sb.append( "\n\t" + "hostAddresses: " + part.getClientAddresses() );
LOG.debug( sb.toString() );
}
catch ( Exception e )
{
// This is a monitor. No exceptions should bubble up.
LOG.error( I18n.err( I18n.ERR_155 ), e );
}
}
}
/**
* Get a PrincipalStoreEntry given a principal. The ErrorType is used to indicate
* whether any resulting error pertains to a server or client.
*/
private static PrincipalStoreEntry getEntry( KerberosPrincipal principal, PrincipalStore store, ErrorType errorType )
throws KerberosException
{
PrincipalStoreEntry entry = null;
try
{
entry = store.getPrincipal( principal );
}
catch ( Exception e )
{
throw new KerberosException( errorType, e );
}
if ( entry == null )
{
throw new KerberosException( errorType );
}
if ( entry.getKeyMap() == null || entry.getKeyMap().isEmpty() )
{
throw new KerberosException( ErrorType.KDC_ERR_NULL_KEY );
}
return entry;
}
/**
* Prepares a pre-authentication error message containing required
* encryption types.
*
* @param encryptionTypes
* @return The error message as bytes.
*/
private static byte[] preparePreAuthenticationError( List<EncryptionType> clientEncryptionTypes,
List<EncryptionType> serverEncryptionTypes )
{
PaData[] paDataSequence = new PaData[2];
PaData paData = new PaData();
paData.setPaDataType( PaDataType.PA_ENC_TIMESTAMP );
paData.setPaDataValue( Strings.EMPTY_BYTES );
paDataSequence[0] = paData;
ETypeInfo eTypeInfo = new ETypeInfo();
for ( EncryptionType encryptionType : clientEncryptionTypes )
{
if ( serverEncryptionTypes.contains( encryptionType ) )
{
ETypeInfoEntry etypeInfoEntry = new ETypeInfoEntry( encryptionType, null );
eTypeInfo.addETypeInfoEntry( etypeInfoEntry );
}
}
byte[] encTypeInfo = null;
try
{
ByteBuffer buffer = ByteBuffer.allocate( eTypeInfo.computeLength() );
encTypeInfo = eTypeInfo.encode( buffer ).array();
}
catch ( EncoderException ioe )
{
return null;
}
PaData responsePaData = new PaData( PaDataType.PA_ENCTYPE_INFO, encTypeInfo );
MethodData methodData = new MethodData();
methodData.addPaData( responsePaData );
try
{
ByteBuffer buffer = ByteBuffer.allocate( methodData.computeLength() );
return methodData.encode( buffer ).array();
}
catch ( EncoderException ee )
{
return null;
}
}
}