1 /*
   2  * Copyright (c) 2005, 2013, 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.spnego;
  27 
  28 import java.io.*;
  29 import java.security.Provider;
  30 import org.ietf.jgss.*;
  31 import sun.security.jgss.*;
  32 import sun.security.jgss.spi.*;
  33 import sun.security.util.*;
  34 
  35 /**
  36  * Implements the mechanism specific context class for SPNEGO
  37  * GSS-API mechanism
  38  *
  39  * @author Seema Malkani
  40  * @since 1.6
  41  */
  42 public class SpNegoContext implements GSSContextSpi {
  43 
  44     /*
  45      * The different states that this context can be in.
  46      */
  47     private static final int STATE_NEW = 1;
  48     private static final int STATE_IN_PROCESS = 2;
  49     private static final int STATE_DONE = 3;
  50     private static final int STATE_DELETED = 4;
  51 
  52     private int state = STATE_NEW;
  53 
  54     /*
  55      * Optional features that the application can set and their default
  56      * values.
  57      */
  58     private boolean credDelegState = false;
  59     private boolean mutualAuthState = true;
  60     private boolean replayDetState = true;
  61     private boolean sequenceDetState = true;
  62     private boolean confState = true;
  63     private boolean integState = true;
  64     private boolean delegPolicyState = false;
  65 
  66     private GSSNameSpi peerName = null;
  67     private GSSNameSpi myName = null;
  68     private SpNegoCredElement myCred = null;
  69 
  70     private GSSContext mechContext = null;
  71     private byte[] DER_mechTypes = null;
  72 
  73     private int lifetime;
  74     private ChannelBinding channelBinding;
  75     private boolean initiator;
  76 
  77     // the underlying negotiated mechanism
  78     private Oid internal_mech = null;
  79 
  80     // the SpNegoMechFactory that creates this context
  81     final private SpNegoMechFactory factory;
  82 
  83     // debug property
  84     static final boolean DEBUG =
  85         java.security.AccessController.doPrivileged(
  86             new sun.security.action.GetBooleanAction
  87             ("sun.security.spnego.debug")).booleanValue();
  88 
  89     /**
  90      * Constructor for SpNegoContext to be called on the context initiator's
  91      * side.
  92      */
  93     public SpNegoContext(SpNegoMechFactory factory, GSSNameSpi peerName,
  94                         GSSCredentialSpi myCred,
  95                         int lifetime) throws GSSException {
  96 
  97         if (peerName == null)
  98             throw new IllegalArgumentException("Cannot have null peer name");
  99         if ((myCred != null) && !(myCred instanceof SpNegoCredElement)) {
 100             throw new IllegalArgumentException("Wrong cred element type");
 101         }
 102         this.peerName = peerName;
 103         this.myCred = (SpNegoCredElement) myCred;
 104         this.lifetime = lifetime;
 105         this.initiator = true;
 106         this.factory = factory;
 107     }
 108 
 109     /**
 110      * Constructor for SpNegoContext to be called on the context acceptor's
 111      * side.
 112      */
 113     public SpNegoContext(SpNegoMechFactory factory, GSSCredentialSpi myCred)
 114             throws GSSException {
 115         if ((myCred != null) && !(myCred instanceof SpNegoCredElement)) {
 116             throw new IllegalArgumentException("Wrong cred element type");
 117         }
 118         this.myCred = (SpNegoCredElement) myCred;
 119         this.initiator = false;
 120         this.factory = factory;
 121     }
 122 
 123     /**
 124      * Constructor for SpNegoContext to import a previously exported context.
 125      */
 126     public SpNegoContext(SpNegoMechFactory factory, byte [] interProcessToken)
 127         throws GSSException {
 128         throw new GSSException(GSSException.UNAVAILABLE,
 129                                -1, "GSS Import Context not available");
 130     }
 131 
 132     /**
 133      * Requests that confidentiality be available.
 134      */
 135     public final void requestConf(boolean value) throws GSSException {
 136         if (state == STATE_NEW && isInitiator())
 137             confState  = value;
 138     }
 139 
 140     /**
 141      * Is confidentiality available?
 142      */
 143     public final boolean getConfState() {
 144         return confState;
 145     }
 146 
 147     /**
 148      * Requests that integrity be available.
 149      */
 150     public final void requestInteg(boolean value) throws GSSException {
 151         if (state == STATE_NEW && isInitiator())
 152             integState  = value;
 153     }
 154 
 155     /**
 156      * Requests that deleg policy be respected.
 157      */
 158     public final void requestDelegPolicy(boolean value) throws GSSException {
 159         if (state == STATE_NEW && isInitiator())
 160             delegPolicyState = value;
 161     }
 162 
 163     /**
 164      * Is integrity available?
 165      */
 166     public final boolean getIntegState() {
 167         return integState;
 168     }
 169 
 170     /**
 171      * Is deleg policy respected?
 172      */
 173     public final boolean getDelegPolicyState() {
 174         if (isInitiator() && mechContext != null &&
 175                 mechContext instanceof GSSContextImpl &&
 176                 (state == STATE_IN_PROCESS || state == STATE_DONE)) {
 177             return ((GSSContextImpl)mechContext).getDelegPolicyState();
 178         } else {
 179             return delegPolicyState;
 180         }
 181     }
 182 
 183     /**
 184      * Requests that credential delegation be done during context
 185      * establishment.
 186      */
 187     public final void requestCredDeleg(boolean value) throws GSSException {
 188         if (state == STATE_NEW && isInitiator())
 189             credDelegState  = value;
 190     }
 191 
 192     /**
 193      * Is credential delegation enabled?
 194      */
 195     public final boolean getCredDelegState() {
 196         if (isInitiator() && mechContext != null &&
 197                 (state == STATE_IN_PROCESS || state == STATE_DONE)) {
 198             return mechContext.getCredDelegState();
 199         } else {
 200             return credDelegState;
 201         }
 202     }
 203 
 204     /**
 205      * Requests that mutual authentication be done during context
 206      * establishment. Since this is fromm the client's perspective, it
 207      * essentially requests that the server be authenticated.
 208      */
 209     public final void requestMutualAuth(boolean value) throws GSSException {
 210         if (state == STATE_NEW && isInitiator()) {
 211             mutualAuthState  = value;
 212         }
 213     }
 214 
 215     /**
 216      * Is mutual authentication enabled? Since this is from the client's
 217      * perspective, it essentially meas that the server is being
 218      * authenticated.
 219      */
 220     public final boolean getMutualAuthState() {
 221         return mutualAuthState;
 222     }
 223 
 224     /**
 225      * Returns the mechanism oid.
 226      *
 227      * @return the Oid of this context
 228      */
 229     public final Oid getMech() {
 230         if (isEstablished()) {
 231             return getNegotiatedMech();
 232         }
 233         return (SpNegoMechFactory.GSS_SPNEGO_MECH_OID);
 234     }
 235 
 236     public final Oid getNegotiatedMech() {
 237         return (internal_mech);
 238     }
 239 
 240     public final Provider getProvider() {
 241         return SpNegoMechFactory.PROVIDER;
 242     }
 243 
 244     public final void dispose() throws GSSException {
 245         mechContext = null;
 246         state = STATE_DELETED;
 247     }
 248 
 249     /**
 250      * Tests if this is the initiator side of the context.
 251      *
 252      * @return boolean indicating if this is initiator (true)
 253      *  or target (false)
 254      */
 255     public final boolean isInitiator() {
 256         return initiator;
 257     }
 258 
 259     /**
 260      * Tests if the context can be used for per-message service.
 261      * Context may allow the calls to the per-message service
 262      * functions before being fully established.
 263      *
 264      * @return boolean indicating if per-message methods can
 265      *  be called.
 266      */
 267     public final boolean isProtReady() {
 268         return (state == STATE_DONE);
 269     }
 270 
 271     /**
 272      * Initiator context establishment call. This method may be
 273      * required to be called several times. A CONTINUE_NEEDED return
 274      * call indicates that more calls are needed after the next token
 275      * is received from the peer.
 276      *
 277      * @param is contains the token received from the peer. On the
 278      *        first call it will be ignored.
 279      * @return any token required to be sent to the peer
 280      * It is responsibility of the caller to send the token
 281      * to its peer for processing.
 282      * @exception GSSException
 283      */
 284     public final byte[] initSecContext(InputStream is, int mechTokenSize)
 285         throws GSSException {
 286 
 287         byte[] retVal = null;
 288         NegTokenInit initToken = null;
 289         byte[] mechToken = null;
 290         int errorCode = GSSException.FAILURE;
 291 
 292         if (DEBUG) {
 293             System.out.println("Entered SpNego.initSecContext with " +
 294                                 "state=" + printState(state));
 295         }
 296         if (!isInitiator()) {
 297             throw new GSSException(GSSException.FAILURE, -1,
 298                 "initSecContext on an acceptor GSSContext");
 299         }
 300 
 301         try {
 302             if (state == STATE_NEW) {
 303                 state = STATE_IN_PROCESS;
 304 
 305                 errorCode = GSSException.NO_CRED;
 306 
 307                 // determine available mech set
 308                 Oid[] mechList = getAvailableMechs();
 309                 DER_mechTypes = getEncodedMechs(mechList);
 310 
 311                 // pull out first mechanism
 312                 internal_mech = mechList[0];
 313 
 314                 // get the token for first mechanism
 315                 mechToken = GSS_initSecContext(null);
 316 
 317                 errorCode = GSSException.DEFECTIVE_TOKEN;
 318                 // generate SPNEGO token
 319                 initToken = new NegTokenInit(DER_mechTypes, getContextFlags(),
 320                                         mechToken, null);
 321                 if (DEBUG) {
 322                     System.out.println("SpNegoContext.initSecContext: " +
 323                                 "sending token of type = " +
 324                                 SpNegoToken.getTokenName(initToken.getType()));
 325                 }
 326                 // get the encoded token
 327                 retVal = initToken.getEncoded();
 328 
 329             } else if (state == STATE_IN_PROCESS) {
 330 
 331                 errorCode = GSSException.FAILURE;
 332                 if (is == null) {
 333                     throw new GSSException(errorCode, -1,
 334                                 "No token received from peer!");
 335                 }
 336 
 337                 errorCode = GSSException.DEFECTIVE_TOKEN;
 338                 byte[] server_token = new byte[is.available()];
 339                 SpNegoToken.readFully(is, server_token);
 340                 if (DEBUG) {
 341                     System.out.println("SpNegoContext.initSecContext: " +
 342                                         "process received token = " +
 343                                         SpNegoToken.getHexBytes(server_token));
 344                 }
 345 
 346                 // read the SPNEGO token
 347                 // token will be validated when parsing
 348                 NegTokenTarg targToken = new NegTokenTarg(server_token);
 349 
 350                 if (DEBUG) {
 351                     System.out.println("SpNegoContext.initSecContext: " +
 352                                 "received token of type = " +
 353                                 SpNegoToken.getTokenName(targToken.getType()));
 354                 }
 355 
 356                 // pull out mechanism
 357                 internal_mech = targToken.getSupportedMech();
 358                 if (internal_mech == null) {
 359                     // return wth failure
 360                     throw new GSSException(errorCode, -1,
 361                                 "supported mechanism from server is null");
 362                 }
 363 
 364                 // get the negotiated result
 365                 SpNegoToken.NegoResult negoResult = null;
 366                 int result = targToken.getNegotiatedResult();
 367                 switch (result) {
 368                     case 0:
 369                         negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE;
 370                         state = STATE_DONE;
 371                         break;
 372                     case 1:
 373                         negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE;
 374                         state = STATE_IN_PROCESS;
 375                         break;
 376                     case 2:
 377                         negoResult = SpNegoToken.NegoResult.REJECT;
 378                         state = STATE_DELETED;
 379                         break;
 380                     default:
 381                         state = STATE_DONE;
 382                         break;
 383                 }
 384 
 385                 errorCode = GSSException.BAD_MECH;
 386 
 387                 if (negoResult == SpNegoToken.NegoResult.REJECT) {
 388                     throw new GSSException(errorCode, -1,
 389                                         internal_mech.toString());
 390                 }
 391 
 392                 errorCode = GSSException.DEFECTIVE_TOKEN;
 393 
 394                 if ((negoResult == SpNegoToken.NegoResult.ACCEPT_COMPLETE) ||
 395                     (negoResult == SpNegoToken.NegoResult.ACCEPT_INCOMPLETE)) {
 396 
 397                     // pull out the mechanism token
 398                     byte[] accept_token = targToken.getResponseToken();
 399                     if (accept_token == null) {
 400                         if (!isMechContextEstablished()) {
 401                             // return with failure
 402                             throw new GSSException(errorCode, -1,
 403                                     "mechanism token from server is null");
 404                         }
 405                     } else {
 406                         mechToken = GSS_initSecContext(accept_token);
 407                     }
 408                     // verify MIC
 409                     if (!GSSUtil.useMSInterop()) {
 410                         byte[] micToken = targToken.getMechListMIC();
 411                         if (!verifyMechListMIC(DER_mechTypes, micToken)) {
 412                             throw new GSSException(errorCode, -1,
 413                                 "verification of MIC on MechList Failed!");
 414                         }
 415                     }
 416                     if (isMechContextEstablished()) {
 417                         state = STATE_DONE;
 418                         retVal = mechToken;
 419                         if (DEBUG) {
 420                             System.out.println("SPNEGO Negotiated Mechanism = "
 421                                 + internal_mech + " " +
 422                                 GSSUtil.getMechStr(internal_mech));
 423                         }
 424                     } else {
 425                         // generate SPNEGO token
 426                         initToken = new NegTokenInit(null, null,
 427                                                 mechToken, null);
 428                         if (DEBUG) {
 429                             System.out.println("SpNegoContext.initSecContext:" +
 430                                 " continue sending token of type = " +
 431                                 SpNegoToken.getTokenName(initToken.getType()));
 432                         }
 433                         // get the encoded token
 434                         retVal = initToken.getEncoded();
 435                     }
 436                 }
 437 
 438             } else {
 439                 // XXX Use logging API
 440                 if (DEBUG) {
 441                     System.out.println(state);
 442                 }
 443             }
 444             if (DEBUG) {
 445                 if (retVal != null) {
 446                     System.out.println("SNegoContext.initSecContext: " +
 447                         "sending token = " + SpNegoToken.getHexBytes(retVal));
 448                 }
 449             }
 450         } catch (GSSException e) {
 451             GSSException gssException =
 452                         new GSSException(errorCode, -1, e.getMessage());
 453             gssException.initCause(e);
 454             throw gssException;
 455         } catch (IOException e) {
 456             GSSException gssException =
 457                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
 458             gssException.initCause(e);
 459             throw gssException;
 460         }
 461 
 462         return retVal;
 463     }
 464 
 465 
 466     /**
 467      * Acceptor's context establishment call. This method may be
 468      * required to be called several times. A CONTINUE_NEEDED return
 469      * call indicates that more calls are needed after the next token
 470      * is received from the peer.
 471      *
 472      * @param is contains the token received from the peer.
 473      * @return any token required to be sent to the peer
 474      * It is responsibility of the caller to send the token
 475      * to its peer for processing.
 476      * @exception GSSException
 477      */
 478     public final byte[] acceptSecContext(InputStream is, int mechTokenSize)
 479         throws GSSException {
 480 
 481         byte[] retVal = null;
 482         SpNegoToken.NegoResult negoResult;
 483         boolean valid = true;
 484 
 485         if (DEBUG) {
 486             System.out.println("Entered SpNegoContext.acceptSecContext with " +
 487                                "state=" +  printState(state));
 488         }
 489 
 490         if (isInitiator()) {
 491             throw new GSSException(GSSException.FAILURE, -1,
 492                                    "acceptSecContext on an initiator " +
 493                                    "GSSContext");
 494         }
 495         try {
 496             if (state == STATE_NEW) {
 497                 state = STATE_IN_PROCESS;
 498 
 499                 // read data
 500                 byte[] token = new byte[is.available()];
 501                 SpNegoToken.readFully(is, token);
 502                 if (DEBUG) {
 503                     System.out.println("SpNegoContext.acceptSecContext: " +
 504                                         "receiving token = " +
 505                                         SpNegoToken.getHexBytes(token));
 506                 }
 507 
 508                 // read the SPNEGO token
 509                 // token will be validated when parsing
 510                 NegTokenInit initToken = new NegTokenInit(token);
 511 
 512                 if (DEBUG) {
 513                     System.out.println("SpNegoContext.acceptSecContext: " +
 514                                 "received token of type = " +
 515                                 SpNegoToken.getTokenName(initToken.getType()));
 516                 }
 517 
 518                 Oid[] mechList = initToken.getMechTypeList();
 519                 DER_mechTypes = initToken.getMechTypes();
 520                 if (DER_mechTypes == null) {
 521                     valid = false;
 522                 }
 523 
 524                 /*
 525                  * Select the best match between the list of mechs
 526                  * that the initiator requested and the list that
 527                  * the acceptor will support.
 528                  */
 529                 Oid[] supported_mechSet = getAvailableMechs();
 530                 Oid mech_wanted =
 531                         negotiate_mech_type(supported_mechSet, mechList);
 532                 if (mech_wanted == null) {
 533                     valid = false;
 534                 }
 535                 // save the desired mechanism
 536                 internal_mech = mech_wanted;
 537 
 538                 // get the token for mechanism
 539                 byte[] accept_token;
 540 
 541                 if (mechList[0].equals(mech_wanted)) {
 542                     // get the mechanism token
 543                     byte[] mechToken = initToken.getMechToken();
 544                     if (mechToken == null) {
 545                         throw new GSSException(GSSException.FAILURE, -1,
 546                                 "mechToken is missing");
 547                     }
 548                     accept_token = GSS_acceptSecContext(mechToken);
 549                 } else {
 550                     accept_token = null;
 551                 }
 552 
 553                 // verify MIC
 554                 if (!GSSUtil.useMSInterop() && valid) {
 555                     valid = verifyMechListMIC(DER_mechTypes,
 556                                                 initToken.getMechListMIC());
 557                 }
 558 
 559                 // determine negotiated result status
 560                 if (valid) {
 561                     if (isMechContextEstablished()) {
 562                         negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE;
 563                         state = STATE_DONE;
 564                         // now set the context flags for acceptor
 565                         setContextFlags();
 566                         // print the negotiated mech info
 567                         if (DEBUG) {
 568                             System.out.println("SPNEGO Negotiated Mechanism = "
 569                                 + internal_mech + " " +
 570                                 GSSUtil.getMechStr(internal_mech));
 571                         }
 572                     } else {
 573                         negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE;
 574                         state = STATE_IN_PROCESS;
 575                     }
 576                 } else {
 577                     negoResult = SpNegoToken.NegoResult.REJECT;
 578                     state = STATE_DONE;
 579                 }
 580 
 581                 if (DEBUG) {
 582                     System.out.println("SpNegoContext.acceptSecContext: " +
 583                                 "mechanism wanted = " + mech_wanted);
 584                     System.out.println("SpNegoContext.acceptSecContext: " +
 585                                 "negotiated result = " + negoResult);
 586                 }
 587 
 588                 // generate SPNEGO token
 589                 NegTokenTarg targToken = new NegTokenTarg(negoResult.ordinal(),
 590                                 mech_wanted, accept_token, null);
 591                 if (DEBUG) {
 592                     System.out.println("SpNegoContext.acceptSecContext: " +
 593                                 "sending token of type = " +
 594                                 SpNegoToken.getTokenName(targToken.getType()));
 595                 }
 596                 // get the encoded token
 597                 retVal = targToken.getEncoded();
 598 
 599             } else if (state == STATE_IN_PROCESS) {
 600                 // read data
 601                 byte[] token = new byte[is.available()];
 602                 SpNegoToken.readFully(is, token);
 603                 if (DEBUG) {
 604                     System.out.println("SpNegoContext.acceptSecContext: " +
 605                             "receiving token = " +
 606                             SpNegoToken.getHexBytes(token));
 607                 }
 608 
 609                 // read the SPNEGO token
 610                 // token will be validated when parsing
 611                 NegTokenTarg inputToken = new NegTokenTarg(token);
 612 
 613                 if (DEBUG) {
 614                     System.out.println("SpNegoContext.acceptSecContext: " +
 615                             "received token of type = " +
 616                             SpNegoToken.getTokenName(inputToken.getType()));
 617                 }
 618 
 619                 // read the token
 620                 byte[] client_token = inputToken.getResponseToken();
 621                 byte[] accept_token = GSS_acceptSecContext(client_token);
 622                 if (accept_token == null) {
 623                     valid = false;
 624                 }
 625 
 626                 // determine negotiated result status
 627                 if (valid) {
 628                     if (isMechContextEstablished()) {
 629                         negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE;
 630                         state = STATE_DONE;
 631                     } else {
 632                         negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE;
 633                         state = STATE_IN_PROCESS;
 634                     }
 635                 } else {
 636                     negoResult = SpNegoToken.NegoResult.REJECT;
 637                     state = STATE_DONE;
 638                 }
 639 
 640                 // generate SPNEGO token
 641                 NegTokenTarg targToken = new NegTokenTarg(negoResult.ordinal(),
 642                                 null, accept_token, null);
 643                 if (DEBUG) {
 644                     System.out.println("SpNegoContext.acceptSecContext: " +
 645                                 "sending token of type = " +
 646                                 SpNegoToken.getTokenName(targToken.getType()));
 647                 }
 648                 // get the encoded token
 649                 retVal = targToken.getEncoded();
 650 
 651             } else {
 652                 // XXX Use logging API
 653                 if (DEBUG) {
 654                     System.out.println("AcceptSecContext: state = " + state);
 655                 }
 656             }
 657             if (DEBUG) {
 658                     System.out.println("SpNegoContext.acceptSecContext: " +
 659                         "sending token = " + SpNegoToken.getHexBytes(retVal));
 660             }
 661         } catch (IOException e) {
 662             GSSException gssException =
 663                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
 664             gssException.initCause(e);
 665             throw gssException;
 666         }
 667 
 668         if (state == STATE_DONE) {
 669             // now set the context flags for acceptor
 670             setContextFlags();
 671         }
 672         return retVal;
 673     }
 674 
 675     /**
 676      * obtain the available mechanisms
 677      */
 678     private Oid[] getAvailableMechs() {
 679         if (myCred != null) {
 680             Oid[] mechs = new Oid[1];
 681             mechs[0] = myCred.getInternalMech();
 682             return mechs;
 683         } else {
 684             return factory.availableMechs;
 685         }
 686     }
 687 
 688     /**
 689      * get ther DER encoded MechList
 690      */
 691     private byte[] getEncodedMechs(Oid[] mechSet)
 692         throws IOException, GSSException {
 693 
 694         DerOutputStream mech = new DerOutputStream();
 695         for (int i = 0; i < mechSet.length; i++) {
 696             byte[] mechType = mechSet[i].getDER();
 697             mech.write(mechType);
 698         }
 699         // insert in SEQUENCE
 700         DerOutputStream mechTypeList = new DerOutputStream();
 701         mechTypeList.write(DerValue.tag_Sequence, mech);
 702         byte[] encoded = mechTypeList.toByteArray();
 703         return encoded;
 704     }
 705 
 706     /**
 707      * get the context flags
 708      */
 709     private BitArray getContextFlags() {
 710         BitArray out = new BitArray(7);
 711 
 712         if (getCredDelegState()) out.set(0, true);
 713         if (getMutualAuthState()) out.set(1, true);
 714         if (getReplayDetState()) out.set(2, true);
 715         if (getSequenceDetState()) out.set(3, true);
 716         if (getConfState()) out.set(5, true);
 717         if (getIntegState()) out.set(6, true);
 718 
 719         return out;
 720     }
 721 
 722     // Only called on acceptor side. On the initiator side, most flags
 723     // are already set at request. For those that might get chanegd,
 724     // state from mech below is used.
 725     private void setContextFlags() {
 726 
 727         if (mechContext != null) {
 728             // default for cred delegation is false
 729             if (mechContext.getCredDelegState()) {
 730                 credDelegState = true;
 731             }
 732             // default for the following are true
 733             if (!mechContext.getMutualAuthState()) {
 734                 mutualAuthState = false;
 735             }
 736             if (!mechContext.getReplayDetState()) {
 737                 replayDetState = false;
 738             }
 739             if (!mechContext.getSequenceDetState()) {
 740                 sequenceDetState = false;
 741             }
 742             if (!mechContext.getIntegState()) {
 743                 integState = false;
 744             }
 745             if (!mechContext.getConfState()) {
 746                 confState = false;
 747             }
 748         }
 749     }
 750 
 751     /**
 752      * generate MIC on mechList. Not used at the moment.
 753      */
 754     /*private byte[] generateMechListMIC(byte[] mechTypes)
 755         throws GSSException {
 756 
 757         // sanity check the required input
 758         if (mechTypes == null) {
 759             if (DEBUG) {
 760                 System.out.println("SpNegoContext: no MIC token included");
 761             }
 762             return null;
 763         }
 764 
 765         // check if mechanism supports integrity
 766         if (!mechContext.getIntegState()) {
 767             if (DEBUG) {
 768                 System.out.println("SpNegoContext: no MIC token included" +
 769                         " - mechanism does not support integrity");
 770             }
 771             return null;
 772         }
 773 
 774         // compute MIC on DER encoded mechanism list
 775         byte[] mic = null;
 776         try {
 777             MessageProp prop = new MessageProp(0, true);
 778             mic = getMIC(mechTypes, 0, mechTypes.length, prop);
 779             if (DEBUG) {
 780                 System.out.println("SpNegoContext: getMIC = " +
 781                                         SpNegoToken.getHexBytes(mic));
 782             }
 783         } catch (GSSException e) {
 784             mic = null;
 785             if (DEBUG) {
 786                 System.out.println("SpNegoContext: no MIC token included" +
 787                         " - getMIC failed : " + e.getMessage());
 788             }
 789         }
 790         return mic;
 791     }*/
 792 
 793     /**
 794      * verify MIC on MechList
 795      */
 796     private boolean verifyMechListMIC(byte[] mechTypes, byte[] token)
 797         throws GSSException {
 798 
 799         // sanity check the input
 800         if (token == null) {
 801             if (DEBUG) {
 802                 System.out.println("SpNegoContext: no MIC token validation");
 803             }
 804             return true;
 805         }
 806 
 807         // check if mechanism supports integrity
 808         if (!mechContext.getIntegState()) {
 809             if (DEBUG) {
 810                 System.out.println("SpNegoContext: no MIC token validation" +
 811                         " - mechanism does not support integrity");
 812             }
 813             return true;
 814         }
 815 
 816         // now verify the token
 817         boolean valid = false;
 818         try {
 819             MessageProp prop = new MessageProp(0, true);
 820             verifyMIC(token, 0, token.length, mechTypes,
 821                         0, mechTypes.length, prop);
 822             valid = true;
 823         } catch (GSSException e) {
 824             valid = false;
 825             if (DEBUG) {
 826                 System.out.println("SpNegoContext: MIC validation failed! " +
 827                                         e.getMessage());
 828             }
 829         }
 830         return valid;
 831     }
 832 
 833     /**
 834      * call gss_init_sec_context for the corresponding underlying mechanism
 835      */
 836     private byte[] GSS_initSecContext(byte[] token) throws GSSException {
 837         byte[] tok = null;
 838 
 839         if (mechContext == null) {
 840             // initialize mech context
 841             GSSName serverName =
 842                 factory.manager.createName(peerName.toString(),
 843                     peerName.getStringNameType(), internal_mech);
 844             GSSCredential cred = null;
 845             if (myCred != null) {
 846                 // create context with provided credential
 847                 cred = new GSSCredentialImpl(factory.manager,
 848                     myCred.getInternalCred());
 849             }
 850             mechContext =
 851                     factory.manager.createContext(serverName,
 852                     internal_mech, cred, GSSContext.DEFAULT_LIFETIME);
 853             mechContext.requestConf(confState);
 854             mechContext.requestInteg(integState);
 855             mechContext.requestCredDeleg(credDelegState);
 856             mechContext.requestMutualAuth(mutualAuthState);
 857             mechContext.requestReplayDet(replayDetState);
 858             mechContext.requestSequenceDet(sequenceDetState);
 859             if (mechContext instanceof GSSContextImpl) {
 860                 ((GSSContextImpl)mechContext).requestDelegPolicy(
 861                         delegPolicyState);
 862             }
 863         }
 864 
 865         // pass token
 866         if (token != null) {
 867             tok = token;
 868         } else {
 869             tok = new byte[0];
 870         }
 871 
 872         // pass token to mechanism initSecContext
 873         byte[] init_token = mechContext.initSecContext(tok, 0, tok.length);
 874 
 875         return init_token;
 876     }
 877 
 878     /**
 879      * call gss_accept_sec_context for the corresponding underlying mechanism
 880      */
 881     private byte[] GSS_acceptSecContext(byte[] token) throws GSSException {
 882 
 883         if (mechContext == null) {
 884             // initialize mech context
 885             GSSCredential cred = null;
 886             if (myCred != null) {
 887                 // create context with provided credential
 888                 cred = new GSSCredentialImpl(factory.manager,
 889                 myCred.getInternalCred());
 890             }
 891             mechContext = factory.manager.createContext(cred);
 892         }
 893 
 894         // pass token to mechanism acceptSecContext
 895         byte[] accept_token =
 896                 mechContext.acceptSecContext(token, 0, token.length);
 897 
 898         return accept_token;
 899     }
 900 
 901     /**
 902      * This routine compares the recieved mechset to the mechset that
 903      * this server can support. It looks sequentially through the mechset
 904      * and the first one that matches what the server can support is
 905      * chosen as the negotiated mechanism. If one is found, negResult
 906      * is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult
 907      * is set to REJECT.
 908      */
 909     private static Oid negotiate_mech_type(Oid[] supported_mechSet,
 910                                         Oid[] mechSet) {
 911         for (int i = 0; i < supported_mechSet.length; i++) {
 912             for (int j = 0; j < mechSet.length; j++) {
 913                 if (mechSet[j].equals(supported_mechSet[i])) {
 914                     if (DEBUG) {
 915                         System.out.println("SpNegoContext: " +
 916                                 "negotiated mechanism = " + mechSet[j]);
 917                     }
 918                     return (mechSet[j]);
 919                 }
 920             }
 921         }
 922         return null;
 923     }
 924 
 925     public final boolean isEstablished() {
 926         return (state == STATE_DONE);
 927     }
 928 
 929     public final boolean isMechContextEstablished() {
 930         if (mechContext != null) {
 931             return mechContext.isEstablished();
 932         } else {
 933             if (DEBUG) {
 934                 System.out.println("The underlying mechanism context has " +
 935                                         "not been initialized");
 936             }
 937             return false;
 938         }
 939     }
 940 
 941     public final byte [] export() throws GSSException {
 942         throw new GSSException(GSSException.UNAVAILABLE, -1,
 943                                "GSS Export Context not available");
 944     }
 945 
 946     /**
 947      * Sets the channel bindings to be used during context
 948      * establishment.
 949      */
 950     public final void setChannelBinding(ChannelBinding channelBinding)
 951         throws GSSException {
 952         this.channelBinding = channelBinding;
 953     }
 954 
 955     final ChannelBinding getChannelBinding() {
 956         return channelBinding;
 957     }
 958 
 959     /*
 960      * Anonymity is a little different in that after an application
 961      * requests anonymity it will want to know whether the mechanism
 962      * can support it or not, prior to sending any tokens across for
 963      * context establishment. Since this is from the initiator's
 964      * perspective, it essentially requests that the initiator be
 965      * anonymous.
 966      */
 967     public final void requestAnonymity(boolean value) throws GSSException {
 968         // Ignore silently. Application will check back with
 969         // getAnonymityState.
 970     }
 971 
 972     // RFC 2853 actually calls for this to be called after context
 973     // establishment to get the right answer, but that is
 974     // incorrect. The application may not want to send over any
 975     // tokens if anonymity is not available.
 976     public final boolean getAnonymityState() {
 977         return false;
 978     }
 979 
 980     /**
 981      * Requests the desired lifetime. Can only be used on the context
 982      * initiator's side.
 983      */
 984     public void requestLifetime(int lifetime) throws GSSException {
 985         if (state == STATE_NEW && isInitiator())
 986             this.lifetime = lifetime;
 987     }
 988 
 989     /**
 990      * The lifetime remaining for this context.
 991      */
 992     public final int getLifetime() {
 993         if (mechContext != null) {
 994             return mechContext.getLifetime();
 995         } else {
 996             return GSSContext.INDEFINITE_LIFETIME;
 997         }
 998     }
 999 
1000     public final boolean isTransferable() throws GSSException {
1001         return false;
1002     }
1003 
1004     /**
1005      * Requests that sequence checking be done on the GSS wrap and MIC
1006      * tokens.
1007      */
1008     public final void requestSequenceDet(boolean value) throws GSSException {
1009         if (state == STATE_NEW && isInitiator())
1010             sequenceDetState  = value;
1011     }
1012 
1013     /**
1014      * Is sequence checking enabled on the GSS Wrap and MIC tokens?
1015      * We enable sequence checking if replay detection is enabled.
1016      */
1017     public final boolean getSequenceDetState() {
1018         return sequenceDetState || replayDetState;
1019     }
1020 
1021     /**
1022      * Requests that replay detection be done on the GSS wrap and MIC
1023      * tokens.
1024      */
1025     public final void requestReplayDet(boolean value) throws GSSException {
1026         if (state == STATE_NEW && isInitiator())
1027             replayDetState  = value;
1028     }
1029 
1030     /**
1031      * Is replay detection enabled on the GSS wrap and MIC tokens?
1032      * We enable replay detection if sequence checking is enabled.
1033      */
1034     public final boolean getReplayDetState() {
1035         return replayDetState || sequenceDetState;
1036     }
1037 
1038     public final GSSNameSpi getTargName() throws GSSException {
1039         // fill-in the GSSName
1040         // get the peer name for the mechanism
1041         if (mechContext != null) {
1042             GSSNameImpl targName = (GSSNameImpl)mechContext.getTargName();
1043             peerName = targName.getElement(internal_mech);
1044             return peerName;
1045         } else {
1046             if (DEBUG) {
1047                 System.out.println("The underlying mechanism context has " +
1048                                         "not been initialized");
1049             }
1050             return null;
1051         }
1052     }
1053 
1054     public final GSSNameSpi getSrcName() throws GSSException {
1055         // fill-in the GSSName
1056         // get the src name for the mechanism
1057         if (mechContext != null) {
1058             GSSNameImpl srcName = (GSSNameImpl)mechContext.getSrcName();
1059             myName = srcName.getElement(internal_mech);
1060             return myName;
1061         } else {
1062             if (DEBUG) {
1063                 System.out.println("The underlying mechanism context has " +
1064                                         "not been initialized");
1065             }
1066             return null;
1067         }
1068     }
1069 
1070     /**
1071      * Returns the delegated credential for the context. This
1072      * is an optional feature of contexts which not all
1073      * mechanisms will support. A context can be requested to
1074      * support credential delegation by using the <b>CRED_DELEG</b>.
1075      * This is only valid on the acceptor side of the context.
1076      * @return GSSCredentialSpi object for the delegated credential
1077      * @exception GSSException
1078      * @see GSSContext#getCredDelegState
1079      */
1080     public final GSSCredentialSpi getDelegCred() throws GSSException {
1081         if (state != STATE_IN_PROCESS && state != STATE_DONE)
1082             throw new GSSException(GSSException.NO_CONTEXT);
1083         if (mechContext != null) {
1084             GSSCredentialImpl delegCred =
1085                         (GSSCredentialImpl)mechContext.getDelegCred();
1086             if (delegCred == null) {
1087                 return null;
1088             }
1089             // determine delegated cred element usage
1090             boolean initiate = false;
1091             if (delegCred.getUsage() == GSSCredential.INITIATE_ONLY) {
1092                 initiate = true;
1093             }
1094             GSSCredentialSpi mechCred =
1095                     delegCred.getElement(internal_mech, initiate);
1096             SpNegoCredElement cred = new SpNegoCredElement(mechCred);
1097             return cred.getInternalCred();
1098         } else {
1099             throw new GSSException(GSSException.NO_CONTEXT, -1,
1100                                 "getDelegCred called in invalid state!");
1101         }
1102     }
1103 
1104     public final int getWrapSizeLimit(int qop, boolean confReq,
1105                                        int maxTokSize) throws GSSException {
1106         if (mechContext != null) {
1107             return mechContext.getWrapSizeLimit(qop, confReq, maxTokSize);
1108         } else {
1109             throw new GSSException(GSSException.NO_CONTEXT, -1,
1110                                 "getWrapSizeLimit called in invalid state!");
1111         }
1112     }
1113 
1114     public final byte[] wrap(byte inBuf[], int offset, int len,
1115                              MessageProp msgProp) throws GSSException {
1116         if (mechContext != null) {
1117             return mechContext.wrap(inBuf, offset, len, msgProp);
1118         } else {
1119             throw new GSSException(GSSException.NO_CONTEXT, -1,
1120                                 "Wrap called in invalid state!");
1121         }
1122     }
1123 
1124     public final void wrap(InputStream is, OutputStream os,
1125                             MessageProp msgProp) throws GSSException {
1126         if (mechContext != null) {
1127             mechContext.wrap(is, os, msgProp);
1128         } else {
1129             throw new GSSException(GSSException.NO_CONTEXT, -1,
1130                                 "Wrap called in invalid state!");
1131         }
1132     }
1133 
1134     public final byte[] unwrap(byte inBuf[], int offset, int len,
1135                                MessageProp msgProp)
1136         throws GSSException {
1137         if (mechContext != null) {
1138             return mechContext.unwrap(inBuf, offset, len, msgProp);
1139         } else {
1140             throw new GSSException(GSSException.NO_CONTEXT, -1,
1141                                 "UnWrap called in invalid state!");
1142         }
1143     }
1144 
1145     public final void unwrap(InputStream is, OutputStream os,
1146                              MessageProp msgProp) throws GSSException {
1147         if (mechContext != null) {
1148             mechContext.unwrap(is, os, msgProp);
1149         } else {
1150             throw new GSSException(GSSException.NO_CONTEXT, -1,
1151                                 "UnWrap called in invalid state!");
1152         }
1153     }
1154 
1155     public final byte[] getMIC(byte []inMsg, int offset, int len,
1156                                MessageProp msgProp)
1157         throws GSSException {
1158         if (mechContext != null) {
1159             return mechContext.getMIC(inMsg, offset, len, msgProp);
1160         } else {
1161             throw new GSSException(GSSException.NO_CONTEXT, -1,
1162                                 "getMIC called in invalid state!");
1163         }
1164     }
1165 
1166     public final void getMIC(InputStream is, OutputStream os,
1167                               MessageProp msgProp) throws GSSException {
1168         if (mechContext != null) {
1169             mechContext.getMIC(is, os, msgProp);
1170         } else {
1171             throw new GSSException(GSSException.NO_CONTEXT, -1,
1172                                 "getMIC called in invalid state!");
1173         }
1174     }
1175 
1176     public final void verifyMIC(byte []inTok, int tokOffset, int tokLen,
1177                                 byte[] inMsg, int msgOffset, int msgLen,
1178                                 MessageProp msgProp)
1179         throws GSSException {
1180         if (mechContext != null) {
1181             mechContext.verifyMIC(inTok, tokOffset, tokLen, inMsg, msgOffset,
1182                                 msgLen,  msgProp);
1183         } else {
1184             throw new GSSException(GSSException.NO_CONTEXT, -1,
1185                                 "verifyMIC called in invalid state!");
1186         }
1187     }
1188 
1189     public final void verifyMIC(InputStream is, InputStream msgStr,
1190                                  MessageProp msgProp) throws GSSException {
1191         if (mechContext != null) {
1192             mechContext.verifyMIC(is, msgStr, msgProp);
1193         } else {
1194             throw new GSSException(GSSException.NO_CONTEXT, -1,
1195                                 "verifyMIC called in invalid state!");
1196         }
1197     }
1198 
1199     private static String printState(int state) {
1200         switch (state) {
1201           case STATE_NEW:
1202                 return ("STATE_NEW");
1203           case STATE_IN_PROCESS:
1204                 return ("STATE_IN_PROCESS");
1205           case STATE_DONE:
1206                 return ("STATE_DONE");
1207           case STATE_DELETED:
1208                 return ("STATE_DELETED");
1209           default:
1210                 return ("Unknown state " + state);
1211         }
1212     }
1213 
1214     /**
1215      * Retrieve attribute of the context for {@code type}.
1216      */
1217     public Object inquireSecContext(String type)
1218             throws GSSException {
1219         if (mechContext == null) {
1220             throw new GSSException(GSSException.NO_CONTEXT, -1,
1221                     "Underlying mech not established.");
1222         }
1223         if (mechContext instanceof GSSContextImpl) {
1224             return ((GSSContextImpl)mechContext).inquireSecContext(type);
1225         } else {
1226             throw new GSSException(GSSException.BAD_MECH, -1,
1227                     "inquireSecContext not supported by underlying mech.");
1228         }
1229     }
1230 }
1231