Path: blob/master/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java
41161 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.jgss.krb5;2627import org.ietf.jgss.*;28import sun.security.util.HexDumpEncoder;29import sun.security.jgss.GSSUtil;30import sun.security.jgss.GSSCaller;31import sun.security.jgss.spi.*;32import sun.security.jgss.TokenTracker;33import sun.security.krb5.*;34import java.io.InputStream;35import java.io.OutputStream;36import java.io.IOException;37import java.security.Provider;38import java.security.AccessController;39import java.security.AccessControlContext;40import java.security.Key;41import java.security.PrivilegedActionException;42import java.security.PrivilegedExceptionAction;43import javax.security.auth.Subject;44import javax.security.auth.kerberos.ServicePermission;45import javax.security.auth.kerberos.KerberosCredMessage;46import javax.security.auth.kerberos.KerberosPrincipal;47import javax.security.auth.kerberos.KerberosTicket;48import sun.security.krb5.internal.Ticket;49import sun.security.krb5.internal.AuthorizationData;5051/**52* Implements the mechanism specific context class for the Kerberos v553* GSS-API mechanism.54*55* @author Mayank Upadhyay56* @author Ram Marti57* @since 1.458*/59class Krb5Context implements GSSContextSpi {6061/*62* The different states that this context can be in.63*/6465private static final int STATE_NEW = 1;66private static final int STATE_IN_PROCESS = 2;67private static final int STATE_DONE = 3;68private static final int STATE_DELETED = 4;6970private int state = STATE_NEW;7172public static final int SESSION_KEY = 0;73public static final int INITIATOR_SUBKEY = 1;74public static final int ACCEPTOR_SUBKEY = 2;7576/*77* Optional features that the application can set and their default78* values.79*/8081private boolean credDelegState = false; // now only useful at client82private boolean mutualAuthState = true;83private boolean replayDetState = true;84private boolean sequenceDetState = true;85private boolean confState = true;86private boolean integState = true;87private boolean delegPolicyState = false;8889private boolean isConstrainedDelegationTried = false;9091private int mySeqNumber;92private int peerSeqNumber;93private int keySrc;94private TokenTracker peerTokenTracker;9596private CipherHelper cipherHelper = null;9798/*99* Separate locks for the sequence numbers allow the application to100* receive tokens at the same time that it is sending tokens. Note101* that the application must synchronize the generation and102* transmission of tokens such that tokens are processed in the same103* order that they are generated. This is important when sequence104* checking of per-message tokens is enabled.105*/106107private Object mySeqNumberLock = new Object();108private Object peerSeqNumberLock = new Object();109110private EncryptionKey key;111private Krb5NameElement myName;112private Krb5NameElement peerName;113private int lifetime;114private boolean initiator;115private ChannelBinding channelBinding;116117private Krb5CredElement myCred;118private Krb5CredElement delegatedCred; // Set only on acceptor side119120// XXX See if the required info from these can be extracted and121// stored elsewhere122private Credentials tgt;123private Credentials serviceCreds;124private KrbApReq apReq;125Ticket serviceTicket;126private final GSSCaller caller;127private static final boolean DEBUG = Krb5Util.DEBUG;128129/**130* Constructor for Krb5Context to be called on the context initiator's131* side.132*/133Krb5Context(GSSCaller caller, Krb5NameElement peerName, Krb5CredElement myCred,134int lifetime)135throws GSSException {136137if (peerName == null)138throw new IllegalArgumentException("Cannot have null peer name");139140this.caller = caller;141this.peerName = peerName;142this.myCred = myCred;143this.lifetime = lifetime;144this.initiator = true;145}146147/**148* Constructor for Krb5Context to be called on the context acceptor's149* side.150*/151Krb5Context(GSSCaller caller, Krb5CredElement myCred)152throws GSSException {153this.caller = caller;154this.myCred = myCred;155this.initiator = false;156}157158/**159* Constructor for Krb5Context to import a previously exported context.160*/161public Krb5Context(GSSCaller caller, byte[] interProcessToken)162throws GSSException {163throw new GSSException(GSSException.UNAVAILABLE,164-1, "GSS Import Context not available");165}166167/**168* Method to determine if the context can be exported and then169* re-imported.170*/171public final boolean isTransferable() throws GSSException {172return false;173}174175/**176* The lifetime remaining for this context.177*/178public final int getLifetime() {179// XXX Return service ticket lifetime180return GSSContext.INDEFINITE_LIFETIME;181}182183/*184* Methods that may be invoked by the GSS framework in response185* to an application request for setting/getting these186* properties.187*188* These can only be called on the initiator side.189*190* Notice that an application can only request these191* properties. The mechanism may or may not support them. The192* application must make getXXX calls after context establishment193* to see if the mechanism implementations on both sides support194* these features. requestAnonymity is an exception where the195* application will want to call getAnonymityState prior to sending any196* GSS token during context establishment.197*198* Also note that the requests can only be placed before context199* establishment starts. i.e. when state is STATE_NEW200*/201202/**203* Requests the desired lifetime. Can only be used on the context204* initiator's side.205*/206public void requestLifetime(int lifetime) throws GSSException {207if (state == STATE_NEW && isInitiator())208this.lifetime = lifetime;209}210211/**212* Requests that confidentiality be available.213*/214public final void requestConf(boolean value) throws GSSException {215if (state == STATE_NEW && isInitiator())216confState = value;217}218219/**220* Is confidentiality available?221*/222public final boolean getConfState() {223return confState;224}225226/**227* Requests that integrity be available.228*/229public final void requestInteg(boolean value) throws GSSException {230if (state == STATE_NEW && isInitiator())231integState = value;232}233234/**235* Is integrity available?236*/237public final boolean getIntegState() {238return integState;239}240241/**242* Requests that credential delegation be done during context243* establishment.244*/245public final void requestCredDeleg(boolean value) throws GSSException {246if (state == STATE_NEW && isInitiator()) {247if (myCred == null || !(myCred instanceof Krb5ProxyCredential)) {248credDelegState = value;249}250}251}252253/**254* Is credential delegation enabled?255*/256public final boolean getCredDelegState() {257if (isInitiator()) {258return credDelegState;259} else {260// Server side deleg state is not flagged by credDelegState.261// It can use constrained delegation.262tryConstrainedDelegation();263return delegatedCred != null;264}265}266267/**268* Requests that mutual authentication be done during context269* establishment. Since this is fromm the client's perspective, it270* essentially requests that the server be authenticated.271*/272public final void requestMutualAuth(boolean value) throws GSSException {273if (state == STATE_NEW && isInitiator()) {274mutualAuthState = value;275}276}277278/**279* Is mutual authentication enabled? Since this is from the client's280* perspective, it essentially meas that the server is being281* authenticated.282*/283public final boolean getMutualAuthState() {284return mutualAuthState;285}286287/**288* Requests that replay detection be done on the GSS wrap and MIC289* tokens.290*/291public final void requestReplayDet(boolean value) throws GSSException {292if (state == STATE_NEW && isInitiator())293replayDetState = value;294}295296/**297* Is replay detection enabled on the GSS wrap and MIC tokens?298* We enable replay detection if sequence checking is enabled.299*/300public final boolean getReplayDetState() {301return replayDetState || sequenceDetState;302}303304/**305* Requests that sequence checking be done on the GSS wrap and MIC306* tokens.307*/308public final void requestSequenceDet(boolean value) throws GSSException {309if (state == STATE_NEW && isInitiator())310sequenceDetState = value;311}312313/**314* Is sequence checking enabled on the GSS Wrap and MIC tokens?315* We enable sequence checking if replay detection is enabled.316*/317public final boolean getSequenceDetState() {318return sequenceDetState || replayDetState;319}320321/**322* Requests that the deleg policy be respected.323*/324public final void requestDelegPolicy(boolean value) {325if (state == STATE_NEW && isInitiator())326delegPolicyState = value;327}328329/**330* Is deleg policy respected?331*/332public final boolean getDelegPolicyState() {333return delegPolicyState;334}335336/*337* Anonymity is a little different in that after an application338* requests anonymity it will want to know whether the mechanism339* can support it or not, prior to sending any tokens across for340* context establishment. Since this is from the initiator's341* perspective, it essentially requests that the initiator be342* anonymous.343*/344345public final void requestAnonymity(boolean value) throws GSSException {346// Ignore silently. Application will check back with347// getAnonymityState.348}349350// RFC 2853 actually calls for this to be called after context351// establishment to get the right answer, but that is352// incorrect. The application may not want to send over any353// tokens if anonymity is not available.354public final boolean getAnonymityState() {355return false;356}357358/*359* Package private methods invoked by other Krb5 plugin classes.360*/361362/**363* Get the context specific DESCipher instance, invoked in364* MessageToken.init()365*/366final CipherHelper getCipherHelper(EncryptionKey ckey) throws GSSException {367EncryptionKey cipherKey = null;368if (cipherHelper == null) {369cipherKey = (getKey() == null) ? ckey: getKey();370cipherHelper = new CipherHelper(cipherKey);371}372return cipherHelper;373}374375final int incrementMySequenceNumber() {376int retVal;377synchronized (mySeqNumberLock) {378retVal = mySeqNumber;379mySeqNumber = retVal + 1;380}381return retVal;382}383384final void resetMySequenceNumber(int seqNumber) {385if (DEBUG) {386System.out.println("Krb5Context setting mySeqNumber to: "387+ seqNumber);388}389synchronized (mySeqNumberLock) {390mySeqNumber = seqNumber;391}392}393394final void resetPeerSequenceNumber(int seqNumber) {395if (DEBUG) {396System.out.println("Krb5Context setting peerSeqNumber to: "397+ seqNumber);398}399synchronized (peerSeqNumberLock) {400peerSeqNumber = seqNumber;401peerTokenTracker = new TokenTracker(peerSeqNumber);402}403}404405final void setKey(int keySrc, EncryptionKey key) throws GSSException {406this.key = key;407this.keySrc = keySrc;408// %%% to do: should clear old cipherHelper first409cipherHelper = new CipherHelper(key); // Need to use new key410}411412public final int getKeySrc() {413return keySrc;414}415416private final EncryptionKey getKey() {417return key;418}419420/**421* Called on the acceptor side to store the delegated credentials422* received in the AcceptSecContextToken.423*/424final void setDelegCred(Krb5CredElement delegatedCred) {425this.delegatedCred = delegatedCred;426}427428/*429* While the application can only request the following features,430* other classes in the package can call the actual set methods431* for them. They are called as context establishment tokens are432* received on an acceptor side and the context feature list that433* the initiator wants becomes known.434*/435436/*437* This method is also called by InitialToken.OverloadedChecksum if the438* TGT is not forwardable and the user requested delegation.439*/440final void setCredDelegState(boolean state) {441credDelegState = state;442}443444final void setMutualAuthState(boolean state) {445mutualAuthState = state;446}447448final void setReplayDetState(boolean state) {449replayDetState = state;450}451452final void setSequenceDetState(boolean state) {453sequenceDetState = state;454}455456final void setConfState(boolean state) {457confState = state;458}459460final void setIntegState(boolean state) {461integState = state;462}463464final void setDelegPolicyState(boolean state) {465delegPolicyState = state;466}467468/**469* Sets the channel bindings to be used during context470* establishment.471*/472public final void setChannelBinding(ChannelBinding channelBinding)473throws GSSException {474this.channelBinding = channelBinding;475}476477final ChannelBinding getChannelBinding() {478return channelBinding;479}480481/**482* Returns the mechanism oid.483*484* @return the Oid of this context485*/486public final Oid getMech() {487return (Krb5MechFactory.GSS_KRB5_MECH_OID);488}489490/**491* Returns the context initiator name.492*493* @return initiator name494* @exception GSSException495*/496public final GSSNameSpi getSrcName() throws GSSException {497return (isInitiator()? myName : peerName);498}499500/**501* Returns the context acceptor.502*503* @return context acceptor(target) name504* @exception GSSException505*/506public final GSSNameSpi getTargName() throws GSSException {507return (!isInitiator()? myName : peerName);508}509510/**511* Returns the delegated credential for the context. This512* is an optional feature of contexts which not all513* mechanisms will support. A context can be requested to514* support credential delegation by using the <b>CRED_DELEG</b>,515* or it can request for a constrained delegation.516* This is only valid on the acceptor side of the context.517* @return GSSCredentialSpi object for the delegated credential518* @exception GSSException519* @see GSSContext#getDelegCredState520*/521public final GSSCredentialSpi getDelegCred() throws GSSException {522if (state != STATE_IN_PROCESS && state != STATE_DONE)523throw new GSSException(GSSException.NO_CONTEXT);524if (isInitiator()) {525throw new GSSException(GSSException.NO_CRED);526}527tryConstrainedDelegation();528if (delegatedCred == null) {529throw new GSSException(GSSException.NO_CRED);530}531return delegatedCred;532}533534private void tryConstrainedDelegation() {535if (state != STATE_IN_PROCESS && state != STATE_DONE) {536return;537}538// We will only try constrained delegation once (if necessary).539if (!isConstrainedDelegationTried) {540if (delegatedCred == null) {541if (DEBUG) {542System.out.println(">>> Constrained deleg from " + caller);543}544// The constrained delegation part. The acceptor needs to have545// isInitiator=true in order to get a TGT, either earlier at546// logon stage, if useSubjectCredsOnly, or now.547try {548delegatedCred = new Krb5ProxyCredential(549Krb5InitCredential.getInstance(550GSSCaller.CALLER_ACCEPT, myName, lifetime),551peerName, serviceTicket);552} catch (GSSException gsse) {553// OK, delegatedCred is null then554}555}556isConstrainedDelegationTried = true;557}558}559/**560* Tests if this is the initiator side of the context.561*562* @return boolean indicating if this is initiator (true)563* or target (false)564*/565public final boolean isInitiator() {566return initiator;567}568569/**570* Tests if the context can be used for per-message service.571* Context may allow the calls to the per-message service572* functions before being fully established.573*574* @return boolean indicating if per-message methods can575* be called.576*/577public final boolean isProtReady() {578return (state == STATE_DONE);579}580581/**582* Initiator context establishment call. This method may be583* required to be called several times. A CONTINUE_NEEDED return584* call indicates that more calls are needed after the next token585* is received from the peer.586*587* @param is contains the token received from the peer. On the588* first call it will be ignored.589* @return any token required to be sent to the peer590* It is responsibility of the caller591* to send the token to its peer for processing.592* @exception GSSException593*/594public final byte[] initSecContext(InputStream is, int mechTokenSize)595throws GSSException {596597byte[] retVal = null;598InitialToken token = null;599int errorCode = GSSException.FAILURE;600if (DEBUG) {601System.out.println("Entered Krb5Context.initSecContext with " +602"state=" + printState(state));603}604if (!isInitiator()) {605throw new GSSException(GSSException.FAILURE, -1,606"initSecContext on an acceptor " +607"GSSContext");608}609610try {611if (state == STATE_NEW) {612state = STATE_IN_PROCESS;613614errorCode = GSSException.NO_CRED;615616if (myCred == null) {617myCred = Krb5InitCredential.getInstance(caller, myName,618GSSCredential.DEFAULT_LIFETIME);619myCred = Krb5ProxyCredential.tryImpersonation(620caller, (Krb5InitCredential)myCred);621} else if (!myCred.isInitiatorCredential()) {622throw new GSSException(errorCode, -1,623"No TGT available");624}625myName = (Krb5NameElement) myCred.getName();626final Krb5ProxyCredential second;627if (myCred instanceof Krb5InitCredential) {628second = null;629tgt = ((Krb5InitCredential) myCred).getKrb5Credentials();630} else {631second = (Krb5ProxyCredential) myCred;632tgt = second.self.getKrb5Credentials();633}634635checkPermission(peerName.getKrb5PrincipalName().getName(),636"initiate");637/*638* If useSubjectCredsonly is true then639* we check whether we already have the ticket640* for this service in the Subject and reuse it641*/642643@SuppressWarnings("removal")644final AccessControlContext acc =645AccessController.getContext();646647if (GSSUtil.useSubjectCredsOnly(caller)) {648KerberosTicket kerbTicket = null;649try {650// get service ticket from caller's subject651@SuppressWarnings("removal")652var tmp = AccessController.doPrivileged(653new PrivilegedExceptionAction<KerberosTicket>() {654public KerberosTicket run() throws Exception {655// XXX to be cleaned656// highly consider just calling:657// Subject.getSubject658// SubjectComber.find659// instead of Krb5Util.getServiceTicket660return Krb5Util.getServiceTicket(661GSSCaller.CALLER_UNKNOWN,662// since it's useSubjectCredsOnly here,663// don't worry about the null664second == null ?665myName.getKrb5PrincipalName().getName():666second.getName().getKrb5PrincipalName().getName(),667peerName.getKrb5PrincipalName().getName(),668acc);669}});670kerbTicket = tmp;671} catch (PrivilegedActionException e) {672if (DEBUG) {673System.out.println("Attempt to obtain service"674+ " ticket from the subject failed!");675}676}677if (kerbTicket != null) {678if (DEBUG) {679System.out.println("Found service ticket in " +680"the subject" +681kerbTicket);682}683684// convert Ticket to serviceCreds685// XXX Should merge these two object types686// avoid converting back and forth687serviceCreds = Krb5Util.ticketToCreds(kerbTicket);688}689}690if (serviceCreds == null) {691// either we did not find the serviceCreds in the692// Subject or useSubjectCreds is false693if (DEBUG) {694System.out.println("Service ticket not found in " +695"the subject");696}697// Get Service ticket using the Kerberos protocols698if (second == null) {699serviceCreds = Credentials.acquireServiceCreds(700peerName.getKrb5PrincipalName().getName(),701tgt);702} else {703serviceCreds = Credentials.acquireS4U2proxyCreds(704peerName.getKrb5PrincipalName().getName(),705second.tkt,706second.getName().getKrb5PrincipalName(),707tgt);708}709if (GSSUtil.useSubjectCredsOnly(caller)) {710@SuppressWarnings("removal")711final Subject subject =712AccessController.doPrivileged(713new java.security.PrivilegedAction<Subject>() {714public Subject run() {715return (Subject.getSubject(acc));716}717});718if (subject != null &&719!subject.isReadOnly()) {720/*721* Store the service credentials as722* javax.security.auth.kerberos.KerberosTicket in723* the Subject. We could wait until the context is724* successfully established; however it is easier725* to do it here and there is no harm.726*/727final KerberosTicket kt =728Krb5Util.credsToTicket(serviceCreds);729@SuppressWarnings("removal")730var dummy = AccessController.doPrivileged (731new java.security.PrivilegedAction<Void>() {732public Void run() {733subject.getPrivateCredentials().add(kt);734return null;735}736});737} else {738// log it for debugging purpose739if (DEBUG) {740System.out.println("Subject is " +741"readOnly;Kerberos Service "+742"ticket not stored");743}744}745}746}747748errorCode = GSSException.FAILURE;749token = new InitSecContextToken(this, tgt, serviceCreds);750apReq = ((InitSecContextToken)token).getKrbApReq();751retVal = token.encode();752myCred = null;753if (!getMutualAuthState()) {754state = STATE_DONE;755}756if (DEBUG) {757System.out.println("Created InitSecContextToken:\n"+758new HexDumpEncoder().encodeBuffer(retVal));759}760} else if (state == STATE_IN_PROCESS) {761// No need to write anything;762// just validate the incoming token763new AcceptSecContextToken(this, serviceCreds, apReq, is);764apReq = null;765state = STATE_DONE;766} else {767// XXX Use logging API?768if (DEBUG) {769System.out.println(state);770}771}772} catch (KrbException e) {773if (DEBUG) {774e.printStackTrace();775}776GSSException gssException =777new GSSException(errorCode, -1, e.getMessage());778gssException.initCause(e);779throw gssException;780} catch (IOException e) {781GSSException gssException =782new GSSException(errorCode, -1, e.getMessage());783gssException.initCause(e);784throw gssException;785}786return retVal;787}788789public final boolean isEstablished() {790return (state == STATE_DONE);791}792793/**794* Acceptor's context establishment call. This method may be795* required to be called several times. A CONTINUE_NEEDED return796* call indicates that more calls are needed after the next token797* is received from the peer.798*799* @param is contains the token received from the peer.800* @return any token required to be sent to the peer801* It is responsibility of the caller802* to send the token to its peer for processing.803* @exception GSSException804*/805public final byte[] acceptSecContext(InputStream is, int mechTokenSize)806throws GSSException {807808byte[] retVal = null;809810if (DEBUG) {811System.out.println("Entered Krb5Context.acceptSecContext with " +812"state=" + printState(state));813}814815if (isInitiator()) {816throw new GSSException(GSSException.FAILURE, -1,817"acceptSecContext on an initiator " +818"GSSContext");819}820try {821if (state == STATE_NEW) {822state = STATE_IN_PROCESS;823if (myCred == null) {824myCred = Krb5AcceptCredential.getInstance(caller, myName);825} else if (!myCred.isAcceptorCredential()) {826throw new GSSException(GSSException.NO_CRED, -1,827"No Secret Key available");828}829myName = (Krb5NameElement) myCred.getName();830831// If there is already a bound name, check now832if (myName != null) {833Krb5MechFactory.checkAcceptCredPermission(myName, myName);834}835836InitSecContextToken token = new InitSecContextToken(this,837(Krb5AcceptCredential) myCred, is);838PrincipalName clientName = token.getKrbApReq().getClient();839peerName = Krb5NameElement.getInstance(clientName);840841// If unbound, check after the bound name is found842if (myName == null) {843myName = Krb5NameElement.getInstance(844token.getKrbApReq().getCreds().getServer());845Krb5MechFactory.checkAcceptCredPermission(myName, myName);846}847848if (getMutualAuthState()) {849retVal = new AcceptSecContextToken(this,850token.getKrbApReq()).encode();851}852serviceTicket = token.getKrbApReq().getCreds().getTicket();853myCred = null;854state = STATE_DONE;855} else {856// XXX Use logging API?857if (DEBUG) {858System.out.println(state);859}860}861} catch (KrbException e) {862GSSException gssException =863new GSSException(GSSException.FAILURE, -1, e.getMessage());864gssException.initCause(e);865throw gssException;866} catch (IOException e) {867if (DEBUG) {868e.printStackTrace();869}870GSSException gssException =871new GSSException(GSSException.FAILURE, -1, e.getMessage());872gssException.initCause(e);873throw gssException;874}875876return retVal;877}878879/**880* Queries the context for largest data size to accommodate881* the specified protection and be <= maxTokSize.882*883* @param qop the quality of protection that the context will be884* asked to provide.885* @param confReq a flag indicating whether confidentiality will be886* requested or not887* @param outputSize the maximum size of the output token888* @return the maximum size for the input message that can be889* provided to the wrap() method in order to guarantee that these890* requirements are met.891* @throws GSSException892*/893public final int getWrapSizeLimit(int qop, boolean confReq,894int maxTokSize) throws GSSException {895896int retVal = 0;897if (cipherHelper.getProto() == 0) {898retVal = WrapToken.getSizeLimit(qop, confReq, maxTokSize,899getCipherHelper(null));900} else if (cipherHelper.getProto() == 1) {901retVal = WrapToken_v2.getSizeLimit(qop, confReq, maxTokSize,902getCipherHelper(null));903}904return retVal;905}906907/*908* Per-message calls depend on the sequence number. The sequence number909* synchronization is at a finer granularity because wrap and getMIC910* care about the local sequence number (mySeqNumber) where are unwrap911* and verifyMIC care about the remote sequence number (peerSeqNumber).912*/913914public final byte[] wrap(byte[] inBuf, int offset, int len,915MessageProp msgProp) throws GSSException {916if (DEBUG) {917System.out.println("Krb5Context.wrap: data=["918+ getHexBytes(inBuf, offset, len)919+ "]");920}921922if (state != STATE_DONE)923throw new GSSException(GSSException.NO_CONTEXT, -1,924"Wrap called in invalid state!");925926byte[] encToken = null;927try {928if (cipherHelper.getProto() == 0) {929WrapToken token =930new WrapToken(this, msgProp, inBuf, offset, len);931encToken = token.encode();932} else if (cipherHelper.getProto() == 1) {933WrapToken_v2 token =934new WrapToken_v2(this, msgProp, inBuf, offset, len);935encToken = token.encode();936}937if (DEBUG) {938System.out.println("Krb5Context.wrap: token=["939+ getHexBytes(encToken, 0, encToken.length)940+ "]");941}942return encToken;943} catch (IOException e) {944encToken = null;945GSSException gssException =946new GSSException(GSSException.FAILURE, -1, e.getMessage());947gssException.initCause(e);948throw gssException;949}950}951952public final int wrap(byte[] inBuf, int inOffset, int len,953byte[] outBuf, int outOffset,954MessageProp msgProp) throws GSSException {955956if (state != STATE_DONE)957throw new GSSException(GSSException.NO_CONTEXT, -1,958"Wrap called in invalid state!");959960int retVal = 0;961try {962if (cipherHelper.getProto() == 0) {963WrapToken token =964new WrapToken(this, msgProp, inBuf, inOffset, len);965retVal = token.encode(outBuf, outOffset);966} else if (cipherHelper.getProto() == 1) {967WrapToken_v2 token =968new WrapToken_v2(this, msgProp, inBuf, inOffset, len);969retVal = token.encode(outBuf, outOffset);970}971if (DEBUG) {972System.out.println("Krb5Context.wrap: token=["973+ getHexBytes(outBuf, outOffset, retVal)974+ "]");975}976return retVal;977} catch (IOException e) {978retVal = 0;979GSSException gssException =980new GSSException(GSSException.FAILURE, -1, e.getMessage());981gssException.initCause(e);982throw gssException;983}984}985986public final void wrap(byte[] inBuf, int offset, int len,987OutputStream os, MessageProp msgProp)988throws GSSException {989990if (state != STATE_DONE)991throw new GSSException(GSSException.NO_CONTEXT, -1,992"Wrap called in invalid state!");993994byte[] encToken = null;995try {996if (cipherHelper.getProto() == 0) {997WrapToken token =998new WrapToken(this, msgProp, inBuf, offset, len);999token.encode(os);1000if (DEBUG) {1001encToken = token.encode();1002}1003} else if (cipherHelper.getProto() == 1) {1004WrapToken_v2 token =1005new WrapToken_v2(this, msgProp, inBuf, offset, len);1006token.encode(os);1007if (DEBUG) {1008encToken = token.encode();1009}1010}1011} catch (IOException e) {1012GSSException gssException =1013new GSSException(GSSException.FAILURE, -1, e.getMessage());1014gssException.initCause(e);1015throw gssException;1016}10171018if (DEBUG) {1019System.out.println("Krb5Context.wrap: token=["1020+ getHexBytes(encToken, 0, encToken.length)1021+ "]");1022}1023}10241025public final void wrap(InputStream is, OutputStream os,1026MessageProp msgProp) throws GSSException {10271028byte[] data;1029try {1030data = new byte[is.available()];1031is.read(data);1032} catch (IOException e) {1033GSSException gssException =1034new GSSException(GSSException.FAILURE, -1, e.getMessage());1035gssException.initCause(e);1036throw gssException;1037}1038wrap(data, 0, data.length, os, msgProp);1039}10401041public final byte[] unwrap(byte[] inBuf, int offset, int len,1042MessageProp msgProp)1043throws GSSException {10441045if (DEBUG) {1046System.out.println("Krb5Context.unwrap: token=["1047+ getHexBytes(inBuf, offset, len)1048+ "]");1049}10501051if (state != STATE_DONE) {1052throw new GSSException(GSSException.NO_CONTEXT, -1,1053" Unwrap called in invalid state!");1054}10551056byte[] data = null;1057if (cipherHelper.getProto() == 0) {1058WrapToken token =1059new WrapToken(this, inBuf, offset, len, msgProp);1060data = token.getData();1061setSequencingAndReplayProps(token, msgProp);1062} else if (cipherHelper.getProto() == 1) {1063WrapToken_v2 token =1064new WrapToken_v2(this, inBuf, offset, len, msgProp);1065data = token.getData();1066setSequencingAndReplayProps(token, msgProp);1067}10681069if (DEBUG) {1070System.out.println("Krb5Context.unwrap: data=["1071+ getHexBytes(data, 0, data.length)1072+ "]");1073}10741075return data;1076}10771078public final int unwrap(byte[] inBuf, int inOffset, int len,1079byte[] outBuf, int outOffset,1080MessageProp msgProp) throws GSSException {10811082if (state != STATE_DONE)1083throw new GSSException(GSSException.NO_CONTEXT, -1,1084"Unwrap called in invalid state!");10851086if (cipherHelper.getProto() == 0) {1087WrapToken token =1088new WrapToken(this, inBuf, inOffset, len, msgProp);1089len = token.getData(outBuf, outOffset);1090setSequencingAndReplayProps(token, msgProp);1091} else if (cipherHelper.getProto() == 1) {1092WrapToken_v2 token =1093new WrapToken_v2(this, inBuf, inOffset, len, msgProp);1094len = token.getData(outBuf, outOffset);1095setSequencingAndReplayProps(token, msgProp);1096}1097return len;1098}10991100public final int unwrap(InputStream is,1101byte[] outBuf, int outOffset,1102MessageProp msgProp) throws GSSException {11031104if (state != STATE_DONE)1105throw new GSSException(GSSException.NO_CONTEXT, -1,1106"Unwrap called in invalid state!");11071108int len = 0;1109if (cipherHelper.getProto() == 0) {1110WrapToken token = new WrapToken(this, is, msgProp);1111len = token.getData(outBuf, outOffset);1112setSequencingAndReplayProps(token, msgProp);1113} else if (cipherHelper.getProto() == 1) {1114WrapToken_v2 token = new WrapToken_v2(this, is, msgProp);1115len = token.getData(outBuf, outOffset);1116setSequencingAndReplayProps(token, msgProp);1117}1118return len;1119}112011211122public final void unwrap(InputStream is, OutputStream os,1123MessageProp msgProp) throws GSSException {11241125if (state != STATE_DONE)1126throw new GSSException(GSSException.NO_CONTEXT, -1,1127"Unwrap called in invalid state!");11281129byte[] data = null;1130if (cipherHelper.getProto() == 0) {1131WrapToken token = new WrapToken(this, is, msgProp);1132data = token.getData();1133setSequencingAndReplayProps(token, msgProp);1134} else if (cipherHelper.getProto() == 1) {1135WrapToken_v2 token = new WrapToken_v2(this, is, msgProp);1136data = token.getData();1137setSequencingAndReplayProps(token, msgProp);1138}11391140try {1141os.write(data);1142} catch (IOException e) {1143GSSException gssException =1144new GSSException(GSSException.FAILURE, -1, e.getMessage());1145gssException.initCause(e);1146throw gssException;1147}1148}11491150public final byte[] getMIC(byte[] inMsg, int offset, int len,1151MessageProp msgProp)1152throws GSSException {11531154byte[] micToken = null;1155try {1156if (cipherHelper.getProto() == 0) {1157MicToken token =1158new MicToken(this, msgProp, inMsg, offset, len);1159micToken = token.encode();1160} else if (cipherHelper.getProto() == 1) {1161MicToken_v2 token =1162new MicToken_v2(this, msgProp, inMsg, offset, len);1163micToken = token.encode();1164}1165return micToken;1166} catch (IOException e) {1167micToken = null;1168GSSException gssException =1169new GSSException(GSSException.FAILURE, -1, e.getMessage());1170gssException.initCause(e);1171throw gssException;1172}1173}11741175private int getMIC(byte[] inMsg, int offset, int len,1176byte[] outBuf, int outOffset,1177MessageProp msgProp)1178throws GSSException {11791180int retVal = 0;1181try {1182if (cipherHelper.getProto() == 0) {1183MicToken token =1184new MicToken(this, msgProp, inMsg, offset, len);1185retVal = token.encode(outBuf, outOffset);1186} else if (cipherHelper.getProto() == 1) {1187MicToken_v2 token =1188new MicToken_v2(this, msgProp, inMsg, offset, len);1189retVal = token.encode(outBuf, outOffset);1190}1191return retVal;1192} catch (IOException e) {1193retVal = 0;1194GSSException gssException =1195new GSSException(GSSException.FAILURE, -1, e.getMessage());1196gssException.initCause(e);1197throw gssException;1198}1199}12001201/*1202* Checksum calculation requires a byte[]. Hence might as well pass1203* a byte[] into the MicToken constructor. However, writing the1204* token can be optimized for cases where the application passed in1205* an OutputStream.1206*/12071208private void getMIC(byte[] inMsg, int offset, int len,1209OutputStream os, MessageProp msgProp)1210throws GSSException {12111212try {1213if (cipherHelper.getProto() == 0) {1214MicToken token =1215new MicToken(this, msgProp, inMsg, offset, len);1216token.encode(os);1217} else if (cipherHelper.getProto() == 1) {1218MicToken_v2 token =1219new MicToken_v2(this, msgProp, inMsg, offset, len);1220token.encode(os);1221}1222} catch (IOException e) {1223GSSException gssException =1224new GSSException(GSSException.FAILURE, -1, e.getMessage());1225gssException.initCause(e);1226throw gssException;1227}1228}12291230public final void getMIC(InputStream is, OutputStream os,1231MessageProp msgProp) throws GSSException {1232byte[] data;1233try {1234data = new byte[is.available()];1235is.read(data);1236} catch (IOException e) {1237GSSException gssException =1238new GSSException(GSSException.FAILURE, -1, e.getMessage());1239gssException.initCause(e);1240throw gssException;1241}1242getMIC(data, 0, data.length, os, msgProp);1243}12441245public final void verifyMIC(byte[] inTok, int tokOffset, int tokLen,1246byte[] inMsg, int msgOffset, int msgLen,1247MessageProp msgProp)1248throws GSSException {12491250if (cipherHelper.getProto() == 0) {1251MicToken token =1252new MicToken(this, inTok, tokOffset, tokLen, msgProp);1253token.verify(inMsg, msgOffset, msgLen);1254setSequencingAndReplayProps(token, msgProp);1255} else if (cipherHelper.getProto() == 1) {1256MicToken_v2 token =1257new MicToken_v2(this, inTok, tokOffset, tokLen, msgProp);1258token.verify(inMsg, msgOffset, msgLen);1259setSequencingAndReplayProps(token, msgProp);1260}1261}12621263private void verifyMIC(InputStream is,1264byte[] inMsg, int msgOffset, int msgLen,1265MessageProp msgProp)1266throws GSSException {12671268if (cipherHelper.getProto() == 0) {1269MicToken token = new MicToken(this, is, msgProp);1270token.verify(inMsg, msgOffset, msgLen);1271setSequencingAndReplayProps(token, msgProp);1272} else if (cipherHelper.getProto() == 1) {1273MicToken_v2 token = new MicToken_v2(this, is, msgProp);1274token.verify(inMsg, msgOffset, msgLen);1275setSequencingAndReplayProps(token, msgProp);1276}1277}12781279public final void verifyMIC(InputStream is, InputStream msgStr,1280MessageProp mProp) throws GSSException {1281byte[] msg;1282try {1283msg = new byte[msgStr.available()];1284msgStr.read(msg);1285} catch (IOException e) {1286GSSException gssException =1287new GSSException(GSSException.FAILURE, -1, e.getMessage());1288gssException.initCause(e);1289throw gssException;1290}1291verifyMIC(is, msg, 0, msg.length, mProp);1292}12931294/**1295* Produces a token representing this context. After this call1296* the context will no longer be usable until an import is1297* performed on the returned token.1298*1299* @param os the output token will be written to this stream1300* @exception GSSException1301*/1302public final byte[] export() throws GSSException {1303throw new GSSException(GSSException.UNAVAILABLE, -1,1304"GSS Export Context not available");1305}13061307/**1308* Releases context resources and terminates the1309* context between 2 peer.1310*1311* @exception GSSException with major codes NO_CONTEXT, FAILURE.1312*/13131314public final void dispose() throws GSSException {1315state = STATE_DELETED;1316delegatedCred = null;1317tgt = null;1318serviceCreds = null;1319key = null;1320}13211322public final Provider getProvider() {1323return Krb5MechFactory.PROVIDER;1324}13251326/**1327* Sets replay and sequencing information for a message token received1328* form the peer.1329*/1330private void setSequencingAndReplayProps(MessageToken token,1331MessageProp prop) {1332if (replayDetState || sequenceDetState) {1333int seqNum = token.getSequenceNumber();1334peerTokenTracker.getProps(seqNum, prop);1335}1336}13371338/**1339* Sets replay and sequencing information for a message token received1340* form the peer.1341*/1342private void setSequencingAndReplayProps(MessageToken_v2 token,1343MessageProp prop) {1344if (replayDetState || sequenceDetState) {1345int seqNum = token.getSequenceNumber();1346peerTokenTracker.getProps(seqNum, prop);1347}1348}13491350private void checkPermission(String principal, String action) {1351@SuppressWarnings("removal")1352SecurityManager sm = System.getSecurityManager();1353if (sm != null) {1354ServicePermission perm =1355new ServicePermission(principal, action);1356sm.checkPermission(perm);1357}1358}13591360private static String getHexBytes(byte[] bytes, int pos, int len) {13611362StringBuilder sb = new StringBuilder();1363for (int i = 0; i < len; i++) {13641365int b1 = (bytes[i]>>4) & 0x0f;1366int b2 = bytes[i] & 0x0f;13671368sb.append(Integer.toHexString(b1));1369sb.append(Integer.toHexString(b2));1370sb.append(' ');1371}1372return sb.toString();1373}13741375private static String printState(int state) {1376switch (state) {1377case STATE_NEW:1378return ("STATE_NEW");1379case STATE_IN_PROCESS:1380return ("STATE_IN_PROCESS");1381case STATE_DONE:1382return ("STATE_DONE");1383case STATE_DELETED:1384return ("STATE_DELETED");1385default:1386return ("Unknown state " + state);1387}1388}13891390GSSCaller getCaller() {1391// Currently used by InitialToken only1392return caller;1393}13941395/**1396* The session key returned by inquireSecContext(KRB5_INQ_SSPI_SESSION_KEY)1397*/1398static class KerberosSessionKey implements Key {1399private static final long serialVersionUID = 699307378954123869L;14001401@SuppressWarnings("serial") // Not statically typed as Serializable1402private final EncryptionKey key;14031404KerberosSessionKey(EncryptionKey key) {1405this.key = key;1406}14071408@Override1409public String getAlgorithm() {1410return Integer.toString(key.getEType());1411}14121413@Override1414public String getFormat() {1415return "RAW";1416}14171418@Override1419public byte[] getEncoded() {1420return key.getBytes().clone();1421}14221423@Override1424public String toString() {1425return "Kerberos session key: etype: " + key.getEType() + "\n" +1426new HexDumpEncoder().encodeBuffer(key.getBytes());1427}1428}14291430/**1431* Return the mechanism-specific attribute associated with {@code type}.1432*/1433public Object inquireSecContext(String type)1434throws GSSException {1435if (!isEstablished()) {1436throw new GSSException(GSSException.NO_CONTEXT, -1,1437"Security context not established.");1438}1439switch (type) {1440case "KRB5_GET_SESSION_KEY":1441return new KerberosSessionKey(key);1442case "KRB5_GET_SESSION_KEY_EX":1443return new javax.security.auth.kerberos.EncryptionKey(1444key.getBytes(), key.getEType());1445case "KRB5_GET_TKT_FLAGS":1446return tktFlags.clone();1447case "KRB5_GET_AUTHZ_DATA":1448if (isInitiator()) {1449throw new GSSException(GSSException.UNAVAILABLE, -1,1450"AuthzData not available on initiator side.");1451} else {1452return authzData;1453}1454case "KRB5_GET_AUTHTIME":1455return authTime;1456case "KRB5_GET_KRB_CRED":1457if (!isInitiator()) {1458throw new GSSException(GSSException.UNAVAILABLE, -1,1459"KRB_CRED not available on acceptor side.");1460}1461KerberosPrincipal sender = new KerberosPrincipal(1462myName.getKrb5PrincipalName().getName());1463KerberosPrincipal recipient = new KerberosPrincipal(1464peerName.getKrb5PrincipalName().getName());1465try {1466byte[] krbCred = new KrbCred(tgt, serviceCreds, key)1467.getMessage();1468return new KerberosCredMessage(1469sender, recipient, krbCred);1470} catch (KrbException | IOException e) {1471GSSException gsse = new GSSException(GSSException.UNAVAILABLE, -1,1472"KRB_CRED not generated correctly.");1473gsse.initCause(e);1474throw gsse;1475}1476}1477throw new GSSException(GSSException.UNAVAILABLE, -1,1478"Inquire type not supported.");1479}14801481// Helpers for inquireSecContext1482private boolean[] tktFlags;1483private String authTime;1484private AuthorizationData authzData;14851486public void setTktFlags(boolean[] tktFlags) {1487this.tktFlags = tktFlags;1488}14891490public void setAuthTime(String authTime) {1491this.authTime = authTime;1492}14931494public void setAuthzData(AuthorizationData authzData) {1495this.authzData = authzData;1496}14971498}149915001501