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                         (GSSUtil.isKerberosMech(mechList[0]) &&
 543                          GSSUtil.isKerberosMech(mech_wanted))) {
 544                     // get the mechanism token
 545                     if (DEBUG && !mech_wanted.equals(mechList[0])) {
 546                         System.out.println("SpNegoContext.acceptSecContext: " +
 547                                 "negotiated mech adjusted to " + mechList[0]);
 548                     }
 549                     byte[] mechToken = initToken.getMechToken();
 550                     if (mechToken == null) {
 551                         throw new GSSException(GSSException.FAILURE, -1,
 552                                 "mechToken is missing");
 553                     }
 554                     accept_token = GSS_acceptSecContext(mechToken);
 555                     mech_wanted = mechList[0];
 556                 } else {
 557                     accept_token = null;
 558                 }
 559 
 560                 // verify MIC
 561                 if (!GSSUtil.useMSInterop() && valid) {
 562                     valid = verifyMechListMIC(DER_mechTypes,
 563                                                 initToken.getMechListMIC());
 564                 }
 565 
 566                 // determine negotiated result status
 567                 if (valid) {
 568                     if (isMechContextEstablished()) {
 569                         negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE;
 570                         state = STATE_DONE;
 571                         // now set the context flags for acceptor
 572                         setContextFlags();
 573                         // print the negotiated mech info
 574                         if (DEBUG) {
 575                             System.out.println("SPNEGO Negotiated Mechanism = "
 576                                 + internal_mech + " " +
 577                                 GSSUtil.getMechStr(internal_mech));
 578                         }
 579                     } else {
 580                         negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE;
 581                         state = STATE_IN_PROCESS;
 582                     }
 583                 } else {
 584                     negoResult = SpNegoToken.NegoResult.REJECT;
 585                     state = STATE_DONE;
 586                 }
 587 
 588                 if (DEBUG) {
 589                     System.out.println("SpNegoContext.acceptSecContext: " +
 590                                 "mechanism wanted = " + mech_wanted);
 591                     System.out.println("SpNegoContext.acceptSecContext: " +
 592                                 "negotiated result = " + negoResult);
 593                 }
 594 
 595                 // generate SPNEGO token
 596                 NegTokenTarg targToken = new NegTokenTarg(negoResult.ordinal(),
 597                                 mech_wanted, accept_token, null);
 598                 if (DEBUG) {
 599                     System.out.println("SpNegoContext.acceptSecContext: " +
 600                                 "sending token of type = " +
 601                                 SpNegoToken.getTokenName(targToken.getType()));
 602                 }
 603                 // get the encoded token
 604                 retVal = targToken.getEncoded();
 605 
 606             } else if (state == STATE_IN_PROCESS) {
 607                 // read data
 608                 byte[] token = new byte[is.available()];
 609                 SpNegoToken.readFully(is, token);
 610                 if (DEBUG) {
 611                     System.out.println("SpNegoContext.acceptSecContext: " +
 612                             "receiving token = " +
 613                             SpNegoToken.getHexBytes(token));
 614                 }
 615 
 616                 // read the SPNEGO token
 617                 // token will be validated when parsing
 618                 NegTokenTarg inputToken = new NegTokenTarg(token);
 619 
 620                 if (DEBUG) {
 621                     System.out.println("SpNegoContext.acceptSecContext: " +
 622                             "received token of type = " +
 623                             SpNegoToken.getTokenName(inputToken.getType()));
 624                 }
 625 
 626                 // read the token
 627                 byte[] client_token = inputToken.getResponseToken();
 628                 byte[] accept_token = GSS_acceptSecContext(client_token);
 629                 if (accept_token == null) {
 630                     valid = false;
 631                 }
 632 
 633                 // determine negotiated result status
 634                 if (valid) {
 635                     if (isMechContextEstablished()) {
 636                         negoResult = SpNegoToken.NegoResult.ACCEPT_COMPLETE;
 637                         state = STATE_DONE;
 638                     } else {
 639                         negoResult = SpNegoToken.NegoResult.ACCEPT_INCOMPLETE;
 640                         state = STATE_IN_PROCESS;
 641                     }
 642                 } else {
 643                     negoResult = SpNegoToken.NegoResult.REJECT;
 644                     state = STATE_DONE;
 645                 }
 646 
 647                 // generate SPNEGO token
 648                 NegTokenTarg targToken = new NegTokenTarg(negoResult.ordinal(),
 649                                 null, accept_token, null);
 650                 if (DEBUG) {
 651                     System.out.println("SpNegoContext.acceptSecContext: " +
 652                                 "sending token of type = " +
 653                                 SpNegoToken.getTokenName(targToken.getType()));
 654                 }
 655                 // get the encoded token
 656                 retVal = targToken.getEncoded();
 657 
 658             } else {
 659                 // XXX Use logging API
 660                 if (DEBUG) {
 661                     System.out.println("AcceptSecContext: state = " + state);
 662                 }
 663             }
 664             if (DEBUG) {
 665                     System.out.println("SpNegoContext.acceptSecContext: " +
 666                         "sending token = " + SpNegoToken.getHexBytes(retVal));
 667             }
 668         } catch (IOException e) {
 669             GSSException gssException =
 670                 new GSSException(GSSException.FAILURE, -1, e.getMessage());
 671             gssException.initCause(e);
 672             throw gssException;
 673         }
 674 
 675         if (state == STATE_DONE) {
 676             // now set the context flags for acceptor
 677             setContextFlags();
 678         }
 679         return retVal;
 680     }
 681 
 682     /**
 683      * obtain the available mechanisms
 684      */
 685     private Oid[] getAvailableMechs() {
 686         if (myCred != null) {
 687             Oid[] mechs = new Oid[1];
 688             mechs[0] = myCred.getInternalMech();
 689             return mechs;
 690         } else {
 691             return factory.availableMechs;
 692         }
 693     }
 694 
 695     /**
 696      * get ther DER encoded MechList
 697      */
 698     private byte[] getEncodedMechs(Oid[] mechSet)
 699         throws IOException, GSSException {
 700 
 701         DerOutputStream mech = new DerOutputStream();
 702         for (int i = 0; i < mechSet.length; i++) {
 703             byte[] mechType = mechSet[i].getDER();
 704             mech.write(mechType);
 705         }
 706         // insert in SEQUENCE
 707         DerOutputStream mechTypeList = new DerOutputStream();
 708         mechTypeList.write(DerValue.tag_Sequence, mech);
 709         byte[] encoded = mechTypeList.toByteArray();
 710         return encoded;
 711     }
 712 
 713     /**
 714      * get the context flags
 715      */
 716     private BitArray getContextFlags() {
 717         BitArray out = new BitArray(7);
 718 
 719         if (getCredDelegState()) out.set(0, true);
 720         if (getMutualAuthState()) out.set(1, true);
 721         if (getReplayDetState()) out.set(2, true);
 722         if (getSequenceDetState()) out.set(3, true);
 723         if (getConfState()) out.set(5, true);
 724         if (getIntegState()) out.set(6, true);
 725 
 726         return out;
 727     }
 728 
 729     // Only called on acceptor side. On the initiator side, most flags
 730     // are already set at request. For those that might get chanegd,
 731     // state from mech below is used.
 732     private void setContextFlags() {
 733 
 734         if (mechContext != null) {
 735             // default for cred delegation is false
 736             if (mechContext.getCredDelegState()) {
 737                 credDelegState = true;
 738             }
 739             // default for the following are true
 740             if (!mechContext.getMutualAuthState()) {
 741                 mutualAuthState = false;
 742             }
 743             if (!mechContext.getReplayDetState()) {
 744                 replayDetState = false;
 745             }
 746             if (!mechContext.getSequenceDetState()) {
 747                 sequenceDetState = false;
 748             }
 749             if (!mechContext.getIntegState()) {
 750                 integState = false;
 751             }
 752             if (!mechContext.getConfState()) {
 753                 confState = false;
 754             }
 755         }
 756     }
 757 
 758     /**
 759      * generate MIC on mechList. Not used at the moment.
 760      */
 761     /*private byte[] generateMechListMIC(byte[] mechTypes)
 762         throws GSSException {
 763 
 764         // sanity check the required input
 765         if (mechTypes == null) {
 766             if (DEBUG) {
 767                 System.out.println("SpNegoContext: no MIC token included");
 768             }
 769             return null;
 770         }
 771 
 772         // check if mechanism supports integrity
 773         if (!mechContext.getIntegState()) {
 774             if (DEBUG) {
 775                 System.out.println("SpNegoContext: no MIC token included" +
 776                         " - mechanism does not support integrity");
 777             }
 778             return null;
 779         }
 780 
 781         // compute MIC on DER encoded mechanism list
 782         byte[] mic = null;
 783         try {
 784             MessageProp prop = new MessageProp(0, true);
 785             mic = getMIC(mechTypes, 0, mechTypes.length, prop);
 786             if (DEBUG) {
 787                 System.out.println("SpNegoContext: getMIC = " +
 788                                         SpNegoToken.getHexBytes(mic));
 789             }
 790         } catch (GSSException e) {
 791             mic = null;
 792             if (DEBUG) {
 793                 System.out.println("SpNegoContext: no MIC token included" +
 794                         " - getMIC failed : " + e.getMessage());
 795             }
 796         }
 797         return mic;
 798     }*/
 799 
 800     /**
 801      * verify MIC on MechList
 802      */
 803     private boolean verifyMechListMIC(byte[] mechTypes, byte[] token)
 804         throws GSSException {
 805 
 806         // sanity check the input
 807         if (token == null) {
 808             if (DEBUG) {
 809                 System.out.println("SpNegoContext: no MIC token validation");
 810             }
 811             return true;
 812         }
 813 
 814         // check if mechanism supports integrity
 815         if (!mechContext.getIntegState()) {
 816             if (DEBUG) {
 817                 System.out.println("SpNegoContext: no MIC token validation" +
 818                         " - mechanism does not support integrity");
 819             }
 820             return true;
 821         }
 822 
 823         // now verify the token
 824         boolean valid = false;
 825         try {
 826             MessageProp prop = new MessageProp(0, true);
 827             verifyMIC(token, 0, token.length, mechTypes,
 828                         0, mechTypes.length, prop);
 829             valid = true;
 830         } catch (GSSException e) {
 831             valid = false;
 832             if (DEBUG) {
 833                 System.out.println("SpNegoContext: MIC validation failed! " +
 834                                         e.getMessage());
 835             }
 836         }
 837         return valid;
 838     }
 839 
 840     /**
 841      * call gss_init_sec_context for the corresponding underlying mechanism
 842      */
 843     private byte[] GSS_initSecContext(byte[] token) throws GSSException {
 844         byte[] tok = null;
 845 
 846         if (mechContext == null) {
 847             // initialize mech context
 848             GSSName serverName =
 849                 factory.manager.createName(peerName.toString(),
 850                     peerName.getStringNameType(), internal_mech);
 851             GSSCredential cred = null;
 852             if (myCred != null) {
 853                 // create context with provided credential
 854                 cred = new GSSCredentialImpl(factory.manager,
 855                     myCred.getInternalCred());
 856             }
 857             mechContext =
 858                     factory.manager.createContext(serverName,
 859                     internal_mech, cred, GSSContext.DEFAULT_LIFETIME);
 860             mechContext.requestConf(confState);
 861             mechContext.requestInteg(integState);
 862             mechContext.requestCredDeleg(credDelegState);
 863             mechContext.requestMutualAuth(mutualAuthState);
 864             mechContext.requestReplayDet(replayDetState);
 865             mechContext.requestSequenceDet(sequenceDetState);
 866             if (mechContext instanceof GSSContextImpl) {
 867                 ((GSSContextImpl)mechContext).requestDelegPolicy(
 868                         delegPolicyState);
 869             }
 870         }
 871 
 872         // pass token
 873         if (token != null) {
 874             tok = token;
 875         } else {
 876             tok = new byte[0];
 877         }
 878 
 879         // pass token to mechanism initSecContext
 880         byte[] init_token = mechContext.initSecContext(tok, 0, tok.length);
 881 
 882         return init_token;
 883     }
 884 
 885     /**
 886      * call gss_accept_sec_context for the corresponding underlying mechanism
 887      */
 888     private byte[] GSS_acceptSecContext(byte[] token) throws GSSException {
 889 
 890         if (mechContext == null) {
 891             // initialize mech context
 892             GSSCredential cred = null;
 893             if (myCred != null) {
 894                 // create context with provided credential
 895                 cred = new GSSCredentialImpl(factory.manager,
 896                 myCred.getInternalCred());
 897             }
 898             mechContext = factory.manager.createContext(cred);
 899         }
 900 
 901         // pass token to mechanism acceptSecContext
 902         byte[] accept_token =
 903                 mechContext.acceptSecContext(token, 0, token.length);
 904 
 905         return accept_token;
 906     }
 907 
 908     /**
 909      * This routine compares the recieved mechset to the mechset that
 910      * this server can support. It looks sequentially through the mechset
 911      * and the first one that matches what the server can support is
 912      * chosen as the negotiated mechanism. If one is found, negResult
 913      * is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult
 914      * is set to REJECT.
 915      */
 916     private static Oid negotiate_mech_type(Oid[] supported_mechSet,
 917                                         Oid[] mechSet) {
 918         for (int i = 0; i < supported_mechSet.length; i++) {
 919             for (int j = 0; j < mechSet.length; j++) {
 920                 if (mechSet[j].equals(supported_mechSet[i])) {
 921                     if (DEBUG) {
 922                         System.out.println("SpNegoContext: " +
 923                                 "negotiated mechanism = " + mechSet[j]);
 924                     }
 925                     return (mechSet[j]);
 926                 }
 927             }
 928         }
 929         return null;
 930     }
 931 
 932     public final boolean isEstablished() {
 933         return (state == STATE_DONE);
 934     }
 935 
 936     public final boolean isMechContextEstablished() {
 937         if (mechContext != null) {
 938             return mechContext.isEstablished();
 939         } else {
 940             if (DEBUG) {
 941                 System.out.println("The underlying mechanism context has " +
 942                                         "not been initialized");
 943             }
 944             return false;
 945         }
 946     }
 947 
 948     public final byte [] export() throws GSSException {
 949         throw new GSSException(GSSException.UNAVAILABLE, -1,
 950                                "GSS Export Context not available");
 951     }
 952 
 953     /**
 954      * Sets the channel bindings to be used during context
 955      * establishment.
 956      */
 957     public final void setChannelBinding(ChannelBinding channelBinding)
 958         throws GSSException {
 959         this.channelBinding = channelBinding;
 960     }
 961 
 962     final ChannelBinding getChannelBinding() {
 963         return channelBinding;
 964     }
 965 
 966     /*
 967      * Anonymity is a little different in that after an application
 968      * requests anonymity it will want to know whether the mechanism
 969      * can support it or not, prior to sending any tokens across for
 970      * context establishment. Since this is from the initiator's
 971      * perspective, it essentially requests that the initiator be
 972      * anonymous.
 973      */
 974     public final void requestAnonymity(boolean value) throws GSSException {
 975         // Ignore silently. Application will check back with
 976         // getAnonymityState.
 977     }
 978 
 979     // RFC 2853 actually calls for this to be called after context
 980     // establishment to get the right answer, but that is
 981     // incorrect. The application may not want to send over any
 982     // tokens if anonymity is not available.
 983     public final boolean getAnonymityState() {
 984         return false;
 985     }
 986 
 987     /**
 988      * Requests the desired lifetime. Can only be used on the context
 989      * initiator's side.
 990      */
 991     public void requestLifetime(int lifetime) throws GSSException {
 992         if (state == STATE_NEW && isInitiator())
 993             this.lifetime = lifetime;
 994     }
 995 
 996     /**
 997      * The lifetime remaining for this context.
 998      */
 999     public final int getLifetime() {
1000         if (mechContext != null) {
1001             return mechContext.getLifetime();
1002         } else {
1003             return GSSContext.INDEFINITE_LIFETIME;
1004         }
1005     }
1006 
1007     public final boolean isTransferable() throws GSSException {
1008         return false;
1009     }
1010 
1011     /**
1012      * Requests that sequence checking be done on the GSS wrap and MIC
1013      * tokens.
1014      */
1015     public final void requestSequenceDet(boolean value) throws GSSException {
1016         if (state == STATE_NEW && isInitiator())
1017             sequenceDetState  = value;
1018     }
1019 
1020     /**
1021      * Is sequence checking enabled on the GSS Wrap and MIC tokens?
1022      * We enable sequence checking if replay detection is enabled.
1023      */
1024     public final boolean getSequenceDetState() {
1025         return sequenceDetState || replayDetState;
1026     }
1027 
1028     /**
1029      * Requests that replay detection be done on the GSS wrap and MIC
1030      * tokens.
1031      */
1032     public final void requestReplayDet(boolean value) throws GSSException {
1033         if (state == STATE_NEW && isInitiator())
1034             replayDetState  = value;
1035     }
1036 
1037     /**
1038      * Is replay detection enabled on the GSS wrap and MIC tokens?
1039      * We enable replay detection if sequence checking is enabled.
1040      */
1041     public final boolean getReplayDetState() {
1042         return replayDetState || sequenceDetState;
1043     }
1044 
1045     public final GSSNameSpi getTargName() throws GSSException {
1046         // fill-in the GSSName
1047         // get the peer name for the mechanism
1048         if (mechContext != null) {
1049             GSSNameImpl targName = (GSSNameImpl)mechContext.getTargName();
1050             peerName = targName.getElement(internal_mech);
1051             return peerName;
1052         } else {
1053             if (DEBUG) {
1054                 System.out.println("The underlying mechanism context has " +
1055                                         "not been initialized");
1056             }
1057             return null;
1058         }
1059     }
1060 
1061     public final GSSNameSpi getSrcName() throws GSSException {
1062         // fill-in the GSSName
1063         // get the src name for the mechanism
1064         if (mechContext != null) {
1065             GSSNameImpl srcName = (GSSNameImpl)mechContext.getSrcName();
1066             myName = srcName.getElement(internal_mech);
1067             return myName;
1068         } else {
1069             if (DEBUG) {
1070                 System.out.println("The underlying mechanism context has " +
1071                                         "not been initialized");
1072             }
1073             return null;
1074         }
1075     }
1076 
1077     /**
1078      * Returns the delegated credential for the context. This
1079      * is an optional feature of contexts which not all
1080      * mechanisms will support. A context can be requested to
1081      * support credential delegation by using the <b>CRED_DELEG</b>.
1082      * This is only valid on the acceptor side of the context.
1083      * @return GSSCredentialSpi object for the delegated credential
1084      * @exception GSSException
1085      * @see GSSContext#getCredDelegState
1086      */
1087     public final GSSCredentialSpi getDelegCred() throws GSSException {
1088         if (state != STATE_IN_PROCESS && state != STATE_DONE)
1089             throw new GSSException(GSSException.NO_CONTEXT);
1090         if (mechContext != null) {
1091             GSSCredentialImpl delegCred =
1092                         (GSSCredentialImpl)mechContext.getDelegCred();
1093             if (delegCred == null) {
1094                 return null;
1095             }
1096             // determine delegated cred element usage
1097             boolean initiate = false;
1098             if (delegCred.getUsage() == GSSCredential.INITIATE_ONLY) {
1099                 initiate = true;
1100             }
1101             GSSCredentialSpi mechCred =
1102                     delegCred.getElement(internal_mech, initiate);
1103             SpNegoCredElement cred = new SpNegoCredElement(mechCred);
1104             return cred.getInternalCred();
1105         } else {
1106             throw new GSSException(GSSException.NO_CONTEXT, -1,
1107                                 "getDelegCred called in invalid state!");
1108         }
1109     }
1110 
1111     public final int getWrapSizeLimit(int qop, boolean confReq,
1112                                        int maxTokSize) throws GSSException {
1113         if (mechContext != null) {
1114             return mechContext.getWrapSizeLimit(qop, confReq, maxTokSize);
1115         } else {
1116             throw new GSSException(GSSException.NO_CONTEXT, -1,
1117                                 "getWrapSizeLimit called in invalid state!");
1118         }
1119     }
1120 
1121     public final byte[] wrap(byte inBuf[], int offset, int len,
1122                              MessageProp msgProp) throws GSSException {
1123         if (mechContext != null) {
1124             return mechContext.wrap(inBuf, offset, len, msgProp);
1125         } else {
1126             throw new GSSException(GSSException.NO_CONTEXT, -1,
1127                                 "Wrap called in invalid state!");
1128         }
1129     }
1130 
1131     public final void wrap(InputStream is, OutputStream os,
1132                             MessageProp msgProp) throws GSSException {
1133         if (mechContext != null) {
1134             mechContext.wrap(is, os, msgProp);
1135         } else {
1136             throw new GSSException(GSSException.NO_CONTEXT, -1,
1137                                 "Wrap called in invalid state!");
1138         }
1139     }
1140 
1141     public final byte[] unwrap(byte inBuf[], int offset, int len,
1142                                MessageProp msgProp)
1143         throws GSSException {
1144         if (mechContext != null) {
1145             return mechContext.unwrap(inBuf, offset, len, msgProp);
1146         } else {
1147             throw new GSSException(GSSException.NO_CONTEXT, -1,
1148                                 "UnWrap called in invalid state!");
1149         }
1150     }
1151 
1152     public final void unwrap(InputStream is, OutputStream os,
1153                              MessageProp msgProp) throws GSSException {
1154         if (mechContext != null) {
1155             mechContext.unwrap(is, os, msgProp);
1156         } else {
1157             throw new GSSException(GSSException.NO_CONTEXT, -1,
1158                                 "UnWrap called in invalid state!");
1159         }
1160     }
1161 
1162     public final byte[] getMIC(byte []inMsg, int offset, int len,
1163                                MessageProp msgProp)
1164         throws GSSException {
1165         if (mechContext != null) {
1166             return mechContext.getMIC(inMsg, offset, len, msgProp);
1167         } else {
1168             throw new GSSException(GSSException.NO_CONTEXT, -1,
1169                                 "getMIC called in invalid state!");
1170         }
1171     }
1172 
1173     public final void getMIC(InputStream is, OutputStream os,
1174                               MessageProp msgProp) throws GSSException {
1175         if (mechContext != null) {
1176             mechContext.getMIC(is, os, msgProp);
1177         } else {
1178             throw new GSSException(GSSException.NO_CONTEXT, -1,
1179                                 "getMIC called in invalid state!");
1180         }
1181     }
1182 
1183     public final void verifyMIC(byte []inTok, int tokOffset, int tokLen,
1184                                 byte[] inMsg, int msgOffset, int msgLen,
1185                                 MessageProp msgProp)
1186         throws GSSException {
1187         if (mechContext != null) {
1188             mechContext.verifyMIC(inTok, tokOffset, tokLen, inMsg, msgOffset,
1189                                 msgLen,  msgProp);
1190         } else {
1191             throw new GSSException(GSSException.NO_CONTEXT, -1,
1192                                 "verifyMIC called in invalid state!");
1193         }
1194     }
1195 
1196     public final void verifyMIC(InputStream is, InputStream msgStr,
1197                                  MessageProp msgProp) throws GSSException {
1198         if (mechContext != null) {
1199             mechContext.verifyMIC(is, msgStr, msgProp);
1200         } else {
1201             throw new GSSException(GSSException.NO_CONTEXT, -1,
1202                                 "verifyMIC called in invalid state!");
1203         }
1204     }
1205 
1206     private static String printState(int state) {
1207         switch (state) {
1208           case STATE_NEW:
1209                 return ("STATE_NEW");
1210           case STATE_IN_PROCESS:
1211                 return ("STATE_IN_PROCESS");
1212           case STATE_DONE:
1213                 return ("STATE_DONE");
1214           case STATE_DELETED:
1215                 return ("STATE_DELETED");
1216           default:
1217                 return ("Unknown state " + state);
1218         }
1219     }
1220 
1221     /**
1222      * Retrieve attribute of the context for {@code type}.
1223      */
1224     public Object inquireSecContext(String type)
1225             throws GSSException {
1226         if (mechContext == null) {
1227             throw new GSSException(GSSException.NO_CONTEXT, -1,
1228                     "Underlying mech not established.");
1229         }
1230         if (mechContext instanceof GSSContextImpl) {
1231             return ((GSSContextImpl)mechContext).inquireSecContext(type);
1232         } else {
1233             throw new GSSException(GSSException.BAD_MECH, -1,
1234                     "inquireSecContext not supported by underlying mech.");
1235         }
1236     }
1237 }
1238