blob: c1834d81dbbf1654bfd0529f8bd098cdb5bfb7e3 [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.Set;
31import java.util.HashMap;
32import java.util.HashSet;
33import java.util.Arrays;
34import java.io.IOException;
35import java.io.UnsupportedEncodingException;
36import sun.security.util.ObjectIdentifier;
37import sun.security.util.DerInputStream;
38import sun.security.util.DerOutputStream;
39
40/**
41 * This is the implementation class for GSSName. Conceptually the
42 * GSSName is a container with mechanism specific name elements. Each
43 * name element is a representation of how that particular mechanism
44 * would canonicalize this principal.
45 *
46 * Generally a GSSName is created by an application when it supplies
47 * a sequence of bytes and a nametype that helps each mechanism
48 * decide how to interpret those bytes.
49 *
50 * It is not necessary to create name elements for each available
51 * mechanism at the time the application creates the GSSName. This
52 * implementation does this lazily, as and when name elements for
53 * mechanisms are required to be handed out. (Generally, other GSS
54 * classes like GSSContext and GSSCredential request specific
55 * elements depending on the mechanisms that they are dealing with.)
56 * Assume that getting a mechanism to parse the applciation specified
57 * bytes is an expensive call.
58 *
59 * When a GSSName is canonicalized wrt some mechanism, it is supposed
60 * to discard all elements of other mechanisms and retain only the
61 * element for this mechanism. In GSS terminology this is called a
62 * Mechanism Name or MN. This implementation tries to retain the
63 * application provided bytes and name type just in case the MN is
64 * asked to produce an element for a mechanism that is different.
65 *
66 * When a GSSName is to be exported, the name element for the desired
67 * mechanism is converted to a byte representation and written
68 * out. It might happen that a name element for that mechanism cannot
69 * be obtained. This happens when the mechanism is just not supported
70 * in this GSS-API or when the mechanism is supported but bytes
71 * corresponding to the nametypes that it understands are not
72 * available in this GSSName.
73 *
74 * This class is safe for sharing. Each retrieval of a name element
75 * from getElement() might potentially add a new element to the
76 * hashmap of elements, but getElement() is synchronized.
77 *
78 * @author Mayank Upadhyay
79 * @since 1.4
80 */
81
82public class GSSNameImpl implements GSSName {
83
84 private GSSManagerImpl gssManager = null;
85
86 /*
87 * Store whatever the application passed in. We will use this to
88 * get individual mechanisms to create name elements as and when
89 * needed.
90 * Store both the String and the byte[]. Leave I18N to the
91 * mechanism by allowing it to extract bytes from the String!
92 */
93
94 private String appNameStr = null;
95 private byte[] appNameBytes = null;
96 private Oid appNameType = null;
97
98 /*
99 * When we figure out what the printable name would be, we store
100 * both the name and its type.
101 */
102
103 private String printableName = null;
104 private Oid printableNameType = null;
105
106 private HashMap<Oid, GSSNameSpi> elements = null;
107 private GSSNameSpi mechElement = null;
108
109 static GSSNameImpl wrapElement(GSSManagerImpl gssManager,
110 GSSNameSpi mechElement) throws GSSException {
111 return (mechElement == null ?
112 null : new GSSNameImpl(gssManager, mechElement));
113 }
114
115 GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement) {
116 this.gssManager = gssManager;
117 appNameStr = printableName = mechElement.toString();
118 appNameType = printableNameType = mechElement.getStringNameType();
119 this.mechElement = mechElement;
120 elements = new HashMap<Oid, GSSNameSpi>(1);
121 elements.put(mechElement.getMechanism(), this.mechElement);
122 }
123
124 GSSNameImpl(GSSManagerImpl gssManager,
125 Object appName,
126 Oid appNameType)
127 throws GSSException {
128 this(gssManager, appName, appNameType, null);
129 }
130
131 GSSNameImpl(GSSManagerImpl gssManager,
132 Object appName,
133 Oid appNameType,
134 Oid mech)
135 throws GSSException {
136
137 if (appName == null)
138 throw new GSSExceptionImpl(GSSException.BAD_NAME,
139 "Cannot import null name");
140 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
141 if (NT_EXPORT_NAME.equals(appNameType)) {
142 importName(gssManager, appName);
143 } else {
144 init(gssManager, appName, appNameType, mech);
145 }
146 }
147
148 private void init(GSSManagerImpl gssManager,
149 Object appName, Oid appNameType,
150 Oid mech)
151 throws GSSException {
152
153 this.gssManager = gssManager;
154 this.elements =
155 new HashMap<Oid, GSSNameSpi>(gssManager.getMechs().length);
156
157 if (appName instanceof String) {
158 this.appNameStr = (String) appName;
159 /*
160 * If appNameType is null, then the nametype for this printable
161 * string is determined only by interrogating the
162 * mechanism. Thus, defer the setting of printableName and
163 * printableNameType till later.
164 */
165 if (appNameType != null) {
166 printableName = appNameStr;
167 printableNameType = appNameType;
168 }
169 } else {
170 this.appNameBytes = (byte[]) appName;
171 }
172
173 this.appNameType = appNameType;
174
175 mechElement = getElement(mech);
176
177 /*
178 * printableName will be null if appName was in a byte[] or if
179 * appName was in a String but appNameType was null.
180 */
181 if (printableName == null) {
182 printableName = mechElement.toString();
183 printableNameType = mechElement.getStringNameType();
184 }
185
186 /*
187 * At this point the GSSNameImpl has the following set:
188 * appNameStr or appNameBytes
189 * appNameType (could be null)
190 * printableName
191 * printableNameType
192 * mechElement (which also exists in the hashmap of elements)
193 */
194 }
195
196 private void importName(GSSManagerImpl gssManager,
197 Object appName)
198 throws GSSException {
199
200 int pos = 0;
201 byte[] bytes = null;
202
203 if (appName instanceof String) {
204 try {
205 bytes = ((String) appName).getBytes("UTF-8");
206 } catch (UnsupportedEncodingException e) {
207 // Won't happen
208 }
209 } else
210 bytes = (byte[]) appName;
211
212 if ((bytes[pos++] != 0x04) ||
213 (bytes[pos++] != 0x01))
214 throw new GSSExceptionImpl(GSSException.BAD_NAME,
215 "Exported name token id is corrupted!");
216
217 int oidLen = (((0xFF & bytes[pos++]) << 8) |
218 (0xFF & bytes[pos++]));
219 ObjectIdentifier temp = null;
220 try {
221 DerInputStream din = new DerInputStream(bytes, pos,
222 oidLen);
223 temp = new ObjectIdentifier(din);
224 } catch (IOException e) {
225 throw new GSSExceptionImpl(GSSException.BAD_NAME,
226 "Exported name Object identifier is corrupted!");
227 }
228 Oid oid = new Oid(temp.toString());
229 pos += oidLen;
230 int mechPortionLen = (((0xFF & bytes[pos++]) << 24) |
231 ((0xFF & bytes[pos++]) << 16) |
232 ((0xFF & bytes[pos++]) << 8) |
233 (0xFF & bytes[pos++]));
234 byte[] mechPortion = new byte[mechPortionLen];
235 System.arraycopy(bytes, pos, mechPortion, 0, mechPortionLen);
236
237 init(gssManager, mechPortion, NT_EXPORT_NAME, oid);
238 }
239
240 public GSSName canonicalize(Oid mech) throws GSSException {
241 if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
242
243 return wrapElement(gssManager, getElement(mech));
244 }
245
246 /**
247 * This method may return false negatives. But if it says two
248 * names are equals, then there is some mechanism that
249 * authenticates them as the same principal.
250 */
251 public boolean equals(GSSName other) throws GSSException {
252
253 if (this.isAnonymous() || other.isAnonymous())
254 return false;
255
256 if (other == this)
257 return true;
258
259 if (! (other instanceof GSSNameImpl))
260 return equals(gssManager.createName(other.toString(),
261 other.getStringNameType()));
262
263 /*
264 * XXX Do a comparison of the appNameStr/appNameBytes if
265 * available. If that fails, then proceed with this test.
266 */
267
268 GSSNameImpl that = (GSSNameImpl) other;
269
270 GSSNameSpi myElement = this.mechElement;
271 GSSNameSpi element = that.mechElement;
272
273 /*
274 * XXX If they are not of the same mechanism type, convert both to
275 * Kerberos since it is guaranteed to be present.
276 */
277 if ((myElement == null) && (element != null)) {
278 myElement = this.getElement(element.getMechanism());
279 } else if ((myElement != null) && (element == null)) {
280 element = that.getElement(myElement.getMechanism());
281 }
282
283 if (myElement != null && element != null) {
284 return myElement.equals(element);
285 }
286
287 if ((this.appNameType != null) &&
288 (that.appNameType != null)) {
289 if (!this.appNameType.equals(that.appNameType)) {
290 return false;
291 }
292 byte[] myBytes = null;
293 byte[] bytes = null;
294 try {
295 myBytes =
296 (this.appNameStr != null ?
297 this.appNameStr.getBytes("UTF-8") :
298 this.appNameBytes);
299 bytes =
300 (that.appNameStr != null ?
301 that.appNameStr.getBytes("UTF-8") :
302 that.appNameBytes);
303 } catch (UnsupportedEncodingException e) {
304 // Won't happen
305 }
306
307 return Arrays.equals(myBytes, bytes);
308 }
309
310 return false;
311
312 }
313
314 /**
315 * Returns a hashcode value for this GSSName.
316 *
317 * @return a hashCode value
318 */
319 public int hashCode() {
320 /*
321 * XXX
322 * In order to get this to work reliably and properly(!), obtain a
323 * Kerberos name element for the name and then call hashCode on its
324 * string representation. But this cannot be done if the nametype
325 * is not one of those supported by the Kerberos provider and hence
326 * this name cannot be imported by Kerberos. In that case return a
327 * constant value!
328 */
329
330 return 1;
331 }
332
333 public boolean equals(Object another) {
334
335 try {
336 // XXX This can lead to an infinite loop. Extract info
337 // and create a GSSNameImpl with it.
338
339 if (another instanceof GSSName)
340 return equals((GSSName) another);
341 } catch (GSSException e) {
342 // Squelch it and return false
343 }
344
345 return false;
346 }
347
348 /**
349 * Returns a flat name representation for this object. The name
350 * format is defined in RFC 2743:
351 *<pre>
352 * Length Name Description
353 * 2 TOK_ID Token Identifier
354 * For exported name objects, this
355 * must be hex 04 01.
356 * 2 MECH_OID_LEN Length of the Mechanism OID
357 * MECH_OID_LEN MECH_OID Mechanism OID, in DER
358 * 4 NAME_LEN Length of name
359 * NAME_LEN NAME Exported name; format defined in
360 * applicable mechanism draft.
361 *</pre>
362 *
363 * Note that it is not required to canonicalize a name before
364 * calling export(). i.e., the name need not be an MN. If it is
365 * not an MN, an implementation defined algorithm can be used for
366 * choosing the mechanism which should export this name.
367 *
368 * @return the flat name representation for this object
369 * @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,
370 * BAD_NAME, FAILURE.
371 */
372 public byte[] export() throws GSSException {
373
374 if (mechElement == null) {
375 /* Use default mech */
376 mechElement = getElement(ProviderList.DEFAULT_MECH_OID);
377 }
378
379 byte[] mechPortion = mechElement.export();
380 byte[] oidBytes = null;
381 ObjectIdentifier oid = null;
382
383 try {
384 oid = new ObjectIdentifier
385 (mechElement.getMechanism().toString());
386 } catch (IOException e) {
387 throw new GSSExceptionImpl(GSSException.FAILURE,
388 "Invalid OID String ");
389 }
390 DerOutputStream dout = new DerOutputStream();
391 try {
392 dout.putOID(oid);
393 } catch (IOException e) {
394 throw new GSSExceptionImpl(GSSException.FAILURE,
395 "Could not ASN.1 Encode "
396 + oid.toString());
397 }
398 oidBytes = dout.toByteArray();
399
400 byte[] retVal = new byte[2
401 + 2 + oidBytes.length
402 + 4 + mechPortion.length];
403 int pos = 0;
404 retVal[pos++] = 0x04;
405 retVal[pos++] = 0x01;
406 retVal[pos++] = (byte) (oidBytes.length>>>8);
407 retVal[pos++] = (byte) oidBytes.length;
408 System.arraycopy(oidBytes, 0, retVal, pos, oidBytes.length);
409 pos += oidBytes.length;
410 retVal[pos++] = (byte) (mechPortion.length>>>24);
411 retVal[pos++] = (byte) (mechPortion.length>>>16);
412 retVal[pos++] = (byte) (mechPortion.length>>>8);
413 retVal[pos++] = (byte) mechPortion.length;
414 System.arraycopy(mechPortion, 0, retVal, pos, mechPortion.length);
415 return retVal;
416 }
417
418 public String toString() {
419 return printableName;
420
421 }
422
423 public Oid getStringNameType() throws GSSException {
424 return printableNameType;
425 }
426
427 public boolean isAnonymous() {
428 if (printableNameType == null) {
429 return false;
430 } else {
431 return GSSName.NT_ANONYMOUS.equals(printableNameType);
432 }
433 }
434
435 public boolean isMN() {
436 return true; // Since always canonicalized for some mech
437 }
438
439 public synchronized GSSNameSpi getElement(Oid mechOid)
440 throws GSSException {
441
442 GSSNameSpi retVal = elements.get(mechOid);
443
444 if (retVal == null) {
445 if (appNameStr != null) {
446 retVal = gssManager.getNameElement
447 (appNameStr, appNameType, mechOid);
448 } else {
449 retVal = gssManager.getNameElement
450 (appNameBytes, appNameType, mechOid);
451 }
452 elements.put(mechOid, retVal);
453 }
454 return retVal;
455 }
456
457 Set<GSSNameSpi> getElements() {
458 return new HashSet<GSSNameSpi>(elements.values());
459 }
460
461 private static String getNameTypeStr(Oid nameTypeOid) {
462
463 if (nameTypeOid == null)
464 return "(NT is null)";
465
466 if (nameTypeOid.equals(NT_USER_NAME))
467 return "NT_USER_NAME";
468 if (nameTypeOid.equals(NT_HOSTBASED_SERVICE))
469 return "NT_HOSTBASED_SERVICE";
470 if (nameTypeOid.equals(NT_EXPORT_NAME))
471 return "NT_EXPORT_NAME";
472 if (nameTypeOid.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))
473 return "NT_GSS_KRB5_PRINCIPAL";
474 else
475 return "Unknown";
476 }
477}