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