|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.jgss.krb5; |
|
|
|
import com.sun.security.jgss.InquireType; |
|
import org.ietf.jgss.*; |
|
import sun.misc.HexDumpEncoder; |
|
import sun.security.jgss.GSSUtil; |
|
import sun.security.jgss.GSSCaller; |
|
import sun.security.jgss.spi.*; |
|
import sun.security.jgss.TokenTracker; |
|
import sun.security.krb5.*; |
|
import java.io.InputStream; |
|
import java.io.OutputStream; |
|
import java.io.IOException; |
|
import java.security.Provider; |
|
import java.security.AccessController; |
|
import java.security.AccessControlContext; |
|
import java.security.Key; |
|
import java.security.PrivilegedExceptionAction; |
|
import java.security.PrivilegedActionException; |
|
import javax.crypto.Cipher; |
|
import javax.security.auth.Subject; |
|
import javax.security.auth.kerberos.*; |
|
import sun.security.krb5.internal.Ticket; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class Krb5Context implements GSSContextSpi { |
|
|
|
/* |
|
* The different states that this context can be in. |
|
*/ |
|
|
|
private static final int STATE_NEW = 1; |
|
private static final int STATE_IN_PROCESS = 2; |
|
private static final int STATE_DONE = 3; |
|
private static final int STATE_DELETED = 4; |
|
|
|
private int state = STATE_NEW; |
|
|
|
public static final int SESSION_KEY = 0; |
|
public static final int INITIATOR_SUBKEY = 1; |
|
public static final int ACCEPTOR_SUBKEY = 2; |
|
|
|
/* |
|
* Optional features that the application can set and their default |
|
* values. |
|
*/ |
|
|
|
private boolean credDelegState = false; |
|
private boolean mutualAuthState = true; |
|
private boolean replayDetState = true; |
|
private boolean sequenceDetState = true; |
|
private boolean confState = true; |
|
private boolean integState = true; |
|
private boolean delegPolicyState = false; |
|
|
|
private boolean isConstrainedDelegationTried = false; |
|
|
|
private int mySeqNumber; |
|
private int peerSeqNumber; |
|
private int keySrc; |
|
private TokenTracker peerTokenTracker; |
|
|
|
private CipherHelper cipherHelper = null; |
|
|
|
/* |
|
* Separate locks for the sequence numbers allow the application to |
|
* receive tokens at the same time that it is sending tokens. Note |
|
* that the application must synchronize the generation and |
|
* transmission of tokens such that tokens are processed in the same |
|
* order that they are generated. This is important when sequence |
|
* checking of per-message tokens is enabled. |
|
*/ |
|
|
|
private Object mySeqNumberLock = new Object(); |
|
private Object peerSeqNumberLock = new Object(); |
|
|
|
private EncryptionKey key; |
|
private Krb5NameElement myName; |
|
private Krb5NameElement peerName; |
|
private int lifetime; |
|
private boolean initiator; |
|
private ChannelBinding channelBinding; |
|
|
|
private Krb5CredElement myCred; |
|
private Krb5CredElement delegatedCred; |
|
|
|
// XXX See if the required info from these can be extracted and |
|
|
|
private Credentials serviceCreds; |
|
private KrbApReq apReq; |
|
Ticket serviceTicket; |
|
final private GSSCaller caller; |
|
private static final boolean DEBUG = Krb5Util.DEBUG; |
|
|
|
|
|
|
|
|
|
*/ |
|
Krb5Context(GSSCaller caller, Krb5NameElement peerName, Krb5CredElement myCred, |
|
int lifetime) |
|
throws GSSException { |
|
|
|
if (peerName == null) |
|
throw new IllegalArgumentException("Cannot have null peer name"); |
|
|
|
this.caller = caller; |
|
this.peerName = peerName; |
|
this.myCred = myCred; |
|
this.lifetime = lifetime; |
|
this.initiator = true; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
Krb5Context(GSSCaller caller, Krb5CredElement myCred) |
|
throws GSSException { |
|
this.caller = caller; |
|
this.myCred = myCred; |
|
this.initiator = false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Krb5Context(GSSCaller caller, byte [] interProcessToken) |
|
throws GSSException { |
|
throw new GSSException(GSSException.UNAVAILABLE, |
|
-1, "GSS Import Context not available"); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean isTransferable() throws GSSException { |
|
return false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final int getLifetime() { |
|
|
|
return GSSContext.INDEFINITE_LIFETIME; |
|
} |
|
|
|
/* |
|
* Methods that may be invoked by the GSS framework in response |
|
* to an application request for setting/getting these |
|
* properties. |
|
* |
|
* These can only be called on the initiator side. |
|
* |
|
* Notice that an application can only request these |
|
* properties. The mechanism may or may not support them. The |
|
* application must make getXXX calls after context establishment |
|
* to see if the mechanism implementations on both sides support |
|
* these features. requestAnonymity is an exception where the |
|
* application will want to call getAnonymityState prior to sending any |
|
* GSS token during context establishment. |
|
* |
|
* Also note that the requests can only be placed before context |
|
* establishment starts. i.e. when state is STATE_NEW |
|
*/ |
|
|
|
|
|
|
|
|
|
*/ |
|
public void requestLifetime(int lifetime) throws GSSException { |
|
if (state == STATE_NEW && isInitiator()) |
|
this.lifetime = lifetime; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final void requestConf(boolean value) throws GSSException { |
|
if (state == STATE_NEW && isInitiator()) |
|
confState = value; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final boolean getConfState() { |
|
return confState; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final void requestInteg(boolean value) throws GSSException { |
|
if (state == STATE_NEW && isInitiator()) |
|
integState = value; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final boolean getIntegState() { |
|
return integState; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final void requestCredDeleg(boolean value) throws GSSException { |
|
if (state == STATE_NEW && isInitiator()) { |
|
if (myCred == null || !(myCred instanceof Krb5ProxyCredential)) { |
|
credDelegState = value; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final boolean getCredDelegState() { |
|
if (isInitiator()) { |
|
return credDelegState; |
|
} else { |
|
// Server side deleg state is not flagged by credDelegState. |
|
|
|
tryConstrainedDelegation(); |
|
return delegatedCred != null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final void requestMutualAuth(boolean value) throws GSSException { |
|
if (state == STATE_NEW && isInitiator()) { |
|
mutualAuthState = value; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean getMutualAuthState() { |
|
return mutualAuthState; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final void requestReplayDet(boolean value) throws GSSException { |
|
if (state == STATE_NEW && isInitiator()) |
|
replayDetState = value; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean getReplayDetState() { |
|
return replayDetState || sequenceDetState; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final void requestSequenceDet(boolean value) throws GSSException { |
|
if (state == STATE_NEW && isInitiator()) |
|
sequenceDetState = value; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean getSequenceDetState() { |
|
return sequenceDetState || replayDetState; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final void requestDelegPolicy(boolean value) { |
|
if (state == STATE_NEW && isInitiator()) |
|
delegPolicyState = value; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final boolean getDelegPolicyState() { |
|
return delegPolicyState; |
|
} |
|
|
|
/* |
|
* Anonymity is a little different in that after an application |
|
* requests anonymity it will want to know whether the mechanism |
|
* can support it or not, prior to sending any tokens across for |
|
* context establishment. Since this is from the initiator's |
|
* perspective, it essentially requests that the initiator be |
|
* anonymous. |
|
*/ |
|
|
|
public final void requestAnonymity(boolean value) throws GSSException { |
|
// Ignore silently. Application will check back with |
|
// getAnonymityState. |
|
} |
|
|
|
// RFC 2853 actually calls for this to be called after context |
|
// establishment to get the right answer, but that is |
|
// incorrect. The application may not want to send over any |
|
|
|
public final boolean getAnonymityState() { |
|
return false; |
|
} |
|
|
|
/* |
|
* Package private methods invoked by other Krb5 plugin classes. |
|
*/ |
|
|
|
|
|
|
|
|
|
*/ |
|
final CipherHelper getCipherHelper(EncryptionKey ckey) throws GSSException { |
|
EncryptionKey cipherKey = null; |
|
if (cipherHelper == null) { |
|
cipherKey = (getKey() == null) ? ckey: getKey(); |
|
cipherHelper = new CipherHelper(cipherKey); |
|
} |
|
return cipherHelper; |
|
} |
|
|
|
final int incrementMySequenceNumber() { |
|
int retVal; |
|
synchronized (mySeqNumberLock) { |
|
retVal = mySeqNumber; |
|
mySeqNumber = retVal + 1; |
|
} |
|
return retVal; |
|
} |
|
|
|
final void resetMySequenceNumber(int seqNumber) { |
|
if (DEBUG) { |
|
System.out.println("Krb5Context setting mySeqNumber to: " |
|
+ seqNumber); |
|
} |
|
synchronized (mySeqNumberLock) { |
|
mySeqNumber = seqNumber; |
|
} |
|
} |
|
|
|
final void resetPeerSequenceNumber(int seqNumber) { |
|
if (DEBUG) { |
|
System.out.println("Krb5Context setting peerSeqNumber to: " |
|
+ seqNumber); |
|
} |
|
synchronized (peerSeqNumberLock) { |
|
peerSeqNumber = seqNumber; |
|
peerTokenTracker = new TokenTracker(peerSeqNumber); |
|
} |
|
} |
|
|
|
final void setKey(int keySrc, EncryptionKey key) throws GSSException { |
|
this.key = key; |
|
this.keySrc = keySrc; |
|
// %%% to do: should clear old cipherHelper first |
|
cipherHelper = new CipherHelper(key); |
|
} |
|
|
|
public final int getKeySrc() { |
|
return keySrc; |
|
} |
|
|
|
private final EncryptionKey getKey() { |
|
return key; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
final void setDelegCred(Krb5CredElement delegatedCred) { |
|
this.delegatedCred = delegatedCred; |
|
} |
|
|
|
/* |
|
* While the application can only request the following features, |
|
* other classes in the package can call the actual set methods |
|
* for them. They are called as context establishment tokens are |
|
* received on an acceptor side and the context feature list that |
|
* the initiator wants becomes known. |
|
*/ |
|
|
|
|
|
|
|
|
|
*/ |
|
final void setCredDelegState(boolean state) { |
|
credDelegState = state; |
|
} |
|
|
|
final void setMutualAuthState(boolean state) { |
|
mutualAuthState = state; |
|
} |
|
|
|
final void setReplayDetState(boolean state) { |
|
replayDetState = state; |
|
} |
|
|
|
final void setSequenceDetState(boolean state) { |
|
sequenceDetState = state; |
|
} |
|
|
|
final void setConfState(boolean state) { |
|
confState = state; |
|
} |
|
|
|
final void setIntegState(boolean state) { |
|
integState = state; |
|
} |
|
|
|
final void setDelegPolicyState(boolean state) { |
|
delegPolicyState = state; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final void setChannelBinding(ChannelBinding channelBinding) |
|
throws GSSException { |
|
this.channelBinding = channelBinding; |
|
} |
|
|
|
final ChannelBinding getChannelBinding() { |
|
return channelBinding; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final Oid getMech() { |
|
return (Krb5MechFactory.GSS_KRB5_MECH_OID); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final GSSNameSpi getSrcName() throws GSSException { |
|
return (isInitiator()? myName : peerName); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final GSSNameSpi getTargName() throws GSSException { |
|
return (!isInitiator()? myName : peerName); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final GSSCredentialSpi getDelegCred() throws GSSException { |
|
if (state != STATE_IN_PROCESS && state != STATE_DONE) |
|
throw new GSSException(GSSException.NO_CONTEXT); |
|
if (isInitiator()) { |
|
throw new GSSException(GSSException.NO_CRED); |
|
} |
|
tryConstrainedDelegation(); |
|
if (delegatedCred == null) { |
|
throw new GSSException(GSSException.NO_CRED); |
|
} |
|
return delegatedCred; |
|
} |
|
|
|
private void tryConstrainedDelegation() { |
|
if (state != STATE_IN_PROCESS && state != STATE_DONE) { |
|
return; |
|
} |
|
|
|
if (!isConstrainedDelegationTried) { |
|
if (delegatedCred == null) { |
|
if (DEBUG) { |
|
System.out.println(">>> Constrained deleg from " + caller); |
|
} |
|
// The constrained delegation part. The acceptor needs to have |
|
// isInitiator=true in order to get a TGT, either earlier at |
|
|
|
try { |
|
delegatedCred = new Krb5ProxyCredential( |
|
Krb5InitCredential.getInstance( |
|
GSSCaller.CALLER_ACCEPT, myName, lifetime), |
|
peerName, serviceTicket); |
|
} catch (GSSException gsse) { |
|
// OK, delegatedCred is null then |
|
} |
|
} |
|
isConstrainedDelegationTried = true; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean isInitiator() { |
|
return initiator; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean isProtReady() { |
|
return (state == STATE_DONE); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final byte[] initSecContext(InputStream is, int mechTokenSize) |
|
throws GSSException { |
|
|
|
byte[] retVal = null; |
|
InitialToken token = null; |
|
int errorCode = GSSException.FAILURE; |
|
if (DEBUG) { |
|
System.out.println("Entered Krb5Context.initSecContext with " + |
|
"state=" + printState(state)); |
|
} |
|
if (!isInitiator()) { |
|
throw new GSSException(GSSException.FAILURE, -1, |
|
"initSecContext on an acceptor " + |
|
"GSSContext"); |
|
} |
|
|
|
try { |
|
if (state == STATE_NEW) { |
|
state = STATE_IN_PROCESS; |
|
|
|
errorCode = GSSException.NO_CRED; |
|
|
|
if (myCred == null) { |
|
myCred = Krb5InitCredential.getInstance(caller, myName, |
|
GSSCredential.DEFAULT_LIFETIME); |
|
myCred = Krb5ProxyCredential.tryImpersonation( |
|
caller, (Krb5InitCredential)myCred); |
|
} else if (!myCred.isInitiatorCredential()) { |
|
throw new GSSException(errorCode, -1, |
|
"No TGT available"); |
|
} |
|
myName = (Krb5NameElement) myCred.getName(); |
|
Credentials tgt; |
|
final Krb5ProxyCredential second; |
|
if (myCred instanceof Krb5InitCredential) { |
|
second = null; |
|
tgt = ((Krb5InitCredential) myCred).getKrb5Credentials(); |
|
} else { |
|
second = (Krb5ProxyCredential) myCred; |
|
tgt = second.self.getKrb5Credentials(); |
|
} |
|
|
|
checkPermission(peerName.getKrb5PrincipalName().getName(), |
|
"initiate"); |
|
/* |
|
* If useSubjectCredsonly is true then |
|
* we check whether we already have the ticket |
|
* for this service in the Subject and reuse it |
|
*/ |
|
|
|
final AccessControlContext acc = |
|
AccessController.getContext(); |
|
|
|
if (GSSUtil.useSubjectCredsOnly(caller)) { |
|
KerberosTicket kerbTicket = null; |
|
try { |
|
|
|
kerbTicket = AccessController.doPrivileged( |
|
new PrivilegedExceptionAction<KerberosTicket>() { |
|
public KerberosTicket run() throws Exception { |
|
// XXX to be cleaned |
|
// highly consider just calling: |
|
// Subject.getSubject |
|
// SubjectComber.find |
|
|
|
return Krb5Util.getServiceTicket( |
|
GSSCaller.CALLER_UNKNOWN, |
|
// since it's useSubjectCredsOnly here, |
|
|
|
second == null ? |
|
myName.getKrb5PrincipalName().getName(): |
|
second.getName().getKrb5PrincipalName().getName(), |
|
peerName.getKrb5PrincipalName().getName(), |
|
acc); |
|
}}); |
|
} catch (PrivilegedActionException e) { |
|
if (DEBUG) { |
|
System.out.println("Attempt to obtain service" |
|
+ " ticket from the subject failed!"); |
|
} |
|
} |
|
if (kerbTicket != null) { |
|
if (DEBUG) { |
|
System.out.println("Found service ticket in " + |
|
"the subject" + |
|
kerbTicket); |
|
} |
|
|
|
// convert Ticket to serviceCreds |
|
// XXX Should merge these two object types |
|
|
|
serviceCreds = Krb5Util.ticketToCreds(kerbTicket); |
|
} |
|
} |
|
if (serviceCreds == null) { |
|
// either we did not find the serviceCreds in the |
|
|
|
if (DEBUG) { |
|
System.out.println("Service ticket not found in " + |
|
"the subject"); |
|
} |
|
|
|
if (second == null) { |
|
serviceCreds = Credentials.acquireServiceCreds( |
|
peerName.getKrb5PrincipalName().getName(), |
|
tgt); |
|
} else { |
|
serviceCreds = Credentials.acquireS4U2proxyCreds( |
|
peerName.getKrb5PrincipalName().getName(), |
|
second.tkt, |
|
second.getName().getKrb5PrincipalName(), |
|
tgt); |
|
} |
|
if (GSSUtil.useSubjectCredsOnly(caller)) { |
|
final Subject subject = |
|
AccessController.doPrivileged( |
|
new java.security.PrivilegedAction<Subject>() { |
|
public Subject run() { |
|
return (Subject.getSubject(acc)); |
|
} |
|
}); |
|
if (subject != null && |
|
!subject.isReadOnly()) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final KerberosTicket kt = |
|
Krb5Util.credsToTicket(serviceCreds); |
|
AccessController.doPrivileged ( |
|
new java.security.PrivilegedAction<Void>() { |
|
public Void run() { |
|
subject.getPrivateCredentials().add(kt); |
|
return null; |
|
} |
|
}); |
|
} else { |
|
|
|
if (DEBUG) { |
|
System.out.println("Subject is " + |
|
"readOnly;Kerberos Service "+ |
|
"ticket not stored"); |
|
} |
|
} |
|
} |
|
} |
|
|
|
errorCode = GSSException.FAILURE; |
|
token = new InitSecContextToken(this, tgt, serviceCreds); |
|
apReq = ((InitSecContextToken)token).getKrbApReq(); |
|
retVal = token.encode(); |
|
myCred = null; |
|
if (!getMutualAuthState()) { |
|
state = STATE_DONE; |
|
} |
|
if (DEBUG) { |
|
System.out.println("Created InitSecContextToken:\n"+ |
|
new HexDumpEncoder().encodeBuffer(retVal)); |
|
} |
|
} else if (state == STATE_IN_PROCESS) { |
|
// No need to write anything; |
|
|
|
new AcceptSecContextToken(this, serviceCreds, apReq, is); |
|
serviceCreds = null; |
|
apReq = null; |
|
state = STATE_DONE; |
|
} else { |
|
|
|
if (DEBUG) { |
|
System.out.println(state); |
|
} |
|
} |
|
} catch (KrbException e) { |
|
if (DEBUG) { |
|
e.printStackTrace(); |
|
} |
|
GSSException gssException = |
|
new GSSException(errorCode, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} catch (IOException e) { |
|
GSSException gssException = |
|
new GSSException(errorCode, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
return retVal; |
|
} |
|
|
|
public final boolean isEstablished() { |
|
return (state == STATE_DONE); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final byte[] acceptSecContext(InputStream is, int mechTokenSize) |
|
throws GSSException { |
|
|
|
byte[] retVal = null; |
|
|
|
if (DEBUG) { |
|
System.out.println("Entered Krb5Context.acceptSecContext with " + |
|
"state=" + printState(state)); |
|
} |
|
|
|
if (isInitiator()) { |
|
throw new GSSException(GSSException.FAILURE, -1, |
|
"acceptSecContext on an initiator " + |
|
"GSSContext"); |
|
} |
|
try { |
|
if (state == STATE_NEW) { |
|
state = STATE_IN_PROCESS; |
|
if (myCred == null) { |
|
myCred = Krb5AcceptCredential.getInstance(caller, myName); |
|
} else if (!myCred.isAcceptorCredential()) { |
|
throw new GSSException(GSSException.NO_CRED, -1, |
|
"No Secret Key available"); |
|
} |
|
myName = (Krb5NameElement) myCred.getName(); |
|
|
|
|
|
if (myName != null) { |
|
Krb5MechFactory.checkAcceptCredPermission(myName, myName); |
|
} |
|
|
|
InitSecContextToken token = new InitSecContextToken(this, |
|
(Krb5AcceptCredential) myCred, is); |
|
PrincipalName clientName = token.getKrbApReq().getClient(); |
|
peerName = Krb5NameElement.getInstance(clientName); |
|
|
|
|
|
if (myName == null) { |
|
myName = Krb5NameElement.getInstance( |
|
token.getKrbApReq().getCreds().getServer()); |
|
Krb5MechFactory.checkAcceptCredPermission(myName, myName); |
|
} |
|
|
|
if (getMutualAuthState()) { |
|
retVal = new AcceptSecContextToken(this, |
|
token.getKrbApReq()).encode(); |
|
} |
|
serviceTicket = token.getKrbApReq().getCreds().getTicket(); |
|
myCred = null; |
|
state = STATE_DONE; |
|
} else { |
|
|
|
if (DEBUG) { |
|
System.out.println(state); |
|
} |
|
} |
|
} catch (KrbException e) { |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} catch (IOException e) { |
|
if (DEBUG) { |
|
e.printStackTrace(); |
|
} |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
|
|
return retVal; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final int getWrapSizeLimit(int qop, boolean confReq, |
|
int maxTokSize) throws GSSException { |
|
|
|
int retVal = 0; |
|
if (cipherHelper.getProto() == 0) { |
|
retVal = WrapToken.getSizeLimit(qop, confReq, maxTokSize, |
|
getCipherHelper(null)); |
|
} else if (cipherHelper.getProto() == 1) { |
|
retVal = WrapToken_v2.getSizeLimit(qop, confReq, maxTokSize, |
|
getCipherHelper(null)); |
|
} |
|
return retVal; |
|
} |
|
|
|
/* |
|
* Per-message calls depend on the sequence number. The sequence number |
|
* synchronization is at a finer granularity because wrap and getMIC |
|
* care about the local sequence number (mySeqNumber) where are unwrap |
|
* and verifyMIC care about the remote sequence number (peerSeqNumber). |
|
*/ |
|
|
|
public final byte[] wrap(byte inBuf[], int offset, int len, |
|
MessageProp msgProp) throws GSSException { |
|
if (DEBUG) { |
|
System.out.println("Krb5Context.wrap: data=[" |
|
+ getHexBytes(inBuf, offset, len) |
|
+ "]"); |
|
} |
|
|
|
if (state != STATE_DONE) |
|
throw new GSSException(GSSException.NO_CONTEXT, -1, |
|
"Wrap called in invalid state!"); |
|
|
|
byte[] encToken = null; |
|
try { |
|
if (cipherHelper.getProto() == 0) { |
|
WrapToken token = |
|
new WrapToken(this, msgProp, inBuf, offset, len); |
|
encToken = token.encode(); |
|
} else if (cipherHelper.getProto() == 1) { |
|
WrapToken_v2 token = |
|
new WrapToken_v2(this, msgProp, inBuf, offset, len); |
|
encToken = token.encode(); |
|
} |
|
if (DEBUG) { |
|
System.out.println("Krb5Context.wrap: token=[" |
|
+ getHexBytes(encToken, 0, encToken.length) |
|
+ "]"); |
|
} |
|
return encToken; |
|
} catch (IOException e) { |
|
encToken = null; |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
} |
|
|
|
public final int wrap(byte inBuf[], int inOffset, int len, |
|
byte[] outBuf, int outOffset, |
|
MessageProp msgProp) throws GSSException { |
|
|
|
if (state != STATE_DONE) |
|
throw new GSSException(GSSException.NO_CONTEXT, -1, |
|
"Wrap called in invalid state!"); |
|
|
|
int retVal = 0; |
|
try { |
|
if (cipherHelper.getProto() == 0) { |
|
WrapToken token = |
|
new WrapToken(this, msgProp, inBuf, inOffset, len); |
|
retVal = token.encode(outBuf, outOffset); |
|
} else if (cipherHelper.getProto() == 1) { |
|
WrapToken_v2 token = |
|
new WrapToken_v2(this, msgProp, inBuf, inOffset, len); |
|
retVal = token.encode(outBuf, outOffset); |
|
} |
|
if (DEBUG) { |
|
System.out.println("Krb5Context.wrap: token=[" |
|
+ getHexBytes(outBuf, outOffset, retVal) |
|
+ "]"); |
|
} |
|
return retVal; |
|
} catch (IOException e) { |
|
retVal = 0; |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
} |
|
|
|
public final void wrap(byte inBuf[], int offset, int len, |
|
OutputStream os, MessageProp msgProp) |
|
throws GSSException { |
|
|
|
if (state != STATE_DONE) |
|
throw new GSSException(GSSException.NO_CONTEXT, -1, |
|
"Wrap called in invalid state!"); |
|
|
|
byte[] encToken = null; |
|
try { |
|
if (cipherHelper.getProto() == 0) { |
|
WrapToken token = |
|
new WrapToken(this, msgProp, inBuf, offset, len); |
|
token.encode(os); |
|
if (DEBUG) { |
|
encToken = token.encode(); |
|
} |
|
} else if (cipherHelper.getProto() == 1) { |
|
WrapToken_v2 token = |
|
new WrapToken_v2(this, msgProp, inBuf, offset, len); |
|
token.encode(os); |
|
if (DEBUG) { |
|
encToken = token.encode(); |
|
} |
|
} |
|
} catch (IOException e) { |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
|
|
if (DEBUG) { |
|
System.out.println("Krb5Context.wrap: token=[" |
|
+ getHexBytes(encToken, 0, encToken.length) |
|
+ "]"); |
|
} |
|
} |
|
|
|
public final void wrap(InputStream is, OutputStream os, |
|
MessageProp msgProp) throws GSSException { |
|
|
|
byte[] data; |
|
try { |
|
data = new byte[is.available()]; |
|
is.read(data); |
|
} catch (IOException e) { |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
wrap(data, 0, data.length, os, msgProp); |
|
} |
|
|
|
public final byte[] unwrap(byte inBuf[], int offset, int len, |
|
MessageProp msgProp) |
|
throws GSSException { |
|
|
|
if (DEBUG) { |
|
System.out.println("Krb5Context.unwrap: token=[" |
|
+ getHexBytes(inBuf, offset, len) |
|
+ "]"); |
|
} |
|
|
|
if (state != STATE_DONE) { |
|
throw new GSSException(GSSException.NO_CONTEXT, -1, |
|
" Unwrap called in invalid state!"); |
|
} |
|
|
|
byte[] data = null; |
|
if (cipherHelper.getProto() == 0) { |
|
WrapToken token = |
|
new WrapToken(this, inBuf, offset, len, msgProp); |
|
data = token.getData(); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} else if (cipherHelper.getProto() == 1) { |
|
WrapToken_v2 token = |
|
new WrapToken_v2(this, inBuf, offset, len, msgProp); |
|
data = token.getData(); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} |
|
|
|
if (DEBUG) { |
|
System.out.println("Krb5Context.unwrap: data=[" |
|
+ getHexBytes(data, 0, data.length) |
|
+ "]"); |
|
} |
|
|
|
return data; |
|
} |
|
|
|
public final int unwrap(byte inBuf[], int inOffset, int len, |
|
byte[] outBuf, int outOffset, |
|
MessageProp msgProp) throws GSSException { |
|
|
|
if (state != STATE_DONE) |
|
throw new GSSException(GSSException.NO_CONTEXT, -1, |
|
"Unwrap called in invalid state!"); |
|
|
|
if (cipherHelper.getProto() == 0) { |
|
WrapToken token = |
|
new WrapToken(this, inBuf, inOffset, len, msgProp); |
|
len = token.getData(outBuf, outOffset); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} else if (cipherHelper.getProto() == 1) { |
|
WrapToken_v2 token = |
|
new WrapToken_v2(this, inBuf, inOffset, len, msgProp); |
|
len = token.getData(outBuf, outOffset); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} |
|
return len; |
|
} |
|
|
|
public final int unwrap(InputStream is, |
|
byte[] outBuf, int outOffset, |
|
MessageProp msgProp) throws GSSException { |
|
|
|
if (state != STATE_DONE) |
|
throw new GSSException(GSSException.NO_CONTEXT, -1, |
|
"Unwrap called in invalid state!"); |
|
|
|
int len = 0; |
|
if (cipherHelper.getProto() == 0) { |
|
WrapToken token = new WrapToken(this, is, msgProp); |
|
len = token.getData(outBuf, outOffset); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} else if (cipherHelper.getProto() == 1) { |
|
WrapToken_v2 token = new WrapToken_v2(this, is, msgProp); |
|
len = token.getData(outBuf, outOffset); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} |
|
return len; |
|
} |
|
|
|
|
|
public final void unwrap(InputStream is, OutputStream os, |
|
MessageProp msgProp) throws GSSException { |
|
|
|
if (state != STATE_DONE) |
|
throw new GSSException(GSSException.NO_CONTEXT, -1, |
|
"Unwrap called in invalid state!"); |
|
|
|
byte[] data = null; |
|
if (cipherHelper.getProto() == 0) { |
|
WrapToken token = new WrapToken(this, is, msgProp); |
|
data = token.getData(); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} else if (cipherHelper.getProto() == 1) { |
|
WrapToken_v2 token = new WrapToken_v2(this, is, msgProp); |
|
data = token.getData(); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} |
|
|
|
try { |
|
os.write(data); |
|
} catch (IOException e) { |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
} |
|
|
|
public final byte[] getMIC(byte []inMsg, int offset, int len, |
|
MessageProp msgProp) |
|
throws GSSException { |
|
|
|
byte[] micToken = null; |
|
try { |
|
if (cipherHelper.getProto() == 0) { |
|
MicToken token = |
|
new MicToken(this, msgProp, inMsg, offset, len); |
|
micToken = token.encode(); |
|
} else if (cipherHelper.getProto() == 1) { |
|
MicToken_v2 token = |
|
new MicToken_v2(this, msgProp, inMsg, offset, len); |
|
micToken = token.encode(); |
|
} |
|
return micToken; |
|
} catch (IOException e) { |
|
micToken = null; |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
} |
|
|
|
private int getMIC(byte []inMsg, int offset, int len, |
|
byte[] outBuf, int outOffset, |
|
MessageProp msgProp) |
|
throws GSSException { |
|
|
|
int retVal = 0; |
|
try { |
|
if (cipherHelper.getProto() == 0) { |
|
MicToken token = |
|
new MicToken(this, msgProp, inMsg, offset, len); |
|
retVal = token.encode(outBuf, outOffset); |
|
} else if (cipherHelper.getProto() == 1) { |
|
MicToken_v2 token = |
|
new MicToken_v2(this, msgProp, inMsg, offset, len); |
|
retVal = token.encode(outBuf, outOffset); |
|
} |
|
return retVal; |
|
} catch (IOException e) { |
|
retVal = 0; |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
} |
|
|
|
/* |
|
* Checksum calculation requires a byte[]. Hence might as well pass |
|
* a byte[] into the MicToken constructor. However, writing the |
|
* token can be optimized for cases where the application passed in |
|
* an OutputStream. |
|
*/ |
|
|
|
private void getMIC(byte[] inMsg, int offset, int len, |
|
OutputStream os, MessageProp msgProp) |
|
throws GSSException { |
|
|
|
try { |
|
if (cipherHelper.getProto() == 0) { |
|
MicToken token = |
|
new MicToken(this, msgProp, inMsg, offset, len); |
|
token.encode(os); |
|
} else if (cipherHelper.getProto() == 1) { |
|
MicToken_v2 token = |
|
new MicToken_v2(this, msgProp, inMsg, offset, len); |
|
token.encode(os); |
|
} |
|
} catch (IOException e) { |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
} |
|
|
|
public final void getMIC(InputStream is, OutputStream os, |
|
MessageProp msgProp) throws GSSException { |
|
byte[] data; |
|
try { |
|
data = new byte[is.available()]; |
|
is.read(data); |
|
} catch (IOException e) { |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
getMIC(data, 0, data.length, os, msgProp); |
|
} |
|
|
|
public final void verifyMIC(byte []inTok, int tokOffset, int tokLen, |
|
byte[] inMsg, int msgOffset, int msgLen, |
|
MessageProp msgProp) |
|
throws GSSException { |
|
|
|
if (cipherHelper.getProto() == 0) { |
|
MicToken token = |
|
new MicToken(this, inTok, tokOffset, tokLen, msgProp); |
|
token.verify(inMsg, msgOffset, msgLen); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} else if (cipherHelper.getProto() == 1) { |
|
MicToken_v2 token = |
|
new MicToken_v2(this, inTok, tokOffset, tokLen, msgProp); |
|
token.verify(inMsg, msgOffset, msgLen); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} |
|
} |
|
|
|
private void verifyMIC(InputStream is, |
|
byte[] inMsg, int msgOffset, int msgLen, |
|
MessageProp msgProp) |
|
throws GSSException { |
|
|
|
if (cipherHelper.getProto() == 0) { |
|
MicToken token = new MicToken(this, is, msgProp); |
|
token.verify(inMsg, msgOffset, msgLen); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} else if (cipherHelper.getProto() == 1) { |
|
MicToken_v2 token = new MicToken_v2(this, is, msgProp); |
|
token.verify(inMsg, msgOffset, msgLen); |
|
setSequencingAndReplayProps(token, msgProp); |
|
} |
|
} |
|
|
|
public final void verifyMIC(InputStream is, InputStream msgStr, |
|
MessageProp mProp) throws GSSException { |
|
byte[] msg; |
|
try { |
|
msg = new byte[msgStr.available()]; |
|
msgStr.read(msg); |
|
} catch (IOException e) { |
|
GSSException gssException = |
|
new GSSException(GSSException.FAILURE, -1, e.getMessage()); |
|
gssException.initCause(e); |
|
throw gssException; |
|
} |
|
verifyMIC(is, msg, 0, msg.length, mProp); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final byte [] export() throws GSSException { |
|
throw new GSSException(GSSException.UNAVAILABLE, -1, |
|
"GSS Export Context not available"); |
|
} |
|
|
|
/** |
|
* Releases context resources and terminates the |
|
* context between 2 peer. |
|
* |
|
* @exception GSSException with major codes NO_CONTEXT, FAILURE. |
|
*/ |
|
|
|
public final void dispose() throws GSSException { |
|
state = STATE_DELETED; |
|
delegatedCred = null; |
|
} |
|
|
|
public final Provider getProvider() { |
|
return Krb5MechFactory.PROVIDER; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void setSequencingAndReplayProps(MessageToken token, |
|
MessageProp prop) { |
|
if (replayDetState || sequenceDetState) { |
|
int seqNum = token.getSequenceNumber(); |
|
peerTokenTracker.getProps(seqNum, prop); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void setSequencingAndReplayProps(MessageToken_v2 token, |
|
MessageProp prop) { |
|
if (replayDetState || sequenceDetState) { |
|
int seqNum = token.getSequenceNumber(); |
|
peerTokenTracker.getProps(seqNum, prop); |
|
} |
|
} |
|
|
|
private void checkPermission(String principal, String action) { |
|
SecurityManager sm = System.getSecurityManager(); |
|
if (sm != null) { |
|
ServicePermission perm = |
|
new ServicePermission(principal, action); |
|
sm.checkPermission(perm); |
|
} |
|
} |
|
|
|
private static String getHexBytes(byte[] bytes, int pos, int len) { |
|
|
|
StringBuffer sb = new StringBuffer(); |
|
for (int i = 0; i < len; i++) { |
|
|
|
int b1 = (bytes[i]>>4) & 0x0f; |
|
int b2 = bytes[i] & 0x0f; |
|
|
|
sb.append(Integer.toHexString(b1)); |
|
sb.append(Integer.toHexString(b2)); |
|
sb.append(' '); |
|
} |
|
return sb.toString(); |
|
} |
|
|
|
private static String printState(int state) { |
|
switch (state) { |
|
case STATE_NEW: |
|
return ("STATE_NEW"); |
|
case STATE_IN_PROCESS: |
|
return ("STATE_IN_PROCESS"); |
|
case STATE_DONE: |
|
return ("STATE_DONE"); |
|
case STATE_DELETED: |
|
return ("STATE_DELETED"); |
|
default: |
|
return ("Unknown state " + state); |
|
} |
|
} |
|
|
|
GSSCaller getCaller() { |
|
|
|
return caller; |
|
} |
|
|
|
|
|
|
|
*/ |
|
static class KerberosSessionKey implements Key { |
|
private static final long serialVersionUID = 699307378954123869L; |
|
|
|
private final EncryptionKey key; |
|
|
|
KerberosSessionKey(EncryptionKey key) { |
|
this.key = key; |
|
} |
|
|
|
@Override |
|
public String getAlgorithm() { |
|
return Integer.toString(key.getEType()); |
|
} |
|
|
|
@Override |
|
public String getFormat() { |
|
return "RAW"; |
|
} |
|
|
|
@Override |
|
public byte[] getEncoded() { |
|
return key.getBytes().clone(); |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return "Kerberos session key: etype: " + key.getEType() + "\n" + |
|
new sun.misc.HexDumpEncoder().encodeBuffer(key.getBytes()); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Object inquireSecContext(InquireType type) |
|
throws GSSException { |
|
if (!isEstablished()) { |
|
throw new GSSException(GSSException.NO_CONTEXT, -1, |
|
"Security context not established."); |
|
} |
|
switch (type) { |
|
case KRB5_GET_SESSION_KEY: |
|
return new KerberosSessionKey(key); |
|
case KRB5_GET_TKT_FLAGS: |
|
return tktFlags.clone(); |
|
case KRB5_GET_AUTHZ_DATA: |
|
if (isInitiator()) { |
|
throw new GSSException(GSSException.UNAVAILABLE, -1, |
|
"AuthzData not available on initiator side."); |
|
} else { |
|
return (authzData==null)?null:authzData.clone(); |
|
} |
|
case KRB5_GET_AUTHTIME: |
|
return authTime; |
|
} |
|
throw new GSSException(GSSException.UNAVAILABLE, -1, |
|
"Inquire type not supported."); |
|
} |
|
|
|
|
|
private boolean[] tktFlags; |
|
private String authTime; |
|
private com.sun.security.jgss.AuthorizationDataEntry[] authzData; |
|
|
|
public void setTktFlags(boolean[] tktFlags) { |
|
this.tktFlags = tktFlags; |
|
} |
|
|
|
public void setAuthTime(String authTime) { |
|
this.authTime = authTime; |
|
} |
|
|
|
public void setAuthzData(com.sun.security.jgss.AuthorizationDataEntry[] authzData) { |
|
this.authzData = authzData; |
|
} |
|
} |