blob: f2355cb6c1cd449dcc0993c97f3ab0130429f61c [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-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;
27
28import org.ietf.jgss.*;
29import sun.security.jgss.spi.*;
30import java.util.*;
31
32public class GSSCredentialImpl implements GSSCredential {
33
34 private GSSManagerImpl gssManager = null;
35 private boolean destroyed = false;
36
37 /*
38 * We store all elements in a hashtable, using <oid, usage> as the
39 * key. This makes it easy to locate the specific kind of credential we
40 * need. The implementation needs to be optimized for the case where
41 * there is just one element (tempCred).
42 */
43 private Hashtable<SearchKey, GSSCredentialSpi> hashtable = null;
44
45 // XXX Optimization for single mech usage
46 private GSSCredentialSpi tempCred = null;
47
48 GSSCredentialImpl(GSSManagerImpl gssManager, int usage)
49 throws GSSException {
50 this(gssManager, null, GSSCredential.DEFAULT_LIFETIME,
51 (Oid[]) null, usage);
52 }
53
54 GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
55 int lifetime, Oid mech, int usage)
56 throws GSSException {
57 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
58
59 init(gssManager);
60 add(name, lifetime, lifetime, mech, usage);
61 }
62
63 GSSCredentialImpl(GSSManagerImpl gssManager, GSSName name,
64 int lifetime, Oid mechs[], int usage)
65 throws GSSException {
66 init(gssManager);
67 boolean defaultList = false;
68 if (mechs == null) {
69 mechs = gssManager.getMechs();
70 defaultList = true;
71 }
72
73 for (int i = 0; i < mechs.length; i++) {
74 try {
75 add(name, lifetime, lifetime, mechs[i], usage);
76 } catch (GSSException e) {
77 if (defaultList) {
78 // Try the next mechanism
79 GSSUtil.debug("Ignore " + e + " while acquring cred for "
80 + mechs[i]);
81 //e.printStackTrace();
82 } else throw e; // else try the next mechanism
83 }
84 }
85 if ((hashtable.size() == 0) || (usage != getUsage()))
86 throw new GSSException(GSSException.NO_CRED);
87 }
88
89 public GSSCredentialImpl(GSSManagerImpl gssManager,
90 GSSCredentialSpi mechElement) throws GSSException {
91
92 init(gssManager);
93 int usage = GSSCredential.ACCEPT_ONLY;
94 if (mechElement.isInitiatorCredential()) {
95 if (mechElement.isAcceptorCredential()) {
96 usage = GSSCredential.INITIATE_AND_ACCEPT;
97 } else {
98 usage = GSSCredential.INITIATE_ONLY;
99 }
100 }
101 SearchKey key = new SearchKey(mechElement.getMechanism(),
102 usage);
103 tempCred = mechElement;
104 hashtable.put(key, tempCred);
105 }
106
107 void init(GSSManagerImpl gssManager) {
108 this.gssManager = gssManager;
109 hashtable = new Hashtable<SearchKey, GSSCredentialSpi>(
110 gssManager.getMechs().length);
111 }
112
113 public void dispose() throws GSSException {
114 if (!destroyed) {
115 GSSCredentialSpi element;
116 Enumeration<GSSCredentialSpi> values = hashtable.elements();
117 while (values.hasMoreElements()) {
118 element = values.nextElement();
119 element.dispose();
120 }
121 destroyed = true;
122 }
123 }
124
125 public GSSName getName() throws GSSException {
126 if (destroyed) {
127 throw new IllegalStateException("This credential is " +
128 "no longer valid");
129 }
130 return GSSNameImpl.wrapElement(gssManager, tempCred.getName());
131 }
132
133 public GSSName getName(Oid mech) throws GSSException {
134
135 if (destroyed) {
136 throw new IllegalStateException("This credential is " +
137 "no longer valid");
138 }
139
140 SearchKey key = null;
141 GSSCredentialSpi element = null;
142
143 if (mech == null) 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();
185 e.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 ?
196 tempInitLife:
197 tempAcceptLife);
198 }
199 if (min > tempLife)
200 min = tempLife;
201 }
202
203 return min;
204 }
205
206 public int getRemainingInitLifetime(Oid mech) throws GSSException {
207
208 if (destroyed) {
209 throw new IllegalStateException("This credential is " +
210 "no longer valid");
211 }
212
213 GSSCredentialSpi element = null;
214 SearchKey key = null;
215 boolean found = false;
216 int max = 0;
217
218 if (mech == null) 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) mech = ProviderList.DEFAULT_MECH_OID;
259
260 key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
261 element = hashtable.get(key);
262
263 if (element != null) {
264 found = true;
265 if (max < element.getAcceptLifetime())
266 max = element.getAcceptLifetime();
267 }
268
269 key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
270 element = hashtable.get(key);
271
272 if (element != null) {
273 found = true;
274 if (max < element.getAcceptLifetime())
275 max = element.getAcceptLifetime();
276 }
277
278 if (!found) {
279 throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
280 }
281
282 return max;
283
284 }
285
286 /**
287 * Returns the usage mode for this credential. Returns
288 * INITIATE_AND_ACCEPT if any one element contained in it supports
289 * INITIATE_AND_ACCEPT or if two different elements exist where one
290 * support INITIATE_ONLY and the other supports ACCEPT_ONLY.
291 */
292 public int getUsage() throws GSSException {
293
294 if (destroyed) {
295 throw new IllegalStateException("This credential is " +
296 "no longer valid");
297 }
298
299 SearchKey tempKey;
300 boolean initiate = false;
301 boolean accept = false;
302
303 for (Enumeration<SearchKey> e = hashtable.keys();
304 e.hasMoreElements(); ) {
305 tempKey = e.nextElement();
306 if (tempKey.getUsage() == INITIATE_ONLY)
307 initiate = true;
308 else if (tempKey.getUsage() == ACCEPT_ONLY)
309 accept = true;
310 else
311 return INITIATE_AND_ACCEPT;
312 }
313 if (initiate) {
314 if (accept)
315 return INITIATE_AND_ACCEPT;
316 else
317 return INITIATE_ONLY;
318 } else
319 return ACCEPT_ONLY;
320 }
321
322 public int getUsage(Oid mech) throws GSSException {
323
324 if (destroyed) {
325 throw new IllegalStateException("This credential is " +
326 "no longer valid");
327 }
328
329 GSSCredentialSpi element = null;
330 SearchKey key = null;
331 boolean initiate = false;
332 boolean accept = false;
333
334 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
335
336 key = new SearchKey(mech, GSSCredential.INITIATE_ONLY);
337 element = hashtable.get(key);
338
339 if (element != null) {
340 initiate = true;
341 }
342
343 key = new SearchKey(mech, GSSCredential.ACCEPT_ONLY);
344 element = hashtable.get(key);
345
346 if (element != null) {
347 accept = true;
348 }
349
350 key = new SearchKey(mech, GSSCredential.INITIATE_AND_ACCEPT);
351 element = hashtable.get(key);
352
353 if (element != null) {
354 initiate = true;
355 accept = true;
356 }
357
358 if (initiate && accept)
359 return GSSCredential.INITIATE_AND_ACCEPT;
360 else if (initiate)
361 return GSSCredential.INITIATE_ONLY;
362 else if (accept)
363 return GSSCredential.ACCEPT_ONLY;
364 else {
365 throw new GSSExceptionImpl(GSSException.BAD_MECH, mech);
366 }
367 }
368
369 public Oid[] getMechs() throws GSSException {
370
371 if (destroyed) {
372 throw new IllegalStateException("This credential is " +
373 "no longer valid");
374 }
375 Vector<Oid> result = new Vector<Oid>(hashtable.size());
376
377 for (Enumeration<SearchKey> e = hashtable.keys();
378 e.hasMoreElements(); ) {
379 SearchKey tempKey = e.nextElement();
380 result.addElement(tempKey.getMech());
381 }
382 return result.toArray(new Oid[0]);
383 }
384
385 public void add(GSSName name, int initLifetime, int acceptLifetime,
386 Oid mech, int usage) throws GSSException {
387
388 if (destroyed) {
389 throw new IllegalStateException("This credential is " +
390 "no longer valid");
391 }
392 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
393
394 SearchKey key = new SearchKey(mech, usage);
395 if (hashtable.containsKey(key)) {
396 throw new GSSExceptionImpl(GSSException.DUPLICATE_ELEMENT,
397 "Duplicate element found: " +
398 getElementStr(mech, usage));
399 }
400
401 // XXX If not instance of GSSNameImpl then throw exception
402 // Application mixing GSS implementations
403 GSSNameSpi nameElement = (name == null ? null :
404 ((GSSNameImpl)name).getElement(mech));
405
406 tempCred = gssManager.getCredentialElement(nameElement,
407 initLifetime,
408 acceptLifetime,
409 mech,
410 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() ||
428 !tempCred.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,
446 acceptLifetime,
447 mech,
448 desiredUsage);
449
450 key = new SearchKey(mech, desiredUsage);
451 hashtable.put(key, tempCred);
452 } else {
453 hashtable.put(key, tempCred);
454 }
455 }
456 }
457
458 public boolean equals(Object another) {
459
460 if (destroyed) {
461 throw new IllegalStateException("This credential is " +
462 "no longer valid");
463 }
464
465 if (this == another) {
466 return true;
467 }
468
469 if (!(another instanceof GSSCredentialImpl)) {
470 return false;
471 }
472
473 // NOTE: The specification does not define the criteria to compare
474 // credentials.
475 /*
476 * XXX
477 * The RFC says: "Tests if this GSSCredential refers to the same
478 * entity as the supplied object. The two credentials must be
479 * acquired over the same mechanisms and must refer to the same
480 * principal. Returns "true" if the two GSSCredentials refer to
481 * the same entity; "false" otherwise."
482 *
483 * Well, when do two credentials refer to the same principal? Do
484 * they need to have one GSSName in common for the different
485 * GSSName's that the credential elements return? Or do all
486 * GSSName's have to be in common when the names are exported with
487 * their respective mechanisms for the credential elements?
488 */
489 return false;
490
491 }
492
493 /**
494 * Returns a hashcode value for this GSSCredential.
495 *
496 * @return a hashCode value
497 */
498 public int hashCode() {
499
500 if (destroyed) {
501 throw new IllegalStateException("This credential is " +
502 "no longer valid");
503 }
504
505 // NOTE: The specification does not define the criteria to compare
506 // credentials.
507 /*
508 * XXX
509 * Decide on a criteria for equals first then do this.
510 */
511 return 1;
512 }
513
514 /**
515 * Returns the specified mechanism's credential-element.
516 *
517 * @param mechOid - the oid for mechanism to retrieve
518 * @param throwExcep - boolean indicating if the function is
519 * to throw exception or return null when element is not
520 * found.
521 * @return mechanism credential object
522 * @exception GSSException of invalid mechanism
523 */
524 public GSSCredentialSpi getElement(Oid mechOid, boolean initiate)
525 throws GSSException {
526
527 if (destroyed) {
528 throw new IllegalStateException("This credential is " +
529 "no longer valid");
530 }
531
532 SearchKey key;
533 GSSCredentialSpi element;
534
535 if (mechOid == null) {
536 /*
537 * First see if the default mechanism satisfies the
538 * desired usage.
539 */
540 mechOid = ProviderList.DEFAULT_MECH_OID;
541 key = new SearchKey(mechOid,
542 initiate? INITIATE_ONLY : ACCEPT_ONLY);
543 element = hashtable.get(key);
544 if (element == null) {
545 key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);
546 element = hashtable.get(key);
547 if (element == null) {
548 /*
549 * Now just return any element that satisfies the
550 * desired usage.
551 */
552 Object[] elements = hashtable.entrySet().toArray();
553 for (int i = 0; i < elements.length; i++) {
554 element = (GSSCredentialSpi)
555 ((Map.Entry)elements[i]).getValue();
556 if (element.isInitiatorCredential() == initiate)
557 break;
558 } // for loop
559 }
560 }
561 } else {
562
563 if (initiate)
564 key = new SearchKey(mechOid, INITIATE_ONLY);
565 else
566 key = new SearchKey(mechOid, ACCEPT_ONLY);
567
568 element = hashtable.get(key);
569
570 if (element == null) {
571 key = new SearchKey(mechOid, INITIATE_AND_ACCEPT);
572 element = hashtable.get(key);
573 }
574 }
575
576 if (element == null)
577 throw new GSSExceptionImpl(GSSException.NO_CRED,
578 "No credential found for: " +
579 mechOid + getElementStr(mechOid,
580 initiate? INITIATE_ONLY : ACCEPT_ONLY));
581 return element;
582 }
583
584 Set<GSSCredentialSpi> getElements() {
585 HashSet<GSSCredentialSpi> retVal =
586 new HashSet<GSSCredentialSpi>(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 =
599 displayString.concat(" usage: Initiate");
600 } else if (usage == GSSCredential.ACCEPT_ONLY) {
601 displayString =
602 displayString.concat(" usage: Accept");
603 } else {
604 displayString =
605 displayString.concat(" usage: Initiate and Accept");
606 }
607 return displayString;
608 }
609
610 public String toString() {
611
612 if (destroyed) {
613 throw new IllegalStateException("This credential is " +
614 "no longer valid");
615 }
616
617 GSSCredentialSpi element = null;
618 StringBuffer buffer = new StringBuffer("[GSSCredential: ");
619 Object[] elements = hashtable.entrySet().toArray();
620 for (int i = 0; i < elements.length; i++) {
621 try {
622 buffer.append('\n');
623 element = (GSSCredentialSpi)
624 ((Map.Entry)elements[i]).getValue();
625 buffer.append(element.getName());
626 buffer.append(' ');
627 buffer.append(element.getMechanism());
628 buffer.append(element.isInitiatorCredential() ?
629 " Initiate" : "");
630 buffer.append(element.isAcceptorCredential() ?
631 " Accept" : "");
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 public SearchKey(Oid mechOid, int usage) {
647
648 this.mechOid = mechOid;
649 this.usage = usage;
650 }
651 public Oid getMech() {
652 return mechOid;
653 }
654 public int getUsage() {
655 return usage;
656 }
657 public boolean equals(Object other) {
658 if (! (other instanceof SearchKey))
659 return false;
660 SearchKey that = (SearchKey) other;
661 return ((this.mechOid.equals(that.mechOid)) &&
662 (this.usage == that.usage));
663 }
664 public int hashCode() {
665 return mechOid.hashCode();
666 }
667 }
668
669}