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