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