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