/*
* Copyright (C) 2006-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.jlan.server.auth.kerberos;
import java.io.IOException;
import java.util.Iterator;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosKey;
import org.alfresco.jlan.debug.Debug;
import org.alfresco.jlan.server.auth.AuthContext;
import org.alfresco.jlan.server.auth.asn.DER;
import org.alfresco.jlan.server.auth.asn.DERBuffer;
import org.alfresco.jlan.server.auth.asn.DERObject;
import org.alfresco.jlan.server.auth.asn.DEROid;
import org.alfresco.jlan.util.HexDump;
import sun.security.krb5.EncryptedData;
import sun.security.krb5.EncryptionKey;
/**
* Kerberos Authentication Context Class
*
* @author gkspencer
*/
public class KrbAuthContext extends AuthContext {
// AP-REQ
private KerberosApReq m_apReq;
// Encrypted Kerberos ticket part and authenticator
private EncKrbTicket m_encTkt;
private KrbAuthenticator m_krbAuth;
// Enable debug output
private boolean m_debug;
/**
* Default constructor
*/
public KrbAuthContext()
{
}
/**
* Parse the AP-REQ blob to extract the encypted ticket and authenticator
*
* @param subj Subject
* @param apReq KerberosApReq
* @exception IOException
*/
public final void parseKerberosApReq( Subject subj, KerberosApReq apReq)
throws IOException
{
// Save the AP-REQ
m_apReq = apReq;
// Parse the Kerberos ticket
KrbTicket krbTkt = new KrbTicket( apReq.getTicket());
if ( Debug.EnableDbg && hasDebug())
Debug.println("Kerberos ticket - " + krbTkt);
// Get the private key or session key
Set<KerberosKey> krbKeySet = subj.getPrivateCredentials( KerberosKey.class);
Iterator<KerberosKey> keyIter = krbKeySet.iterator();
while ( keyIter.hasNext())
{
// Get the current key
KerberosKey krbKey = keyIter.next();
if ( krbKey.getKeyType() == krbTkt.getEncryptedType())
{
// Try the current encryption key
EncryptionKey encKey = new EncryptionKey( krbKey.getEncoded(), krbKey.getKeyType(), new Integer(2));
// Decrypt the encrypted ticket
try
{
// Decrypt the encrypted part of the Kerberos ticket
EncryptedData encPart = new EncryptedData( krbTkt.getEncryptedType(), krbTkt.getEncryptedPartKeyVersion() != -1 ? new Integer(krbTkt.getEncryptedPartKeyVersion()) : null,
krbTkt.getEncryptedPart());
byte[] decPart = encPart.decrypt( encKey, 2);
if ( Debug.EnableDbg && hasDebug())
Debug.println( "Decrypted ticket = Len=" + decPart.length + ", key=[Type=" + encKey.getEType() + ", Kvno=" + encKey.getKeyVersionNumber() + ", Key=" + HexDump.hexString(encKey.getBytes()) + "]");
DERBuffer derBuf = new DERBuffer( decPart);
byte[] encTktByts = derBuf.unpackApplicationSpecificBytes();
if ( encTktByts != null)
{
// Create the encrypted Kerberos ticket part
m_encTkt = new EncKrbTicket( encTktByts);
if ( Debug.EnableDbg && hasDebug())
Debug.println( "Enc Krb Ticket Part = " + m_encTkt);
}
}
catch (Exception ex)
{
if ( Debug.EnableDbg && hasDebug())
Debug.println("Ticket Error: " + ex);
}
}
}
// Use the session key to decrypt the authenticator
if ( m_encTkt != null)
{
// Create the session key
EncryptionKey encKey = new EncryptionKey( m_encTkt.getEncryptionKeyType(), m_encTkt.getEncryptionKey());
// Decrypt the authenticator
try
{
// Decrypt the authenticator
EncryptedData encPart = new EncryptedData( apReq.getAuthenticatorEncType(), apReq.getAuthenticatorKeyVersion() != -1 ? new Integer(apReq.getAuthenticatorKeyVersion()) : null,
apReq.getAuthenticator());
byte[] decPart = encPart.decrypt( encKey, 11);
if ( Debug.EnableDbg && hasDebug())
Debug.println( "Decrypted authenticator = Len=" + decPart.length + ", key=[Type=" + encKey.getEType() + ", Kvno=" + encKey.getKeyVersionNumber() + ", Key=" + HexDump.hexString(encKey.getBytes()) + "]");
DERBuffer derBuf = new DERBuffer( decPart);
byte[] krbAuthByts = derBuf.unpackApplicationSpecificBytes();
if ( krbAuthByts != null) {
// Parse the authenticator
m_krbAuth = new KrbAuthenticator( krbAuthByts);
if ( Debug.EnableDbg && hasDebug())
Debug.println( "Krb Authenticator = " + m_krbAuth);
}
}
catch (Exception ex)
{
if ( Debug.EnableDbg && hasDebug())
Debug.println("Auth Error: " + ex);
}
}
else
throw new IOException("Failed to decrypt Kerberos ticket");
}
/**
* Parse the Kerberos AP-REP and return the updated response
*
* @param respTok byte[]
* @return byte[]
* @exception Exception
*/
public final byte[] parseKerberosApRep( byte[] respTok)
throws Exception
{
// Parse the response token
DERBuffer derBuf = new DERBuffer( respTok);
byte[] aprepBlob = null;
// Get the application specific object
DEROid oid = null;
int tokId = 0;
DERObject derObj = derBuf.unpackApplicationSpecific();
if ( derObj != null)
{
// Read the OID and token id
if ( derObj instanceof DEROid)
oid = (DEROid) derObj;
tokId = derBuf.unpackByte();
tokId += derBuf.unpackByte() >> 8;
// Read the AP-REP object
if ( DER.isApplicationSpecific( derBuf.peekType()))
aprepBlob = derBuf.unpackApplicationSpecificBytes();
}
// Parse the Kerberos AP-REP
KerberosApRep krbApRep = new KerberosApRep( aprepBlob);
if ( Debug.EnableDbg && hasDebug())
Debug.println("Kerberos AP-REP - " + krbApRep);
// Create the session key
EncryptionKey encKey = new EncryptionKey( m_encTkt.getEncryptionKeyType(), m_encTkt.getEncryptionKey());
// Decrypt the AP-REP
byte[] updRespTok = null;
// Decrypt the AP-REP
EncryptedData encPart = new EncryptedData( krbApRep.getEncryptionType(), krbApRep.getKeyVersion() != -1 ? new Integer(krbApRep.getKeyVersion()) : null,
krbApRep.getEncryptedPart());
byte[] decPart = encPart.decrypt( encKey, 12);
if ( Debug.EnableDbg && hasDebug())
Debug.println( "Decrypted AP-REP Len=" + decPart.length + ", key=[Type=" + encKey.getEType() + ", Key=" + HexDump.hexString(encKey.getBytes()) + "]");
derBuf = new DERBuffer( decPart);
byte[] encApRepByts = derBuf.unpackApplicationSpecificBytes();
if ( encApRepByts != null)
{
// Parse the AP-REP encrypted part
Debug.println("EncApRep bytes:");
HexDump.Dump(decPart, decPart.length, 0, Debug.getDebugInterface());
EncApRepPart encApRep = new EncApRepPart( encApRepByts);
if ( Debug.EnableDbg && hasDebug())
Debug.println( "EncApRep = " + encApRep);
// Add the sub-key from the client AP-REQ
if ( encApRep.getSubKey() == null)
{
// Use the subkey sent by the client
encApRep.setSubkey( m_krbAuth.getSubKeyType(), m_krbAuth.getSubKey());
// DEBUG
if ( Debug.EnableDbg && hasDebug())
Debug.println("Using client sub-key, type=" + m_krbAuth.getSubKeyType() + ", key=" + HexDump.hexString( m_krbAuth.getSubKey()));
// Rebuild the ASN.1 encoded AP-REP part
decPart = encApRep.encodeApRep();
Debug.println("Re-encoded EncapRep bytes:");
HexDump.Dump( decPart, decPart.length, 0, Debug.getDebugInterface());
// Encrypt the updated AP-REP part
encPart = new EncryptedData( encKey, decPart, 12);
// Rebuild the Kerberos AP-REP
krbApRep.setEncryptedPart( krbApRep.getEncryptionType(), encPart.getBytes(), krbApRep.getKeyVersion());
// ASN.1 encode the AP-REP
aprepBlob = krbApRep.encodeApRep();
// Rebuild the response token
//
// Pack the OID
DERBuffer oidBuf = new DERBuffer();
oid.derEncode( oidBuf);
byte[] oidBytes = oidBuf.getBytes();
updRespTok = new byte[ aprepBlob.length + 2 + oidBytes.length];
int pos = 0;
System.arraycopy( oidBytes, 0, updRespTok, pos, oidBytes.length);
pos += oidBytes.length;
updRespTok[ pos++] = (byte) (tokId & 0xFF);
updRespTok[ pos++] = (byte) (( tokId >> 8) & 0xFF);
System.arraycopy( aprepBlob, 0, updRespTok, pos, aprepBlob.length);
// Wrap up as an application specific object
DERBuffer appBuf = new DERBuffer();
appBuf.packApplicationSpecific( updRespTok);
updRespTok = appBuf.getBytes();
}
}
// Return the updated response token
return updRespTok;
}
/**
* Check if debug output is enabled
*
* @return boolean
*/
public final boolean hasDebug() {
return m_debug;
}
/**
* Enable/disable debug output
*
* @param ena boolean
*/
public final void setDebug(boolean ena) {
m_debug = ena;
}
/**
* Return the Kerberos authentication context details as a string
*
* @return String
*/
public String toString()
{
StringBuilder str = new StringBuilder();
str.append("[KrbAuthCtx:AP-REQ=");
str.append(m_apReq);
str.append(",EncTkt=");
str.append(m_encTkt);
str.append(",KrbAuth=");
str.append(m_krbAuth);
str.append("]");
return str.toString();
}
}