/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.jgss.krb5;
import org.ietf.jgss.*;
import sun.security.util.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.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.ServicePermission;
import javax.security.auth.kerberos.KerberosCredMessage;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket;
import sun.security.krb5.internal.Ticket;
import sun.security.krb5.internal.AuthorizationData;
/**
* Implements the mechanism specific context class for the Kerberos v5
* GSS-API mechanism.
*
* @author Mayank Upadhyay
* @author Ram Marti
* @since 1.4
*/
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; // now only useful at client
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; // Set only on acceptor side
// XXX See if the required info from these can be extracted and
// stored elsewhere
private Credentials tgt;
private Credentials serviceCreds;
private KrbApReq apReq;
Ticket serviceTicket;
final private GSSCaller caller;
private static final boolean DEBUG = Krb5Util.DEBUG;
/**
* Constructor for Krb5Context to be called on the context initiator's
* side.
*/
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;
}
/**
* Constructor for Krb5Context to be called on the context acceptor's
* side.
*/
Krb5Context(GSSCaller caller, Krb5CredElement myCred)
throws GSSException {
this.caller = caller;
this.myCred = myCred;
this.initiator = false;
}
/**
* Constructor for Krb5Context to import a previously exported context.
*/
public Krb5Context(GSSCaller caller, byte[] interProcessToken)
throws GSSException {
throw new GSSException(GSSException.UNAVAILABLE,
-1, "GSS Import Context not available");
}
/**
* Method to determine if the context can be exported and then
* re-imported.
*/
public final boolean isTransferable() throws GSSException {
return false;
}
/**
* The lifetime remaining for this context.
*/
public final int getLifetime() {
// XXX Return service ticket lifetime
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
*/
/**
* Requests the desired lifetime. Can only be used on the context
* initiator's side.
*/
public void requestLifetime(int lifetime) throws GSSException {
if (state == STATE_NEW && isInitiator())
this.lifetime = lifetime;
}
/**
* Requests that confidentiality be available.
*/
public final void requestConf(boolean value) throws GSSException {
if (state == STATE_NEW && isInitiator())
confState = value;
}
/**
* Is confidentiality available?
*/
public final boolean getConfState() {
return confState;
}
/**
* Requests that integrity be available.
*/
public final void requestInteg(boolean value) throws GSSException {
if (state == STATE_NEW && isInitiator())
integState = value;
}
/**
* Is integrity available?
*/
public final boolean getIntegState() {
return integState;
}
/**
* Requests that credential delegation be done during context
* establishment.
*/
public final void requestCredDeleg(boolean value) throws GSSException {
if (state == STATE_NEW && isInitiator()) {
if (myCred == null || !(myCred instanceof Krb5ProxyCredential)) {
credDelegState = value;
}
}
}
/**
* Is credential delegation enabled?
*/
public final boolean getCredDelegState() {
if (isInitiator()) {
return credDelegState;
} else {
// Server side deleg state is not flagged by credDelegState.
// It can use constrained delegation.
tryConstrainedDelegation();
return delegatedCred != null;
}
}
/**
* Requests that mutual authentication be done during context
* establishment. Since this is fromm the client's perspective, it
* essentially requests that the server be authenticated.
*/
public final void requestMutualAuth(boolean value) throws GSSException {
if (state == STATE_NEW && isInitiator()) {
mutualAuthState = value;
}
}
/**
* Is mutual authentication enabled? Since this is from the client's
* perspective, it essentially meas that the server is being
* authenticated.
*/
public final boolean getMutualAuthState() {
return mutualAuthState;
}
/**
* Requests that replay detection be done on the GSS wrap and MIC
* tokens.
*/
public final void requestReplayDet(boolean value) throws GSSException {
if (state == STATE_NEW && isInitiator())
replayDetState = value;
}
/**
* Is replay detection enabled on the GSS wrap and MIC tokens?
* We enable replay detection if sequence checking is enabled.
*/
public final boolean getReplayDetState() {
return replayDetState || sequenceDetState;
}
/**
* Requests that sequence checking be done on the GSS wrap and MIC
* tokens.
*/
public final void requestSequenceDet(boolean value) throws GSSException {
if (state == STATE_NEW && isInitiator())
sequenceDetState = value;
}
/**
* Is sequence checking enabled on the GSS Wrap and MIC tokens?
* We enable sequence checking if replay detection is enabled.
*/
public final boolean getSequenceDetState() {
return sequenceDetState || replayDetState;
}
/**
* Requests that the deleg policy be respected.
*/
public final void requestDelegPolicy(boolean value) {
if (state == STATE_NEW && isInitiator())
delegPolicyState = value;
}
/**
* Is deleg policy respected?
*/
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
// tokens if anonymity is not available.
public final boolean getAnonymityState() {
return false;
}
/*
* Package private methods invoked by other Krb5 plugin classes.
*/
/**
* Get the context specific DESCipher instance, invoked in
* MessageToken.init()
*/
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); // Need to use new key
}
public final int getKeySrc() {
return keySrc;
}
private final EncryptionKey getKey() {
return key;
}
/**
* Called on the acceptor side to store the delegated credentials
* received in the AcceptSecContextToken.
*/
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.
*/
/*
* This method is also called by InitialToken.OverloadedChecksum if the
* TGT is not forwardable and the user requested delegation.
*/
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;
}
/**
* Sets the channel bindings to be used during context
* establishment.
*/
public final void setChannelBinding(ChannelBinding channelBinding)
throws GSSException {
this.channelBinding = channelBinding;
}
final ChannelBinding getChannelBinding() {
return channelBinding;
}
/**
* Returns the mechanism oid.
*
* @return the Oid of this context
*/
public final Oid getMech() {
return (Krb5MechFactory.GSS_KRB5_MECH_OID);
}
/**
* Returns the context initiator name.
*
* @return initiator name
* @exception GSSException
*/
public final GSSNameSpi getSrcName() throws GSSException {
return (isInitiator()? myName : peerName);
}
/**
* Returns the context acceptor.
*
* @return context acceptor(target) name
* @exception GSSException
*/
public final GSSNameSpi getTargName() throws GSSException {
return (!isInitiator()? myName : peerName);
}
/**
* Returns the delegated credential for the context. This
* is an optional feature of contexts which not all
* mechanisms will support. A context can be requested to
* support credential delegation by using the <b>CRED_DELEG</b>,
* or it can request for a constrained delegation.
* This is only valid on the acceptor side of the context.
* @return GSSCredentialSpi object for the delegated credential
* @exception GSSException
* @see GSSContext#getDelegCredState
*/
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;
}
// We will only try constrained delegation once (if necessary).
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
// logon stage, if useSubjectCredsOnly, or now.
try {
delegatedCred = new Krb5ProxyCredential(
Krb5InitCredential.getInstance(
GSSCaller.CALLER_ACCEPT, myName, lifetime),
peerName, serviceTicket);
} catch (GSSException gsse) {
// OK, delegatedCred is null then
}
}
isConstrainedDelegationTried = true;
}
}
/**
* Tests if this is the initiator side of the context.
*
* @return boolean indicating if this is initiator (true)
* or target (false)
*/
public final boolean isInitiator() {
return initiator;
}
/**
* Tests if the context can be used for per-message service.
* Context may allow the calls to the per-message service
* functions before being fully established.
*
* @return boolean indicating if per-message methods can
* be called.
*/
public final boolean isProtReady() {
return (state == STATE_DONE);
}
/**
* Initiator context establishment call. This method may be
* required to be called several times. A CONTINUE_NEEDED return
* call indicates that more calls are needed after the next token
* is received from the peer.
*
* @param is contains the token received from the peer. On the
* first call it will be ignored.
* @return any token required to be sent to the peer
* It is responsibility of the caller
* to send the token to its peer for processing.
* @exception GSSException
*/
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();
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 {
// get service ticket from caller's subject
kerbTicket = AccessController.doPrivileged(
new PrivilegedExceptionAction<KerberosTicket>() {
public KerberosTicket run() throws Exception {
// XXX to be cleaned
// highly consider just calling:
// Subject.getSubject
// SubjectComber.find
// instead of Krb5Util.getServiceTicket
return Krb5Util.getServiceTicket(
GSSCaller.CALLER_UNKNOWN,
// since it's useSubjectCredsOnly here,
// don't worry about the null
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
// avoid converting back and forth
serviceCreds = Krb5Util.ticketToCreds(kerbTicket);
}
}
if (serviceCreds == null) {
// either we did not find the serviceCreds in the
// Subject or useSubjectCreds is false
if (DEBUG) {
System.out.println("Service ticket not found in " +
"the subject");
}
// Get Service ticket using the Kerberos protocols
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()) {
/*
* Store the service credentials as
* javax.security.auth.kerberos.KerberosTicket in
* the Subject. We could wait until the context is
* successfully established; however it is easier
* to do it here and there is no harm.
*/
final KerberosTicket kt =
Krb5Util.credsToTicket(serviceCreds);
AccessController.doPrivileged (
new java.security.PrivilegedAction<Void>() {
public Void run() {
subject.getPrivateCredentials().add(kt);
return null;
}
});
} else {
// log it for debugging purpose
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;
// just validate the incoming token
new AcceptSecContextToken(this, serviceCreds, apReq, is);
apReq = null;
state = STATE_DONE;
} else {
// XXX Use logging API?
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);
}
/**
* Acceptor's context establishment call. This method may be
* required to be called several times. A CONTINUE_NEEDED return
* call indicates that more calls are needed after the next token
* is received from the peer.
*
* @param is contains the token received from the peer.
* @return any token required to be sent to the peer
* It is responsibility of the caller
* to send the token to its peer for processing.
* @exception GSSException
*/
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 there is already a bound name, check now
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 unbound, check after the bound name is found
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 {
// XXX Use logging API?
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;
}
/**
* Queries the context for largest data size to accommodate
* the specified protection and be <= maxTokSize.
*
* @param qop the quality of protection that the context will be
* asked to provide.
* @param confReq a flag indicating whether confidentiality will be
* requested or not
* @param outputSize the maximum size of the output token
* @return the maximum size for the input message that can be
* provided to the wrap() method in order to guarantee that these
* requirements are met.
* @throws GSSException
*/
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);
}
/**
* Produces a token representing this context. After this call
* the context will no longer be usable until an import is
* performed on the returned token.
*
* @param os the output token will be written to this stream
* @exception GSSException
*/
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;
tgt = null;
serviceCreds = null;
key = null;
}
public final Provider getProvider() {
return Krb5MechFactory.PROVIDER;
}
/**
* Sets replay and sequencing information for a message token received
* form the peer.
*/
private void setSequencingAndReplayProps(MessageToken token,
MessageProp prop) {
/**代码未完, 请加载全部代码(NowJava.com).**/