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 java.util.*;
031:
032: public class GSSCredentialImpl implements GSSCredential {
033:
034: private GSSManagerImpl gssManager = null;
035: private boolean destroyed = false;
036:
037: /*
038: * We store all elements in a hashtable, using <oid, usage> as the
039: * key. This makes it easy to locate the specific kind of credential we
040: * need. The implementation needs to be optimized for the case where
041: * there is just one element (tempCred).
042: */
043: private Hashtable<SearchKey, GSSCredentialSpi> hashtable = null;
044:
045: // XXX Optimization for single mech usage
046: private GSSCredentialSpi tempCred = null;
047:
048: GSSCredentialImpl(GSSManagerImpl gssManager, int usage)
049: throws GSSException {
050: this (gssManager, null, GSSCredential.DEFAULT_LIFETIME,
051: (Oid[]) null, usage);
052: }
053:
054: GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
055: int lifetime, Oid mech, int usage) throws GSSException {
056: if (mech == null)
057: mech = ProviderList.DEFAULT_MECH_OID;
058:
059: init(gssManager);
060: add(name, lifetime, lifetime, mech, usage);
061: }
062:
063: GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
064: int lifetime, Oid mechs[], int usage) throws GSSException {
065: init(gssManager);
066: boolean defaultList = false;
067: if (mechs == null) {
068: mechs = gssManager.getMechs();
069: defaultList = true;
070: }
071:
072: for (int i = 0; i < mechs.length; i++) {
073: try {
074: add(name, lifetime, lifetime, mechs[i], usage);
075: } catch (GSSException e) {
076: if (defaultList) {
077: // Try the next mechanism
078: GSSUtil.debug("Ignore " + e
079: + " while acquring cred for " + mechs[i]);
080: //e.printStackTrace();
081: } else
082: throw e; // else try the next mechanism
083: }
084: }
085: if ((hashtable.size() == 0) || (usage != getUsage()))
086: throw new GSSException(GSSException.NO_CRED);
087: }
088:
089: public GSSCredentialImpl(GSSManagerImpl gssManager,
090: GSSCredentialSpi mechElement) throws GSSException {
091:
092: init(gssManager);
093: int usage = GSSCredential.ACCEPT_ONLY;
094: if (mechElement.isInitiatorCredential()) {
095: if (mechElement.isAcceptorCredential()) {
096: usage = GSSCredential.INITIATE_AND_ACCEPT;
097: } else {
098: usage = GSSCredential.INITIATE_ONLY;
099: }
100: }
101: SearchKey key = new SearchKey(mechElement.getMechanism(), usage);
102: tempCred = mechElement;
103: hashtable.put(key, tempCred);
104: }
105:
106: void init(GSSManagerImpl gssManager) {
107: this .gssManager = gssManager;
108: hashtable = new Hashtable<SearchKey, GSSCredentialSpi>(
109: gssManager.getMechs().length);
110: }
111:
112: public void dispose() throws GSSException {
113: if (!destroyed) {
114: GSSCredentialSpi element;
115: Enumeration<GSSCredentialSpi> values = hashtable.elements();
116: while (values.hasMoreElements()) {
117: element = values.nextElement();
118: element.dispose();
119: }
120: destroyed = true;
121: }
122: }
123:
124: public GSSName getName() throws GSSException {
125: if (destroyed) {
126: throw new IllegalStateException("This credential is "
127: + "no longer valid");
128: }
129: return GSSNameImpl.wrapElement(gssManager, tempCred.getName());
130: }
131:
132: public GSSName getName(Oid mech) throws GSSException {
133:
134: if (destroyed) {
135: throw new IllegalStateException("This credential is "
136: + "no longer valid");
137: }
138:
139: SearchKey key = null;
140: GSSCredentialSpi element = null;
141:
142: if (mech == null)
143: mech = ProviderList.DEFAULT_MECH_OID;
144:
145: key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
146: element = hashtable.get(key);
147:
148: if (element == null) {
149: key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
150: element = hashtable.get(key);
151: }
152:
153: if (element == null) {
154: key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
155: element = hashtable.get(key);
156: }
157:
158: if (element == null) {
159: throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
160: }
161:
162: return GSSNameImpl.wrapElement(gssManager, element.getName());
163:
164: }
165:
166: /**
167: * Returns the remaining lifetime of this credential. The remaining
168: * lifetime is defined as the minimum lifetime, either for initiate or
169: * for accept, across all elements contained in it. Not terribly
170: * useful, but required by GSS-API.
171: */
172: public int getRemainingLifetime() throws GSSException {
173:
174: if (destroyed) {
175: throw new IllegalStateException("This credential is "
176: + "no longer valid");
177: }
178:
179: SearchKey tempKey;
180: GSSCredentialSpi tempCred;
181: int tempLife = 0, tempInitLife = 0, tempAcceptLife = 0;
182: int min = INDEFINITE_LIFETIME;
183:
184: for (Enumeration<SearchKey> e = hashtable.keys(); e
185: .hasMoreElements();) {
186: tempKey = e.nextElement();
187: tempCred = hashtable.get(tempKey);
188: if (tempKey.getUsage() == INITIATE_ONLY)
189: tempLife = tempCred.getInitLifetime();
190: else if (tempKey.getUsage() == ACCEPT_ONLY)
191: tempLife = tempCred.getAcceptLifetime();
192: else {
193: tempInitLife = tempCred.getInitLifetime();
194: tempAcceptLife = tempCred.getAcceptLifetime();
195: tempLife = (tempInitLife < tempAcceptLife ? tempInitLife
196: : tempAcceptLife);
197: }
198: if (min > tempLife)
199: min = tempLife;
200: }
201:
202: return min;
203: }
204:
205: public int getRemainingInitLifetime(Oid mech) throws GSSException {
206:
207: if (destroyed) {
208: throw new IllegalStateException("This credential is "
209: + "no longer valid");
210: }
211:
212: GSSCredentialSpi element = null;
213: SearchKey key = null;
214: boolean found = false;
215: int max = 0;
216:
217: if (mech == null)
218: mech = ProviderList.DEFAULT_MECH_OID;
219:
220: key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
221: element = hashtable.get(key);
222:
223: if (element != null) {
224: found = true;
225: if (max < element.getInitLifetime())
226: max = element.getInitLifetime();
227: }
228:
229: key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
230: element = hashtable.get(key);
231:
232: if (element != null) {
233: found = true;
234: if (max < element.getInitLifetime())
235: max = element.getInitLifetime();
236: }
237:
238: if (!found) {
239: throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
240: }
241:
242: return max;
243:
244: }
245:
246: public int getRemainingAcceptLifetime(Oid mech) throws GSSException {
247:
248: if (destroyed) {
249: throw new IllegalStateException("This credential is "
250: + "no longer valid");
251: }
252:
253: GSSCredentialSpi element = null;
254: SearchKey key = null;
255: boolean found = false;
256: int max = 0;
257:
258: if (mech == null)
259: mech = ProviderList.DEFAULT_MECH_OID;
260:
261: key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
262: element = hashtable.get(key);
263:
264: if (element != null) {
265: found = true;
266: if (max < element.getAcceptLifetime())
267: max = element.getAcceptLifetime();
268: }
269:
270: key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
271: element = hashtable.get(key);
272:
273: if (element != null) {
274: found = true;
275: if (max < element.getAcceptLifetime())
276: max = element.getAcceptLifetime();
277: }
278:
279: if (!found) {
280: throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
281: }
282:
283: return max;
284:
285: }
286:
287: /**
288: * Returns the usage mode for this credential. Returns
289: * INITIATE_AND_ACCEPT if any one element contained in it supports
290: * INITIATE_AND_ACCEPT or if two different elements exist where one
291: * support INITIATE_ONLY and the other supports ACCEPT_ONLY.
292: */
293: public int getUsage() throws GSSException {
294:
295: if (destroyed) {
296: throw new IllegalStateException("This credential is "
297: + "no longer valid");
298: }
299:
300: SearchKey tempKey;
301: boolean initiate = false;
302: boolean accept = false;
303:
304: for (Enumeration<SearchKey> e = hashtable.keys(); e
305: .hasMoreElements();) {
306: tempKey = e.nextElement();
307: if (tempKey.getUsage() == INITIATE_ONLY)
308: initiate = true;
309: else if (tempKey.getUsage() == ACCEPT_ONLY)
310: accept = true;
311: else
312: return INITIATE_AND_ACCEPT;
313: }
314: if (initiate) {
315: if (accept)
316: return INITIATE_AND_ACCEPT;
317: else
318: return INITIATE_ONLY;
319: } else
320: return ACCEPT_ONLY;
321: }
322:
323: public int getUsage(Oid mech) throws GSSException {
324:
325: if (destroyed) {
326: throw new IllegalStateException("This credential is "
327: + "no longer valid");
328: }
329:
330: GSSCredentialSpi element = null;
331: SearchKey key = null;
332: boolean initiate = false;
333: boolean accept = false;
334:
335: if (mech == null)
336: mech = ProviderList.DEFAULT_MECH_OID;
337:
338: key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
339: element = hashtable.get(key);
340:
341: if (element != null) {
342: initiate = true;
343: }
344:
345: key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
346: element = hashtable.get(key);
347:
348: if (element != null) {
349: accept = true;
350: }
351:
352: key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
353: element = hashtable.get(key);
354:
355: if (element != null) {
356: initiate = true;
357: accept = true;
358: }
359:
360: if (initiate && accept)
361: return GSSCredential.INITIATE_AND_ACCEPT;
362: else if (initiate)
363: return GSSCredential.INITIATE_ONLY;
364: else if (accept)
365: return GSSCredential.ACCEPT_ONLY;
366: else {
367: throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
368: }
369: }
370:
371: public Oid[] getMechs() throws GSSException {
372:
373: if (destroyed) {
374: throw new IllegalStateException("This credential is "
375: + "no longer valid");
376: }
377: Vector<Oid> result = new Vector<Oid>(hashtable.size());
378:
379: for (Enumeration<SearchKey> e = hashtable.keys(); e
380: .hasMoreElements();) {
381: SearchKey tempKey = e.nextElement();
382: result.addElement(tempKey.getMech());
383: }
384: return result.toArray(new Oid[0]);
385: }
386:
387: public void add(GSSName name, int initLifetime, int acceptLifetime,
388: Oid mech, int usage) throws GSSException {
389:
390: if (destroyed) {
391: throw new IllegalStateException("This credential is "
392: + "no longer valid");
393: }
394: if (mech == null)
395: mech = ProviderList.DEFAULT_MECH_OID;
396:
397: SearchKey key = new SearchKey(mech, usage);
398: if (hashtable.containsKey(key)) {
399: throw new GSSExceptionImpl(GSSException.DUPLICATE_ELEMENT,
400: "Duplicate element found: "
401: + getElementStr(mech, usage));
402: }
403:
404: // XXX If not instance of GSSNameImpl then throw exception
405: // Application mixing GSS implementations
406: GSSNameSpi nameElement = (name == null ? null
407: : ((GSSNameImpl) name).getElement(mech));
408:
409: tempCred = gssManager.getCredentialElement(nameElement,
410: initLifetime, acceptLifetime, mech, usage);
411: /*
412: * Not all mechanisms support the concept of one credential element
413: * that can be used for both initiating and accepting a context. In
414: * the event that an application requests usage INITIATE_AND_ACCEPT
415: * for a credential from such a mechanism, the GSS framework will
416: * need to obtain two different credential elements from the
417: * mechanism, one that will have usage INITIATE_ONLY and another
418: * that will have usage ACCEPT_ONLY. The mechanism will help the
419: * GSS-API realize this by returning a credential element with
420: * usage INITIATE_ONLY or ACCEPT_ONLY prompting it to make another
421: * call to getCredentialElement, this time with the other usage
422: * mode.
423: */
424:
425: if (tempCred != null) {
426: if (usage == GSSCredential.INITIATE_AND_ACCEPT
427: && (!tempCred.isAcceptorCredential() || !tempCred
428: .isInitiatorCredential())) {
429:
430: int currentUsage;
431: int desiredUsage;
432:
433: if (!tempCred.isInitiatorCredential()) {
434: currentUsage = GSSCredential.ACCEPT_ONLY;
435: desiredUsage = GSSCredential.INITIATE_ONLY;
436: } else {
437: currentUsage = GSSCredential.INITIATE_ONLY;
438: desiredUsage = GSSCredential.ACCEPT_ONLY;
439: }
440:
441: key = new SearchKey(mech, currentUsage);
442: hashtable.put(key, tempCred);
443:
444: tempCred = gssManager.getCredentialElement(nameElement,
445: initLifetime, acceptLifetime, mech,
446: desiredUsage);
447:
448: key = new SearchKey(mech, desiredUsage);
449: hashtable.put(key, tempCred);
450: } else {
451: hashtable.put(key, tempCred);
452: }
453: }
454: }
455:
456: public boolean equals(Object another) {
457:
458: if (destroyed) {
459: throw new IllegalStateException("This credential is "
460: + "no longer valid");
461: }
462:
463: if (this == another) {
464: return true;
465: }
466:
467: if (!(another instanceof GSSCredentialImpl)) {
468: return false;
469: }
470:
471: // NOTE: The specification does not define the criteria to compare
472: // credentials.
473: /*
474: * XXX
475: * The RFC says: "Tests if this GSSCredential refers to the same
476: * entity as the supplied object. The two credentials must be
477: * acquired over the same mechanisms and must refer to the same
478: * principal. Returns "true" if the two GSSCredentials refer to
479: * the same entity; "false" otherwise."
480: *
481: * Well, when do two credentials refer to the same principal? Do
482: * they need to have one GSSName in common for the different
483: * GSSName's that the credential elements return? Or do all
484: * GSSName's have to be in common when the names are exported with
485: * their respective mechanisms for the credential elements?
486: */
487: return false;
488:
489: }
490:
491: /**
492: * Returns a hashcode value for this GSSCredential.
493: *
494: * @return a hashCode value
495: */
496: public int hashCode() {
497:
498: if (destroyed) {
499: throw new IllegalStateException("This credential is "
500: + "no longer valid");
501: }
502:
503: // NOTE: The specification does not define the criteria to compare
504: // credentials.
505: /*
506: * XXX
507: * Decide on a criteria for equals first then do this.
508: */
509: return 1;
510: }
511:
512: /**
513: * Returns the specified mechanism's credential-element.
514: *
515: * @param mechOid - the oid for mechanism to retrieve
516: * @param throwExcep - boolean indicating if the function is
517: * to throw exception or return null when element is not
518: * found.
519: * @return mechanism credential object
520: * @exception GSSException of invalid mechanism
521: */
522: public GSSCredentialSpi getElement(Oid mechOid, boolean initiate)
523: throws GSSException {
524:
525: if (destroyed) {
526: throw new IllegalStateException("This credential is "
527: + "no longer valid");
528: }
529:
530: SearchKey key;
531: GSSCredentialSpi element;
532:
533: if (mechOid == null) {
534: /*
535: * First see if the default mechanism satisfies the
536: * desired usage.
537: */
538: mechOid = ProviderList.DEFAULT_MECH_OID;
539: key = new SearchKey(mechOid, initiate ? INITIATE_ONLY
540: : ACCEPT_ONLY);
541: element = hashtable.get(key);
542: if (element == null) {
543: key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);
544: element = hashtable.get(key);
545: if (element == null) {
546: /*
547: * Now just return any element that satisfies the
548: * desired usage.
549: */
550: Object[] elements = hashtable.entrySet().toArray();
551: for (int i = 0; i < elements.length; i++) {
552: element = (GSSCredentialSpi) ((Map.Entry) elements[i])
553: .getValue();
554: if (element.isInitiatorCredential() == initiate)
555: break;
556: } // for loop
557: }
558: }
559: } else {
560:
561: if (initiate)
562: key = new SearchKey(mechOid, INITIATE_ONLY);
563: else
564: key = new SearchKey(mechOid, ACCEPT_ONLY);
565:
566: element = hashtable.get(key);
567:
568: if (element == null) {
569: key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);
570: element = hashtable.get(key);
571: }
572: }
573:
574: if (element == null)
575: throw new GSSExceptionImpl(GSSException.NO_CRED,
576: "No credential found for: "
577: + mechOid
578: + getElementStr(mechOid,
579: initiate ? INITIATE_ONLY
580: : ACCEPT_ONLY));
581: return element;
582: }
583:
584: Set<GSSCredentialSpi> getElements() {
585: HashSet<GSSCredentialSpi> retVal = new HashSet<GSSCredentialSpi>(
586: hashtable.size());
587: Enumeration<GSSCredentialSpi> values = hashtable.elements();
588: while (values.hasMoreElements()) {
589: GSSCredentialSpi o = values.nextElement();
590: retVal.add(o);
591: }
592: return retVal;
593: }
594:
595: private static String getElementStr(Oid mechOid, int usage) {
596: String displayString = mechOid.toString();
597: if (usage == GSSCredential.INITIATE_ONLY) {
598: displayString = displayString.concat(" usage: Initiate");
599: } else if (usage == GSSCredential.ACCEPT_ONLY) {
600: displayString = displayString.concat(" usage: Accept");
601: } else {
602: displayString = displayString
603: .concat(" usage: Initiate and Accept");
604: }
605: return displayString;
606: }
607:
608: public String toString() {
609:
610: if (destroyed) {
611: throw new IllegalStateException("This credential is "
612: + "no longer valid");
613: }
614:
615: GSSCredentialSpi element = null;
616: StringBuffer buffer = new StringBuffer("[GSSCredential: ");
617: Object[] elements = hashtable.entrySet().toArray();
618: for (int i = 0; i < elements.length; i++) {
619: try {
620: buffer.append('\n');
621: element = (GSSCredentialSpi) ((Map.Entry) elements[i])
622: .getValue();
623: buffer.append(element.getName());
624: buffer.append(' ');
625: buffer.append(element.getMechanism());
626: buffer
627: .append(element.isInitiatorCredential() ? " Initiate"
628: : "");
629: buffer
630: .append(element.isAcceptorCredential() ? " Accept"
631: : "");
632: buffer.append(" [");
633: buffer.append(element.toString());
634: buffer.append(']');
635: } catch (GSSException e) {
636: // skip to next element
637: }
638: }
639: buffer.append(']');
640: return buffer.toString();
641: }
642:
643: static class SearchKey {
644: private Oid mechOid = null;
645: private int usage = GSSCredential.INITIATE_AND_ACCEPT;
646:
647: public SearchKey(Oid mechOid, int usage) {
648:
649: this .mechOid = mechOid;
650: this .usage = usage;
651: }
652:
653: public Oid getMech() {
654: return mechOid;
655: }
656:
657: public int getUsage() {
658: return usage;
659: }
660:
661: public boolean equals(Object other) {
662: if (!(other instanceof SearchKey))
663: return false;
664: SearchKey that = (SearchKey) other;
665: return ((this .mechOid.equals(that.mechOid)) && (this .usage == that.usage));
666: }
667:
668: public int hashCode() {
669: return mechOid.hashCode();
670: }
671: }
672:
673: }
|