--- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java Thu Jun 11 00:21:56 2015 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,463 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, 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.ssl.krb5;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.security.AccessController;
-import java.security.AccessControlContext;
-import java.security.PrivilegedExceptionAction;
-import java.security.PrivilegedActionException;
-import java.security.SecureRandom;
-import java.net.InetAddress;
-import java.security.PrivilegedAction;
-
-import javax.security.auth.kerberos.KerberosTicket;
-import javax.security.auth.kerberos.KerberosKey;
-import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.kerberos.ServicePermission;
-import sun.security.jgss.GSSCaller;
-
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.EncryptedData;
-import sun.security.krb5.PrincipalName;
-import sun.security.krb5.internal.Ticket;
-import sun.security.krb5.internal.EncTicketPart;
-import sun.security.krb5.internal.crypto.KeyUsage;
-
-import sun.security.jgss.krb5.Krb5Util;
-import sun.security.jgss.krb5.ServiceCreds;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.internal.Krb5;
-
-import sun.security.ssl.Debug;
-import sun.security.ssl.HandshakeInStream;
-import sun.security.ssl.HandshakeOutStream;
-import sun.security.ssl.Krb5Helper;
-import sun.security.ssl.ProtocolVersion;
-
-/**
- * This is Kerberos option in the client key exchange message
- * (CLIENT -> SERVER). It holds the Kerberos ticket and the encrypted
- * premaster secret encrypted with the session key sealed in the ticket.
- * From RFC 2712:
- * struct
- * {
- * opaque Ticket;
- * opaque authenticator; // optional
- * opaque EncryptedPreMasterSecret; // encrypted with the session key
- * // which is sealed in the ticket
- * } KerberosWrapper;
- *
- *
- * Ticket and authenticator are encrypted as per RFC 1510 (in ASN.1)
- * Encrypted pre-master secret has the same structure as it does for RSA
- * except for Kerberos, the encryption key is the session key instead of
- * the RSA public key.
- *
- * XXX authenticator currently ignored
- *
- */
-public final class KerberosClientKeyExchangeImpl
- extends sun.security.ssl.KerberosClientKeyExchange {
-
- private KerberosPreMasterSecret preMaster;
- private byte[] encodedTicket;
- private KerberosPrincipal peerPrincipal;
- private KerberosPrincipal localPrincipal;
-
- public KerberosClientKeyExchangeImpl() {
- }
-
- /**
- * Creates an instance of KerberosClientKeyExchange consisting of the
- * Kerberos service ticket, authenticator and encrypted premaster secret.
- * Called by client handshaker.
- *
- * @param serverName name of server with which to do handshake;
- * this is used to get the Kerberos service ticket
- * @param protocolVersion Maximum version supported by client (i.e,
- * version it requested in client hello)
- * @param rand random number generator to use for generating pre-master
- * secret
- */
- @Override
- public void init(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);
- }
-
- /**
- * Creates an instance of KerberosClientKeyExchange from its ASN.1 encoding.
- * Used by ServerHandshaker to verify and obtain premaster secret.
- *
- * @param protocolVersion current protocol version
- * @param clientVersion version requested by client in its ClientHello;
- * used by premaster secret version check
- * @param rand random number generator used for generating random
- * premaster secret if ticket and/or premaster verification fails
- * @param input inputstream from which to get ASN.1-encoded KerberosWrapper
- * @param acc the AccessControlContext of the handshaker
- * @param serviceCreds server's creds
- */
- @Override
- public void init(ProtocolVersion protocolVersion,
- ProtocolVersion clientVersion,
- SecureRandom rand, HandshakeInStream input, AccessControlContext acc, Object serviceCreds)
- throws IOException {
-
- // Read ticket
- encodedTicket = input.getBytes16();
-
- if (debug != null && Debug.isOn("verbose")) {
- Debug.println(System.out,
- "encoded Kerberos service ticket", encodedTicket);
- }
-
- EncryptionKey sessionKey = 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(Krb5Helper.getServicePermission(
- 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");
- }
- 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
- peerPrincipal =
- new KerberosPrincipal(encTicketPart.cname.getName());
- localPrincipal = 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, input, sessionKey);
- } else {
- // Generate bogus premaster secret
- preMaster = new KerberosPreMasterSecret(clientVersion, rand);
- }
- }
-
- @Override
- public int messageLength() {
- return (6 + encodedTicket.length + preMaster.getEncrypted().length);
- }
-
- @Override
- public void send(HandshakeOutStream s) throws IOException {
- s.putBytes16(encodedTicket);
- s.putBytes16(null); // XXX no authenticator
- 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());
- }
- }
-
- // 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 byte[] getUnencryptedPreMasterSecret() {
- return preMaster.getUnencrypted();
- }
-
- @Override
- public KerberosPrincipal getPeerPrincipal() {
- return peerPrincipal;
- }
-
- @Override
- public KerberosPrincipal 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;
- }
-}