001: /*
002: * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.jgss;
027:
028: import org.ietf.jgss.*;
029: import sun.security.jgss.spi.*;
030: import sun.security.jgss.*;
031: import sun.security.util.ObjectIdentifier;
032: import java.io.InputStream;
033: import java.io.OutputStream;
034: import java.io.ByteArrayInputStream;
035: import java.io.ByteArrayOutputStream;
036: import java.io.IOException;
037:
038: /**
039: * This class represents the JGSS security context and its associated
040: * operations. JGSS security contexts are established between
041: * peers using locally established credentials. Multiple contexts
042: * may exist simultaneously between a pair of peers, using the same
043: * or different set of credentials. The JGSS is independent of
044: * the underlying transport protocols and depends on its callers to
045: * transport the tokens between peers.
046: * <p>
047: * The context object can be thought of as having 3 implicit states:
048: * before it is established, during its context establishment, and
049: * after a fully established context exists.
050: * <p>
051: * Before the context establishment phase is initiated, the context
052: * initiator may request specific characteristics desired of the
053: * established context. These can be set using the set methods. After the
054: * context is established, the caller can check the actual characteristic
055: * and services offered by the context using the query methods.
056: * <p>
057: * The context establishment phase begins with the first call to the
058: * initSecContext method by the context initiator. During this phase the
059: * initSecContext and acceptSecContext methods will produce GSS-API
060: * authentication tokens which the calling application needs to send to its
061: * peer. The initSecContext and acceptSecContext methods may
062: * return a CONTINUE_NEEDED code which indicates that a token is needed
063: * from its peer in order to continue the context establishment phase. A
064: * return code of COMPLETE signals that the local end of the context is
065: * established. This may still require that a token be sent to the peer,
066: * depending if one is produced by GSS-API. The isEstablished method can
067: * also be used to determine if the local end of the context has been
068: * fully established. During the context establishment phase, the
069: * isProtReady method may be called to determine if the context can be
070: * used for the per-message operations. This allows implementation to
071: * use per-message operations on contexts which aren't fully established.
072: * <p>
073: * After the context has been established or the isProtReady method
074: * returns "true", the query routines can be invoked to determine the actual
075: * characteristics and services of the established context. The
076: * application can also start using the per-message methods of wrap and
077: * getMIC to obtain cryptographic operations on application supplied data.
078: * <p>
079: * When the context is no longer needed, the application should call
080: * dispose to release any system resources the context may be using.
081: * <DL><DT><B>RFC 2078</b>
082: * <DD>This class corresponds to the context level calls together with
083: * the per message calls of RFC 2078. The gss_init_sec_context and
084: * gss_accept_sec_context calls have been made simpler by only taking
085: * required parameters. The context can have its properties set before
086: * the first call to initSecContext. The supplementary status codes for the
087: * per-message operations are returned in an instance of the MessageProp
088: * class, which is used as an argument in these calls.</dl>
089: */
090: class GSSContextImpl implements GSSContext {
091:
092: private GSSManagerImpl gssManager = null;
093:
094: // private flags for the context state
095: private static final int PRE_INIT = 1;
096: private static final int IN_PROGRESS = 2;
097: private static final int READY = 3;
098: private static final int DELETED = 4;
099:
100: // instance variables
101: private int currentState = PRE_INIT;
102: private boolean initiator;
103:
104: private GSSContextSpi mechCtxt = null;
105: private Oid mechOid = null;
106: private ObjectIdentifier objId = null;
107:
108: private GSSCredentialImpl myCred = null;
109: private GSSCredentialImpl delegCred = null;
110:
111: private GSSNameImpl srcName = null;
112: private GSSNameImpl targName = null;
113:
114: private int reqLifetime = INDEFINITE_LIFETIME;
115: private ChannelBinding channelBindings = null;
116:
117: private boolean reqConfState = true;
118: private boolean reqIntegState = true;
119: private boolean reqMutualAuthState = true;
120: private boolean reqReplayDetState = true;
121: private boolean reqSequenceDetState = true;
122: private boolean reqCredDelegState = false;
123: private boolean reqAnonState = false;
124:
125: /**
126: * Creates a GSSContextImp on the context initiator's side.
127: */
128: public GSSContextImpl(GSSManagerImpl gssManager, GSSName peer,
129: Oid mech, GSSCredential myCred, int lifetime)
130: throws GSSException {
131: if ((peer == null) || !(peer instanceof GSSNameImpl)) {
132: throw new GSSException(GSSException.BAD_NAME);
133: }
134: if (mech == null)
135: mech = ProviderList.DEFAULT_MECH_OID;
136:
137: this .gssManager = gssManager;
138: this .myCred = (GSSCredentialImpl) myCred; // XXX Check first
139: reqLifetime = lifetime;
140: targName = (GSSNameImpl) peer;
141: this .mechOid = mech;
142: initiator = true;
143: }
144:
145: /**
146: * Creates a GSSContextImpl on the context acceptor's side.
147: */
148: public GSSContextImpl(GSSManagerImpl gssManager,
149: GSSCredential myCred) throws GSSException {
150: this .gssManager = gssManager;
151: this .myCred = (GSSCredentialImpl) myCred; // XXX Check first
152: initiator = false;
153: }
154:
155: /**
156: * Creates a GSSContextImpl out of a previously exported
157: * GSSContext.
158: *
159: * @see #isTransferable
160: */
161: public GSSContextImpl(GSSManagerImpl gssManager,
162: byte[] interProcessToken) throws GSSException {
163: this .gssManager = gssManager;
164: mechCtxt = gssManager.getMechanismContext(interProcessToken);
165: initiator = mechCtxt.isInitiator();
166: this .mechOid = mechCtxt.getMech();
167: }
168:
169: public byte[] initSecContext(byte inputBuf[], int offset, int len)
170: throws GSSException {
171: /*
172: * Size of ByteArrayOutputStream will double each time that extra
173: * bytes are to be written. Usually, without delegation, a GSS
174: * initial token containing the Kerberos AP-REQ is between 400 and
175: * 600 bytes.
176: */
177: ByteArrayOutputStream bos = new ByteArrayOutputStream(600);
178: ByteArrayInputStream bin = new ByteArrayInputStream(inputBuf,
179: offset, len);
180: int size = initSecContext(bin, bos);
181: return (size == 0 ? null : bos.toByteArray());
182: }
183:
184: public int initSecContext(InputStream inStream,
185: OutputStream outStream) throws GSSException {
186:
187: if (mechCtxt != null && currentState != IN_PROGRESS) {
188: throw new GSSExceptionImpl(GSSException.FAILURE,
189: "Illegal call to initSecContext");
190: }
191:
192: GSSHeader gssHeader = null;
193: int inTokenLen = -1;
194: GSSCredentialSpi credElement = null;
195: boolean firstToken = false;
196:
197: try {
198: if (mechCtxt == null) {
199: if (myCred != null) {
200: try {
201: credElement = myCred.getElement(mechOid, true);
202: } catch (GSSException ge) {
203: if (GSSUtil.isSpNegoMech(mechOid)
204: && ge.getMajor() == GSSException.NO_CRED) {
205: credElement = myCred.getElement(myCred
206: .getMechs()[0], true);
207: } else {
208: throw ge;
209: }
210: }
211: }
212: GSSNameSpi nameElement = targName.getElement(mechOid);
213: mechCtxt = gssManager.getMechanismContext(nameElement,
214: credElement, reqLifetime, mechOid);
215: mechCtxt.requestConf(reqConfState);
216: mechCtxt.requestInteg(reqIntegState);
217: mechCtxt.requestCredDeleg(reqCredDelegState);
218: mechCtxt.requestMutualAuth(reqMutualAuthState);
219: mechCtxt.requestReplayDet(reqReplayDetState);
220: mechCtxt.requestSequenceDet(reqSequenceDetState);
221: mechCtxt.requestAnonymity(reqAnonState);
222: mechCtxt.setChannelBinding(channelBindings);
223:
224: objId = new ObjectIdentifier(mechOid.toString());
225:
226: currentState = IN_PROGRESS;
227: firstToken = true;
228: } else {
229: if (mechCtxt.getProvider().getName().equals(
230: "SunNativeGSS")
231: || GSSUtil.isSpNegoMech(mechOid)) {
232: // do not parse GSS header for native provider or SPNEGO
233: // mech
234: } else {
235: // parse GSS header
236: gssHeader = new GSSHeader(inStream);
237: if (!gssHeader.getOid().equals((Object) objId))
238: throw new GSSExceptionImpl(
239: GSSException.DEFECTIVE_TOKEN,
240: "Mechanism not equal to "
241: + mechOid.toString()
242: + " in initSecContext token");
243: inTokenLen = gssHeader.getMechTokenLength();
244: }
245: }
246:
247: byte[] obuf = mechCtxt.initSecContext(inStream, inTokenLen);
248:
249: int retVal = 0;
250:
251: if (obuf != null) {
252: retVal = obuf.length;
253: if (mechCtxt.getProvider().getName().equals(
254: "SunNativeGSS")
255: || (!firstToken && GSSUtil
256: .isSpNegoMech(mechOid))) {
257: // do not add GSS header for native provider or SPNEGO
258: // except for the first SPNEGO token
259: } else {
260: // add GSS header
261: gssHeader = new GSSHeader(objId, obuf.length);
262: retVal += gssHeader.encode(outStream);
263: }
264: outStream.write(obuf);
265: }
266:
267: if (mechCtxt.isEstablished())
268: currentState = READY;
269:
270: return retVal;
271:
272: } catch (IOException e) {
273: throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN, e
274: .getMessage());
275: }
276: }
277:
278: public byte[] acceptSecContext(byte inTok[], int offset, int len)
279: throws GSSException {
280:
281: /*
282: * Usually initial GSS token containing a Kerberos AP-REP is less
283: * than 100 bytes.
284: */
285: ByteArrayOutputStream bos = new ByteArrayOutputStream(100);
286: acceptSecContext(new ByteArrayInputStream(inTok, offset, len),
287: bos);
288: return bos.toByteArray();
289: }
290:
291: public void acceptSecContext(InputStream inStream,
292: OutputStream outStream) throws GSSException {
293:
294: if (mechCtxt != null && currentState != IN_PROGRESS) {
295: throw new GSSExceptionImpl(GSSException.FAILURE,
296: "Illegal call to acceptSecContext");
297: }
298:
299: GSSHeader gssHeader = null;
300: int inTokenLen = -1;
301: GSSCredentialSpi credElement = null;
302:
303: try {
304: if (mechCtxt == null) {
305: // mechOid will be null for an acceptor's context
306: gssHeader = new GSSHeader(inStream);
307: inTokenLen = gssHeader.getMechTokenLength();
308:
309: /*
310: * Convert ObjectIdentifier to Oid
311: */
312: objId = gssHeader.getOid();
313: mechOid = new Oid(objId.toString());
314: // System.out.println("Entered GSSContextImpl.acceptSecContext"
315: // + " with mechanism = " + mechOid);
316: if (myCred != null) {
317: credElement = myCred.getElement(mechOid, false);
318: }
319:
320: mechCtxt = gssManager.getMechanismContext(credElement,
321: mechOid);
322: mechCtxt.setChannelBinding(channelBindings);
323:
324: currentState = IN_PROGRESS;
325: } else {
326: if (mechCtxt.getProvider().getName().equals(
327: "SunNativeGSS")
328: || (GSSUtil.isSpNegoMech(mechOid))) {
329: // do not parse GSS header for native provider and SPNEGO
330: } else {
331: // parse GSS Header
332: gssHeader = new GSSHeader(inStream);
333: if (!gssHeader.getOid().equals((Object) objId))
334: throw new GSSExceptionImpl(
335: GSSException.DEFECTIVE_TOKEN,
336: "Mechanism not equal to "
337: + mechOid.toString()
338: + " in acceptSecContext token");
339: inTokenLen = gssHeader.getMechTokenLength();
340: }
341: }
342:
343: byte[] obuf = mechCtxt.acceptSecContext(inStream,
344: inTokenLen);
345:
346: if (obuf != null) {
347: int retVal = obuf.length;
348: if (mechCtxt.getProvider().getName().equals(
349: "SunNativeGSS")
350: || (GSSUtil.isSpNegoMech(mechOid))) {
351: // do not add GSS header for native provider and SPNEGO
352: } else {
353: // add GSS header
354: gssHeader = new GSSHeader(objId, obuf.length);
355: retVal += gssHeader.encode(outStream);
356: }
357: outStream.write(obuf);
358: }
359:
360: if (mechCtxt.isEstablished()) {
361: currentState = READY;
362: }
363: } catch (IOException e) {
364: throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN, e
365: .getMessage());
366: }
367: }
368:
369: public boolean isEstablished() {
370: if (mechCtxt == null)
371: return false;
372: else
373: return (currentState == READY);
374: }
375:
376: public int getWrapSizeLimit(int qop, boolean confReq,
377: int maxTokenSize) throws GSSException {
378: if (mechCtxt != null)
379: return mechCtxt
380: .getWrapSizeLimit(qop, confReq, maxTokenSize);
381: else
382: throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
383: "No mechanism context yet!");
384: }
385:
386: public byte[] wrap(byte inBuf[], int offset, int len,
387: MessageProp msgProp) throws GSSException {
388: if (mechCtxt != null)
389: return mechCtxt.wrap(inBuf, offset, len, msgProp);
390: else
391: throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
392: "No mechanism context yet!");
393: }
394:
395: public void wrap(InputStream inStream, OutputStream outStream,
396: MessageProp msgProp) throws GSSException {
397: if (mechCtxt != null)
398: mechCtxt.wrap(inStream, outStream, msgProp);
399: else
400: throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
401: "No mechanism context yet!");
402: }
403:
404: public byte[] unwrap(byte[] inBuf, int offset, int len,
405: MessageProp msgProp) throws GSSException {
406: if (mechCtxt != null)
407: return mechCtxt.unwrap(inBuf, offset, len, msgProp);
408: else
409: throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
410: "No mechanism context yet!");
411: }
412:
413: public void unwrap(InputStream inStream, OutputStream outStream,
414: MessageProp msgProp) throws GSSException {
415: if (mechCtxt != null)
416: mechCtxt.unwrap(inStream, outStream, msgProp);
417: else
418: throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
419: "No mechanism context yet!");
420: }
421:
422: public byte[] getMIC(byte[] inMsg, int offset, int len,
423: MessageProp msgProp) throws GSSException {
424: if (mechCtxt != null)
425: return mechCtxt.getMIC(inMsg, offset, len, msgProp);
426: else
427: throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
428: "No mechanism context yet!");
429: }
430:
431: public void getMIC(InputStream inStream, OutputStream outStream,
432: MessageProp msgProp) throws GSSException {
433: if (mechCtxt != null)
434: mechCtxt.getMIC(inStream, outStream, msgProp);
435: else
436: throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
437: "No mechanism context yet!");
438: }
439:
440: public void verifyMIC(byte[] inTok, int tokOffset, int tokLen,
441: byte[] inMsg, int msgOffset, int msgLen, MessageProp msgProp)
442: throws GSSException {
443: if (mechCtxt != null)
444: mechCtxt.verifyMIC(inTok, tokOffset, tokLen, inMsg,
445: msgOffset, msgLen, msgProp);
446: else
447: throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
448: "No mechanism context yet!");
449: }
450:
451: public void verifyMIC(InputStream tokStream, InputStream msgStream,
452: MessageProp msgProp) throws GSSException {
453: if (mechCtxt != null)
454: mechCtxt.verifyMIC(tokStream, msgStream, msgProp);
455: else
456: throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
457: "No mechanism context yet!");
458: }
459:
460: public byte[] export() throws GSSException {
461: // Defaults to null to match old behavior
462: byte[] result = null;
463: // Only allow context export from native provider since JGSS
464: // still has not defined its own interprocess token format
465: if (mechCtxt.isTransferable()
466: && mechCtxt.getProvider().getName().equals(
467: "SunNativeGSS")) {
468: result = mechCtxt.export();
469: }
470: return result;
471: }
472:
473: public void requestMutualAuth(boolean state) throws GSSException {
474: if (mechCtxt == null)
475: reqMutualAuthState = state;
476: }
477:
478: public void requestReplayDet(boolean state) throws GSSException {
479: if (mechCtxt == null)
480: reqReplayDetState = state;
481: }
482:
483: public void requestSequenceDet(boolean state) throws GSSException {
484: if (mechCtxt == null)
485: reqSequenceDetState = state;
486: }
487:
488: public void requestCredDeleg(boolean state) throws GSSException {
489: if (mechCtxt == null)
490: reqCredDelegState = state;
491: }
492:
493: public void requestAnonymity(boolean state) throws GSSException {
494: if (mechCtxt == null)
495: reqAnonState = state;
496: }
497:
498: public void requestConf(boolean state) throws GSSException {
499: if (mechCtxt == null)
500: reqConfState = state;
501: }
502:
503: public void requestInteg(boolean state) throws GSSException {
504: if (mechCtxt == null)
505: reqIntegState = state;
506: }
507:
508: public void requestLifetime(int lifetime) throws GSSException {
509: if (mechCtxt == null)
510: reqLifetime = lifetime;
511: }
512:
513: public void setChannelBinding(ChannelBinding channelBindings)
514: throws GSSException {
515:
516: if (mechCtxt == null)
517: this .channelBindings = channelBindings;
518:
519: }
520:
521: public boolean getCredDelegState() {
522: if (mechCtxt != null)
523: return mechCtxt.getCredDelegState();
524: else
525: return reqCredDelegState;
526: }
527:
528: public boolean getMutualAuthState() {
529: if (mechCtxt != null)
530: return mechCtxt.getMutualAuthState();
531: else
532: return reqMutualAuthState;
533: }
534:
535: public boolean getReplayDetState() {
536: if (mechCtxt != null)
537: return mechCtxt.getReplayDetState();
538: else
539: return reqReplayDetState;
540: }
541:
542: public boolean getSequenceDetState() {
543: if (mechCtxt != null)
544: return mechCtxt.getSequenceDetState();
545: else
546: return reqSequenceDetState;
547: }
548:
549: public boolean getAnonymityState() {
550: if (mechCtxt != null)
551: return mechCtxt.getAnonymityState();
552: else
553: return reqAnonState;
554: }
555:
556: public boolean isTransferable() throws GSSException {
557: if (mechCtxt != null)
558: return mechCtxt.isTransferable();
559: else
560: return false;
561: }
562:
563: public boolean isProtReady() {
564: if (mechCtxt != null)
565: return mechCtxt.isProtReady();
566: else
567: return false;
568: }
569:
570: public boolean getConfState() {
571: if (mechCtxt != null)
572: return mechCtxt.getConfState();
573: else
574: return reqConfState;
575: }
576:
577: public boolean getIntegState() {
578: if (mechCtxt != null)
579: return mechCtxt.getIntegState();
580: else
581: return reqIntegState;
582: }
583:
584: public int getLifetime() {
585: if (mechCtxt != null)
586: return mechCtxt.getLifetime();
587: else
588: return reqLifetime;
589: }
590:
591: public GSSName getSrcName() throws GSSException {
592: if (srcName == null) {
593: srcName = GSSNameImpl.wrapElement(gssManager, mechCtxt
594: .getSrcName());
595: }
596: return srcName;
597: }
598:
599: public GSSName getTargName() throws GSSException {
600: if (targName == null) {
601: targName = GSSNameImpl.wrapElement(gssManager, mechCtxt
602: .getTargName());
603: }
604: return targName;
605: }
606:
607: public Oid getMech() throws GSSException {
608: if (mechCtxt != null) {
609: return mechCtxt.getMech();
610: }
611: return mechOid;
612: }
613:
614: public GSSCredential getDelegCred() throws GSSException {
615:
616: if (mechCtxt == null)
617: throw new GSSExceptionImpl(GSSException.NO_CONTEXT,
618: "No mechanism context yet!");
619: GSSCredentialSpi delCredElement = mechCtxt.getDelegCred();
620: return (delCredElement == null ? null : new GSSCredentialImpl(
621: gssManager, delCredElement));
622: }
623:
624: public boolean isInitiator() throws GSSException {
625: return initiator;
626: }
627:
628: public void dispose() throws GSSException {
629: currentState = DELETED;
630: if (mechCtxt != null) {
631: mechCtxt.dispose();
632: mechCtxt = null;
633: }
634: myCred = null;
635: srcName = null;
636: targName = null;
637: }
638: }
|