001: /*
002: * Copyright 2005 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.wrapper;
027:
028: import org.ietf.jgss.*;
029: import java.security.Provider;
030: import sun.security.jgss.GSSHeader;
031: import sun.security.jgss.GSSUtil;
032: import sun.security.jgss.GSSExceptionImpl;
033: import sun.security.jgss.spi.*;
034: import sun.security.util.DerValue;
035: import sun.security.util.ObjectIdentifier;
036: import sun.security.jgss.spnego.NegTokenInit;
037: import sun.security.jgss.spnego.NegTokenTarg;
038: import javax.security.auth.kerberos.DelegationPermission;
039: import java.io.*;
040:
041: /**
042: * This class is essentially a wrapper class for the gss_ctx_id_t
043: * structure of the native GSS library.
044: * @author Valerie Peng
045: * @version 1.15, 05/05/07
046: * @since 1.6
047: */
048: class NativeGSSContext implements GSSContextSpi {
049:
050: private static final int GSS_C_DELEG_FLAG = 1;
051: private static final int GSS_C_MUTUAL_FLAG = 2;
052: private static final int GSS_C_REPLAY_FLAG = 4;
053: private static final int GSS_C_SEQUENCE_FLAG = 8;
054: private static final int GSS_C_CONF_FLAG = 16;
055: private static final int GSS_C_INTEG_FLAG = 32;
056: private static final int GSS_C_ANON_FLAG = 64;
057: private static final int GSS_C_PROT_READY_FLAG = 128;
058: private static final int GSS_C_TRANS_FLAG = 256;
059:
060: private static final int NUM_OF_INQUIRE_VALUES = 6;
061:
062: private long pContext = 0; // Pointer to the gss_ctx_id_t structure
063: private GSSNameElement srcName;
064: private GSSNameElement targetName;
065: private GSSCredElement cred;
066: private boolean isInitiator;
067: private boolean isEstablished;
068: private Oid actualMech; // Assigned during context establishment
069:
070: private ChannelBinding cb;
071: private GSSCredElement delegatedCred;
072: private int flags;
073: private int lifetime = GSSCredential.DEFAULT_LIFETIME;
074: private final GSSLibStub cStub;
075:
076: private boolean skipDelegPermCheck;
077: private boolean skipServicePermCheck;
078:
079: // Retrieve the (preferred) mech out of SPNEGO tokens, i.e.
080: // NegTokenInit & NegTokenTarg
081: private static Oid getMechFromSpNegoToken(byte[] token,
082: boolean isInitiator) throws GSSException {
083: Oid mech = null;
084: if (isInitiator) {
085: GSSHeader header = null;
086: try {
087: header = new GSSHeader(new ByteArrayInputStream(token));
088: } catch (IOException ioe) {
089: throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
090: }
091: int negTokenLen = header.getMechTokenLength();
092: byte[] negToken = new byte[negTokenLen];
093: System.arraycopy(token, token.length - negTokenLen,
094: negToken, 0, negToken.length);
095:
096: NegTokenInit ntok = new NegTokenInit(negToken);
097: if (ntok.getMechToken() != null) {
098: Oid[] mechList = ntok.getMechTypeList();
099: mech = mechList[0];
100: }
101: } else {
102: NegTokenTarg ntok = new NegTokenTarg(token);
103: mech = ntok.getSupportedMech();
104: }
105: return mech;
106: }
107:
108: // Perform the Service permission check
109: private void doServicePermCheck() throws GSSException {
110: if (System.getSecurityManager() != null) {
111: String action = (isInitiator ? "initiate" : "accept");
112: // Need to check Service permission for accessing
113: // initiator cred for SPNEGO during context establishment
114: if (GSSUtil.isSpNegoMech(cStub.getMech()) && isInitiator
115: && !isEstablished) {
116: if (srcName == null) {
117: // Check by creating default initiator KRB5 cred
118: GSSCredElement tempCred = new GSSCredElement(
119: null,
120: lifetime,
121: GSSCredential.INITIATE_ONLY,
122: GSSLibStub
123: .getInstance(GSSUtil.GSS_KRB5_MECH_OID));
124: tempCred.dispose();
125: } else {
126: String tgsName = Krb5Util.getTGSName(srcName);
127: Krb5Util.checkServicePermission(tgsName, action);
128: }
129: }
130: String targetStr = targetName.getKrbName();
131: Krb5Util.checkServicePermission(targetStr, action);
132: skipServicePermCheck = true;
133: }
134: }
135:
136: // Perform the Delegation permission check
137: private void doDelegPermCheck() throws GSSException {
138: SecurityManager sm = System.getSecurityManager();
139: if (sm != null) {
140: String targetStr = targetName.getKrbName();
141: String tgsStr = Krb5Util.getTGSName(targetName);
142: StringBuffer buf = new StringBuffer("\"");
143: buf.append(targetStr).append("\" \"");
144: buf.append(tgsStr).append('\"');
145: String krbPrincPair = buf.toString();
146: SunNativeProvider.debug("Checking DelegationPermission ("
147: + krbPrincPair + ")");
148: DelegationPermission perm = new DelegationPermission(
149: krbPrincPair);
150: sm.checkPermission(perm);
151: skipDelegPermCheck = true;
152: }
153: }
154:
155: private byte[] retrieveToken(InputStream is, int mechTokenLen)
156: throws GSSException {
157: try {
158: byte[] result = null;
159: if (mechTokenLen != -1) {
160: // Need to add back the GSS header for a complete GSS token
161: SunNativeProvider
162: .debug("Precomputed mechToken length: "
163: + mechTokenLen);
164: GSSHeader gssHeader = new GSSHeader(
165: new ObjectIdentifier(cStub.getMech().toString()),
166: mechTokenLen);
167: ByteArrayOutputStream baos = new ByteArrayOutputStream(
168: 600);
169:
170: byte[] mechToken = new byte[mechTokenLen];
171: int len = is.read(mechToken);
172: assert (mechTokenLen == len);
173: gssHeader.encode(baos);
174: baos.write(mechToken);
175: result = baos.toByteArray();
176: } else {
177: // Must be unparsed GSS token or SPNEGO's NegTokenTarg token
178: assert (mechTokenLen == -1);
179: DerValue dv = new DerValue(is);
180: result = dv.toByteArray();
181: }
182: SunNativeProvider.debug("Complete Token length: "
183: + result.length);
184: return result;
185: } catch (IOException ioe) {
186: throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
187: }
188: }
189:
190: // Constructor for context initiator
191: NativeGSSContext(GSSNameElement peer, GSSCredElement myCred,
192: int time, GSSLibStub stub) throws GSSException {
193: if (peer == null) {
194: throw new GSSException(GSSException.FAILURE, 1, "null peer");
195: }
196: cStub = stub;
197: cred = myCred;
198: targetName = peer;
199: isInitiator = true;
200: lifetime = time;
201:
202: if (GSSUtil.isKerberosMech(cStub.getMech())) {
203: doServicePermCheck();
204: if (cred == null) {
205: cred = new GSSCredElement(null, lifetime,
206: GSSCredential.INITIATE_ONLY, cStub);
207: }
208: srcName = cred.getName();
209: }
210: }
211:
212: // Constructor for context acceptor
213: NativeGSSContext(GSSCredElement myCred, GSSLibStub stub)
214: throws GSSException {
215: cStub = stub;
216: cred = myCred;
217:
218: if (cred != null)
219: targetName = cred.getName();
220:
221: isInitiator = false;
222: // Defer Service permission check for default acceptor cred
223: // to acceptSecContext()
224: if (GSSUtil.isKerberosMech(cStub.getMech())
225: && targetName != null) {
226: doServicePermCheck();
227: }
228:
229: // srcName and potentially targetName (when myCred is null)
230: // will be set in GSSLibStub.acceptContext(...)
231: }
232:
233: // Constructor for imported context
234: NativeGSSContext(long pCtxt, GSSLibStub stub) throws GSSException {
235: assert (pContext != 0);
236: pContext = pCtxt;
237: cStub = stub;
238:
239: // Set everything except cred, cb, delegatedCred
240: long[] info = cStub.inquireContext(pContext);
241: if (info.length != NUM_OF_INQUIRE_VALUES) {
242: throw new RuntimeException(
243: "Bug w/ GSSLibStub.inquireContext()");
244: }
245: srcName = new GSSNameElement(info[0], cStub);
246: targetName = new GSSNameElement(info[1], cStub);
247: isInitiator = (info[2] != 0);
248: isEstablished = (info[3] != 0);
249: flags = (int) info[4];
250: lifetime = (int) info[5];
251:
252: // Do Service Permission check when importing SPNEGO context
253: // just to be safe
254: Oid mech = cStub.getMech();
255: if (GSSUtil.isSpNegoMech(mech) || GSSUtil.isKerberosMech(mech)) {
256: doServicePermCheck();
257: }
258: }
259:
260: public Provider getProvider() {
261: return SunNativeProvider.INSTANCE;
262: }
263:
264: public byte[] initSecContext(InputStream is, int mechTokenLen)
265: throws GSSException {
266: byte[] outToken = null;
267: if ((!isEstablished) && (isInitiator)) {
268: byte[] inToken = null;
269: // Ignore the specified input stream on the first call
270: if (pContext != 0) {
271: inToken = retrieveToken(is, mechTokenLen);
272: SunNativeProvider.debug("initSecContext=> inToken len="
273: + inToken.length);
274: }
275:
276: if (!getCredDelegState())
277: skipDelegPermCheck = true;
278:
279: if (GSSUtil.isKerberosMech(cStub.getMech())
280: && !skipDelegPermCheck) {
281: doDelegPermCheck();
282: }
283:
284: long pCred = (cred == null ? 0 : cred.pCred);
285: outToken = cStub.initContext(pCred, targetName.pName, cb,
286: inToken, this );
287: SunNativeProvider.debug("initSecContext=> outToken len="
288: + (outToken == null ? 0 : outToken.length));
289:
290: // Only inspect the token when the permission check
291: // has not been performed
292: if (GSSUtil.isSpNegoMech(cStub.getMech())
293: && outToken != null) {
294: // WORKAROUND for SEAM bug#6287358
295: actualMech = getMechFromSpNegoToken(outToken, true);
296:
297: if (GSSUtil.isKerberosMech(actualMech)) {
298: if (!skipServicePermCheck)
299: doServicePermCheck();
300: if (!skipDelegPermCheck)
301: doDelegPermCheck();
302: }
303: }
304:
305: if (isEstablished) {
306: if (srcName == null) {
307: srcName = new GSSNameElement(cStub.getContextName(
308: pContext, true), cStub);
309: }
310: if (cred == null) {
311: cred = new GSSCredElement(srcName, lifetime,
312: GSSCredential.INITIATE_ONLY, cStub);
313: }
314: }
315: }
316: return outToken;
317: }
318:
319: public byte[] acceptSecContext(InputStream is, int mechTokenLen)
320: throws GSSException {
321: byte[] outToken = null;
322: if ((!isEstablished) && (!isInitiator)) {
323: byte[] inToken = retrieveToken(is, mechTokenLen);
324: SunNativeProvider.debug("acceptSecContext=> inToken len="
325: + inToken.length);
326: long pCred = (cred == null ? 0 : cred.pCred);
327: outToken = cStub.acceptContext(pCred, cb, inToken, this );
328: SunNativeProvider.debug("acceptSecContext=> outToken len="
329: + (outToken == null ? 0 : outToken.length));
330:
331: if (targetName == null) {
332: targetName = new GSSNameElement(cStub.getContextName(
333: pContext, false), cStub);
334: // Replace the current default acceptor cred now that
335: // the context acceptor name is available
336: if (cred != null)
337: cred.dispose();
338: cred = new GSSCredElement(targetName, lifetime,
339: GSSCredential.ACCEPT_ONLY, cStub);
340: }
341:
342: // Only inspect token when the permission check has not
343: // been performed
344: if (GSSUtil.isSpNegoMech(cStub.getMech())
345: && (outToken != null) && !skipServicePermCheck) {
346: if (GSSUtil.isKerberosMech(getMechFromSpNegoToken(
347: outToken, false))) {
348: doServicePermCheck();
349: }
350: }
351: }
352: return outToken;
353: }
354:
355: public boolean isEstablished() {
356: return isEstablished;
357: }
358:
359: public void dispose() throws GSSException {
360: srcName = null;
361: targetName = null;
362: cred = null;
363: delegatedCred = null;
364: if (pContext != 0) {
365: pContext = cStub.deleteContext(pContext);
366: pContext = 0;
367: }
368: }
369:
370: public int getWrapSizeLimit(int qop, boolean confReq,
371: int maxTokenSize) throws GSSException {
372: return cStub.wrapSizeLimit(pContext, (confReq ? 1 : 0), qop,
373: maxTokenSize);
374: }
375:
376: public byte[] wrap(byte[] inBuf, int offset, int len,
377: MessageProp msgProp) throws GSSException {
378: byte[] data = inBuf;
379: if ((offset != 0) || (len != inBuf.length)) {
380: data = new byte[len];
381: System.arraycopy(inBuf, offset, data, 0, len);
382: }
383: return cStub.wrap(pContext, data, msgProp);
384: }
385:
386: public void wrap(byte inBuf[], int offset, int len,
387: OutputStream os, MessageProp msgProp) throws GSSException {
388: try {
389: byte[] result = wrap(inBuf, offset, len, msgProp);
390: os.write(result);
391: } catch (IOException ioe) {
392: throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
393: }
394: }
395:
396: public int wrap(byte[] inBuf, int inOffset, int len, byte[] outBuf,
397: int outOffset, MessageProp msgProp) throws GSSException {
398: byte[] result = wrap(inBuf, inOffset, len, msgProp);
399: System.arraycopy(result, 0, outBuf, outOffset, result.length);
400: return result.length;
401: }
402:
403: public void wrap(InputStream inStream, OutputStream outStream,
404: MessageProp msgProp) throws GSSException {
405: try {
406: byte[] data = new byte[inStream.available()];
407: int length = inStream.read(data);
408: byte[] token = wrap(data, 0, length, msgProp);
409: outStream.write(token);
410: } catch (IOException ioe) {
411: throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
412: }
413: }
414:
415: public byte[] unwrap(byte[] inBuf, int offset, int len,
416: MessageProp msgProp) throws GSSException {
417: if ((offset != 0) || (len != inBuf.length)) {
418: byte[] temp = new byte[len];
419: System.arraycopy(inBuf, offset, temp, 0, len);
420: return cStub.unwrap(pContext, temp, msgProp);
421: } else {
422: return cStub.unwrap(pContext, inBuf, msgProp);
423: }
424: }
425:
426: public int unwrap(byte[] inBuf, int inOffset, int len,
427: byte[] outBuf, int outOffset, MessageProp msgProp)
428: throws GSSException {
429: byte[] result = null;
430: if ((inOffset != 0) || (len != inBuf.length)) {
431: byte[] temp = new byte[len];
432: System.arraycopy(inBuf, inOffset, temp, 0, len);
433: result = cStub.unwrap(pContext, temp, msgProp);
434: } else {
435: result = cStub.unwrap(pContext, inBuf, msgProp);
436: }
437: System.arraycopy(result, 0, outBuf, outOffset, result.length);
438: return result.length;
439: }
440:
441: public void unwrap(InputStream inStream, OutputStream outStream,
442: MessageProp msgProp) throws GSSException {
443: try {
444: byte[] wrapped = new byte[inStream.available()];
445: int wLength = inStream.read(wrapped);
446: byte[] data = unwrap(wrapped, 0, wLength, msgProp);
447: outStream.write(data);
448: outStream.flush();
449: } catch (IOException ioe) {
450: throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
451: }
452: }
453:
454: public int unwrap(InputStream inStream, byte[] outBuf,
455: int outOffset, MessageProp msgProp) throws GSSException {
456: byte[] wrapped = null;
457: int wLength = 0;
458: try {
459: wrapped = new byte[inStream.available()];
460: wLength = inStream.read(wrapped);
461: byte[] result = unwrap(wrapped, 0, wLength, msgProp);
462: } catch (IOException ioe) {
463: throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
464: }
465: byte[] result = unwrap(wrapped, 0, wLength, msgProp);
466: System.arraycopy(result, 0, outBuf, outOffset, result.length);
467: return result.length;
468: }
469:
470: public byte[] getMIC(byte[] in, int offset, int len,
471: MessageProp msgProp) throws GSSException {
472: int qop = (msgProp == null ? 0 : msgProp.getQOP());
473: byte[] inMsg = in;
474: if ((offset != 0) || (len != in.length)) {
475: inMsg = new byte[len];
476: System.arraycopy(in, offset, inMsg, 0, len);
477: }
478: return cStub.getMic(pContext, qop, inMsg);
479: }
480:
481: public void getMIC(InputStream inStream, OutputStream outStream,
482: MessageProp msgProp) throws GSSException {
483: try {
484: int length = 0;
485: byte[] msg = new byte[inStream.available()];
486: length = inStream.read(msg);
487:
488: byte[] msgToken = getMIC(msg, 0, length, msgProp);
489: if ((msgToken != null) && msgToken.length != 0) {
490: outStream.write(msgToken);
491: }
492: } catch (IOException ioe) {
493: throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
494: }
495: }
496:
497: public void verifyMIC(byte[] inToken, int tOffset, int tLen,
498: byte[] inMsg, int mOffset, int mLen, MessageProp msgProp)
499: throws GSSException {
500: byte[] token = inToken;
501: byte[] msg = inMsg;
502: if ((tOffset != 0) || (tLen != inToken.length)) {
503: token = new byte[tLen];
504: System.arraycopy(inToken, tOffset, token, 0, tLen);
505: }
506: if ((mOffset != 0) || (mLen != inMsg.length)) {
507: msg = new byte[mLen];
508: System.arraycopy(inMsg, mOffset, msg, 0, mLen);
509: }
510: cStub.verifyMic(pContext, token, msg, msgProp);
511: }
512:
513: public void verifyMIC(InputStream tokStream, InputStream msgStream,
514: MessageProp msgProp) throws GSSException {
515: try {
516: byte[] msg = new byte[msgStream.available()];
517: int mLength = msgStream.read(msg);
518: byte[] tok = new byte[tokStream.available()];
519: int tLength = tokStream.read(tok);
520: verifyMIC(tok, 0, tLength, msg, 0, mLength, msgProp);
521: } catch (IOException ioe) {
522: throw new GSSExceptionImpl(GSSException.FAILURE, ioe);
523: }
524: }
525:
526: public byte[] export() throws GSSException {
527: byte[] result = cStub.exportContext(pContext);
528: pContext = 0;
529: return result;
530: }
531:
532: private void changeFlags(int flagMask, boolean isEnable) {
533: if (isInitiator && pContext == 0) {
534: if (isEnable) {
535: flags |= flagMask;
536: } else {
537: flags &= ~flagMask;
538: }
539: }
540: }
541:
542: public void requestMutualAuth(boolean state) throws GSSException {
543: changeFlags(GSS_C_MUTUAL_FLAG, state);
544: }
545:
546: public void requestReplayDet(boolean state) throws GSSException {
547: changeFlags(GSS_C_REPLAY_FLAG, state);
548: }
549:
550: public void requestSequenceDet(boolean state) throws GSSException {
551: changeFlags(GSS_C_SEQUENCE_FLAG, state);
552: }
553:
554: public void requestCredDeleg(boolean state) throws GSSException {
555: changeFlags(GSS_C_DELEG_FLAG, state);
556: }
557:
558: public void requestAnonymity(boolean state) throws GSSException {
559: changeFlags(GSS_C_ANON_FLAG, state);
560: }
561:
562: public void requestConf(boolean state) throws GSSException {
563: changeFlags(GSS_C_CONF_FLAG, state);
564: }
565:
566: public void requestInteg(boolean state) throws GSSException {
567: changeFlags(GSS_C_INTEG_FLAG, state);
568: }
569:
570: public void requestLifetime(int lifetime) throws GSSException {
571: if (isInitiator && pContext == 0) {
572: this .lifetime = lifetime;
573: }
574: }
575:
576: public void setChannelBinding(ChannelBinding cb)
577: throws GSSException {
578: if (pContext == 0) {
579: this .cb = cb;
580: }
581: }
582:
583: private boolean checkFlags(int flagMask) {
584: return ((flags & flagMask) != 0);
585: }
586:
587: public boolean getCredDelegState() {
588: return checkFlags(GSS_C_DELEG_FLAG);
589: }
590:
591: public boolean getMutualAuthState() {
592: return checkFlags(GSS_C_MUTUAL_FLAG);
593: }
594:
595: public boolean getReplayDetState() {
596: return checkFlags(GSS_C_REPLAY_FLAG);
597: }
598:
599: public boolean getSequenceDetState() {
600: return checkFlags(GSS_C_SEQUENCE_FLAG);
601: }
602:
603: public boolean getAnonymityState() {
604: return checkFlags(GSS_C_ANON_FLAG);
605: }
606:
607: public boolean isTransferable() throws GSSException {
608: return checkFlags(GSS_C_TRANS_FLAG);
609: }
610:
611: public boolean isProtReady() {
612: return checkFlags(GSS_C_PROT_READY_FLAG);
613: }
614:
615: public boolean getConfState() {
616: return checkFlags(GSS_C_CONF_FLAG);
617: }
618:
619: public boolean getIntegState() {
620: return checkFlags(GSS_C_INTEG_FLAG);
621: }
622:
623: public int getLifetime() {
624: return cStub.getContextTime(pContext);
625: }
626:
627: public GSSNameSpi getSrcName() throws GSSException {
628: return srcName;
629: }
630:
631: public GSSNameSpi getTargName() throws GSSException {
632: return targetName;
633: }
634:
635: public Oid getMech() throws GSSException {
636: if (isEstablished && actualMech != null) {
637: return actualMech;
638: } else {
639: return cStub.getMech();
640: }
641: }
642:
643: public GSSCredentialSpi getDelegCred() throws GSSException {
644: return delegatedCred;
645: }
646:
647: public boolean isInitiator() {
648: return isInitiator;
649: }
650:
651: protected void finalize() throws Throwable {
652: dispose();
653: }
654: }
|