1 /* 2 * Copyright (c) 2000, 2011, 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 javax.security.auth.Subject; 29 import javax.security.auth.kerberos.KerberosPrincipal; 30 import javax.security.auth.kerberos.KerberosTicket; 31 import javax.security.auth.kerberos.KerberosKey; 32 import org.ietf.jgss.*; 33 import sun.security.jgss.spi.GSSNameSpi; 34 import sun.security.jgss.spi.GSSCredentialSpi; 35 import sun.security.action.GetPropertyAction; 36 import sun.security.jgss.krb5.Krb5NameElement; 37 import sun.security.jgss.spnego.SpNegoCredElement; 38 import java.util.Set; 39 import java.util.HashSet; 40 import java.util.Vector; 41 import java.util.Iterator; 42 import java.security.AccessController; 43 import java.security.AccessControlContext; 44 import java.security.PrivilegedExceptionAction; 45 import java.security.PrivilegedActionException; 46 import javax.security.auth.callback.CallbackHandler; 47 import javax.security.auth.login.LoginContext; 48 import javax.security.auth.login.LoginException; 49 import sun.security.action.GetBooleanAction; 50 import sun.security.util.ConsoleCallbackHandler; 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 public static final Oid GSS_KRB5_MECH_OID_MS = 63 GSSUtil.createOid("1.2.840.48018.1.2.2"); 64 65 public static final Oid GSS_SPNEGO_MECH_OID = 66 GSSUtil.createOid("1.3.6.1.5.5.2"); 67 68 public static final Oid NT_GSS_KRB5_PRINCIPAL = 69 GSSUtil.createOid("1.2.840.113554.1.2.2.1"); 70 71 private static final String DEFAULT_HANDLER = 72 "auth.login.defaultCallbackHandler"; 73 74 static final boolean DEBUG; 75 static { 76 DEBUG = (AccessController.doPrivileged 77 (new GetBooleanAction("sun.security.jgss.debug"))). 78 booleanValue(); 79 } 80 81 static void debug(String message) { 82 if (DEBUG) { 83 assert(message != null); 84 System.out.println(message); 85 } 86 } 87 88 // NOTE: this method is only for creating Oid objects with 89 // known to be valid <code>oidStr</code> given it ignores 90 // the GSSException 91 public static Oid createOid(String oidStr) { 92 try { 93 return new Oid(oidStr); 94 } catch (GSSException e) { 95 debug("Ignored invalid OID: " + oidStr); 96 return null; 97 } 98 } 99 100 public static boolean isSpNegoMech(Oid oid) { 101 return (GSS_SPNEGO_MECH_OID.equals(oid)); 102 } 103 104 public static boolean isKerberosMech(Oid oid) { 105 return (GSS_KRB5_MECH_OID.equals(oid) || 106 GSS_KRB5_MECH_OID2.equals(oid) || 107 GSS_KRB5_MECH_OID_MS.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.spnego.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 ConsoleCallbackHandler(); 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 <T extends GSSCredentialSpi> Vector<T> 323 searchSubject(final GSSNameSpi name, 324 final Oid mech, 325 final boolean initiate, 326 final Class<? extends T> credCls) { 327 debug("Search Subject for " + getMechStr(mech) + 328 (initiate? " INIT" : " ACCEPT") + " cred (" + 329 (name == null? "<<DEF>>" : name.toString()) + ", " + 330 credCls.getName() + ")"); 331 final AccessControlContext acc = AccessController.getContext(); 332 try { 333 Vector<T> creds = 334 AccessController.doPrivileged 335 (new PrivilegedExceptionAction<Vector<T>>() { 336 public Vector<T> run() throws Exception { 337 Subject accSubj = Subject.getSubject(acc); 338 Vector<T> result = null; 339 if (accSubj != null) { 340 result = new Vector<T>(); 341 Iterator<GSSCredentialImpl> iterator = 342 accSubj.getPrivateCredentials 343 (GSSCredentialImpl.class).iterator(); 344 while (iterator.hasNext()) { 345 GSSCredentialImpl cred = iterator.next(); 346 debug("...Found cred" + cred); 347 try { 348 GSSCredentialSpi ce = 349 cred.getElement(mech, initiate); 350 debug("......Found element: " + ce); 351 if (ce.getClass().equals(credCls) && 352 (name == null || 353 name.equals((Object) ce.getName()))) { 354 result.add(credCls.cast(ce)); 355 } else { 356 debug("......Discard element"); 357 } 358 } catch (GSSException ge) { 359 debug("...Discard cred (" + ge + ")"); 360 } 361 } 362 } else debug("No Subject"); 363 return result; 364 } 365 }); 366 return creds; 367 } catch (PrivilegedActionException pae) { 368 debug("Unexpected exception when searching Subject:"); 369 if (DEBUG) pae.printStackTrace(); 370 return null; 371 } 372 } 373 }