1 /*
2 * Copyright 2000-2009 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
26 package sun.security.jgss;
27
28 import com.sun.security.auth.callback.TextCallbackHandler;
29 import javax.security.auth.Subject;
30 import javax.security.auth.kerberos.KerberosPrincipal;
31 import javax.security.auth.kerberos.KerberosTicket;
32 import javax.security.auth.kerberos.KerberosKey;
33 import org.ietf.jgss.*;
34 import sun.security.jgss.spi.GSSNameSpi;
35 import sun.security.jgss.spi.GSSCredentialSpi;
36 import sun.security.action.GetPropertyAction;
37 import sun.security.jgss.krb5.Krb5NameElement;
38 import sun.security.jgss.spnego.SpNegoCredElement;
39 import java.util.Set;
40 import java.util.HashSet;
41 import java.util.Vector;
42 import java.util.Iterator;
43 import java.security.AccessController;
44 import java.security.AccessControlContext;
45 import java.security.PrivilegedExceptionAction;
46 import java.security.PrivilegedActionException;
47 import javax.security.auth.callback.CallbackHandler;
48 import javax.security.auth.login.LoginContext;
49 import javax.security.auth.login.LoginException;
50 import sun.security.action.GetBooleanAction;
51
52 /**
53 * The GSSUtilImplementation that knows how to work with the internals of
54 * the GSS-API.
55 */
56 public class GSSUtil {
57
58 public static final Oid GSS_KRB5_MECH_OID =
59 GSSUtil.createOid("1.2.840.113554.1.2.2");
60 public static final Oid GSS_KRB5_MECH_OID2 =
61 GSSUtil.createOid("1.3.5.1.5.2");
62
63 public static final Oid GSS_SPNEGO_MECH_OID =
64 GSSUtil.createOid("1.3.6.1.5.5.2");
65
66 public static final Oid NT_GSS_KRB5_PRINCIPAL =
67 GSSUtil.createOid("1.2.840.113554.1.2.2.1");
68
69 public static final Oid NT_HOSTBASED_SERVICE2 =
70 GSSUtil.createOid("1.2.840.113554.1.2.1.4");
71
72 private static final String DEFAULT_HANDLER =
73 "auth.login.defaultCallbackHandler";
74
75 static final boolean DEBUG;
76 static {
77 DEBUG = (AccessController.doPrivileged
78 (new GetBooleanAction("sun.security.jgss.debug"))).
79 booleanValue();
80 }
81
82 static void debug(String message) {
83 if (DEBUG) {
84 assert(message != null);
85 System.out.println(message);
86 }
87 }
88
89 // NOTE: this method is only for creating Oid objects with
90 // known to be valid <code>oidStr</code> given it ignores
91 // the GSSException
92 public static Oid createOid(String oidStr) {
93 try {
94 return new Oid(oidStr);
95 } catch (GSSException e) {
96 debug("Ignored invalid OID: " + oidStr);
97 return null;
98 }
99 }
100
101 public static boolean isSpNegoMech(Oid oid) {
102 return (GSS_SPNEGO_MECH_OID.equals(oid));
103 }
104
105 public static boolean isKerberosMech(Oid oid) {
106 return (GSS_KRB5_MECH_OID.equals(oid) ||
107 GSS_KRB5_MECH_OID2.equals(oid));
108
109 }
110
111 public static String getMechStr(Oid oid) {
112 if (isSpNegoMech(oid)) {
113 return "SPNEGO";
114 } else if (isKerberosMech(oid)) {
115 return "Kerberos V5";
116 } else {
117 return oid.toString();
118 }
119 }
120
121 /**
122 * Note: The current impl only works with Sun's impl of
123 * GSSName and GSSCredential since it depends on package
124 * private APIs.
125 */
126 public static Subject getSubject(GSSName name,
127 GSSCredential creds) {
128
129 HashSet<Object> privCredentials = null;
130 HashSet<Object> pubCredentials = new HashSet<Object>(); // empty Set
131
132 Set<GSSCredentialSpi> gssCredentials = null;
133
134 Set<KerberosPrincipal> krb5Principals =
135 new HashSet<KerberosPrincipal>();
136
137 if (name instanceof GSSNameImpl) {
138 try {
139 GSSNameSpi ne = ((GSSNameImpl) name).getElement
140 (GSS_KRB5_MECH_OID);
141 String krbName = ne.toString();
142 if (ne instanceof Krb5NameElement) {
143 krbName =
144 ((Krb5NameElement) ne).getKrb5PrincipalName().getName();
145 }
146 KerberosPrincipal krbPrinc = new KerberosPrincipal(krbName);
147 krb5Principals.add(krbPrinc);
148 } catch (GSSException ge) {
149 debug("Skipped name " + name + " due to " + ge);
150 }
151 }
152
153 if (creds instanceof GSSCredentialImpl) {
154 gssCredentials = ((GSSCredentialImpl) creds).getElements();
155 privCredentials = new HashSet<Object>(gssCredentials.size());
156 populateCredentials(privCredentials, gssCredentials);
157 } else {
158 privCredentials = new HashSet<Object>(); // empty Set
159 }
160 debug("Created Subject with the following");
161 debug("principals=" + krb5Principals);
162 debug("public creds=" + pubCredentials);
163 debug("private creds=" + privCredentials);
164
165 return new Subject(false, krb5Principals, pubCredentials,
166 privCredentials);
167
168 }
169
170 /**
171 * Populates the set credentials with elements from gssCredentials. At
172 * the same time, it converts any subclasses of KerberosTicket
173 * into KerberosTicket instances and any subclasses of KerberosKey into
174 * KerberosKey instances. (It is not desirable to expose the customer
175 * to sun.security.jgss.krb5.Krb5InitCredential which extends
176 * KerberosTicket and sun.security.jgss.krb5.Kbr5AcceptCredential which
177 * extends KerberosKey.)
178 */
179 private static void populateCredentials(Set<Object> credentials,
180 Set<?> gssCredentials) {
181
182 Object cred;
183
184 Iterator<?> elements = gssCredentials.iterator();
185 while (elements.hasNext()) {
186
187 cred = elements.next();
188
189 // Retrieve the internal cred out of SpNegoCredElement
190 if (cred instanceof SpNegoCredElement) {
191 cred = ((SpNegoCredElement) cred).getInternalCred();
192 }
193
194 if (cred instanceof KerberosTicket) {
195 if (!cred.getClass().getName().equals
196 ("javax.security.auth.kerberos.KerberosTicket")) {
197 KerberosTicket tempTkt = (KerberosTicket) cred;
198 cred = new KerberosTicket(tempTkt.getEncoded(),
199 tempTkt.getClient(),
200 tempTkt.getServer(),
201 tempTkt.getSessionKey().getEncoded(),
202 tempTkt.getSessionKeyType(),
203 tempTkt.getFlags(),
204 tempTkt.getAuthTime(),
205 tempTkt.getStartTime(),
206 tempTkt.getEndTime(),
207 tempTkt.getRenewTill(),
208 tempTkt.getClientAddresses());
209 }
210 credentials.add(cred);
211 } else if (cred instanceof KerberosKey) {
212 if (!cred.getClass().getName().equals
213 ("javax.security.auth.kerberos.KerberosKey")) {
214 KerberosKey tempKey = (KerberosKey) cred;
215 cred = new KerberosKey(tempKey.getPrincipal(),
216 tempKey.getEncoded(),
217 tempKey.getKeyType(),
218 tempKey.getVersionNumber());
219 }
220 credentials.add(cred);
221 } else {
222 // Ignore non-KerberosTicket and non-KerberosKey elements
223 debug("Skipped cred element: " + cred);
224 }
225 }
226 }
227
228 /**
229 * Authenticate using the login module from the specified
230 * configuration entry.
231 *
232 * @param caller the caller of JAAS Login
233 * @param mech the mech to be used
234 * @return the authenticated subject
235 */
236 public static Subject login(GSSCaller caller, Oid mech) throws LoginException {
237
238 CallbackHandler cb = null;
239 if (caller instanceof HttpCaller) {
240 cb = new sun.net.www.protocol.http.NegotiateCallbackHandler(
241 ((HttpCaller)caller).info());
242 } else {
243 String defaultHandler =
244 java.security.Security.getProperty(DEFAULT_HANDLER);
245 // get the default callback handler
246 if ((defaultHandler != null) && (defaultHandler.length() != 0)) {
247 cb = null;
248 } else {
249 cb = new TextCallbackHandler();
250 }
251 }
252
253 // New instance of LoginConfigImpl must be created for each login,
254 // since the entry name is not passed as the first argument, but
255 // generated with caller and mech inside LoginConfigImpl
256 LoginContext lc = new LoginContext("", null, cb,
257 new LoginConfigImpl(caller, mech));
258 lc.login();
259 return lc.getSubject();
260 }
261
262 /**
263 * Determines if the application doesn't mind if the mechanism obtains
264 * the required credentials from outside of the current Subject. Our
265 * Kerberos v5 mechanism would do a JAAS login on behalf of the
266 * application if this were the case.
267 *
268 * The application indicates this by explicitly setting the system
269 * property javax.security.auth.useSubjectCredsOnly to false.
270 */
271 public static boolean useSubjectCredsOnly(GSSCaller caller) {
272
273 // HTTP/SPNEGO doesn't use the standard JAAS framework. Instead, it
274 // uses the java.net.Authenticator style, therefore always return
275 // false here.
276 if (caller instanceof HttpCaller) {
277 return false;
278 }
279 /*
280 * Don't use GetBooleanAction because the default value in the JRE
281 * (when this is unset) has to treated as true.
282 */
283 String propValue = AccessController.doPrivileged(
284 new GetPropertyAction("javax.security.auth.useSubjectCredsOnly",
285 "true"));
286 /*
287 * This property has to be explicitly set to "false". Invalid
288 * values should be ignored and the default "true" assumed.
289 */
290 return (!propValue.equalsIgnoreCase("false"));
291 }
292
293 /**
294 * Determines the SPNEGO interoperability mode with Microsoft;
295 * by default it is set to true.
296 *
297 * To disable it, the application indicates this by explicitly setting
298 * the system property sun.security.spnego.interop to false.
299 */
300 public static boolean useMSInterop() {
301 /*
302 * Don't use GetBooleanAction because the default value in the JRE
303 * (when this is unset) has to treated as true.
304 */
305 String propValue = AccessController.doPrivileged(
306 new GetPropertyAction("sun.security.spnego.msinterop",
307 "true"));
308 /*
309 * This property has to be explicitly set to "false". Invalid
310 * values should be ignored and the default "true" assumed.
311 */
312 return (!propValue.equalsIgnoreCase("false"));
313 }
314
315 /**
316 * Searches the private credentials of current Subject with the
317 * specified criteria and returns the matching GSSCredentialSpi
318 * object out of Sun's impl of GSSCredential. Returns null if
319 * no Subject present or a Vector which contains 0 or more
320 * matching GSSCredentialSpi objects.
321 */
322 public static Vector searchSubject(final GSSNameSpi name,
323 final Oid mech,
324 final boolean initiate,
325 final Class credCls) {
326 debug("Search Subject for " + getMechStr(mech) +
327 (initiate? " INIT" : " ACCEPT") + " cred (" +
328 (name == null? "<<DEF>>" : name.toString()) + ", " +
329 credCls.getName() + ")");
330 final AccessControlContext acc = AccessController.getContext();
331 try {
332 Vector creds =
333 AccessController.doPrivileged
334 (new PrivilegedExceptionAction<Vector>() {
335 public Vector run() throws Exception {
336 Subject accSubj = Subject.getSubject(acc);
337 Vector<GSSCredentialSpi> result = null;
338 if (accSubj != null) {
339 result = new Vector<GSSCredentialSpi>();
340 Iterator<GSSCredentialImpl> iterator =
341 accSubj.getPrivateCredentials
342 (GSSCredentialImpl.class).iterator();
343 while (iterator.hasNext()) {
344 GSSCredentialImpl cred = iterator.next();
345 debug("...Found cred" + cred);
346 try {
347 GSSCredentialSpi ce =
348 cred.getElement(mech, initiate);
349 debug("......Found element: " + ce);
350 if (ce.getClass().equals(credCls) &&
351 (name == null ||
352 name.equals((Object) ce.getName()))) {
353 result.add(ce);
354 } else {
355 debug("......Discard element");
356 }
357 } catch (GSSException ge) {
358 debug("...Discard cred (" + ge + ")");
359 }
360 }
361 } else debug("No Subject");
362 return result;
363 }
364 });
365 return creds;
366 } catch (PrivilegedActionException pae) {
367 debug("Unexpected exception when searching Subject:");
368 if (DEBUG) pae.printStackTrace();
369 return null;
370 }
371 }
372 }
--- EOF ---