1/*
2 * Copyright (c) 2005, 2017, 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
26package sun.security.jgss.wrapper;
27
28import org.ietf.jgss.*;
29import java.security.Provider;
30import java.security.Security;
31import java.io.IOException;
32import java.io.UnsupportedEncodingException;
33import sun.security.krb5.Realm;
34import sun.security.jgss.GSSUtil;
35import sun.security.util.ObjectIdentifier;
36import sun.security.util.DerInputStream;
37import sun.security.util.DerOutputStream;
38import sun.security.jgss.GSSUtil;
39import sun.security.jgss.GSSExceptionImpl;
40import sun.security.jgss.spi.GSSNameSpi;
41
42import javax.security.auth.kerberos.ServicePermission;
43
44/**
45 * This class is essentially a wrapper class for the gss_name_t
46 * structure of the native GSS library.
47 * @author Valerie Peng
48 * @since 1.6
49 */
50
51public class GSSNameElement implements GSSNameSpi {
52
53    long pName = 0; // Pointer to the gss_name_t structure
54    private String printableName;
55    private Oid printableType;
56    private GSSLibStub cStub;
57
58    static final GSSNameElement DEF_ACCEPTOR = new GSSNameElement();
59
60    private static Oid getNativeNameType(Oid nameType, GSSLibStub stub) {
61        if (GSSUtil.NT_GSS_KRB5_PRINCIPAL.equals(nameType)) {
62            Oid[] supportedNTs = null;
63            try {
64                supportedNTs = stub.inquireNamesForMech();
65            } catch (GSSException ge) {
66                if (ge.getMajor() == GSSException.BAD_MECH &&
67                    GSSUtil.isSpNegoMech(stub.getMech())) {
68                    // Workaround known Heimdal issue and retry with KRB5
69                    try {
70                        stub = GSSLibStub.getInstance
71                            (GSSUtil.GSS_KRB5_MECH_OID);
72                        supportedNTs = stub.inquireNamesForMech();
73                    } catch (GSSException ge2) {
74                        // Should never happen
75                        SunNativeProvider.debug("Name type list unavailable: " +
76                            ge2.getMajorString());
77                    }
78                } else {
79                    SunNativeProvider.debug("Name type list unavailable: " +
80                        ge.getMajorString());
81                }
82            }
83            if (supportedNTs != null) {
84                for (int i = 0; i < supportedNTs.length; i++) {
85                    if (supportedNTs[i].equals(nameType)) return nameType;
86                }
87                // Special handling the specified name type
88                SunNativeProvider.debug("Override " + nameType +
89                    " with mechanism default(null)");
90                return null; // Use mechanism specific default
91            }
92        }
93        return nameType;
94    }
95
96    private GSSNameElement() {
97        printableName = "<DEFAULT ACCEPTOR>";
98    }
99
100    GSSNameElement(long pNativeName, GSSLibStub stub) throws GSSException {
101        assert(stub != null);
102        if (pNativeName == 0) {
103            throw new GSSException(GSSException.BAD_NAME);
104        }
105        // Note: pNativeName is assumed to be a MN.
106        pName = pNativeName;
107        cStub = stub;
108        setPrintables();
109    }
110
111    GSSNameElement(byte[] nameBytes, Oid nameType, GSSLibStub stub)
112        throws GSSException {
113        assert(stub != null);
114        if (nameBytes == null) {
115            throw new GSSException(GSSException.BAD_NAME);
116        }
117        cStub = stub;
118        byte[] name = nameBytes;
119
120        if (nameType != null) {
121            // Special handling the specified name type if
122            // necessary
123            nameType = getNativeNameType(nameType, stub);
124
125            if (GSSName.NT_EXPORT_NAME.equals(nameType)) {
126                // Need to add back the mech Oid portion (stripped
127                // off by GSSNameImpl class prior to calling this
128                // method) for "NT_EXPORT_NAME"
129                byte[] mechBytes = null;
130                DerOutputStream dout = new DerOutputStream();
131                Oid mech = cStub.getMech();
132                try {
133                    dout.putOID(new ObjectIdentifier(mech.toString()));
134                } catch (IOException e) {
135                    throw new GSSExceptionImpl(GSSException.FAILURE, e);
136                }
137                mechBytes = dout.toByteArray();
138                name = new byte[2 + 2 + mechBytes.length + 4 + nameBytes.length];
139                int pos = 0;
140                name[pos++] = 0x04;
141                name[pos++] = 0x01;
142                name[pos++] = (byte) (mechBytes.length>>>8);
143                name[pos++] = (byte) mechBytes.length;
144                System.arraycopy(mechBytes, 0, name, pos, mechBytes.length);
145                pos += mechBytes.length;
146                name[pos++] = (byte) (nameBytes.length>>>24);
147                name[pos++] = (byte) (nameBytes.length>>>16);
148                name[pos++] = (byte) (nameBytes.length>>>8);
149                name[pos++] = (byte) nameBytes.length;
150                System.arraycopy(nameBytes, 0, name, pos, nameBytes.length);
151            }
152        }
153        pName = cStub.importName(name, nameType);
154        setPrintables();
155
156        SecurityManager sm = System.getSecurityManager();
157        if (sm != null && !Realm.AUTODEDUCEREALM) {
158            String krbName = getKrbName();
159            int atPos = krbName.lastIndexOf('@');
160            if (atPos != -1) {
161                String atRealm = krbName.substring(atPos);
162                // getNativeNameType() can modify NT_GSS_KRB5_PRINCIPAL to null
163                if ((nameType == null
164                            || nameType.equals(GSSUtil.NT_GSS_KRB5_PRINCIPAL))
165                        && new String(nameBytes).endsWith(atRealm)) {
166                    // Created from Kerberos name with realm, no need to check
167                } else {
168                    try {
169                        sm.checkPermission(new ServicePermission(atRealm, "-"));
170                    } catch (SecurityException se) {
171                        // Do not chain the actual exception to hide info
172                        throw new GSSException(GSSException.FAILURE);
173                    }
174                }
175            }
176        }
177
178        SunNativeProvider.debug("Imported " + printableName + " w/ type " +
179                                printableType);
180    }
181
182    private void setPrintables() throws GSSException {
183        Object[] printables = null;
184        printables = cStub.displayName(pName);
185        assert((printables != null) && (printables.length == 2));
186        printableName = (String) printables[0];
187        assert(printableName != null);
188        printableType = (Oid) printables[1];
189        if (printableType == null) {
190            printableType = GSSName.NT_USER_NAME;
191        }
192    }
193
194    // Need to be public for GSSUtil.getSubject()
195    public String getKrbName() throws GSSException {
196        long mName = 0;
197        GSSLibStub stub = cStub;
198        if (!GSSUtil.isKerberosMech(cStub.getMech())) {
199            stub = GSSLibStub.getInstance(GSSUtil.GSS_KRB5_MECH_OID);
200        }
201        mName = stub.canonicalizeName(pName);
202        Object[] printables2 = stub.displayName(mName);
203        stub.releaseName(mName);
204        SunNativeProvider.debug("Got kerberized name: " + printables2[0]);
205        return (String) printables2[0];
206    }
207
208    public Provider getProvider() {
209        return SunNativeProvider.INSTANCE;
210    }
211
212    public boolean equals(GSSNameSpi other) throws GSSException {
213        if (!(other instanceof GSSNameElement)) {
214            return false;
215        }
216        return cStub.compareName(pName, ((GSSNameElement)other).pName);
217    }
218
219    public boolean equals(Object other) {
220        if (!(other instanceof GSSNameElement)) {
221            return false;
222        }
223        try {
224            return equals((GSSNameElement) other);
225        } catch (GSSException ex) {
226            return false;
227        }
228    }
229
230    public int hashCode() {
231        return Long.hashCode(pName);
232    }
233
234    public byte[] export() throws GSSException {
235        byte[] nameVal = cStub.exportName(pName);
236
237        // Need to strip off the mech Oid portion of the exported
238        // bytes since GSSNameImpl class will subsequently add it.
239        int pos = 0;
240        if ((nameVal[pos++] != 0x04) ||
241            (nameVal[pos++] != 0x01))
242            throw new GSSException(GSSException.BAD_NAME);
243
244        int mechOidLen  = (((0xFF & nameVal[pos++]) << 8) |
245                           (0xFF & nameVal[pos++]));
246        ObjectIdentifier temp = null;
247        try {
248            DerInputStream din = new DerInputStream(nameVal, pos,
249                                                    mechOidLen);
250            temp = new ObjectIdentifier(din);
251        } catch (IOException e) {
252            throw new GSSExceptionImpl(GSSException.BAD_NAME, e);
253        }
254        Oid mech2 = new Oid(temp.toString());
255        assert(mech2.equals(getMechanism()));
256        pos += mechOidLen;
257        int mechPortionLen = (((0xFF & nameVal[pos++]) << 24) |
258                              ((0xFF & nameVal[pos++]) << 16) |
259                              ((0xFF & nameVal[pos++]) << 8) |
260                              (0xFF & nameVal[pos++]));
261        if (mechPortionLen < 0) {
262            throw new GSSException(GSSException.BAD_NAME);
263        }
264        byte[] mechPortion = new byte[mechPortionLen];
265        System.arraycopy(nameVal, pos, mechPortion, 0, mechPortionLen);
266        return mechPortion;
267    }
268
269    public Oid getMechanism() {
270        return cStub.getMech();
271    }
272
273    public String toString() {
274        return printableName;
275    }
276
277    public Oid getStringNameType() {
278        return printableType;
279    }
280
281    public boolean isAnonymousName() {
282        return (GSSName.NT_ANONYMOUS.equals(printableType));
283    }
284
285    public void dispose() {
286        if (pName != 0) {
287            cStub.releaseName(pName);
288            pName = 0;
289        }
290    }
291
292    @SuppressWarnings("deprecation")
293    protected void finalize() throws Throwable {
294        dispose();
295    }
296}
297