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