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