1 /*
2  * Copyright (c) 2000, 2010, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.jgss;
27 
28 import org.ietf.jgss.*;
29 import sun.security.jgss.spi.*;
30 import java.util.Set;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Arrays;
34 import java.io.IOException;
35 import java.io.UnsupportedEncodingException;
36 import sun.security.util.ObjectIdentifier;
37 import sun.security.util.DerInputStream;
38 import 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 
82 public class GSSNameImpl implements GSSName {
83 
84     /**
85      * The old Oid used in RFC 2853. Now supported as
86      * input parameters in:
87      *
88      * 1. The four overloaded GSSManager.createName(*) methods
89      * 2. GSSManager.getMechsForName(Oid)
90      *
91      * Note that even if a GSSName is created with this old Oid,
92      * its internal name type and getStringNameType() output are
93      * always the new value.
94      */
95     final static Oid oldHostbasedServiceName;
96 
97     static {
98         Oid tmp = null;
99         try {
100             tmp = new Oid("1.3.6.1.5.6.2");
101         } catch (Exception e) {
102             // should never happen
103         }
104         oldHostbasedServiceName = tmp;
105     }
106 
107     private GSSManagerImpl gssManager = null;
108 
109     /*
110      * Store whatever the application passed in. We will use this to
111      * get individual mechanisms to create name elements as and when
112      * needed.
113      * Store both the String and the byte[]. Leave I18N to the
114      * mechanism by allowing it to extract bytes from the String!
115      */
116 
117     private String appNameStr = null;
118     private byte[] appNameBytes = null;
119     private Oid appNameType = null;
120 
121     /*
122      * When we figure out what the printable name would be, we store
123      * both the name and its type.
124      */
125 
126     private String printableName = null;
127     private Oid printableNameType = null;
128 
129     private HashMap<Oid, GSSNameSpi> elements = null;
130     private GSSNameSpi mechElement = null;
131 
wrapElement(GSSManagerImpl gssManager, GSSNameSpi mechElement)132     static GSSNameImpl wrapElement(GSSManagerImpl gssManager,
133         GSSNameSpi mechElement) throws GSSException {
134         return (mechElement == null ?
135             null : new GSSNameImpl(gssManager, mechElement));
136     }
137 
GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement)138     GSSNameImpl(GSSManagerImpl gssManager, GSSNameSpi mechElement) {
139         this.gssManager = gssManager;
140         appNameStr = printableName = mechElement.toString();
141         appNameType = printableNameType = mechElement.getStringNameType();
142         this.mechElement = mechElement;
143         elements = new HashMap<Oid, GSSNameSpi>(1);
144         elements.put(mechElement.getMechanism(), this.mechElement);
145     }
146 
GSSNameImpl(GSSManagerImpl gssManager, Object appName, Oid appNameType)147     GSSNameImpl(GSSManagerImpl gssManager,
148                        Object appName,
149                        Oid appNameType)
150         throws GSSException {
151         this(gssManager, appName, appNameType, null);
152     }
153 
GSSNameImpl(GSSManagerImpl gssManager, Object appName, Oid appNameType, Oid mech)154     GSSNameImpl(GSSManagerImpl gssManager,
155                         Object appName,
156                         Oid appNameType,
157                         Oid mech)
158         throws GSSException {
159 
160         if (oldHostbasedServiceName.equals(appNameType)) {
161             appNameType = GSSName.NT_HOSTBASED_SERVICE;
162         }
163         if (appName == null)
164             throw new GSSExceptionImpl(GSSException.BAD_NAME,
165                                    "Cannot import null name");
166         if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
167         if (NT_EXPORT_NAME.equals(appNameType)) {
168             importName(gssManager, appName);
169         } else {
170             init(gssManager, appName, appNameType, mech);
171         }
172     }
173 
init(GSSManagerImpl gssManager, Object appName, Oid appNameType, Oid mech)174     private void init(GSSManagerImpl gssManager,
175                       Object appName, Oid appNameType,
176                       Oid mech)
177         throws GSSException {
178 
179         this.gssManager = gssManager;
180         this.elements =
181                 new HashMap<Oid, GSSNameSpi>(gssManager.getMechs().length);
182 
183         if (appName instanceof String) {
184             this.appNameStr = (String) appName;
185             /*
186              * If appNameType is null, then the nametype for this printable
187              * string is determined only by interrogating the
188              * mechanism. Thus, defer the setting of printableName and
189              * printableNameType till later.
190              */
191             if (appNameType != null) {
192                 printableName = appNameStr;
193                 printableNameType = appNameType;
194             }
195         } else {
196             this.appNameBytes = (byte[]) appName;
197         }
198 
199         this.appNameType = appNameType;
200 
201         mechElement = getElement(mech);
202 
203         /*
204          * printableName will be null if appName was in a byte[] or if
205          * appName was in a String but appNameType was null.
206          */
207         if (printableName == null) {
208             printableName = mechElement.toString();
209             printableNameType = mechElement.getStringNameType();
210         }
211 
212         /*
213          *  At this point the GSSNameImpl has the following set:
214          *   appNameStr or appNameBytes
215          *   appNameType (could be null)
216          *   printableName
217          *   printableNameType
218          *   mechElement (which also exists in the hashmap of elements)
219          */
220     }
221 
importName(GSSManagerImpl gssManager, Object appName)222     private void importName(GSSManagerImpl gssManager,
223                             Object appName)
224         throws GSSException {
225 
226         int pos = 0;
227         byte[] bytes = null;
228 
229         if (appName instanceof String) {
230             try {
231                 bytes = ((String) appName).getBytes("UTF-8");
232             } catch (UnsupportedEncodingException e) {
233                 // Won't happen
234             }
235         } else
236             bytes = (byte[]) appName;
237 
238         if ((bytes[pos++] != 0x04) ||
239             (bytes[pos++] != 0x01))
240             throw new GSSExceptionImpl(GSSException.BAD_NAME,
241                                    "Exported name token id is corrupted!");
242 
243         int oidLen  = (((0xFF & bytes[pos++]) << 8) |
244                        (0xFF & bytes[pos++]));
245         ObjectIdentifier temp = null;
246         try {
247             DerInputStream din = new DerInputStream(bytes, pos,
248                                                     oidLen);
249             temp = new ObjectIdentifier(din);
250         } catch (IOException e) {
251             throw new GSSExceptionImpl(GSSException.BAD_NAME,
252                        "Exported name Object identifier is corrupted!");
253         }
254         Oid oid = new Oid(temp.toString());
255         pos += oidLen;
256         int mechPortionLen = (((0xFF & bytes[pos++]) << 24) |
257                               ((0xFF & bytes[pos++]) << 16) |
258                               ((0xFF & bytes[pos++]) << 8) |
259                               (0xFF & bytes[pos++]));
260         if (mechPortionLen < 0 || pos > bytes.length - mechPortionLen) {
261              throw new GSSExceptionImpl(GSSException.BAD_NAME,
262                      "Exported name mech name is corrupted!");
263          }
264         byte[] mechPortion = new byte[mechPortionLen];
265         System.arraycopy(bytes, pos, mechPortion, 0, mechPortionLen);
266 
267         init(gssManager, mechPortion, NT_EXPORT_NAME, oid);
268     }
269 
canonicalize(Oid mech)270     public GSSName canonicalize(Oid mech) throws GSSException {
271         if (mech == null) mech = ProviderList.DEFAULT_MECH_OID;
272 
273         return wrapElement(gssManager, getElement(mech));
274     }
275 
276     /**
277      * This method may return false negatives. But if it says two
278      * names are equals, then there is some mechanism that
279      * authenticates them as the same principal.
280      */
equals(GSSName other)281     public boolean equals(GSSName other) throws GSSException {
282 
283         if (this.isAnonymous() || other.isAnonymous())
284             return false;
285 
286         if (other == this)
287             return true;
288 
289         if (! (other instanceof GSSNameImpl))
290             return equals(gssManager.createName(other.toString(),
291                                                 other.getStringNameType()));
292 
293         /*
294          * XXX Do a comparison of the appNameStr/appNameBytes if
295          * available. If that fails, then proceed with this test.
296          */
297 
298         GSSNameImpl that = (GSSNameImpl) other;
299 
300         GSSNameSpi myElement = this.mechElement;
301         GSSNameSpi element = that.mechElement;
302 
303         /*
304          * XXX If they are not of the same mechanism type, convert both to
305          * Kerberos since it is guaranteed to be present.
306          */
307         if ((myElement == null) && (element != null)) {
308             myElement = this.getElement(element.getMechanism());
309         } else if ((myElement != null) && (element == null)) {
310             element = that.getElement(myElement.getMechanism());
311         }
312 
313         if (myElement != null && element != null) {
314             return myElement.equals(element);
315         }
316 
317         if ((this.appNameType != null) &&
318             (that.appNameType != null)) {
319             if (!this.appNameType.equals(that.appNameType)) {
320                 return false;
321             }
322             byte[] myBytes = null;
323             byte[] bytes = null;
324             try {
325                 myBytes =
326                     (this.appNameStr != null ?
327                      this.appNameStr.getBytes("UTF-8") :
328                      this.appNameBytes);
329                 bytes =
330                     (that.appNameStr != null ?
331                      that.appNameStr.getBytes("UTF-8") :
332                      that.appNameBytes);
333             } catch (UnsupportedEncodingException e) {
334                 // Won't happen
335             }
336 
337             return Arrays.equals(myBytes, bytes);
338         }
339 
340         return false;
341 
342     }
343 
344     /**
345      * Returns a hashcode value for this GSSName.
346      *
347      * @return a hashCode value
348      */
hashCode()349     public int hashCode() {
350         /*
351          * XXX
352          * In order to get this to work reliably and properly(!), obtain a
353          * Kerberos name element for the name and then call hashCode on its
354          * string representation. But this cannot be done if the nametype
355          * is not one of those supported by the Kerberos provider and hence
356          * this name cannot be imported by Kerberos. In that case return a
357          * constant value!
358          */
359 
360         return 1;
361     }
362 
equals(Object another)363     public boolean equals(Object another) {
364 
365         try {
366             // XXX This can lead to an infinite loop. Extract info
367             // and create a GSSNameImpl with it.
368 
369             if (another instanceof GSSName)
370                 return equals((GSSName) another);
371         } catch (GSSException e) {
372             // Squelch it and return false
373         }
374 
375             return false;
376     }
377 
378     /**
379      * Returns a flat name representation for this object. The name
380      * format is defined in RFC 2743:
381      *<pre>
382      * Length           Name          Description
383      * 2               TOK_ID          Token Identifier
384      *                                 For exported name objects, this
385      *                                 must be hex 04 01.
386      * 2               MECH_OID_LEN    Length of the Mechanism OID
387      * MECH_OID_LEN    MECH_OID        Mechanism OID, in DER
388      * 4               NAME_LEN        Length of name
389      * NAME_LEN        NAME            Exported name; format defined in
390      *                                 applicable mechanism draft.
391      *</pre>
392      *
393      * Note that it is not required to canonicalize a name before
394      * calling export(). i.e., the name need not be an MN. If it is
395      * not an MN, an implementation defined algorithm can be used for
396      * choosing the mechanism which should export this name.
397      *
398      * @return the flat name representation for this object
399      * @exception GSSException with major codes NAME_NOT_MN, BAD_NAME,
400      *  BAD_NAME, FAILURE.
401      */
export()402     public byte[] export() throws GSSException {
403 
404         if (mechElement == null) {
405             /* Use default mech */
406             mechElement = getElement(ProviderList.DEFAULT_MECH_OID);
407         }
408 
409         byte[] mechPortion = mechElement.export();
410         byte[] oidBytes = null;
411         ObjectIdentifier oid = null;
412 
413         try {
414             oid = new ObjectIdentifier
415                 (mechElement.getMechanism().toString());
416         } catch (IOException e) {
417             throw new GSSExceptionImpl(GSSException.FAILURE,
418                                        "Invalid OID String ");
419         }
420         DerOutputStream dout = new DerOutputStream();
421         try {
422             dout.putOID(oid);
423         } catch (IOException e) {
424             throw new GSSExceptionImpl(GSSException.FAILURE,
425                                    "Could not ASN.1 Encode "
426                                    + oid.toString());
427         }
428         oidBytes = dout.toByteArray();
429 
430         byte[] retVal = new byte[2
431                                 + 2 + oidBytes.length
432                                 + 4 + mechPortion.length];
433         int pos = 0;
434         retVal[pos++] = 0x04;
435         retVal[pos++] = 0x01;
436         retVal[pos++] = (byte) (oidBytes.length>>>8);
437         retVal[pos++] = (byte) oidBytes.length;
438         System.arraycopy(oidBytes, 0, retVal, pos, oidBytes.length);
439         pos += oidBytes.length;
440         retVal[pos++] = (byte) (mechPortion.length>>>24);
441         retVal[pos++] = (byte) (mechPortion.length>>>16);
442         retVal[pos++] = (byte) (mechPortion.length>>>8);
443         retVal[pos++] = (byte)  mechPortion.length;
444         System.arraycopy(mechPortion, 0, retVal, pos, mechPortion.length);
445         return retVal;
446     }
447 
toString()448     public String toString() {
449          return printableName;
450 
451     }
452 
getStringNameType()453     public Oid getStringNameType() throws GSSException {
454         return printableNameType;
455     }
456 
isAnonymous()457     public boolean isAnonymous() {
458         if (printableNameType == null) {
459             return false;
460         } else {
461             return GSSName.NT_ANONYMOUS.equals(printableNameType);
462         }
463     }
464 
isMN()465     public boolean isMN() {
466         return true; // Since always canonicalized for some mech
467     }
468 
getElement(Oid mechOid)469     public synchronized GSSNameSpi getElement(Oid mechOid)
470         throws GSSException {
471 
472         GSSNameSpi retVal = elements.get(mechOid);
473 
474         if (retVal == null) {
475             if (appNameStr != null) {
476                 retVal = gssManager.getNameElement
477                     (appNameStr, appNameType, mechOid);
478             } else {
479                 retVal = gssManager.getNameElement
480                     (appNameBytes, appNameType, mechOid);
481             }
482             elements.put(mechOid, retVal);
483         }
484         return retVal;
485     }
486 
getElements()487     Set<GSSNameSpi> getElements() {
488         return new HashSet<GSSNameSpi>(elements.values());
489     }
490 
getNameTypeStr(Oid nameTypeOid)491     private static String getNameTypeStr(Oid nameTypeOid) {
492 
493         if (nameTypeOid == null)
494             return "(NT is null)";
495 
496         if (nameTypeOid.equals(NT_USER_NAME))
497             return "NT_USER_NAME";
498         if (nameTypeOid.equals(NT_HOSTBASED_SERVICE))
499             return "NT_HOSTBASED_SERVICE";
500         if (nameTypeOid.equals(NT_EXPORT_NAME))
501             return "NT_EXPORT_NAME";
502         if (nameTypeOid.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))
503             return "NT_GSS_KRB5_PRINCIPAL";
504         else
505             return "Unknown";
506     }
507 }
508