--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java Fri May 11 14:55:56 2018 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,563 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.security.krb5.internal.ssl;
-
-import sun.security.ssl.ClientKeyExchange;
-import sun.security.ssl.Debug;
-import sun.security.ssl.ClientKeyExchangeService;
-import sun.security.ssl.HandshakeOutStream;
-
-import sun.security.jgss.GSSCaller;
-import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.krb5.ServiceCreds;
-import sun.security.krb5.EncryptedData;
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.PrincipalName;
-import sun.security.krb5.internal.EncTicketPart;
-import sun.security.krb5.internal.Ticket;
-import sun.security.krb5.internal.crypto.KeyUsage;
-import sun.security.ssl.ProtocolVersion;
-
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-import javax.security.auth.Subject;
-import javax.security.auth.kerberos.KerberosKey;
-import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.kerberos.KerberosTicket;
-import javax.security.auth.kerberos.KeyTab;
-import javax.security.auth.kerberos.ServicePermission;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.net.InetAddress;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.Principal;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.security.SecureRandom;
-import java.util.Set;
-
-/**
- * The provider for TLS_KRB_ cipher suites.
- *
- * @since 9
- */
-public class Krb5KeyExchangeService implements ClientKeyExchangeService {
-
- public static final Debug debug = Debug.getInstance("ssl");
-
- @Override
- public String[] supported() {
- return new String[] { "KRB5", "KRB5_EXPORT" };
- }
-
- @Override
- public Object getServiceCreds(AccessControlContext acc) {
- try {
- ServiceCreds serviceCreds = AccessController.doPrivileged(
- (PrivilegedExceptionAction<ServiceCreds>)
- () -> Krb5Util.getServiceCreds(
- GSSCaller.CALLER_SSL_SERVER, null, acc));
- if (serviceCreds == null) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Kerberos serviceCreds not available");
- }
- return null;
- }
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Using Kerberos creds");
- }
- String serverPrincipal = serviceCreds.getName();
- if (serverPrincipal != null) {
- // When service is bound, we check ASAP. Otherwise,
- // will check after client request is received
- // in in Kerberos ClientKeyExchange
- SecurityManager sm = System.getSecurityManager();
- try {
- if (sm != null) {
- // Eliminate dependency on ServicePermission
- sm.checkPermission(new ServicePermission(
- serverPrincipal, "accept"), acc);
- }
- } catch (SecurityException se) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Permission to access Kerberos"
- + " secret key denied");
- }
- return null;
- }
- }
- return serviceCreds;
- } catch (PrivilegedActionException e) {
- // Likely exception here is LoginException
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Attempt to obtain Kerberos key failed: "
- + e.toString());
- }
- return null;
- }
- }
-
- @Override
- public String getServiceHostName(Principal principal) {
- if (principal == null) {
- return null;
- }
- String hostName = null;
- try {
- PrincipalName princName =
- new PrincipalName(principal.getName(),
- PrincipalName.KRB_NT_SRV_HST);
- String[] nameParts = princName.getNameStrings();
- if (nameParts.length >= 2) {
- hostName = nameParts[1];
- }
- } catch (Exception e) {
- // ignore
- }
- return hostName;
- }
-
-
- @Override
- public boolean isRelated(boolean isClient,
- AccessControlContext acc, Principal p) {
-
- if (p == null) return false;
- try {
- Subject subject = AccessController.doPrivileged(
- (PrivilegedExceptionAction<Subject>)
- () -> Krb5Util.getSubject(
- isClient ? GSSCaller.CALLER_SSL_CLIENT
- : GSSCaller.CALLER_SSL_SERVER,
- acc));
- if (subject == null) {
- if (debug != null && Debug.isOn("session")) {
- System.out.println("Kerberos credentials are" +
- " not present in the current Subject;" +
- " check if " +
- " javax.security.auth.useSubjectAsCreds" +
- " system property has been set to false");
- }
- return false;
- }
- Set<Principal> principals =
- subject.getPrincipals(Principal.class);
- if (principals.contains(p)) {
- // bound to this principal
- return true;
- } else {
- if (isClient) {
- return false;
- } else {
- for (KeyTab pc : subject.getPrivateCredentials(KeyTab.class)) {
- if (!pc.isBound()) {
- return true;
- }
- }
- return false;
- }
- }
- } catch (PrivilegedActionException pae) {
- if (debug != null && Debug.isOn("session")) {
- System.out.println("Attempt to obtain" +
- " subject failed! " + pae);
- }
- return false;
- }
-
- }
-
- public ClientKeyExchange createClientExchange(
- String serverName, AccessControlContext acc,
- ProtocolVersion protocolVerson, SecureRandom rand) throws IOException {
- return new ExchangerImpl(serverName, acc, protocolVerson, rand);
- }
-
- public ClientKeyExchange createServerExchange(
- ProtocolVersion protocolVersion, ProtocolVersion clientVersion,
- SecureRandom rand, byte[] encodedTicket, byte[] encrypted,
- AccessControlContext acc, Object serviceCreds) throws IOException {
- return new ExchangerImpl(protocolVersion, clientVersion, rand,
- encodedTicket, encrypted, acc, serviceCreds);
- }
-
- static class ExchangerImpl extends ClientKeyExchange {
-
- final private KerberosPreMasterSecret preMaster;
- final private byte[] encodedTicket;
- final private KerberosPrincipal peerPrincipal;
- final private KerberosPrincipal localPrincipal;
-
- @Override
- public int messageLength() {
- return encodedTicket.length + preMaster.getEncrypted().length + 6;
- }
-
- @Override
- public void send(HandshakeOutStream s) throws IOException {
- s.putBytes16(encodedTicket);
- s.putBytes16(null);
- s.putBytes16(preMaster.getEncrypted());
- }
-
- @Override
- public void print(PrintStream s) throws IOException {
- s.println("*** ClientKeyExchange, Kerberos");
-
- if (debug != null && Debug.isOn("verbose")) {
- Debug.println(s, "Kerberos service ticket", encodedTicket);
- Debug.println(s, "Random Secret", preMaster.getUnencrypted());
- Debug.println(s, "Encrypted random Secret", preMaster.getEncrypted());
- }
- }
-
- ExchangerImpl(String serverName, AccessControlContext acc,
- ProtocolVersion protocolVersion, SecureRandom rand) throws IOException {
-
- // Get service ticket
- KerberosTicket ticket = getServiceTicket(serverName, acc);
- encodedTicket = ticket.getEncoded();
-
- // Record the Kerberos principals
- peerPrincipal = ticket.getServer();
- localPrincipal = ticket.getClient();
-
- // Optional authenticator, encrypted using session key,
- // currently ignored
-
- // Generate premaster secret and encrypt it using session key
- EncryptionKey sessionKey = new EncryptionKey(
- ticket.getSessionKeyType(),
- ticket.getSessionKey().getEncoded());
-
- preMaster = new KerberosPreMasterSecret(protocolVersion,
- rand, sessionKey);
- }
-
- ExchangerImpl(
- ProtocolVersion protocolVersion, ProtocolVersion clientVersion, SecureRandom rand,
- byte[] encodedTicket, byte[] encrypted,
- AccessControlContext acc, Object serviceCreds) throws IOException {
-
- // Read ticket
- this.encodedTicket = encodedTicket;
-
- if (debug != null && Debug.isOn("verbose")) {
- Debug.println(System.out,
- "encoded Kerberos service ticket", encodedTicket);
- }
-
- EncryptionKey sessionKey = null;
- KerberosPrincipal tmpPeer = null;
- KerberosPrincipal tmpLocal = null;
-
- try {
- Ticket t = new Ticket(encodedTicket);
-
- EncryptedData encPart = t.encPart;
- PrincipalName ticketSname = t.sname;
-
- final ServiceCreds creds = (ServiceCreds)serviceCreds;
- final KerberosPrincipal princ =
- new KerberosPrincipal(ticketSname.toString());
-
- // For bound service, permission already checked at setup
- if (creds.getName() == null) {
- SecurityManager sm = System.getSecurityManager();
- try {
- if (sm != null) {
- // Eliminate dependency on ServicePermission
- sm.checkPermission(new ServicePermission(
- ticketSname.toString(), "accept"), acc);
- }
- } catch (SecurityException se) {
- serviceCreds = null;
- // Do not destroy keys. Will affect Subject
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Permission to access Kerberos"
- + " secret key denied");
- se.printStackTrace(System.out);
- }
- throw new IOException("Kerberos service not allowedy");
- }
- }
- KerberosKey[] serverKeys = AccessController.doPrivileged(
- new PrivilegedAction<KerberosKey[]>() {
- @Override
- public KerberosKey[] run() {
- return creds.getKKeys(princ);
- }
- });
- if (serverKeys.length == 0) {
- throw new IOException("Found no key for " + princ +
- (creds.getName() == null ? "" :
- (", this keytab is for " + creds.getName() + " only")));
- }
-
- /*
- * permission to access and use the secret key of the Kerberized
- * "host" service is done in ServerHandshaker.getKerberosKeys()
- * to ensure server has the permission to use the secret key
- * before promising the client
- */
-
- // See if we have the right key to decrypt the ticket to get
- // the session key.
- int encPartKeyType = encPart.getEType();
- Integer encPartKeyVersion = encPart.getKeyVersionNumber();
- KerberosKey dkey = null;
- try {
- dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys);
- } catch (KrbException ke) { // a kvno mismatch
- throw new IOException(
- "Cannot find key matching version number", ke);
- }
- if (dkey == null) {
- // %%% Should print string repr of etype
- throw new IOException("Cannot find key of appropriate type" +
- " to decrypt ticket - need etype " + encPartKeyType);
- }
-
- EncryptionKey secretKey = new EncryptionKey(
- encPartKeyType,
- dkey.getEncoded());
-
- // Decrypt encPart using server's secret key
- byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET);
-
- // Reset data stream after decryption, remove redundant bytes
- byte[] temp = encPart.reset(bytes);
- EncTicketPart encTicketPart = new EncTicketPart(temp);
-
- // Record the Kerberos Principals
- tmpPeer = new KerberosPrincipal(encTicketPart.cname.getName());
- tmpLocal = new KerberosPrincipal(ticketSname.getName());
-
- sessionKey = encTicketPart.key;
-
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("server principal: " + ticketSname);
- System.out.println("cname: " + encTicketPart.cname.toString());
- }
- } catch (IOException e) {
- throw e;
- } catch (Exception e) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("KerberosWrapper error getting session key,"
- + " generating random secret (" + e.getMessage() + ")");
- }
- sessionKey = null;
- }
-
- //input.getBytes16(); // XXX Read and ignore authenticator
-
- if (sessionKey != null) {
- preMaster = new KerberosPreMasterSecret(protocolVersion,
- clientVersion, rand, encrypted, sessionKey);
- } else {
- // Generate bogus premaster secret
- preMaster = new KerberosPreMasterSecret(clientVersion, rand);
- }
-
- peerPrincipal = tmpPeer;
- localPrincipal = tmpLocal;
- }
-
- // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context
- private static KerberosTicket getServiceTicket(String serverName,
- final AccessControlContext acc) throws IOException {
-
- if ("localhost".equals(serverName) ||
- "localhost.localdomain".equals(serverName)) {
-
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Get the local hostname");
- }
- String localHost = java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction<String>() {
- public String run() {
- try {
- return InetAddress.getLocalHost().getHostName();
- } catch (java.net.UnknownHostException e) {
- if (debug != null && Debug.isOn("handshake")) {
- System.out.println("Warning,"
- + " cannot get the local hostname: "
- + e.getMessage());
- }
- return null;
- }
- }
- });
- if (localHost != null) {
- serverName = localHost;
- }
- }
-
- // Resolve serverName (possibly in IP addr form) to Kerberos principal
- // name for service with hostname
- String serviceName = "host/" + serverName;
- PrincipalName principal;
- try {
- principal = new PrincipalName(serviceName,
- PrincipalName.KRB_NT_SRV_HST);
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- IOException ioe = new IOException("Invalid service principal" +
- " name: " + serviceName);
- ioe.initCause(e);
- throw ioe;
- }
- String realm = principal.getRealmAsString();
-
- final String serverPrincipal = principal.toString();
- final String tgsPrincipal = "krbtgt/" + realm + "@" + realm;
- final String clientPrincipal = null; // use default
-
-
- // check permission to obtain a service ticket to initiate a
- // context with the "host" service
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- sm.checkPermission(new ServicePermission(serverPrincipal,
- "initiate"), acc);
- }
-
- try {
- KerberosTicket ticket = AccessController.doPrivileged(
- new PrivilegedExceptionAction<KerberosTicket>() {
- public KerberosTicket run() throws Exception {
- return Krb5Util.getTicketFromSubjectAndTgs(
- GSSCaller.CALLER_SSL_CLIENT,
- clientPrincipal, serverPrincipal,
- tgsPrincipal, acc);
- }});
-
- if (ticket == null) {
- throw new IOException("Failed to find any kerberos service" +
- " ticket for " + serverPrincipal);
- }
- return ticket;
- } catch (PrivilegedActionException e) {
- IOException ioe = new IOException(
- "Attempt to obtain kerberos service ticket for " +
- serverPrincipal + " failed!");
- ioe.initCause(e);
- throw ioe;
- }
- }
-
- @Override
- public SecretKey clientKeyExchange() {
- byte[] secretBytes = preMaster.getUnencrypted();
- return new SecretKeySpec(secretBytes, "TlsPremasterSecret");
- }
-
- @Override
- public Principal getPeerPrincipal() {
- return peerPrincipal;
- }
-
- @Override
- public Principal getLocalPrincipal() {
- return localPrincipal;
- }
-
- /**
- * Determines if a kvno matches another kvno. Used in the method
- * findKey(etype, version, keys). Always returns true if either input
- * is null or zero, in case any side does not have kvno info available.
- *
- * Note: zero is included because N/A is not a legal value for kvno
- * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
- * that the kvno is N/A might be lost when converting between
- * EncryptionKey and KerberosKey.
- */
- private static boolean versionMatches(Integer v1, int v2) {
- if (v1 == null || v1 == 0 || v2 == 0) {
- return true;
- }
- return v1.equals(v2);
- }
-
- private static KerberosKey findKey(int etype, Integer version,
- KerberosKey[] keys) throws KrbException {
- int ktype;
- boolean etypeFound = false;
-
- // When no matched kvno is found, returns tke key of the same
- // etype with the highest kvno
- int kvno_found = 0;
- KerberosKey key_found = null;
-
- for (int i = 0; i < keys.length; i++) {
- ktype = keys[i].getKeyType();
- if (etype == ktype) {
- int kv = keys[i].getVersionNumber();
- etypeFound = true;
- if (versionMatches(version, kv)) {
- return keys[i];
- } else if (kv > kvno_found) {
- key_found = keys[i];
- kvno_found = kv;
- }
- }
- }
- // Key not found.
- // %%% kludge to allow DES keys to be used for diff etypes
- if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
- etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
- for (int i = 0; i < keys.length; i++) {
- ktype = keys[i].getKeyType();
- if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
- ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
- int kv = keys[i].getVersionNumber();
- etypeFound = true;
- if (versionMatches(version, kv)) {
- return new KerberosKey(keys[i].getPrincipal(),
- keys[i].getEncoded(),
- etype,
- kv);
- } else if (kv > kvno_found) {
- key_found = new KerberosKey(keys[i].getPrincipal(),
- keys[i].getEncoded(),
- etype,
- kv);
- kvno_found = kv;
- }
- }
- }
- }
- if (etypeFound) {
- return key_found;
- }
- return null;
- }
- }
-}