1 /*
   2  * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.jgss.krb5;
  27 
  28 import org.ietf.jgss.*;
  29 import sun.security.util.HexDumpEncoder;
  30 import sun.security.jgss.GSSUtil;
  31 import sun.security.jgss.GSSCaller;
  32 import sun.security.jgss.spi.*;
  33 import sun.security.jgss.TokenTracker;
  34 import sun.security.krb5.*;
  35 import java.io.InputStream;
  36 import java.io.OutputStream;
  37 import java.io.IOException;
  38 import java.security.Provider;
  39 import java.security.AccessController;
  40 import java.security.AccessControlContext;
  41 import java.security.Key;
  42 import java.security.PrivilegedActionException;
  43 import java.security.PrivilegedExceptionAction;
  44 import javax.security.auth.Subject;
  45 import javax.security.auth.kerberos.ServicePermission;
  46 import javax.security.auth.kerberos.KerberosCredMessage;
  47 import javax.security.auth.kerberos.KerberosPrincipal;
  48 import javax.security.auth.kerberos.KerberosTicket;
  49 import sun.security.krb5.internal.Ticket;
  50 import sun.security.krb5.internal.AuthorizationData;
  51 
  52 /**
  53  * Implements the mechanism specific context class for the Kerberos v5
  54  * GSS-API mechanism.
  55  *
  56  * @author Mayank Upadhyay
  57  * @author Ram Marti
  58  * @since 1.4
  59  */
  60 class Krb5Context implements GSSContextSpi {
  61 
  62     /*
  63      * The different states that this context can be in.
  64      */
  65 
  66     private static final int STATE_NEW = 1;
  67     private static final int STATE_IN_PROCESS = 2;
  68     private static final int STATE_DONE = 3;
  69     private static final int STATE_DELETED = 4;
  70 
  71     private int state = STATE_NEW;
  72 
  73     public static final int SESSION_KEY = 0;
  74     public static final int INITIATOR_SUBKEY = 1;
  75     public static final int ACCEPTOR_SUBKEY = 2;
  76 
  77     /*
  78      * Optional features that the application can set and their default
  79      * values.
  80      */
  81 
  82     private boolean credDelegState  = false;    // now only useful at client
  83     private boolean mutualAuthState  = true;
  84     private boolean replayDetState  = true;
  85     private boolean sequenceDetState  = true;
  86     private boolean confState  = true;
  87     private boolean integState  = true;
  88     private boolean delegPolicyState = false;
  89 
  90     private boolean isConstrainedDelegationTried = false;
  91 
  92     private int mySeqNumber;
  93     private int peerSeqNumber;
  94     private int keySrc;
  95     private TokenTracker peerTokenTracker;
  96 
  97     private CipherHelper cipherHelper = null;
  98 
  99     /*
 100      * Separate locks for the sequence numbers allow the application to
 101      * receive tokens at the same time that it is sending tokens. Note
 102      * that the application must synchronize the generation and
 103      * transmission of tokens such that tokens are processed in the same
 104      * order that they are generated. This is important when sequence
 105      * checking of per-message tokens is enabled.
 106      */
 107 
 108     private Object mySeqNumberLock = new Object();
 109     private Object peerSeqNumberLock = new Object();
 110 
 111     private EncryptionKey key;
 112     private Krb5NameElement myName;
 113     private Krb5NameElement peerName;
 114     private int lifetime;
 115     private boolean initiator;
 116     private ChannelBinding channelBinding;
 117 
 118     private Krb5CredElement myCred;
 119     private Krb5CredElement delegatedCred; // Set only on acceptor side
 120 
 121     // XXX See if the required info from these can be extracted and
 122     // stored elsewhere
 123     private Credentials tgt;
 124     private Credentials serviceCreds;
 125     private KrbApReq apReq;
 126     Ticket serviceTicket;
 127     final private GSSCaller caller;
 128     private static final boolean DEBUG = Krb5Util.DEBUG;
 129 
 130     /**
 131      * Constructor for Krb5Context to be called on the context initiator's
 132      * side.
 133      */
 134     Krb5Context(GSSCaller caller, Krb5NameElement peerName, Krb5CredElement myCred,
 135                 int lifetime)
 136         throws GSSException {
 137 
 138         if (peerName == null)
 139             throw new IllegalArgumentException("Cannot have null peer name");
 140 
 141         this.caller = caller;
 142         this.peerName = peerName;
 143         this.myCred = myCred;
 144         this.lifetime = lifetime;
 145         this.initiator = true;
 146     }
 147 
 148     /**
 149      * Constructor for Krb5Context to be called on the context acceptor's
 150      * side.
 151      */
 152     Krb5Context(GSSCaller caller, Krb5CredElement myCred)
 153         throws GSSException {
 154         this.caller = caller;
 155         this.myCred = myCred;
 156         this.initiator = false;
 157     }
 158 
 159     /**
 160      * Constructor for Krb5Context to import a previously exported context.
 161      */
 162     public Krb5Context(GSSCaller caller, byte[] interProcessToken)
 163         throws GSSException {
 164         throw new GSSException(GSSException.UNAVAILABLE,
 165                                -1, "GSS Import Context not available");
 166     }
 167 
 168     /**
 169      * Method to determine if the context can be exported and then
 170      * re-imported.
 171      */
 172     public final boolean isTransferable() throws GSSException {
 173         return false;
 174     }
 175 
 176     /**
 177      * The lifetime remaining for this context.
 178      */
 179     public final int getLifetime() {
 180         // XXX Return service ticket lifetime
 181         return GSSContext.INDEFINITE_LIFETIME;
 182     }
 183 
 184     /*
 185      * Methods that may be invoked by the GSS framework in response
 186      * to an application request for setting/getting these
 187      * properties.
 188      *
 189      * These can only be called on the initiator side.
 190      *
 191      * Notice that an application can only request these
 192      * properties. The mechanism may or may not support them. The
 193      * application must make getXXX calls after context establishment
 194      * to see if the mechanism implementations on both sides support
 195      * these features. requestAnonymity is an exception where the
 196      * application will want to call getAnonymityState prior to sending any
 197      * GSS token during context establishment.
 198      *
 199      * Also note that the requests can only be placed before context
 200      * establishment starts. i.e. when state is STATE_NEW
 201      */
 202 
 203     /**
 204      * Requests the desired lifetime. Can only be used on the context
 205      * initiator's side.
 206      */
 207     public void requestLifetime(int lifetime) throws GSSException {
 208         if (state == STATE_NEW && isInitiator())
 209             this.lifetime = lifetime;
 210     }
 211 
 212     /**
 213      * Requests that confidentiality be available.
 214      */
 215     public final void requestConf(boolean value) throws GSSException {
 216         if (state == STATE_NEW && isInitiator())
 217             confState  = value;
 218     }
 219 
 220     /**
 221      * Is confidentiality available?
 222      */
 223     public final boolean getConfState() {
 224         return confState;
 225     }
 226 
 227     /**
 228      * Requests that integrity be available.
 229      */
 230     public final void requestInteg(boolean value) throws GSSException {
 231         if (state == STATE_NEW && isInitiator())
 232             integState  = value;
 233     }
 234 
 235     /**
 236      * Is integrity available?
 237      */
 238     public final boolean getIntegState() {
 239         return integState;
 240     }
 241 
 242     /**
 243      * Requests that credential delegation be done during context
 244      * establishment.
 245      */
 246     public final void requestCredDeleg(boolean value) throws GSSException {
 247         if (state == STATE_NEW && isInitiator()) {
 248             if (myCred == null || !(myCred instanceof Krb5ProxyCredential)) {
 249                 credDelegState  = value;
 250             }
 251         }
 252     }
 253 
 254     /**
 255      * Is credential delegation enabled?
 256      */
 257     public final boolean getCredDelegState() {
 258         if (isInitiator()) {
 259             return credDelegState;
 260         } else {
 261             // Server side deleg state is not flagged by credDelegState.
 262             // It can use constrained delegation.
 263             tryConstrainedDelegation();
 264             return delegatedCred != null;
 265         }
 266     }
 267 
 268     /**
 269      * Requests that mutual authentication be done during context
 270      * establishment. Since this is fromm the client's perspective, it
 271      * essentially requests that the server be authenticated.
 272      */
 273     public final void requestMutualAuth(boolean value) throws GSSException {
 274         if (state == STATE_NEW && isInitiator()) {
 275             mutualAuthState  = value;
 276         }
 277     }
 278 
 279     /**
 280      * Is mutual authentication enabled? Since this is from the client's
 281      * perspective, it essentially meas that the server is being
 282      * authenticated.
 283      */
 284     public final boolean getMutualAuthState() {
 285         return mutualAuthState;
 286     }
 287 
 288     /**
 289      * Requests that replay detection be done on the GSS wrap and MIC
 290      * tokens.
 291      */
 292     public final void requestReplayDet(boolean value) throws GSSException {
 293         if (state == STATE_NEW && isInitiator())
 294             replayDetState  = value;
 295     }
 296 
 297     /**
 298      * Is replay detection enabled on the GSS wrap and MIC tokens?
 299      * We enable replay detection if sequence checking is enabled.
 300      */
 301     public final boolean getReplayDetState() {
 302         return replayDetState || sequenceDetState;
 303     }
 304 
 305     /**
 306      * Requests that sequence checking be done on the GSS wrap and MIC
 307      * tokens.
 308      */
 309     public final void requestSequenceDet(boolean value) throws GSSException {
 310         if (state == STATE_NEW && isInitiator())
 311             sequenceDetState  = value;
 312     }
 313 
 314     /**
 315      * Is sequence checking enabled on the GSS Wrap and MIC tokens?
 316      * We enable sequence checking if replay detection is enabled.
 317      */
 318     public final boolean getSequenceDetState() {
 319         return sequenceDetState || replayDetState;
 320     }
 321 
 322     /**
 323      * Requests that the deleg policy be respected.
 324      */
 325     public final void requestDelegPolicy(boolean value) {
 326         if (state == STATE_NEW && isInitiator())
 327             delegPolicyState = value;
 328     }
 329 
 330     /**
 331      * Is deleg policy respected?
 332      */
 333     public final boolean getDelegPolicyState() {
 334         return delegPolicyState;
 335     }
 336 
 337     /*
 338      * Anonymity is a little different in that after an application
 339      * requests anonymity it will want to know whether the mechanism
 340      * can support it or not, prior to sending any tokens across for
 341      * context establishment. Since this is from the initiator's
 342      * perspective, it essentially requests that the initiator be
 343      * anonymous.
 344      */
 345 
 346     public final void requestAnonymity(boolean value) throws GSSException {
 347         // Ignore silently. Application will check back with
 348         // getAnonymityState.
 349     }
 350 
 351     // RFC 2853 actually calls for this to be called after context
 352     // establishment to get the right answer, but that is
 353     // incorrect. The application may not want to send over any
 354     // tokens if anonymity is not available.
 355     public final boolean getAnonymityState() {
 356         return false;
 357     }
 358 
 359     /*
 360      * Package private methods invoked by other Krb5 plugin classes.
 361      */
 362 
 363     /**
 364      * Get the context specific DESCipher instance, invoked in
 365      * MessageToken.init()
 366      */
 367     final CipherHelper getCipherHelper(EncryptionKey ckey) throws GSSException {
 368          EncryptionKey cipherKey = null;
 369          if (cipherHelper == null) {
 370             cipherKey = (getKey() == null) ? ckey: getKey();
 371             cipherHelper = new CipherHelper(cipherKey);
 372          }
 373          return cipherHelper;
 374     }
 375 
 376     final int incrementMySequenceNumber() {
 377         int retVal;
 378         synchronized (mySeqNumberLock) {
 379             retVal = mySeqNumber;
 380             mySeqNumber = retVal + 1;
 381         }
 382         return retVal;
 383     }
 384 
 385     final void resetMySequenceNumber(int seqNumber) {
 386         if (DEBUG) {
 387             System.out.println("Krb5Context setting mySeqNumber to: "
 388                                + seqNumber);
 389         }
 390         synchronized (mySeqNumberLock) {
 391             mySeqNumber = seqNumber;
 392         }
 393     }
 394 
 395     final void resetPeerSequenceNumber(int seqNumber) {
 396         if (DEBUG) {
 397             System.out.println("Krb5Context setting peerSeqNumber to: "
 398                                + seqNumber);
 399         }
 400         synchronized (peerSeqNumberLock) {
 401             peerSeqNumber = seqNumber;
 402             peerTokenTracker = new TokenTracker(peerSeqNumber);
 403         }
 404     }
 405 
 406     final void setKey(int keySrc, EncryptionKey key) throws GSSException {
 407         this.key = key;
 408         this.keySrc = keySrc;
 409         // %%% to do: should clear old cipherHelper first
 410         cipherHelper = new CipherHelper(key);  // Need to use new key
 411     }
 412 
 413     public final int getKeySrc() {
 414         return keySrc;
 415     }
 416 
 417     private final EncryptionKey getKey() {
 418         return key;
 419     }
 420 
 421     /**
 422      * Called on the acceptor side to store the delegated credentials
 423      * received in the AcceptSecContextToken.
 424      */
 425     final void setDelegCred(Krb5CredElement delegatedCred) {
 426         this.delegatedCred = delegatedCred;
 427     }
 428 
 429     /*
 430      * While the application can only request the following features,
 431      * other classes in the package can call the actual set methods
 432      * for them. They are called as context establishment tokens are
 433      * received on an acceptor side and the context feature list that
 434      * the initiator wants becomes known.
 435      */
 436 
 437     /*
 438      * This method is also called by InitialToken.OverloadedChecksum if the
 439      * TGT is not forwardable and the user requested delegation.
 440      */
 441     final void setCredDelegState(boolean state) {
 442         credDelegState = state;
 443     }
 444 
 445     final void setMutualAuthState(boolean state) {
 446         mutualAuthState = state;
 447     }
 448 
 449     final void setReplayDetState(boolean state) {
 450         replayDetState = state;
 451     }
 452 
 453     final void setSequenceDetState(boolean state) {
 454         sequenceDetState = state;
 455     }
 456 
 457     final void setConfState(boolean state) {
 458         confState = state;
 459     }
 460 
 461     final void setIntegState(boolean state) {
 462         integState = state;
 463     }
 464 
 465     final void setDelegPolicyState(boolean state) {
 466         delegPolicyState = state;
 467     }
 468 
 469     /**
 470      * Sets the channel bindings to be used during context
 471      * establishment.
 472      */
 473     public final void setChannelBinding(ChannelBinding channelBinding)
 474         throws GSSException {
 475         this.channelBinding = channelBinding;
 476     }
 477 
 478     final ChannelBinding getChannelBinding() {
 479         return channelBinding;
 480     }
 481 
 482     /**
 483      * Returns the mechanism oid.
 484      *
 485      * @return the Oid of this context
 486      */
 487     public final Oid getMech() {
 488         return (Krb5MechFactory.GSS_KRB5_MECH_OID);
 489     }
 490 
 491     /**
 492      * Returns the context initiator name.
 493      *
 494      * @return initiator name
 495      * @exception GSSException
 496      */
 497     public final GSSNameSpi getSrcName() throws GSSException {
 498         return (isInitiator()? myName : peerName);
 499     }
 500 
 501     /**
 502      * Returns the context acceptor.
 503      *
 504      * @return context acceptor(target) name
 505      * @exception GSSException
 506      */
 507     public final GSSNameSpi getTargName() throws GSSException {
 508         return (!isInitiator()? myName : peerName);
 509     }
 510 
 511     /**
 512      * Returns the delegated credential for the context. This
 513      * is an optional feature of contexts which not all
 514      * mechanisms will support. A context can be requested to
 515      * support credential delegation by using the <b>CRED_DELEG</b>,
 516      * or it can request for a constrained delegation.
 517      * This is only valid on the acceptor side of the context.
 518      * @return GSSCredentialSpi object for the delegated credential
 519      * @exception GSSException
 520      * @see GSSContext#getDelegCredState
 521      */
 522     public final GSSCredentialSpi getDelegCred() throws GSSException {
 523         if (state != STATE_IN_PROCESS && state != STATE_DONE)
 524             throw new GSSException(GSSException.NO_CONTEXT);
 525         if (isInitiator()) {
 526             throw new GSSException(GSSException.NO_CRED);
 527         }
 528         tryConstrainedDelegation();
 529         if (delegatedCred == null) {
 530             throw new GSSException(GSSException.NO_CRED);
 531         }
 532         return delegatedCred;
 533     }
 534 
 535     private void tryConstrainedDelegation() {
 536         if (state != STATE_IN_PROCESS && state != STATE_DONE) {
 537             return;
 538         }
 539         // We will only try constrained delegation once (if necessary).
 540         if (!isConstrainedDelegationTried) {
 541             if (delegatedCred == null) {
 542                 if (DEBUG) {
 543                     System.out.println(">>> Constrained deleg from " + caller);
 544                 }
 545                 // The constrained delegation part. The acceptor needs to have
 546                 // isInitiator=true in order to get a TGT, either earlier at
 547                 // logon stage, if useSubjectCredsOnly, or now.
 548                 try {
 549                     delegatedCred = new Krb5ProxyCredential(
 550                         Krb5InitCredential.getInstance(
 551                             GSSCaller.CALLER_ACCEPT, myName, lifetime),
 552                         peerName, serviceTicket);
 553                 } catch (GSSException gsse) {
 554                     // OK, delegatedCred is null then
 555                 }
 556             }
 557             isConstrainedDelegationTried = true;
 558         }
 559     }
 560     /**
 561      * Tests if this is the initiator side of the context.
 562      *
 563      * @return boolean indicating if this is initiator (true)
 564      *  or target (false)
 565      */
 566     public final boolean isInitiator() {
 567         return initiator;
 568     }
 569 
 570     /**
 571      * Tests if the context can be used for per-message service.
 572      * Context may allow the calls to the per-message service
 573      * functions before being fully established.
 574      *
 575      * @return boolean indicating if per-message methods can
 576      *  be called.
 577      */
 578     public final boolean isProtReady() {
 579         return (state == STATE_DONE);
 580     }
 581 
 582     /**
 583      * Initiator context establishment call. This method may be
 584      * required to be called several times. A CONTINUE_NEEDED return
 585      * call indicates that more calls are needed after the next token
 586      * is received from the peer.
 587      *
 588      * @param is contains the token received from the peer. On the
 589      *  first call it will be ignored.
 590      * @return any token required to be sent to the peer
 591      *    It is responsibility of the caller
 592      *    to send the token to its peer for processing.
 593      * @exception GSSException
 594      */
 595     public final byte[] initSecContext(InputStream is, int mechTokenSize)
 596         throws GSSException {
 597 
 598             byte[] retVal = null;
 599             InitialToken token = null;
 600             int errorCode = GSSException.FAILURE;
 601             if (DEBUG) {
 602                 System.out.println("Entered Krb5Context.initSecContext with " +
 603                                    "state=" + printState(state));
 604             }
 605             if (!isInitiator()) {
 606                 throw new GSSException(GSSException.FAILURE, -1,
 607                                        "initSecContext on an acceptor " +
 608                                         "GSSContext");
 609             }
 610 
 611             try {
 612                 if (state == STATE_NEW) {
 613                     state = STATE_IN_PROCESS;
 614 
 615                     errorCode = GSSException.NO_CRED;
 616 
 617                     if (myCred == null) {
 618                         myCred = Krb5InitCredential.getInstance(caller, myName,
 619                                               GSSCredential.DEFAULT_LIFETIME);
 620                     } else if (!myCred.isInitiatorCredential()) {
 621                         throw new GSSException(errorCode, -1,
 622                                            "No TGT available");
 623                     }
 624                     myName = (Krb5NameElement) myCred.getName();
 625                     final Krb5ProxyCredential second;
 626                     if (myCred instanceof Krb5InitCredential) {
 627                         second = null;
 628                         tgt = ((Krb5InitCredential) myCred).getKrb5Credentials();
 629                     } else {
 630                         second = (Krb5ProxyCredential) myCred;
 631                         tgt = second.self.getKrb5Credentials();
 632                     }
 633 
 634                     checkPermission(peerName.getKrb5PrincipalName().getName(),
 635                                     "initiate");
 636                     /*
 637                      * If useSubjectCredsonly is true then
 638                      * we check whether we already have the ticket
 639                      * for this service in the Subject and reuse it
 640                      */
 641 
 642                     final AccessControlContext acc =
 643                         AccessController.getContext();
 644 
 645                     if (GSSUtil.useSubjectCredsOnly(caller)) {
 646                         KerberosTicket kerbTicket = null;
 647                         try {
 648                            // get service ticket from caller's subject
 649                            kerbTicket = AccessController.doPrivileged(
 650                                 new PrivilegedExceptionAction<KerberosTicket>() {
 651                                 public KerberosTicket run() throws Exception {
 652                                     // XXX to be cleaned
 653                                     // highly consider just calling:
 654                                     // Subject.getSubject
 655                                     // SubjectComber.find
 656                                     // instead of Krb5Util.getTicket
 657                                     return Krb5Util.getTicket(
 658                                         GSSCaller.CALLER_UNKNOWN,
 659                                         // since it's useSubjectCredsOnly here,
 660                                         // don't worry about the null
 661                                         second == null ?
 662                                             myName.getKrb5PrincipalName().getName():
 663                                             second.getName().getKrb5PrincipalName().getName(),
 664                                         peerName.getKrb5PrincipalName().getName(),
 665                                         acc);
 666                                 }});
 667                         } catch (PrivilegedActionException e) {
 668                             if (DEBUG) {
 669                                 System.out.println("Attempt to obtain service"
 670                                         + " ticket from the subject failed!");
 671                             }
 672                         }
 673                         if (kerbTicket != null) {
 674                             if (DEBUG) {
 675                                 System.out.println("Found service ticket in " +
 676                                                    "the subject" +
 677                                                    kerbTicket);
 678                             }
 679 
 680                             // convert Ticket to serviceCreds
 681                             // XXX Should merge these two object types
 682                             // avoid converting back and forth
 683                             serviceCreds = Krb5Util.ticketToCreds(kerbTicket);
 684                         }
 685                     }
 686                     if (serviceCreds == null) {
 687                         // either we did not find the serviceCreds in the
 688                         // Subject or useSubjectCreds is false
 689                         if (DEBUG) {
 690                             System.out.println("Service ticket not found in " +
 691                                                "the subject");
 692                         }
 693                         // Get Service ticket using the Kerberos protocols
 694                         if (second == null) {
 695                             serviceCreds = Credentials.acquireServiceCreds(
 696                                      peerName.getKrb5PrincipalName().getName(),
 697                                      tgt);
 698                         } else {
 699                             serviceCreds = Credentials.acquireS4U2proxyCreds(
 700                                     peerName.getKrb5PrincipalName().getName(),
 701                                     second.tkt,
 702                                     second.getName().getKrb5PrincipalName(),
 703                                     tgt);
 704                         }
 705                         if (GSSUtil.useSubjectCredsOnly(caller)) {
 706                             final Subject subject =
 707                                 AccessController.doPrivileged(
 708                                 new java.security.PrivilegedAction<Subject>() {
 709                                     public Subject run() {
 710                                         return (Subject.getSubject(acc));
 711                                     }
 712                                 });
 713                             if (subject != null &&
 714                                 !subject.isReadOnly()) {
 715                                 /*
 716                                 * Store the service credentials as
 717                                 * javax.security.auth.kerberos.KerberosTicket in
 718                                 * the Subject. We could wait until the context is
 719                                 * successfully established; however it is easier
 720                                 * to do it here and there is no harm.
 721                                 */
 722                                 final KerberosTicket kt =
 723                                         Krb5Util.credsToTicket(serviceCreds);
 724                                 AccessController.doPrivileged (
 725                                     new java.security.PrivilegedAction<Void>() {
 726                                       public Void run() {
 727                                         subject.getPrivateCredentials().add(kt);
 728                                         return null;
 729                                       }
 730                                     });
 731                             } else {
 732                                 // log it for debugging purpose
 733                                 if (DEBUG) {
 734                                     System.out.println("Subject is " +
 735                                         "readOnly;Kerberos Service "+
 736                                         "ticket not stored");
 737                                 }
 738                             }
 739                         }
 740                     }
 741 
 742                     errorCode = GSSException.FAILURE;
 743                     token = new InitSecContextToken(this, tgt, serviceCreds);
 744                     apReq = ((InitSecContextToken)token).getKrbApReq();
 745                     retVal = token.encode();
 746                     myCred = null;
 747                     if (!getMutualAuthState()) {
 748                         state = STATE_DONE;
 749                     }
 750                     if (DEBUG) {
 751                         System.out.println("Created InitSecContextToken:\n"+
 752                             new HexDumpEncoder().encodeBuffer(retVal));
 753                     }
 754                 } else if (state == STATE_IN_PROCESS) {
 755                     // No need to write anything;
 756                     // just validate the incoming token
 757                     new AcceptSecContextToken(this, serviceCreds, apReq, is);
 758                     apReq = null;
 759                     state = STATE_DONE;
 760                 } else {
 761                     // XXX Use logging API?
 762                     if (DEBUG) {
 763                         System.out.println(state);
 764                     }
 765                 }
 766             } catch (KrbException e) {
 767                 if (DEBUG) {
 768                     e.printStackTrace();
 769                 }
 770                 GSSException gssException =
 771                         new GSSException(errorCode, -1, e.getMessage());
 772                 gssException.initCause(e);
 773                 throw gssException;
 774             } catch (IOException e) {
 775                 GSSException gssException =
 776                         new GSSException(errorCode, -1, e.getMessage());
 777                 gssException.initCause(e);
 778                 throw gssException;
 779             }
 780             return retVal;
 781         }
 782 
 783     public final boolean isEstablished() {
 784         return (state == STATE_DONE);
 785     }
 786 
 787     /**
 788      * Acceptor's context establishment call. This method may be
 789      * required to be called several times. A CONTINUE_NEEDED return
 790      * call indicates that more calls are needed after the next token
 791      * is received from the peer.
 792      *
 793      * @param is contains the token received from the peer.
 794      * @return any token required to be sent to the peer
 795      *    It is responsibility of the caller
 796      *    to send the token to its peer for processing.
 797      * @exception GSSException
 798      */
 799     public final byte[] acceptSecContext(InputStream is, int mechTokenSize)
 800         throws GSSException {
 801 
 802         byte[] retVal = null;
 803 
 804         if (DEBUG) {
 805             System.out.println("Entered Krb5Context.acceptSecContext with " +
 806                                "state=" +  printState(state));
 807         }
 808 
 809         if (isInitiator()) {
 810             throw new GSSException(GSSException.FAILURE, -1,
 811                                    "acceptSecContext on an initiator " +
 812                                    "GSSContext");
 813         }
 814         try {
 815             if (state == STATE_NEW) {
 816                 state = STATE_IN_PROCESS;
 817                 if (myCred == null) {
 818                     myCred = Krb5AcceptCredential.getInstance(caller, myName);
 819                 } else if (!myCred.isAcceptorCredential()) {
 820                     throw new GSSException(GSSException.NO_CRED, -1,
 821                                            "No Secret Key available");
 822                 }
 823                 myName = (Krb5NameElement) myCred.getName();
 824 
 825                 // If there is already a bound name, check now
 826                 if (myName != null) {
 827                     Krb5MechFactory.checkAcceptCredPermission(myName, myName);
 828                 }
 829 
 830                 InitSecContextToken token = new InitSecContextToken(this,
 831                                                     (Krb5AcceptCredential) myCred, is);
 832                 PrincipalName clientName = token.getKrbApReq().getClient();
 833                 peerName = Krb5NameElement.getInstance(clientName);
 834 
 835                 // If unbound, check after the bound name is found
 836                 if (myName == null) {
 837                     myName = Krb5NameElement.getInstance(
 838                         token.getKrbApReq().getCreds().getServer());
 839                     Krb5MechFactory.checkAcceptCredPermission(myName, myName);
 840                 }
 841 
 842                 if (getMutualAuthState()) {
 843                         retVal = new AcceptSecContextToken(this,
 844                                           token.getKrbApReq()).encode();
 845                 }
 846                 serviceTicket = token.getKrbApReq().getCreds().getTicket();
 847                 myCred = null;
 848                 state = STATE_DONE;
 849             } else  {
 850                 // XXX Use logging API?
 851                 if (DEBUG) {
 852                     System.out.println(state);
 853                 }
 854             }
 855         } catch (KrbException e) {
 856             GSSException gssException =
 857                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
 858             gssException.initCause(e);
 859             throw gssException;
 860         } catch (IOException e) {
 861             if (DEBUG) {
 862                 e.printStackTrace();
 863             }
 864             GSSException gssException =
 865                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
 866             gssException.initCause(e);
 867             throw gssException;
 868         }
 869 
 870         return retVal;
 871     }
 872 
 873     /**
 874      * Queries the context for largest data size to accommodate
 875      * the specified protection and be <= maxTokSize.
 876      *
 877      * @param qop the quality of protection that the context will be
 878      *  asked to provide.
 879      * @param confReq a flag indicating whether confidentiality will be
 880      *  requested or not
 881      * @param outputSize the maximum size of the output token
 882      * @return the maximum size for the input message that can be
 883      *  provided to the wrap() method in order to guarantee that these
 884      *  requirements are met.
 885      * @throws GSSException
 886      */
 887     public final int getWrapSizeLimit(int qop, boolean confReq,
 888                                        int maxTokSize) throws GSSException {
 889 
 890         int retVal = 0;
 891         if (cipherHelper.getProto() == 0) {
 892             retVal = WrapToken.getSizeLimit(qop, confReq, maxTokSize,
 893                                         getCipherHelper(null));
 894         } else if (cipherHelper.getProto() == 1) {
 895             retVal = WrapToken_v2.getSizeLimit(qop, confReq, maxTokSize,
 896                                         getCipherHelper(null));
 897         }
 898         return retVal;
 899     }
 900 
 901     /*
 902      * Per-message calls depend on the sequence number. The sequence number
 903      * synchronization is at a finer granularity because wrap and getMIC
 904      * care about the local sequence number (mySeqNumber) where are unwrap
 905      * and verifyMIC care about the remote sequence number (peerSeqNumber).
 906      */
 907 
 908     public final byte[] wrap(byte[] inBuf, int offset, int len,
 909                              MessageProp msgProp) throws GSSException {
 910         if (DEBUG) {
 911             System.out.println("Krb5Context.wrap: data=["
 912                                + getHexBytes(inBuf, offset, len)
 913                                + "]");
 914         }
 915 
 916         if (state != STATE_DONE)
 917         throw new GSSException(GSSException.NO_CONTEXT, -1,
 918                                "Wrap called in invalid state!");
 919 
 920         byte[] encToken = null;
 921         try {
 922             if (cipherHelper.getProto() == 0) {
 923                 WrapToken token =
 924                         new WrapToken(this, msgProp, inBuf, offset, len);
 925                 encToken = token.encode();
 926             } else if (cipherHelper.getProto() == 1) {
 927                 WrapToken_v2 token =
 928                         new WrapToken_v2(this, msgProp, inBuf, offset, len);
 929                 encToken = token.encode();
 930             }
 931             if (DEBUG) {
 932                 System.out.println("Krb5Context.wrap: token=["
 933                                    + getHexBytes(encToken, 0, encToken.length)
 934                                    + "]");
 935             }
 936             return encToken;
 937         } catch (IOException e) {
 938             encToken = null;
 939             GSSException gssException =
 940                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
 941             gssException.initCause(e);
 942             throw gssException;
 943         }
 944     }
 945 
 946     public final int wrap(byte[] inBuf, int inOffset, int len,
 947                           byte[] outBuf, int outOffset,
 948                           MessageProp msgProp) throws GSSException {
 949 
 950         if (state != STATE_DONE)
 951             throw new GSSException(GSSException.NO_CONTEXT, -1,
 952                                    "Wrap called in invalid state!");
 953 
 954         int retVal = 0;
 955         try {
 956             if (cipherHelper.getProto() == 0) {
 957                 WrapToken token =
 958                         new WrapToken(this, msgProp, inBuf, inOffset, len);
 959                 retVal = token.encode(outBuf, outOffset);
 960             } else if (cipherHelper.getProto() == 1) {
 961                 WrapToken_v2 token =
 962                         new WrapToken_v2(this, msgProp, inBuf, inOffset, len);
 963                 retVal = token.encode(outBuf, outOffset);
 964             }
 965             if (DEBUG) {
 966                 System.out.println("Krb5Context.wrap: token=["
 967                                    + getHexBytes(outBuf, outOffset, retVal)
 968                                    + "]");
 969             }
 970             return retVal;
 971         } catch (IOException e) {
 972             retVal = 0;
 973             GSSException gssException =
 974                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
 975             gssException.initCause(e);
 976             throw gssException;
 977         }
 978     }
 979 
 980     public final void wrap(byte[] inBuf, int offset, int len,
 981                            OutputStream os, MessageProp msgProp)
 982         throws GSSException {
 983 
 984         if (state != STATE_DONE)
 985                 throw new GSSException(GSSException.NO_CONTEXT, -1,
 986                                        "Wrap called in invalid state!");
 987 
 988         byte[] encToken = null;
 989         try {
 990             if (cipherHelper.getProto() == 0) {
 991                 WrapToken token =
 992                         new WrapToken(this, msgProp, inBuf, offset, len);
 993                 token.encode(os);
 994                 if (DEBUG) {
 995                     encToken = token.encode();
 996                 }
 997             } else if (cipherHelper.getProto() == 1) {
 998                 WrapToken_v2 token =
 999                         new WrapToken_v2(this, msgProp, inBuf, offset, len);
1000                 token.encode(os);
1001                 if (DEBUG) {
1002                     encToken = token.encode();
1003                 }
1004             }
1005         } catch (IOException e) {
1006             GSSException gssException =
1007                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
1008             gssException.initCause(e);
1009             throw gssException;
1010         }
1011 
1012         if (DEBUG) {
1013             System.out.println("Krb5Context.wrap: token=["
1014                         + getHexBytes(encToken, 0, encToken.length)
1015                         + "]");
1016         }
1017     }
1018 
1019     public final void wrap(InputStream is, OutputStream os,
1020                             MessageProp msgProp) throws GSSException {
1021 
1022         byte[] data;
1023         try {
1024             data = new byte[is.available()];
1025             is.read(data);
1026         } catch (IOException e) {
1027             GSSException gssException =
1028                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
1029             gssException.initCause(e);
1030             throw gssException;
1031         }
1032         wrap(data, 0, data.length, os, msgProp);
1033     }
1034 
1035     public final byte[] unwrap(byte[] inBuf, int offset, int len,
1036                                MessageProp msgProp)
1037         throws GSSException {
1038 
1039             if (DEBUG) {
1040                 System.out.println("Krb5Context.unwrap: token=["
1041                                    + getHexBytes(inBuf, offset, len)
1042                                    + "]");
1043             }
1044 
1045             if (state != STATE_DONE) {
1046                 throw new GSSException(GSSException.NO_CONTEXT, -1,
1047                                        " Unwrap called in invalid state!");
1048             }
1049 
1050             byte[] data = null;
1051             if (cipherHelper.getProto() == 0) {
1052                 WrapToken token =
1053                         new WrapToken(this, inBuf, offset, len, msgProp);
1054                 data = token.getData();
1055                 setSequencingAndReplayProps(token, msgProp);
1056             } else if (cipherHelper.getProto() == 1) {
1057                 WrapToken_v2 token =
1058                         new WrapToken_v2(this, inBuf, offset, len, msgProp);
1059                 data = token.getData();
1060                 setSequencingAndReplayProps(token, msgProp);
1061             }
1062 
1063             if (DEBUG) {
1064                 System.out.println("Krb5Context.unwrap: data=["
1065                                    + getHexBytes(data, 0, data.length)
1066                                    + "]");
1067             }
1068 
1069             return data;
1070         }
1071 
1072     public final int unwrap(byte[] inBuf, int inOffset, int len,
1073                              byte[] outBuf, int outOffset,
1074                              MessageProp msgProp) throws GSSException {
1075 
1076         if (state != STATE_DONE)
1077             throw new GSSException(GSSException.NO_CONTEXT, -1,
1078                                    "Unwrap called in invalid state!");
1079 
1080         if (cipherHelper.getProto() == 0) {
1081             WrapToken token =
1082                         new WrapToken(this, inBuf, inOffset, len, msgProp);
1083             len = token.getData(outBuf, outOffset);
1084             setSequencingAndReplayProps(token, msgProp);
1085         } else if (cipherHelper.getProto() == 1) {
1086             WrapToken_v2 token =
1087                         new WrapToken_v2(this, inBuf, inOffset, len, msgProp);
1088             len = token.getData(outBuf, outOffset);
1089             setSequencingAndReplayProps(token, msgProp);
1090         }
1091         return len;
1092     }
1093 
1094     public final int unwrap(InputStream is,
1095                             byte[] outBuf, int outOffset,
1096                             MessageProp msgProp) throws GSSException {
1097 
1098         if (state != STATE_DONE)
1099             throw new GSSException(GSSException.NO_CONTEXT, -1,
1100                                    "Unwrap called in invalid state!");
1101 
1102         int len = 0;
1103         if (cipherHelper.getProto() == 0) {
1104             WrapToken token = new WrapToken(this, is, msgProp);
1105             len = token.getData(outBuf, outOffset);
1106             setSequencingAndReplayProps(token, msgProp);
1107         } else if (cipherHelper.getProto() == 1) {
1108             WrapToken_v2 token = new WrapToken_v2(this, is, msgProp);
1109             len = token.getData(outBuf, outOffset);
1110             setSequencingAndReplayProps(token, msgProp);
1111         }
1112         return len;
1113     }
1114 
1115 
1116     public final void unwrap(InputStream is, OutputStream os,
1117                              MessageProp msgProp) throws GSSException {
1118 
1119         if (state != STATE_DONE)
1120             throw new GSSException(GSSException.NO_CONTEXT, -1,
1121                                    "Unwrap called in invalid state!");
1122 
1123         byte[] data = null;
1124         if (cipherHelper.getProto() == 0) {
1125             WrapToken token = new WrapToken(this, is, msgProp);
1126             data = token.getData();
1127             setSequencingAndReplayProps(token, msgProp);
1128         } else if (cipherHelper.getProto() == 1) {
1129             WrapToken_v2 token = new WrapToken_v2(this, is, msgProp);
1130             data = token.getData();
1131             setSequencingAndReplayProps(token, msgProp);
1132         }
1133 
1134         try {
1135             os.write(data);
1136         } catch (IOException e) {
1137             GSSException gssException =
1138                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
1139             gssException.initCause(e);
1140             throw gssException;
1141         }
1142     }
1143 
1144     public final byte[] getMIC(byte[] inMsg, int offset, int len,
1145                                MessageProp msgProp)
1146         throws GSSException {
1147 
1148             byte[] micToken = null;
1149             try {
1150                 if (cipherHelper.getProto() == 0) {
1151                     MicToken token =
1152                         new MicToken(this, msgProp, inMsg, offset, len);
1153                     micToken = token.encode();
1154                 } else if (cipherHelper.getProto() == 1) {
1155                     MicToken_v2 token =
1156                         new MicToken_v2(this, msgProp, inMsg, offset, len);
1157                     micToken = token.encode();
1158                 }
1159                 return micToken;
1160             } catch (IOException e) {
1161                 micToken = null;
1162                 GSSException gssException =
1163                     new GSSException(GSSException.FAILURE, -1, e.getMessage());
1164                 gssException.initCause(e);
1165                 throw gssException;
1166             }
1167         }
1168 
1169     private int getMIC(byte[] inMsg, int offset, int len,
1170                        byte[] outBuf, int outOffset,
1171                        MessageProp msgProp)
1172         throws GSSException {
1173 
1174         int retVal = 0;
1175         try {
1176             if (cipherHelper.getProto() == 0) {
1177                 MicToken token =
1178                         new MicToken(this, msgProp, inMsg, offset, len);
1179                 retVal = token.encode(outBuf, outOffset);
1180             } else if (cipherHelper.getProto() == 1) {
1181                 MicToken_v2 token =
1182                         new MicToken_v2(this, msgProp, inMsg, offset, len);
1183                 retVal = token.encode(outBuf, outOffset);
1184             }
1185             return retVal;
1186         } catch (IOException e) {
1187             retVal = 0;
1188             GSSException gssException =
1189                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
1190             gssException.initCause(e);
1191             throw gssException;
1192         }
1193     }
1194 
1195     /*
1196      * Checksum calculation requires a byte[]. Hence might as well pass
1197      * a byte[] into the MicToken constructor. However, writing the
1198      * token can be optimized for cases where the application passed in
1199      * an OutputStream.
1200      */
1201 
1202     private void getMIC(byte[] inMsg, int offset, int len,
1203                         OutputStream os, MessageProp msgProp)
1204         throws GSSException {
1205 
1206         try {
1207             if (cipherHelper.getProto() == 0) {
1208                 MicToken token =
1209                         new MicToken(this, msgProp, inMsg, offset, len);
1210                 token.encode(os);
1211             } else if (cipherHelper.getProto() == 1) {
1212                 MicToken_v2 token =
1213                         new MicToken_v2(this, msgProp, inMsg, offset, len);
1214                 token.encode(os);
1215             }
1216         } catch (IOException e) {
1217             GSSException gssException =
1218                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
1219             gssException.initCause(e);
1220             throw gssException;
1221         }
1222     }
1223 
1224     public final void getMIC(InputStream is, OutputStream os,
1225                               MessageProp msgProp) throws GSSException {
1226         byte[] data;
1227         try {
1228             data = new byte[is.available()];
1229             is.read(data);
1230         } catch (IOException e) {
1231             GSSException gssException =
1232                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
1233             gssException.initCause(e);
1234             throw gssException;
1235         }
1236         getMIC(data, 0, data.length, os, msgProp);
1237     }
1238 
1239     public final void verifyMIC(byte[] inTok, int tokOffset, int tokLen,
1240                                 byte[] inMsg, int msgOffset, int msgLen,
1241                                 MessageProp msgProp)
1242         throws GSSException {
1243 
1244         if (cipherHelper.getProto() == 0) {
1245             MicToken token =
1246                 new MicToken(this, inTok, tokOffset, tokLen, msgProp);
1247             token.verify(inMsg, msgOffset, msgLen);
1248             setSequencingAndReplayProps(token, msgProp);
1249         } else if (cipherHelper.getProto() == 1) {
1250             MicToken_v2 token =
1251                 new MicToken_v2(this, inTok, tokOffset, tokLen, msgProp);
1252             token.verify(inMsg, msgOffset, msgLen);
1253             setSequencingAndReplayProps(token, msgProp);
1254         }
1255     }
1256 
1257     private void verifyMIC(InputStream is,
1258                            byte[] inMsg, int msgOffset, int msgLen,
1259                            MessageProp msgProp)
1260         throws GSSException {
1261 
1262         if (cipherHelper.getProto() == 0) {
1263             MicToken token = new MicToken(this, is, msgProp);
1264             token.verify(inMsg, msgOffset, msgLen);
1265             setSequencingAndReplayProps(token, msgProp);
1266         } else if (cipherHelper.getProto() == 1) {
1267             MicToken_v2 token = new MicToken_v2(this, is, msgProp);
1268             token.verify(inMsg, msgOffset, msgLen);
1269             setSequencingAndReplayProps(token, msgProp);
1270         }
1271     }
1272 
1273     public final void verifyMIC(InputStream is, InputStream msgStr,
1274                                  MessageProp mProp) throws GSSException {
1275         byte[] msg;
1276         try {
1277             msg = new byte[msgStr.available()];
1278             msgStr.read(msg);
1279         } catch (IOException e) {
1280             GSSException gssException =
1281                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
1282             gssException.initCause(e);
1283             throw gssException;
1284         }
1285         verifyMIC(is, msg, 0, msg.length, mProp);
1286     }
1287 
1288     /**
1289      * Produces a token representing this context. After this call
1290      * the context will no longer be usable until an import is
1291      * performed on the returned token.
1292      *
1293      * @param os the output token will be written to this stream
1294      * @exception GSSException
1295      */
1296     public final byte[] export() throws GSSException {
1297         throw new GSSException(GSSException.UNAVAILABLE, -1,
1298                                "GSS Export Context not available");
1299     }
1300 
1301     /**
1302      * Releases context resources and terminates the
1303      * context between 2 peer.
1304      *
1305      * @exception GSSException with major codes NO_CONTEXT, FAILURE.
1306      */
1307 
1308     public final void dispose() throws GSSException {
1309         state = STATE_DELETED;
1310         delegatedCred = null;
1311         tgt = null;
1312         serviceCreds = null;
1313         key = null;
1314     }
1315 
1316     public final Provider getProvider() {
1317         return Krb5MechFactory.PROVIDER;
1318     }
1319 
1320     /**
1321      * Sets replay and sequencing information for a message token received
1322      * form the peer.
1323      */
1324     private void setSequencingAndReplayProps(MessageToken token,
1325                                              MessageProp prop) {
1326         if (replayDetState || sequenceDetState) {
1327             int seqNum = token.getSequenceNumber();
1328             peerTokenTracker.getProps(seqNum, prop);
1329         }
1330     }
1331 
1332     /**
1333      * Sets replay and sequencing information for a message token received
1334      * form the peer.
1335      */
1336     private void setSequencingAndReplayProps(MessageToken_v2 token,
1337                                              MessageProp prop) {
1338         if (replayDetState || sequenceDetState) {
1339             int seqNum = token.getSequenceNumber();
1340             peerTokenTracker.getProps(seqNum, prop);
1341         }
1342     }
1343 
1344     private void checkPermission(String principal, String action) {
1345         SecurityManager sm = System.getSecurityManager();
1346         if (sm != null) {
1347             ServicePermission perm =
1348                 new ServicePermission(principal, action);
1349             sm.checkPermission(perm);
1350         }
1351     }
1352 
1353     private static String getHexBytes(byte[] bytes, int pos, int len) {
1354 
1355         StringBuilder sb = new StringBuilder();
1356         for (int i = 0; i < len; i++) {
1357 
1358             int b1 = (bytes[i]>>4) & 0x0f;
1359             int b2 = bytes[i] & 0x0f;
1360 
1361             sb.append(Integer.toHexString(b1));
1362             sb.append(Integer.toHexString(b2));
1363             sb.append(' ');
1364         }
1365         return sb.toString();
1366     }
1367 
1368     private static String printState(int state) {
1369         switch (state) {
1370           case STATE_NEW:
1371                 return ("STATE_NEW");
1372           case STATE_IN_PROCESS:
1373                 return ("STATE_IN_PROCESS");
1374           case STATE_DONE:
1375                 return ("STATE_DONE");
1376           case STATE_DELETED:
1377                 return ("STATE_DELETED");
1378           default:
1379                 return ("Unknown state " + state);
1380         }
1381     }
1382 
1383     GSSCaller getCaller() {
1384         // Currently used by InitialToken only
1385         return caller;
1386     }
1387 
1388     /**
1389      * The session key returned by inquireSecContext(KRB5_INQ_SSPI_SESSION_KEY)
1390      */
1391     static class KerberosSessionKey implements Key {
1392         private static final long serialVersionUID = 699307378954123869L;
1393 
1394         @SuppressWarnings("serial") // Not statically typed as Serializable
1395         private final EncryptionKey key;
1396 
1397         KerberosSessionKey(EncryptionKey key) {
1398             this.key = key;
1399         }
1400 
1401         @Override
1402         public String getAlgorithm() {
1403             return Integer.toString(key.getEType());
1404         }
1405 
1406         @Override
1407         public String getFormat() {
1408             return "RAW";
1409         }
1410 
1411         @Override
1412         public byte[] getEncoded() {
1413             return key.getBytes().clone();
1414         }
1415 
1416         @Override
1417         public String toString() {
1418             return "Kerberos session key: etype: " + key.getEType() + "\n" +
1419                     new HexDumpEncoder().encodeBuffer(key.getBytes());
1420         }
1421     }
1422 
1423     /**
1424      * Return the mechanism-specific attribute associated with {@code type}.
1425      */
1426     public Object inquireSecContext(String type)
1427             throws GSSException {
1428         if (!isEstablished()) {
1429              throw new GSSException(GSSException.NO_CONTEXT, -1,
1430                      "Security context not established.");
1431         }
1432         switch (type) {
1433             case "KRB5_GET_SESSION_KEY":
1434                 return new KerberosSessionKey(key);
1435             case "KRB5_GET_SESSION_KEY_EX":
1436                 return new javax.security.auth.kerberos.EncryptionKey(
1437                         key.getBytes(), key.getEType());
1438             case "KRB5_GET_TKT_FLAGS":
1439                 return tktFlags.clone();
1440             case "KRB5_GET_AUTHZ_DATA":
1441                 if (isInitiator()) {
1442                     throw new GSSException(GSSException.UNAVAILABLE, -1,
1443                             "AuthzData not available on initiator side.");
1444                 } else {
1445                     return authzData;
1446                 }
1447             case "KRB5_GET_AUTHTIME":
1448                 return authTime;
1449             case "KRB5_GET_KRB_CRED":
1450                 if (!isInitiator()) {
1451                     throw new GSSException(GSSException.UNAVAILABLE, -1,
1452                             "KRB_CRED not available on acceptor side.");
1453                 }
1454                 KerberosPrincipal sender = new KerberosPrincipal(
1455                         myName.getKrb5PrincipalName().getName());
1456                 KerberosPrincipal recipient = new KerberosPrincipal(
1457                         peerName.getKrb5PrincipalName().getName());
1458                 try {
1459                     byte[] krbCred = new KrbCred(tgt, serviceCreds, key)
1460                             .getMessage();
1461                     return new KerberosCredMessage(
1462                             sender, recipient, krbCred);
1463                 } catch (KrbException | IOException e) {
1464                     GSSException gsse = new GSSException(GSSException.UNAVAILABLE, -1,
1465                             "KRB_CRED not generated correctly.");
1466                     gsse.initCause(e);
1467                     throw gsse;
1468                 }
1469         }
1470         throw new GSSException(GSSException.UNAVAILABLE, -1,
1471                 "Inquire type not supported.");
1472     }
1473 
1474     // Helpers for inquireSecContext
1475     private boolean[] tktFlags;
1476     private String authTime;
1477     private AuthorizationData authzData;
1478 
1479     public void setTktFlags(boolean[] tktFlags) {
1480         this.tktFlags = tktFlags;
1481     }
1482 
1483     public void setAuthTime(String authTime) {
1484         this.authTime = authTime;
1485     }
1486 
1487     public void setAuthzData(AuthorizationData authzData) {
1488         this.authzData = authzData;
1489     }
1490 
1491 }