1 /* 2 * Copyright (c) 2003, 2019, 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.krb5; 27 28 import javax.security.auth.kerberos.KerberosTicket; 29 import javax.security.auth.kerberos.KerberosKey; 30 import javax.security.auth.kerberos.KerberosPrincipal; 31 import javax.security.auth.kerberos.KeyTab; 32 import javax.security.auth.Subject; 33 import javax.security.auth.login.LoginException; 34 import java.security.AccessControlContext; 35 import sun.security.jgss.GSSUtil; 36 import sun.security.jgss.GSSCaller; 37 38 import sun.security.krb5.Credentials; 39 import sun.security.krb5.EncryptionKey; 40 import sun.security.krb5.KrbException; 41 import java.io.IOException; 42 import java.util.ArrayList; 43 import java.util.List; 44 import sun.security.krb5.KerberosSecrets; 45 import sun.security.krb5.PrincipalName; 46 /** 47 * Utilities for obtaining and converting Kerberos tickets. 48 * 49 */ 50 public class Krb5Util { 51 52 static final boolean DEBUG = 53 java.security.AccessController.doPrivileged( 54 new sun.security.action.GetBooleanAction 55 ("sun.security.krb5.debug")).booleanValue(); 56 57 /** 58 * Default constructor 59 */ Krb5Util()60 private Krb5Util() { // Cannot create one of these 61 } 62 63 /** 64 * Retrieve the service ticket for serverPrincipal from caller's Subject 65 * or from Subject obtained by logging in, or if not found, via the 66 * Ticket Granting Service using the TGT obtained from the Subject. 67 * 68 * Caller must have permission to: 69 * - access and update Subject's private credentials 70 * - create LoginContext 71 * - read the auth.login.defaultCallbackHandler security property 72 * 73 * NOTE: This method is used by JSSE Kerberos Cipher Suites 74 */ getTicketFromSubjectAndTgs(GSSCaller caller, String clientPrincipal, String serverPrincipal, String tgsPrincipal, AccessControlContext acc)75 public static KerberosTicket getTicketFromSubjectAndTgs(GSSCaller caller, 76 String clientPrincipal, String serverPrincipal, String tgsPrincipal, 77 AccessControlContext acc) 78 throws LoginException, KrbException, IOException { 79 80 // 1. Try to find service ticket in acc subject 81 Subject accSubj = Subject.getSubject(acc); 82 KerberosTicket ticket = SubjectComber.find(accSubj, 83 serverPrincipal, clientPrincipal, KerberosTicket.class); 84 85 if (ticket != null) { 86 return ticket; // found it 87 } 88 89 Subject loginSubj = null; 90 if (!GSSUtil.useSubjectCredsOnly(caller)) { 91 // 2. Try to get ticket from login 92 try { 93 loginSubj = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); 94 ticket = SubjectComber.find(loginSubj, 95 serverPrincipal, clientPrincipal, KerberosTicket.class); 96 if (ticket != null) { 97 return ticket; // found it 98 } 99 } catch (LoginException e) { 100 // No login entry to use 101 // ignore and continue 102 } 103 } 104 105 // Service ticket not found in subject or login 106 // Try to get TGT to acquire service ticket 107 108 // 3. Try to get TGT from acc subject 109 KerberosTicket tgt = SubjectComber.find(accSubj, 110 tgsPrincipal, clientPrincipal, KerberosTicket.class); 111 112 boolean fromAcc; 113 if (tgt == null && loginSubj != null) { 114 // 4. Try to get TGT from login subject 115 tgt = SubjectComber.find(loginSubj, 116 tgsPrincipal, clientPrincipal, KerberosTicket.class); 117 fromAcc = false; 118 } else { 119 fromAcc = true; 120 } 121 122 // 5. Try to get service ticket using TGT 123 if (tgt != null) { 124 Credentials tgtCreds = ticketToCreds(tgt); 125 Credentials serviceCreds = Credentials.acquireServiceCreds( 126 serverPrincipal, tgtCreds); 127 if (serviceCreds != null) { 128 ticket = credsToTicket(serviceCreds); 129 130 // Store service ticket in acc's Subject 131 if (fromAcc && accSubj != null && !accSubj.isReadOnly()) { 132 accSubj.getPrivateCredentials().add(ticket); 133 } 134 } 135 } 136 return ticket; 137 } 138 139 /** 140 * Retrieves the ticket corresponding to the client/server principal 141 * pair from the Subject in the specified AccessControlContext. 142 */ getServiceTicket(GSSCaller caller, String clientPrincipal, String serverPrincipal, AccessControlContext acc)143 static KerberosTicket getServiceTicket(GSSCaller caller, 144 String clientPrincipal, String serverPrincipal, 145 AccessControlContext acc) throws LoginException { 146 147 // Try to get ticket from acc's Subject 148 Subject accSubj = Subject.getSubject(acc); 149 KerberosTicket ticket = 150 SubjectComber.find(accSubj, serverPrincipal, clientPrincipal, 151 KerberosTicket.class); 152 153 return ticket; 154 } 155 156 /** 157 * Retrieves the initial TGT corresponding to the client principal 158 * from the Subject in the specified AccessControlContext. 159 * If the ticket can not be found in the Subject, and if 160 * useSubjectCredsOnly is false, then obtain ticket from 161 * a LoginContext. 162 */ getInitialTicket(GSSCaller caller, String clientPrincipal, AccessControlContext acc)163 static KerberosTicket getInitialTicket(GSSCaller caller, 164 String clientPrincipal, 165 AccessControlContext acc) throws LoginException { 166 167 // Try to get ticket from acc's Subject 168 Subject accSubj = Subject.getSubject(acc); 169 KerberosTicket ticket = 170 SubjectComber.find(accSubj, null, clientPrincipal, 171 KerberosTicket.class); 172 173 // Try to get ticket from Subject obtained from GSSUtil 174 if (ticket == null && !GSSUtil.useSubjectCredsOnly(caller)) { 175 Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); 176 ticket = SubjectComber.find(subject, 177 null, clientPrincipal, KerberosTicket.class); 178 } 179 return ticket; 180 } 181 182 /** 183 * Retrieves the caller's Subject, or Subject obtained by logging in 184 * via the specified caller. 185 * 186 * Caller must have permission to: 187 * - access the Subject 188 * - create LoginContext 189 * - read the auth.login.defaultCallbackHandler security property 190 * 191 * NOTE: This method is used by JSSE Kerberos Cipher Suites 192 */ getSubject(GSSCaller caller, AccessControlContext acc)193 public static Subject getSubject(GSSCaller caller, 194 AccessControlContext acc) throws LoginException { 195 196 // Try to get the Subject from acc 197 Subject subject = Subject.getSubject(acc); 198 199 // Try to get Subject obtained from GSSUtil 200 if (subject == null && !GSSUtil.useSubjectCredsOnly(caller)) { 201 subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); 202 } 203 return subject; 204 } 205 206 /** 207 * Retrieves the ServiceCreds for the specified server principal from 208 * the Subject in the specified AccessControlContext. If not found, and if 209 * useSubjectCredsOnly is false, then obtain from a LoginContext. 210 * 211 * NOTE: This method is also used by JSSE Kerberos Cipher Suites 212 */ getServiceCreds(GSSCaller caller, String serverPrincipal, AccessControlContext acc)213 public static ServiceCreds getServiceCreds(GSSCaller caller, 214 String serverPrincipal, AccessControlContext acc) 215 throws LoginException { 216 217 Subject accSubj = Subject.getSubject(acc); 218 ServiceCreds sc = null; 219 if (accSubj != null) { 220 sc = ServiceCreds.getInstance(accSubj, serverPrincipal); 221 } 222 if (sc == null && !GSSUtil.useSubjectCredsOnly(caller)) { 223 Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); 224 sc = ServiceCreds.getInstance(subject, serverPrincipal); 225 } 226 return sc; 227 } 228 credsToTicket(Credentials serviceCreds)229 public static KerberosTicket credsToTicket(Credentials serviceCreds) { 230 EncryptionKey sessionKey = serviceCreds.getSessionKey(); 231 KerberosTicket kt = new KerberosTicket( 232 serviceCreds.getEncoded(), 233 new KerberosPrincipal(serviceCreds.getClient().getName()), 234 new KerberosPrincipal(serviceCreds.getServer().getName(), 235 KerberosPrincipal.KRB_NT_SRV_INST), 236 sessionKey.getBytes(), 237 sessionKey.getEType(), 238 serviceCreds.getFlags(), 239 serviceCreds.getAuthTime(), 240 serviceCreds.getStartTime(), 241 serviceCreds.getEndTime(), 242 serviceCreds.getRenewTill(), 243 serviceCreds.getClientAddresses()); 244 PrincipalName clientAlias = serviceCreds.getClientAlias(); 245 PrincipalName serverAlias = serviceCreds.getServerAlias(); 246 if (clientAlias != null) { 247 KerberosSecrets.getJavaxSecurityAuthKerberosAccess() 248 .kerberosTicketSetClientAlias(kt, new KerberosPrincipal( 249 clientAlias.getName(), clientAlias.getNameType())); 250 } 251 if (serverAlias != null) { 252 KerberosSecrets.getJavaxSecurityAuthKerberosAccess() 253 .kerberosTicketSetServerAlias(kt, new KerberosPrincipal( 254 serverAlias.getName(), serverAlias.getNameType())); 255 } 256 return kt; 257 }; 258 ticketToCreds(KerberosTicket kerbTicket)259 public static Credentials ticketToCreds(KerberosTicket kerbTicket) 260 throws KrbException, IOException { 261 KerberosPrincipal clientAlias = KerberosSecrets 262 .getJavaxSecurityAuthKerberosAccess() 263 .kerberosTicketGetClientAlias(kerbTicket); 264 KerberosPrincipal serverAlias = KerberosSecrets 265 .getJavaxSecurityAuthKerberosAccess() 266 .kerberosTicketGetServerAlias(kerbTicket); 267 return new Credentials( 268 kerbTicket.getEncoded(), 269 kerbTicket.getClient().getName(), 270 (clientAlias != null ? clientAlias.getName() : null), 271 kerbTicket.getServer().getName(), 272 (serverAlias != null ? serverAlias.getName() : null), 273 kerbTicket.getSessionKey().getEncoded(), 274 kerbTicket.getSessionKeyType(), 275 kerbTicket.getFlags(), 276 kerbTicket.getAuthTime(), 277 kerbTicket.getStartTime(), 278 kerbTicket.getEndTime(), 279 kerbTicket.getRenewTill(), 280 kerbTicket.getClientAddresses()); 281 } 282 283 /** 284 * A helper method to get a sun..KeyTab from a javax..KeyTab 285 * @param ktab the javax..KeyTab object 286 * @return the sun..KeyTab object 287 */ 288 public static sun.security.krb5.internal.ktab.KeyTab snapshotFromJavaxKeyTab(KeyTab ktab)289 snapshotFromJavaxKeyTab(KeyTab ktab) { 290 return KerberosSecrets.getJavaxSecurityAuthKerberosAccess() 291 .keyTabTakeSnapshot(ktab); 292 } 293 294 /** 295 * A helper method to get EncryptionKeys from a javax..KeyTab 296 * @param ktab the javax..KeyTab object 297 * @param cname the PrincipalName 298 * @return the EKeys, never null, might be empty 299 */ keysFromJavaxKeyTab( KeyTab ktab, PrincipalName cname)300 public static EncryptionKey[] keysFromJavaxKeyTab( 301 KeyTab ktab, PrincipalName cname) { 302 return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname); 303 } 304 } 305