/*
 * Copyright (c) 2000, 2013, 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, Ram Marti
Since:1.4
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * Requests that confidentiality be available. */
public final void requestConf(boolean value) throws GSSException { if (state == STATE_NEW && isInitiator()) confState = value; }
Is confidentiality available?
/** * Is confidentiality available? */
public final boolean getConfState() { return confState; }
Requests that integrity be available.
/** * Requests that integrity be available. */
public final void requestInteg(boolean value) throws GSSException { if (state == STATE_NEW && isInitiator()) integState = value; }
Is integrity available?
/** * Is integrity available? */
public final boolean getIntegState() { return integState; }
Requests that credential delegation be done during context establishment.
/** * 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?
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * Requests that the deleg policy be respected. */
public final void requestDelegPolicy(boolean value) { if (state == STATE_NEW && isInitiator()) delegPolicyState = value; }
Is deleg policy respected?
/** * 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()
/** * 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.
/** * 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.
/** * 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.
Returns:the Oid of this context
/** * 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.
Throws:
Returns:initiator name
/** * Returns the context initiator name. * * @return initiator name * @exception GSSException */
public final GSSNameSpi getSrcName() throws GSSException { return (isInitiator()? myName : peerName); }
Returns the context acceptor.
Throws:
Returns:context acceptor(target) name
/** * 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 CRED_DELEG, or it can request for a constrained delegation. This is only valid on the acceptor side of the context.
Throws:
See Also:
  • GSSContext.getDelegCredState
Returns:GSSCredentialSpi object for the delegated credential
/** * 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.
Returns:boolean indicating if this is initiator (true) or target (false)
/** * 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.
Returns:boolean indicating if per-message methods can be called.
/** * 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.
Params:
  • is – contains the token received from the peer. On the first call it will be ignored.
Throws:
Returns:any token required to be sent to the peer It is responsibility of the caller to send the token to its peer for processing.
/** * 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); } 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.getTicket return Krb5Util.getTicket( 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 till the context is * succesfully established; however it is easier * to do here and there is no harm indoing it here. */ 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.
Params:
  • is – contains the token received from the peer.
Throws:
Returns:any token required to be sent to the peer It is responsibility of the caller to send the token to its peer for processing.
/** * 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.
Params:
  • qop – the quality of protection that the context will be asked to provide.
  • confReq – a flag indicating whether confidentiality will be requested or not
  • outputSize – the maximum size of the output token
Throws:
Returns:the maximum size for the input message that can be provided to the wrap() method in order to guarantee that these requirements are met.
/** * 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.
Params:
  • os – the output token will be written to this stream
Throws:
/** * 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.
Throws:
  • GSSException – with major codes NO_CONTEXT, FAILURE.
/** * 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.
/** * Sets replay and sequencing information for a message token received * form the peer. */
private void setSequencingAndReplayProps(MessageToken token, MessageProp prop) { if (replayDetState || sequenceDetState) { int seqNum = token.getSequenceNumber(); peerTokenTracker.getProps(seqNum, prop); } }
Sets replay and sequencing information for a message token received form the peer.
/** * Sets replay and sequencing information for a message token received * form the peer. */
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) { StringBuilder sb = new StringBuilder(); 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() { // Currently used by InitialToken only return caller; }
The session key returned by inquireSecContext(KRB5_INQ_SSPI_SESSION_KEY)
/** * The session key returned by inquireSecContext(KRB5_INQ_SSPI_SESSION_KEY) */
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 HexDumpEncoder().encodeBuffer(key.getBytes()); } }
Return the mechanism-specific attribute associated with type.
/** * Return the mechanism-specific attribute associated with {@code type}. */
public Object inquireSecContext(String 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_SESSION_KEY_EX": return new javax.security.auth.kerberos.EncryptionKey( key.getBytes(), key.getEType()); 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; } case "KRB5_GET_AUTHTIME": return authTime; case "KRB5_GET_KRB_CRED": if (!isInitiator()) { throw new GSSException(GSSException.UNAVAILABLE, -1, "KRB_CRED not available on acceptor side."); } KerberosPrincipal sender = new KerberosPrincipal( myName.getKrb5PrincipalName().getName()); KerberosPrincipal recipient = new KerberosPrincipal( peerName.getKrb5PrincipalName().getName()); try { byte[] krbCred = new KrbCred(tgt, serviceCreds, key) .getMessage(); return new KerberosCredMessage( sender, recipient, krbCred); } catch (KrbException | IOException e) { GSSException gsse = new GSSException(GSSException.UNAVAILABLE, -1, "KRB_CRED not generated correctly."); gsse.initCause(e); throw gsse; } } throw new GSSException(GSSException.UNAVAILABLE, -1, "Inquire type not supported."); } // Helpers for inquireSecContext private boolean[] tktFlags; private String authTime; private AuthorizationData authzData; public void setTktFlags(boolean[] tktFlags) { this.tktFlags = tktFlags; } public void setAuthTime(String authTime) { this.authTime = authTime; } public void setAuthzData(AuthorizationData authzData) { this.authzData = authzData; } }