blob: 1ea7f9873eaeffde30e5c26551004e958e81ed0b [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.shared.kerberos.components;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.apache.directory.api.asn1.Asn1Object;
import org.apache.directory.api.asn1.EncoderException;
import org.apache.directory.api.asn1.ber.tlv.BerValue;
import org.apache.directory.api.asn1.ber.tlv.TLV;
import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
import org.apache.directory.api.util.Strings;
import org.apache.directory.server.i18n.I18n;
import org.apache.directory.shared.kerberos.KerberosConstants;
import org.apache.directory.shared.kerberos.KerberosTime;
import org.apache.directory.shared.kerberos.codec.options.KdcOptions;
import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
import org.apache.directory.shared.kerberos.messages.Ticket;
/**
* The KDC-REQ-BODY data structure. It will store the object described by the ASN.1 grammar :
* <pre>
* KDC-REQ-BODY ::= SEQUENCE {
* kdc-options [0] KDCOptions,
* cname [1] PrincipalName OPTIONAL
* -- Used only in AS-REQ --,
* realm [2] Realm
* -- Server's realm
* -- Also client's in AS-REQ --,
* sname [3] PrincipalName OPTIONAL,
* from [4] KerberosTime OPTIONAL,
* till [5] KerberosTime,
* rtime [6] KerberosTime OPTIONAL,
* nonce [7] UInt32,
* etype [8] SEQUENCE OF Int32 -- EncryptionType
* -- in preference order --,
* addresses [9] HostAddresses OPTIONAL,
* enc-authorization-data [10] EncryptedData OPTIONAL
* -- AuthorizationData --,
* additional-tickets [11] SEQUENCE OF Ticket OPTIONAL
* -- NOTE: not empty
* }
* </pre>
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class KdcReqBody implements Asn1Object
{
/** The KDC options */
private KdcOptions kdcOptions;
/** The Client Principal, if the request is an AS-REQ */
private PrincipalName cName;
/** The realm */
private String realm;
/** The Server Principal */
private PrincipalName sName;
/** The start time for the requested ticket */
private KerberosTime from;
/** The expiration date for the requested ticket */
private KerberosTime till;
/** The renew-till date for the requested ticket */
private KerberosTime rtime;
/** Random number to avoid MiM attacks */
private int nonce;
/** Set of desired encryption types */
private Set<EncryptionType> eType;
/** Addresses valid for the requested ticket */
private HostAddresses addresses;
/** Encoded authorizationData, used by the TGS-REQ only */
private EncryptedData encAuthorizationData;
/** Additional tickets */
private List<Ticket> additionalTickets;
// Storage for computed lengths
private int kdcOptionsLength;
private int cNameLength;
private int realmLength;
private byte[] realmBytes;
private int sNameLength;
private int fromLength;
private int tillLength;
private int rtimeLength;
private int nonceLength;
private int eTypeLength;
private int eTypeSeqLength;
private int[] eTypeLengths;
private int addressesLength;
private int encAuthzDataLength;
private int additionalTicketLength;
private int additionalTicketSeqLength;
private int[] additionalTicketsLengths;
private int kdcReqBodySeqLength;
private int kdcReqBodyLength;
/**
* Creates a new instance of RequestBody.
*/
public KdcReqBody()
{
additionalTickets = new ArrayList<Ticket>();
eType = new LinkedHashSet<EncryptionType>();
}
/**
* Returns the additional {@link Ticket}s.
*
* @return The additional {@link Ticket}s.
*/
public Ticket[] getAdditionalTickets()
{
return additionalTickets.toArray( new Ticket[]
{} );
}
/**
* Set the list of additional Ticket
* @param additionalTickets the additionalTickets to set
*/
public void setAdditionalTickets( List<Ticket> additionalTickets )
{
this.additionalTickets = additionalTickets;
}
/**
* Add a new Ticket to the list of additional tickets
* @param additionalTickets the additionalTickets to set
*/
public void addAdditionalTicket( Ticket additionalTicket )
{
this.additionalTickets.add( additionalTicket );
}
/**
* Returns the {@link HostAddresses}.
*
* @return The {@link HostAddresses}.
*/
public HostAddresses getAddresses()
{
return addresses;
}
/**
* @param addresses the addresses to set
*/
public void setAddresses( HostAddresses addresses )
{
this.addresses = addresses;
}
/**
* @return the client PrincipalName
*/
public PrincipalName getCName()
{
return cName;
}
/**
* @param cName the cName to set
*/
public void setCName( PrincipalName cName )
{
this.cName = cName;
}
/**
* Returns the encrypted {@link AuthorizationData} as {@link EncryptedData}.
*
* @return The encrypted {@link AuthorizationData} as {@link EncryptedData}.
*/
public EncryptedData getEncAuthorizationData()
{
return encAuthorizationData;
}
/**
* @param encAuthorizationData the encAuthorizationData to set
*/
public void setEncAuthorizationData( EncryptedData encAuthorizationData )
{
this.encAuthorizationData = encAuthorizationData;
}
/**
* Returns the requested {@link EncryptionType}s.
*
* @return The requested {@link EncryptionType}s.
*/
public Set<EncryptionType> getEType()
{
return eType;
}
/**
* @param eType the eType to set
*/
public void setEType( Set<EncryptionType> eType )
{
this.eType = eType;
}
/**
* @param eType the eType to add
*/
public void addEType( EncryptionType eType )
{
this.eType.add( eType );
}
/**
* Returns the from {@link KerberosTime}.
*
* @return The from {@link KerberosTime}.
*/
public KerberosTime getFrom()
{
return from;
}
/**
* @param from the from to set
*/
public void setFrom( KerberosTime from )
{
this.from = from;
}
/**
* Returns the {@link KdcOptions}.
*
* @return The {@link KdcOptions}.
*/
public KdcOptions getKdcOptions()
{
return kdcOptions;
}
/**
* @param kdcOptions the kdcOptions to set
*/
public void setKdcOptions( KdcOptions kdcOptions )
{
this.kdcOptions = kdcOptions;
}
/**
* Returns the nonce.
*
* @return The nonce.
*/
public int getNonce()
{
return nonce;
}
/**
* @param nonce the nonce to set
*/
public void setNonce( int nonce )
{
this.nonce = nonce;
}
/**
* @return the realm
*/
public String getRealm()
{
return realm;
}
/**
* @param realm the realm to set
*/
public void setRealm( String realm )
{
this.realm = realm;
}
/**
* Returns the RenewTime {@link KerberosTime}.
*
* @return The RenewTime {@link KerberosTime}.
*/
public KerberosTime getRTime()
{
return rtime;
}
/**
* @param rtime the renewTime to set
*/
public void setRtime( KerberosTime rtime )
{
this.rtime = rtime;
}
/**
* Returns the server {@link PrincipalName}.
*
* @return The server {@link PrincipalName}.
*/
public PrincipalName getSName()
{
return sName;
}
/**
* @param sName the sName to set
*/
public void setSName( PrincipalName sName )
{
this.sName = sName;
}
/**
* Returns the till {@link KerberosTime}.
*
* @return The till {@link KerberosTime}.
*/
public KerberosTime getTill()
{
return till;
}
/**
* @param till the till to set
*/
public void setTill( KerberosTime till )
{
this.till = till;
}
/**
* Compute the KdcReqBody length
* <pre>
* KdcReqBody :
*
* 0x30 L1 KdcReqBody sequence
* |
* +--> 0xA0 L2 kdc-options tag
* | |
* | +--> 0x03 L2-1 kdc-options (BitString)
* |
* +--> 0xA1 L3 cname tag
* | |
* | +--> 0x30 L3-1 cname (PrincipalName)
* |
* +--> 0xA2 L4 realm tag
* | |
* | +--> 0x1B L4-1 realm (Realm, KerberosString)
* |
* +--> 0xA3 L5 sname tag
* | |
* | +--> 0x30 L5-1 sname (PrincipalName)
* |
* +--> 0xA4 L6 from tag
* | |
* | +--> 0x18 L6-1 from (KerberosTime)
* |
* +--> 0xA5 L7 till tag
* | |
* | +--> 0x18 L7-1 till (KerberosTime)
* |
* +--> 0xA6 L8 rtime tag
* | |
* | +--> 0x18 L8-1 rtime (KerberosTime)
* |
* +--> 0xA7 L9 nonce tag
* | |
* | +--> 0x02 L9-1 nonce (Int)
* |
* +--> 0xA8 L10 etype tag
* | |
* | +--> 0x30 L10-1 SEQ
* | |
* | +--> 0x02 L10-1-1 etype
* | |
* | +--> 0x02 L10-1-2 etype
* | |
* | :
* |
* +--> 0xA9 L11 addresses tag
* | |
* | +--> 0x30 L11-1 addresses (HostAddresses)
* |
* +--> 0xAA L12 enc-authorization-data tag
* | |
* | +--> 0x30 L12-1 enc-authorization-data
* |
* +--> 0xAB L13 additional-tickets tag
* |
* +--> 0x30 L13-1 additional-tickets
* |
* +--> 0x61 L13-1-1 Ticket
* |
* +--> 0x61 L13-1-2 Ticket
* |
* :
* </pre>
*/
@Override
public int computeLength()
{
reset();
// The KdcOptions length
kdcOptionsLength = 1 + 1 + kdcOptions.getBytes().length;
kdcReqBodySeqLength = 1 + TLV.getNbBytes( kdcOptionsLength ) + kdcOptionsLength;
// The cname length
if ( cName != null )
{
cNameLength = cName.computeLength();
kdcReqBodySeqLength += 1 + TLV.getNbBytes( cNameLength ) + cNameLength;
}
// Compute the realm length.
realmBytes = Strings.getBytesUtf8( realm );
realmLength = 1 + TLV.getNbBytes( realmBytes.length ) + realmBytes.length;
kdcReqBodySeqLength += 1 + TLV.getNbBytes( realmLength ) + realmLength;
// The sname length
if ( sName != null )
{
sNameLength = sName.computeLength();
kdcReqBodySeqLength += 1 + TLV.getNbBytes( sNameLength ) + sNameLength;
}
// The from length
if ( from != null )
{
fromLength = 1 + 1 + 0x0F;
kdcReqBodySeqLength += 1 + 1 + fromLength;
}
// The till length
tillLength = 1 + 1 + 0x0F;
kdcReqBodySeqLength += 1 + 1 + tillLength;
// The rtime length
if ( rtime != null )
{
rtimeLength = 1 + 1 + 0x0F;
kdcReqBodySeqLength += 1 + 1 + rtimeLength;
}
// The nonce length
nonceLength = 1 + 1 + BerValue.getNbBytes( nonce );
kdcReqBodySeqLength += 1 + 1 + nonceLength;
// The eType length
eTypeLengths = new int[eType.size()];
int pos = 0;
eTypeSeqLength = 0;
for ( EncryptionType encryptionType : eType )
{
eTypeLengths[pos] = 1 + 1 + BerValue.getNbBytes( encryptionType.getValue() );
eTypeSeqLength += eTypeLengths[pos];
pos++;
}
eTypeLength = 1 + TLV.getNbBytes( eTypeSeqLength ) + eTypeSeqLength;
kdcReqBodySeqLength += 1 + TLV.getNbBytes( eTypeLength ) + eTypeLength;
// The Addresses length
if ( addresses != null )
{
addressesLength = addresses.computeLength();
kdcReqBodySeqLength += 1 + TLV.getNbBytes( addressesLength ) + addressesLength;
}
// The EncAuthorizationData length
if ( encAuthorizationData != null )
{
encAuthzDataLength = encAuthorizationData.computeLength();
kdcReqBodySeqLength += 1 + TLV.getNbBytes( encAuthzDataLength ) + encAuthzDataLength;
}
// The additionalTickets length
if ( additionalTickets.size() != 0 )
{
additionalTicketsLengths = new int[additionalTickets.size()];
additionalTicketSeqLength = 0;
pos = 0;
for ( Ticket ticket : additionalTickets )
{
additionalTicketsLengths[pos] = ticket.computeLength();
additionalTicketSeqLength += additionalTicketsLengths[pos];
pos++;
}
additionalTicketLength = 1 + TLV.getNbBytes( additionalTicketSeqLength ) + additionalTicketSeqLength;
kdcReqBodySeqLength += 1 + TLV.getNbBytes( additionalTicketLength ) + additionalTicketLength;
}
// compute the global size
kdcReqBodyLength = 1 + TLV.getNbBytes( kdcReqBodySeqLength ) + kdcReqBodySeqLength;
return kdcReqBodyLength;
}
/**
* Encode the KDC-REQ-BODY component
*
* @param buffer The buffer containing the encoded result
* @return The encoded component
* @throws EncoderException If the encoding failed
*/
@Override
public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
{
if ( buffer == null )
{
throw new EncoderException( I18n.err( I18n.ERR_148 ) );
}
// The KDC-REQ-BODY SEQ Tag
buffer.put( UniversalTag.SEQUENCE.getValue() );
buffer.put( TLV.getBytes( kdcReqBodySeqLength ) );
// The KdcOptions -----------------------------------------------------
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_KDC_OPTIONS_TAG );
buffer.put( TLV.getBytes( kdcOptionsLength ) );
// The value
BerValue.encode( buffer, kdcOptions );
// The cname if any ---------------------------------------------------
if ( cName != null )
{
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_CNAME_TAG );
buffer.put( TLV.getBytes( cNameLength ) );
// The value
cName.encode( buffer );
}
// The realm ----------------------------------------------------------
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_REALM_TAG );
buffer.put( TLV.getBytes( realmLength ) );
// The value
buffer.put( UniversalTag.GENERAL_STRING.getValue() );
buffer.put( TLV.getBytes( realmBytes.length ) );
buffer.put( realmBytes );
// The sname, if any --------------------------------------------------
if ( sName != null )
{
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_SNAME_TAG );
buffer.put( TLV.getBytes( sNameLength ) );
// The value
sName.encode( buffer );
}
// The from, if any ---------------------------------------------------
if ( from != null )
{
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_FROM_TAG );
buffer.put( TLV.getBytes( fromLength ) );
// The value
buffer.put( UniversalTag.GENERALIZED_TIME.getValue() );
buffer.put( ( byte ) 0x0F );
buffer.put( from.getBytes() );
}
// The till -----------------------------------------------------------
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_TILL_TAG );
buffer.put( TLV.getBytes( tillLength ) );
// The value
buffer.put( UniversalTag.GENERALIZED_TIME.getValue() );
buffer.put( ( byte ) 0x0F );
buffer.put( till.getBytes() );
// The rtime if any ---------------------------------------------------
if ( rtime != null )
{
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_RTIME_TAG );
buffer.put( TLV.getBytes( rtimeLength ) );
// The value
buffer.put( UniversalTag.GENERALIZED_TIME.getValue() );
buffer.put( ( byte ) 0x0F );
buffer.put( rtime.getBytes() );
}
// The nonce ----------------------------------------------------------
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_NONCE_TAG );
buffer.put( TLV.getBytes( nonceLength ) );
// The value
BerValue.encode( buffer, nonce );
// The etype ----------------------------------------------------------
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_ETYPE_TAG );
buffer.put( TLV.getBytes( eTypeLength ) );
// The sequence
buffer.put( UniversalTag.SEQUENCE.getValue() );
buffer.put( TLV.getBytes( eTypeSeqLength ) );
// The values
for ( EncryptionType encryptionType : eType )
{
BerValue.encode( buffer, encryptionType.getValue() );
}
// The addresses if any -----------------------------------------------
if ( addresses != null )
{
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_ADDRESSES_TAG );
buffer.put( TLV.getBytes( addressesLength ) );
// The value
addresses.encode( buffer );
}
// The enc-authorization-data, if any ---------------------------------
if ( encAuthorizationData != null )
{
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_ENC_AUTHZ_DATA_TAG );
buffer.put( TLV.getBytes( encAuthzDataLength ) );
// The value
encAuthorizationData.encode( buffer );
}
// The additional-tickets, if any -------------------------------------
if ( additionalTickets.size() != 0 )
{
// The tag
buffer.put( ( byte ) KerberosConstants.KDC_REQ_BODY_ADDITIONAL_TICKETS_TAG );
buffer.put( TLV.getBytes( additionalTicketLength ) );
// The sequence
buffer.put( UniversalTag.SEQUENCE.getValue() );
buffer.put( TLV.getBytes( additionalTicketSeqLength ) );
// The values
for ( Ticket ticket : additionalTickets )
{
ticket.encode( buffer );
}
}
return buffer;
}
/**
* reset the fields used while computing length
*/
private void reset()
{
kdcOptionsLength = 0;
cNameLength = 0;
realmLength = 0;
realmBytes = null;
sNameLength = 0;
fromLength = 0;
tillLength = 0;
rtimeLength = 0;
nonceLength = 0;
eTypeLength = 0;
eTypeSeqLength = 0;
eTypeLengths = null;
addressesLength = 0;
encAuthzDataLength = 0;
additionalTicketLength = 0;
additionalTicketSeqLength = 0;
additionalTicketsLengths = null;
kdcReqBodySeqLength = 0;
kdcReqBodyLength = 0;
}
/**
* Pretty print the instance
*/
public String toString( String tabs )
{
StringBuilder sb = new StringBuilder();
if ( ( kdcOptions != null ) && ( kdcOptions.size() > 0 ) )
{
sb.append( tabs ).append( "KDCOptions : " ).append( kdcOptions ).append( '\n' );
}
if ( cName != null )
{
sb.append( tabs ).append( "cname : " ).append( cName ).append( '\n' );
}
sb.append( tabs ).append( "realm : " ).append( realm ).append( '\n' );
if ( sName != null )
{
sb.append( tabs ).append( "sname : " ).append( sName ).append( '\n' );
}
if ( from != null )
{
sb.append( tabs ).append( "from : " ).append( from ).append( '\n' );
}
sb.append( tabs ).append( "till : " ).append( till ).append( '\n' );
if ( rtime != null )
{
sb.append( tabs ).append( "rtime : " ).append( rtime ).append( '\n' );
}
sb.append( tabs ).append( "nonce : " ).append( nonce ).append( '\n' );
sb.append( tabs ).append( "etype : " );
boolean isFirst = true;
for ( EncryptionType encryptionType : eType )
{
if ( isFirst )
{
isFirst = false;
}
else
{
sb.append( " " );
}
sb.append( encryptionType );
}
if ( addresses != null )
{
sb.append( '\n' );
sb.append( tabs ).append( "addresses : " );
isFirst = true;
for ( HostAddress hostAddress : addresses.getAddresses() )
{
if ( isFirst )
{
isFirst = false;
}
else
{
sb.append( " " );
}
sb.append( hostAddress );
}
}
if ( encAuthorizationData != null )
{
sb.append( '\n' );
sb.append( tabs ).append( "enc-authorization-data" ).append( encAuthorizationData );
}
if ( additionalTickets.size() != 0 )
{
sb.append( '\n' );
sb.append( tabs ).append( "Tickets : " );
isFirst = true;
for ( Ticket ticket : additionalTickets )
{
if ( isFirst )
{
isFirst = false;
sb.append( '\n' );
}
sb.append( ticket.toString( tabs + " " ) );
}
}
return sb.toString();
}
/**
* @see Object#toString()
*/
public String toString()
{
return toString( "" );
}
}