diff --git a/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java b/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java index c9499f1d1a0f528ed61cebc8ba95c1e2f60ce427..5da153d72ea1c4783746e30ae8ac972f771f63e8 100644 --- a/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java +++ b/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2011, 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 @@ -395,7 +395,13 @@ public class Krb5LoginModule implements LoginModule { private boolean succeeded = false; private boolean commitSucceeded = false; private String username; + + // Encryption keys calculated from password. Assigned when storekey == true + // and useKeyTab == false (or true but not found) private EncryptionKey[] encKeys = null; + + KeyTab ktab = null; + private Credentials cred = null; private PrincipalName principal = null; @@ -663,28 +669,49 @@ public class Krb5LoginModule implements LoginModule { (krb5PrincName.toString(), PrincipalName.KRB_NT_PRINCIPAL); } + + /* + * Before dynamic KeyTab support (6894072), here we check if + * the keytab contains keys for the principal. If no, keytab + * will not be used and password is prompted for. + * + * After 6894072, we normally don't check it, and expect the + * keys can be populated until a real connection is made. The + * check is still done when isInitiator == true, where the keys + * will be used right now. + * + * Probably tricky relations: + * + * useKeyTab is config flag, but when it's true but the ktab + * does not contains keys for principal, we would use password + * and keep the flag unchanged (for reuse?). In this method, + * we use (ktab != null) to check whether keytab is used. + * After this method (and when storeKey == true), we use + * (encKeys == null) to check. + */ if (useKeyTab) { - encKeys = - EncryptionKey.acquireSecretKeys(principal, keyTabName); - - if (debug) { - if (encKeys != null) - System.out.println - ("principal's key obtained from the keytab"); - else - System.out.println - ("Key for the principal " + - principal + - " not available in " + - ((keyTabName == null) ? - "default key tab" : keyTabName)); + ktab = (keyTabName == null) + ? KeyTab.getInstance() + : KeyTab.getInstance(new File(keyTabName)); + if (isInitiator) { + if (Krb5Util.keysFromJavaxKeyTab(ktab, principal).length + == 0) { + ktab = null; + if (debug) { + System.out.println + ("Key for the principal " + + principal + + " not available in " + + ((keyTabName == null) ? + "default key tab" : keyTabName)); + } + } } - } KrbAsReqBuilder builder; - // We can't get the key from the keytab so prompt - if (encKeys == null) { + + if (ktab == null) { promptForPass(getPasswdFromSharedState); builder = new KrbAsReqBuilder(principal, password); if (isInitiator) { @@ -693,9 +720,13 @@ public class Krb5LoginModule implements LoginModule { // updated with PA info cred = builder.action().getCreds(); } - encKeys = builder.getKeys(); + if (storeKey) { + encKeys = builder.getKeys(); + // When encKeys is empty, the login actually fails. + // For compatibility, exception is thrown in commit(). + } } else { - builder = new KrbAsReqBuilder(principal, encKeys); + builder = new KrbAsReqBuilder(principal, ktab); if (isInitiator) { cred = builder.action().getCreds(); } @@ -705,10 +736,15 @@ public class Krb5LoginModule implements LoginModule { if (debug) { System.out.println("principal is " + principal); HexDumpEncoder hd = new HexDumpEncoder(); - for (int i = 0; i < encKeys.length; i++) { - System.out.println("EncryptionKey: keyType=" + - encKeys[i].getEType() + " keyBytes (hex dump)=" + - hd.encodeBuffer(encKeys[i].getBytes())); + if (ktab != null) { + System.out.println("Will use keytab"); + } else if (storeKey) { + for (int i = 0; i < encKeys.length; i++) { + System.out.println("EncryptionKey: keyType=" + + encKeys[i].getEType() + + " keyBytes (hex dump)=" + + hd.encodeBuffer(encKeys[i].getBytes())); + } } } @@ -989,8 +1025,8 @@ public class Krb5LoginModule implements LoginModule { kerbTicket = Krb5Util.credsToTicket(cred); } - if (storeKey) { - if (encKeys == null || encKeys.length <= 0) { + if (storeKey && encKeys != null) { + if (encKeys.length == 0) { succeeded = false; throw new LoginException("Null Server Key "); } @@ -1006,10 +1042,11 @@ public class Krb5LoginModule implements LoginModule { } } - // Let us add the kerbClientPrinc,kerbTicket and kerbKey (if + // Let us add the kerbClientPrinc,kerbTicket and KeyTab/KerbKey (if // storeKey is true) - if (!princSet.contains(kerbClientPrinc)) + if (!princSet.contains(kerbClientPrinc)) { princSet.add(kerbClientPrinc); + } // add the TGT if (kerbTicket != null) { @@ -1018,19 +1055,29 @@ public class Krb5LoginModule implements LoginModule { } if (storeKey) { - for (int i = 0; i < kerbKeys.length; i++) { - if (!privCredSet.contains(kerbKeys[i])) { - privCredSet.add(kerbKeys[i]); + if (encKeys == null) { + if (!privCredSet.contains(ktab)) { + privCredSet.add(ktab); + // Compatibility; also add keys to privCredSet + for (KerberosKey key: ktab.getKeys(kerbClientPrinc)) { + privCredSet.add(new Krb5Util.KeysFromKeyTab(key)); + } } - encKeys[i].destroy(); - encKeys[i] = null; - if (debug) { - System.out.println("Added server's key" - + kerbKeys[i]); - System.out.println("\t\t[Krb5LoginModule] " + - "added Krb5Principal " + - kerbClientPrinc.toString() - + " to Subject"); + } else { + for (int i = 0; i < kerbKeys.length; i ++) { + if (!privCredSet.contains(kerbKeys[i])) { + privCredSet.add(kerbKeys[i]); + } + encKeys[i].destroy(); + encKeys[i] = null; + if (debug) { + System.out.println("Added server's key" + + kerbKeys[i]); + System.out.println("\t\t[Krb5LoginModule] " + + "added Krb5Principal " + + kerbClientPrinc.toString() + + " to Subject"); + } } } } @@ -1106,7 +1153,8 @@ public class Krb5LoginModule implements LoginModule { while (it.hasNext()) { Object o = it.next(); if (o instanceof KerberosTicket || - o instanceof KerberosKey) { + o instanceof KerberosKey || + o instanceof KeyTab) { it.remove(); } } @@ -1161,6 +1209,7 @@ public class Krb5LoginModule implements LoginModule { } else { // remove temp results for the next try encKeys = null; + ktab = null; principal = null; } username = null; diff --git a/src/share/classes/java/lang/ProcessBuilder.java b/src/share/classes/java/lang/ProcessBuilder.java index c580f507d539afaf27262db5c1f51ad41113c453..6caaaeb906d32c24251cb017c313b272e3ba720f 100644 --- a/src/share/classes/java/lang/ProcessBuilder.java +++ b/src/share/classes/java/lang/ProcessBuilder.java @@ -938,6 +938,11 @@ public final class ProcessBuilder * but at the very least the command must be a non-empty list of * non-null strings. * + *

A minimal set of system dependent environment variables may + * be required to start a process on some operating systems. + * As a result, the subprocess may inherit additional environment variable + * settings beyond those in the process builder's {@link #environment()}. + * *

If there is a security manager, its * {@link SecurityManager#checkExec checkExec} * method is called with the first component of this object's diff --git a/src/share/classes/java/lang/Runtime.java b/src/share/classes/java/lang/Runtime.java index 5e4a1bceaba594ce03239a46c404e1da481309bf..3fc29d08376b69e3395758bf529fed7e5ded2d94 100644 --- a/src/share/classes/java/lang/Runtime.java +++ b/src/share/classes/java/lang/Runtime.java @@ -544,6 +544,11 @@ public class Runtime { *

If envp is null, the subprocess inherits the * environment settings of the current process. * + *

A minimal set of system dependent environment variables may + * be required to start a process on some operating systems. + * As a result, the subprocess may inherit additional environment variable + * settings beyond those in the specified environment. + * *

{@link ProcessBuilder#start()} is now the preferred way to * start a process with a modified environment. * diff --git a/src/share/classes/java/util/AbstractSet.java b/src/share/classes/java/util/AbstractSet.java index b99da00826dea463d491209d459e758218c284e4..1f1a49c250e4cdf058c12cdaaca3499c195baeb6 100644 --- a/src/share/classes/java/util/AbstractSet.java +++ b/src/share/classes/java/util/AbstractSet.java @@ -156,9 +156,11 @@ public abstract class AbstractSet extends AbstractCollection implements Se * @throws UnsupportedOperationException if the removeAll operation * is not supported by this set * @throws ClassCastException if the class of an element of this set - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this set contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see #remove(Object) * @see #contains(Object) diff --git a/src/share/classes/java/util/ArrayList.java b/src/share/classes/java/util/ArrayList.java index 128ec5b5fd864259cabdf53d4d1ce4a06d12f2a5..14b0fbf5599b76189fd30f5d89fa6daeba700814 100644 --- a/src/share/classes/java/util/ArrayList.java +++ b/src/share/classes/java/util/ArrayList.java @@ -628,9 +628,11 @@ public class ArrayList extends AbstractList * @param c collection containing elements to be removed from this list * @return {@code true} if this list changed as a result of the call * @throws ClassCastException if the class of an element of this list - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this list contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see Collection#contains(Object) */ @@ -646,9 +648,11 @@ public class ArrayList extends AbstractList * @param c collection containing elements to be retained in this list * @return {@code true} if this list changed as a result of the call * @throws ClassCastException if the class of an element of this list - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this list contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see Collection#contains(Object) */ diff --git a/src/share/classes/java/util/Collection.java b/src/share/classes/java/util/Collection.java index 7d0bcdc5aa366753511117e6ccbe6a4e43ca8b59..b8f5869e861ab449d4b91628f9c2292069f553b8 100644 --- a/src/share/classes/java/util/Collection.java +++ b/src/share/classes/java/util/Collection.java @@ -60,7 +60,8 @@ package java.util; * but is not required to, throw the exception if the collection to be added * is empty. * - *

Some collection implementations have restrictions on the elements that + *

+ * Some collection implementations have restrictions on the elements that * they may contain. For example, some implementations prohibit null elements, * and some have restrictions on the types of their elements. Attempting to * add an ineligible element throws an unchecked exception, typically @@ -152,9 +153,11 @@ public interface Collection extends Iterable { * @return true if this collection contains the specified * element * @throws ClassCastException if the type of the specified element - * is incompatible with this collection (optional) + * is incompatible with this collection + * (optional) * @throws NullPointerException if the specified element is null and this - * collection does not permit null elements (optional) + * collection does not permit null elements + * (optional) */ boolean contains(Object o); @@ -279,9 +282,11 @@ public interface Collection extends Iterable { * @param o element to be removed from this collection, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the type of the specified element - * is incompatible with this collection (optional) + * is incompatible with this collection + * (optional) * @throws NullPointerException if the specified element is null and this - * collection does not permit null elements (optional) + * collection does not permit null elements + * (optional) * @throws UnsupportedOperationException if the remove operation * is not supported by this collection */ @@ -299,10 +304,13 @@ public interface Collection extends Iterable { * in the specified collection * @throws ClassCastException if the types of one or more elements * in the specified collection are incompatible with this - * collection (optional) + * collection + * (optional) * @throws NullPointerException if the specified collection contains one * or more null elements and this collection does not permit null - * elements (optional), or if the specified collection is null + * elements + * (optional), + * or if the specified collection is null. * @see #contains(Object) */ boolean containsAll(Collection c); @@ -346,10 +354,13 @@ public interface Collection extends Iterable { * is not supported by this collection * @throws ClassCastException if the types of one or more elements * in this collection are incompatible with the specified - * collection (optional) + * collection + * (optional) * @throws NullPointerException if this collection contains one or more * null elements and the specified collection does not support - * null elements (optional), or if the specified collection is null + * null elements + * (optional), + * or if the specified collection is null * @see #remove(Object) * @see #contains(Object) */ @@ -367,10 +378,13 @@ public interface Collection extends Iterable { * is not supported by this collection * @throws ClassCastException if the types of one or more elements * in this collection are incompatible with the specified - * collection (optional) + * collection + * (optional) * @throws NullPointerException if this collection contains one or more * null elements and the specified collection does not permit null - * elements (optional), or if the specified collection is null + * elements + * (optional), + * or if the specified collection is null * @see #remove(Object) * @see #contains(Object) */ diff --git a/src/share/classes/java/util/Collections.java b/src/share/classes/java/util/Collections.java index 80221c8795b160277be0a21123a4ca7c5ded2c65..d15a4292ddd818065a4570a0c5ba24ed19497724 100644 --- a/src/share/classes/java/util/Collections.java +++ b/src/share/classes/java/util/Collections.java @@ -3746,9 +3746,10 @@ public class Collections { * @throws NullPointerException if either collection is {@code null}. * @throws NullPointerException if one collection contains a {@code null} * element and {@code null} is not an eligible element for the other collection. - * (optional) + * (optional) * @throws ClassCastException if one collection contains an element that is - * of a type which is ineligible for the other collection. (optional) + * of a type which is ineligible for the other collection. + * (optional) * @since 1.5 */ public static boolean disjoint(Collection c1, Collection c2) { diff --git a/src/share/classes/java/util/Deque.java b/src/share/classes/java/util/Deque.java index b5516211be635b87c03d005c2d322643d7fdc9ba..051ae9cca46b7aa99106e6f2083d400f3745e3ae 100644 --- a/src/share/classes/java/util/Deque.java +++ b/src/share/classes/java/util/Deque.java @@ -351,9 +351,11 @@ public interface Deque extends Queue { * @param o element to be removed from this deque, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) + * is incompatible with this deque + * (optional) * @throws NullPointerException if the specified element is null and this - * deque does not permit null elements (optional) + * deque does not permit null elements + * (optional) */ boolean removeFirstOccurrence(Object o); @@ -369,9 +371,11 @@ public interface Deque extends Queue { * @param o element to be removed from this deque, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) + * is incompatible with this deque + * (optional) * @throws NullPointerException if the specified element is null and this - * deque does not permit null elements (optional) + * deque does not permit null elements + * (optional) */ boolean removeLastOccurrence(Object o); @@ -527,9 +531,11 @@ public interface Deque extends Queue { * @param o element to be removed from this deque, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) + * is incompatible with this deque + * (optional) * @throws NullPointerException if the specified element is null and this - * deque does not permit null elements (optional) + * deque does not permit null elements + * (optional) */ boolean remove(Object o); @@ -542,9 +548,11 @@ public interface Deque extends Queue { * @param o element whose presence in this deque is to be tested * @return true if this deque contains the specified element * @throws ClassCastException if the type of the specified element - * is incompatible with this deque (optional) + * is incompatible with this deque + * (optional) * @throws NullPointerException if the specified element is null and this - * deque does not permit null elements (optional) + * deque does not permit null elements + * (optional) */ boolean contains(Object o); diff --git a/src/share/classes/java/util/List.java b/src/share/classes/java/util/List.java index 638fcc3a7d1276abb5585f8bee8362465077a9a6..d3d2a1b8947e617c8f51ee4d08663469722c110a 100644 --- a/src/share/classes/java/util/List.java +++ b/src/share/classes/java/util/List.java @@ -134,9 +134,11 @@ public interface List extends Collection { * @param o element whose presence in this list is to be tested * @return true if this list contains the specified element * @throws ClassCastException if the type of the specified element - * is incompatible with this list (optional) + * is incompatible with this list + * (optional) * @throws NullPointerException if the specified element is null and this - * list does not permit null elements (optional) + * list does not permit null elements + * (optional) */ boolean contains(Object o); @@ -245,9 +247,11 @@ public interface List extends Collection { * @param o element to be removed from this list, if present * @return true if this list contained the specified element * @throws ClassCastException if the type of the specified element - * is incompatible with this list (optional) + * is incompatible with this list + * (optional) * @throws NullPointerException if the specified element is null and this - * list does not permit null elements (optional) + * list does not permit null elements + * (optional) * @throws UnsupportedOperationException if the remove operation * is not supported by this list */ @@ -265,10 +269,13 @@ public interface List extends Collection { * specified collection * @throws ClassCastException if the types of one or more elements * in the specified collection are incompatible with this - * list (optional) + * list + * (optional) * @throws NullPointerException if the specified collection contains one * or more null elements and this list does not permit null - * elements (optional), or if the specified collection is null + * elements + * (optional), + * or if the specified collection is null * @see #contains(Object) */ boolean containsAll(Collection c); @@ -334,9 +341,11 @@ public interface List extends Collection { * @throws UnsupportedOperationException if the removeAll operation * is not supported by this list * @throws ClassCastException if the class of an element of this list - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this list contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see #remove(Object) * @see #contains(Object) @@ -354,9 +363,11 @@ public interface List extends Collection { * @throws UnsupportedOperationException if the retainAll operation * is not supported by this list * @throws ClassCastException if the class of an element of this list - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this list contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see #remove(Object) * @see #contains(Object) @@ -493,9 +504,11 @@ public interface List extends Collection { * @return the index of the first occurrence of the specified element in * this list, or -1 if this list does not contain the element * @throws ClassCastException if the type of the specified element - * is incompatible with this list (optional) + * is incompatible with this list + * (optional) * @throws NullPointerException if the specified element is null and this - * list does not permit null elements (optional) + * list does not permit null elements + * (optional) */ int indexOf(Object o); @@ -510,9 +523,11 @@ public interface List extends Collection { * @return the index of the last occurrence of the specified element in * this list, or -1 if this list does not contain the element * @throws ClassCastException if the type of the specified element - * is incompatible with this list (optional) + * is incompatible with this list + * (optional) * @throws NullPointerException if the specified element is null and this - * list does not permit null elements (optional) + * list does not permit null elements + * (optional) */ int lastIndexOf(Object o); diff --git a/src/share/classes/java/util/Map.java b/src/share/classes/java/util/Map.java index 2f8f1315c1fe22604a9d9358083ccf247aa0e009..ccdb28813a2f5933cb89c7d0018a3b72c7e79ac6 100644 --- a/src/share/classes/java/util/Map.java +++ b/src/share/classes/java/util/Map.java @@ -144,9 +144,11 @@ public interface Map { * @return true if this map contains a mapping for the specified * key * @throws ClassCastException if the key is of an inappropriate type for - * this map (optional) + * this map + * (optional) * @throws NullPointerException if the specified key is null and this map - * does not permit null keys (optional) + * does not permit null keys + * (optional) */ boolean containsKey(Object key); @@ -162,9 +164,11 @@ public interface Map { * @return true if this map maps one or more keys to the * specified value * @throws ClassCastException if the value is of an inappropriate type for - * this map (optional) + * this map + * (optional) * @throws NullPointerException if the specified value is null and this - * map does not permit null values (optional) + * map does not permit null values + * (optional) */ boolean containsValue(Object value); @@ -187,9 +191,11 @@ public interface Map { * @return the value to which the specified key is mapped, or * {@code null} if this map contains no mapping for the key * @throws ClassCastException if the key is of an inappropriate type for - * this map (optional) + * this map + * (optional) * @throws NullPointerException if the specified key is null and this map - * does not permit null keys (optional) + * does not permit null keys + * (optional) */ V get(Object key); @@ -245,9 +251,11 @@ public interface Map { * @throws UnsupportedOperationException if the remove operation * is not supported by this map * @throws ClassCastException if the key is of an inappropriate type for - * this map (optional) + * this map + * (optional) * @throws NullPointerException if the specified key is null and this - * map does not permit null keys (optional) + * map does not permit null keys + * (optional) */ V remove(Object key); @@ -466,4 +474,5 @@ public interface Map { * @see #equals(Object) */ int hashCode(); + } diff --git a/src/share/classes/java/util/Set.java b/src/share/classes/java/util/Set.java index 8d54f4f9d455865cd9e1831656c33411604bc36f..004e9708e562c4408f9395781183be546cd9f375 100644 --- a/src/share/classes/java/util/Set.java +++ b/src/share/classes/java/util/Set.java @@ -110,9 +110,11 @@ public interface Set extends Collection { * @param o element whose presence in this set is to be tested * @return true if this set contains the specified element * @throws ClassCastException if the type of the specified element - * is incompatible with this set (optional) + * is incompatible with this set + * (optional) * @throws NullPointerException if the specified element is null and this - * set does not permit null elements (optional) + * set does not permit null elements + * (optional) */ boolean contains(Object o); @@ -236,9 +238,11 @@ public interface Set extends Collection { * @param o object to be removed from this set, if present * @return true if this set contained the specified element * @throws ClassCastException if the type of the specified element - * is incompatible with this set (optional) + * is incompatible with this set + * (optional) * @throws NullPointerException if the specified element is null and this - * set does not permit null elements (optional) + * set does not permit null elements + * (optional) * @throws UnsupportedOperationException if the remove operation * is not supported by this set */ @@ -257,10 +261,13 @@ public interface Set extends Collection { * specified collection * @throws ClassCastException if the types of one or more elements * in the specified collection are incompatible with this - * set (optional) + * set + * (optional) * @throws NullPointerException if the specified collection contains one * or more null elements and this set does not permit null - * elements (optional), or if the specified collection is null + * elements + * (optional), + * or if the specified collection is null * @see #contains(Object) */ boolean containsAll(Collection c); @@ -302,9 +309,11 @@ public interface Set extends Collection { * @throws UnsupportedOperationException if the retainAll operation * is not supported by this set * @throws ClassCastException if the class of an element of this set - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this set contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see #remove(Object) */ @@ -322,9 +331,11 @@ public interface Set extends Collection { * @throws UnsupportedOperationException if the removeAll operation * is not supported by this set * @throws ClassCastException if the class of an element of this set - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this set contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see #remove(Object) * @see #contains(Object) diff --git a/src/share/classes/java/util/Vector.java b/src/share/classes/java/util/Vector.java index 0c89889b19bd13f884183a1386da94db0d2c258a..0d69591abce962f3230d8a4a02af21a505db2443 100644 --- a/src/share/classes/java/util/Vector.java +++ b/src/share/classes/java/util/Vector.java @@ -893,10 +893,13 @@ public class Vector * @return true if this Vector changed as a result of the call * @throws ClassCastException if the types of one or more elements * in this vector are incompatible with the specified - * collection (optional) + * collection + * (optional) * @throws NullPointerException if this vector contains one or more null * elements and the specified collection does not support null - * elements (optional), or if the specified collection is null + * elements + * (optional), + * or if the specified collection is null * @since 1.2 */ public synchronized boolean removeAll(Collection c) { @@ -913,10 +916,13 @@ public class Vector * @return true if this Vector changed as a result of the call * @throws ClassCastException if the types of one or more elements * in this vector are incompatible with the specified - * collection (optional) + * collection + * (optional) * @throws NullPointerException if this vector contains one or more null * elements and the specified collection does not support null - * elements (optional), or if the specified collection is null + * elements + * (optional), + * or if the specified collection is null * @since 1.2 */ public synchronized boolean retainAll(Collection c) { diff --git a/src/share/classes/java/util/concurrent/BlockingDeque.java b/src/share/classes/java/util/concurrent/BlockingDeque.java index 3134eae8ec5d5c64abc84aebd6968149d49e193e..7f37f7e66ea3ebe4d47f9edd1cbcad350b3622f0 100644 --- a/src/share/classes/java/util/concurrent/BlockingDeque.java +++ b/src/share/classes/java/util/concurrent/BlockingDeque.java @@ -400,8 +400,10 @@ public interface BlockingDeque extends BlockingQueue, Deque { * @param o element to be removed from this deque, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null + * (optional) */ boolean removeFirstOccurrence(Object o); @@ -416,8 +418,10 @@ public interface BlockingDeque extends BlockingQueue, Deque { * @param o element to be removed from this deque, if present * @return true if an element was removed as a result of this call * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null + * (optional) */ boolean removeLastOccurrence(Object o); @@ -591,8 +595,10 @@ public interface BlockingDeque extends BlockingQueue, Deque { * @param o element to be removed from this deque, if present * @return true if this deque changed as a result of the call * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null + * (optional) */ boolean remove(Object o); @@ -604,8 +610,10 @@ public interface BlockingDeque extends BlockingQueue, Deque { * @param o object to be checked for containment in this deque * @return true if this deque contains the specified element * @throws ClassCastException if the class of the specified element - * is incompatible with this deque (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this deque + * (optional) + * @throws NullPointerException if the specified element is null + * (optional) */ public boolean contains(Object o); diff --git a/src/share/classes/java/util/concurrent/BlockingQueue.java b/src/share/classes/java/util/concurrent/BlockingQueue.java index 9886e6da3c103ed20b321c7ce159a415cf8890ab..4511d27efd76413731a872e9083d7e6eea5e40f4 100644 --- a/src/share/classes/java/util/concurrent/BlockingQueue.java +++ b/src/share/classes/java/util/concurrent/BlockingQueue.java @@ -303,8 +303,10 @@ public interface BlockingQueue extends Queue { * @param o element to be removed from this queue, if present * @return true if this queue changed as a result of the call * @throws ClassCastException if the class of the specified element - * is incompatible with this queue (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this queue + * (optional) + * @throws NullPointerException if the specified element is null + * (optional) */ boolean remove(Object o); @@ -316,8 +318,10 @@ public interface BlockingQueue extends Queue { * @param o object to be checked for containment in this queue * @return true if this queue contains the specified element * @throws ClassCastException if the class of the specified element - * is incompatible with this queue (optional) - * @throws NullPointerException if the specified element is null (optional) + * is incompatible with this queue + * (optional) + * @throws NullPointerException if the specified element is null + * (optional) */ public boolean contains(Object o); diff --git a/src/share/classes/java/util/concurrent/ConcurrentMap.java b/src/share/classes/java/util/concurrent/ConcurrentMap.java index 25cf98ff0f1e5c6e8020ef8bd09969d27239a11f..4434c0563b4334e97411e79eef384e76606e0308 100644 --- a/src/share/classes/java/util/concurrent/ConcurrentMap.java +++ b/src/share/classes/java/util/concurrent/ConcurrentMap.java @@ -103,9 +103,11 @@ public interface ConcurrentMap extends Map { * @throws UnsupportedOperationException if the remove operation * is not supported by this map * @throws ClassCastException if the key or value is of an inappropriate - * type for this map (optional) + * type for this map + * (optional) * @throws NullPointerException if the specified key or value is null, - * and this map does not permit null keys or values (optional) + * and this map does not permit null keys or values + * (optional) */ boolean remove(Object key, Object value); diff --git a/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java index 2774e61136a7a3927e1c96ea6daaa21f8aa54b9c..785ec6fcede229d4c1f7e32c1a6a8ac6b13eee14 100644 --- a/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java +++ b/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java @@ -631,9 +631,11 @@ public class CopyOnWriteArrayList * @param c collection containing elements to be removed from this list * @return true if this list changed as a result of the call * @throws ClassCastException if the class of an element of this list - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this list contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see #remove(Object) */ @@ -671,9 +673,11 @@ public class CopyOnWriteArrayList * @param c collection containing elements to be retained in this list * @return true if this list changed as a result of the call * @throws ClassCastException if the class of an element of this list - * is incompatible with the specified collection (optional) + * is incompatible with the specified collection + * (optional) * @throws NullPointerException if this list contains a null element and the - * specified collection does not permit null elements (optional), + * specified collection does not permit null elements + * (optional), * or if the specified collection is null * @see #remove(Object) */ diff --git a/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java b/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..56ed40eed3e391fd8897d1d5c360076bce1d805a --- /dev/null +++ b/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011, 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 javax.security.auth.kerberos; + +import sun.misc.JavaxSecurityAuthKerberosAccess; +import sun.security.krb5.EncryptionKey; +import sun.security.krb5.PrincipalName; + +class JavaxSecurityAuthKerberosAccessImpl + implements JavaxSecurityAuthKerberosAccess { + public EncryptionKey[] keyTabGetEncryptionKeys( + KeyTab ktab, PrincipalName principal) { + return ktab.getEncryptionKeys(principal); + } +} diff --git a/src/share/classes/javax/security/auth/kerberos/KerberosKey.java b/src/share/classes/javax/security/auth/kerberos/KerberosKey.java index 7eb7118a5bcc7cb889e505f0b989a45a4bb33099..ba530f9583c849a69d08527206814f648de91561 100644 --- a/src/share/classes/javax/security/auth/kerberos/KerberosKey.java +++ b/src/share/classes/javax/security/auth/kerberos/KerberosKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2011, 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 @@ -35,14 +35,16 @@ import javax.security.auth.DestroyFailedException; * principal.

* * All Kerberos JAAS login modules that obtain a principal's password and - * generate the secret key from it should use this class. Where available, - * the login module might even read this secret key directly from a - * Kerberos "keytab". Sometimes, such as when authenticating a server in + * generate the secret key from it should use this class. + * Sometimes, such as when authenticating a server in * the absence of user-to-user authentication, the login module will store * an instance of this class in the private credential set of a * {@link javax.security.auth.Subject Subject} during the commit phase of the * authentication process.

* + * A Kerberos service using a keytab to read secret keys should use + * the {@link KeyTab} class, where latest keys can be read when needed.

+ * * It might be necessary for the application to be granted a * {@link javax.security.auth.PrivateCredentialPermission * PrivateCredentialPermission} if it needs to access the KerberosKey diff --git a/src/share/classes/javax/security/auth/kerberos/KeyTab.java b/src/share/classes/javax/security/auth/kerberos/KeyTab.java new file mode 100644 index 0000000000000000000000000000000000000000..c2f8dd79478baccae14b76c19a02033ddb0a9b7c --- /dev/null +++ b/src/share/classes/javax/security/auth/kerberos/KeyTab.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2011, 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 javax.security.auth.kerberos; + +import java.io.File; +import java.util.Objects; +import sun.misc.SharedSecrets; +import sun.security.krb5.EncryptionKey; +import sun.security.krb5.PrincipalName; +import sun.security.krb5.RealmException; + +/** + * This class encapsulates a keytab file. + *

+ * A Kerberos JAAS login module that obtains long term secret keys from a + * keytab file should use this class. The login module will store + * an instance of this class in the private credential set of a + * {@link javax.security.auth.Subject Subject} during the commit phase of the + * authentication process. + *

+ * It might be necessary for the application to be granted a + * {@link javax.security.auth.PrivateCredentialPermission + * PrivateCredentialPermission} if it needs to access the KeyTab + * instance from a Subject. This permission is not needed when the + * application depends on the default JGSS Kerberos mechanism to access the + * KeyTab. In that case, however, the application will need an appropriate + * {@link javax.security.auth.kerberos.ServicePermission ServicePermission}. + *

+ * The keytab file format is described at + * + * http://www.ioplex.com/utilities/keytab.txt. + * + * @since 1.7 + */ +public final class KeyTab { + + /* + * Impl notes: + * + * This class is only a name, a permanent link to the keytab source + * (can be missing). Itself has no content. In order to read content, + * take a snapshot and read from it. + * + * The snapshot is of type sun.security.krb5.internal.ktab.KeyTab, which + * contains the content of the keytab file when the snapshot is taken. + * Itself has no refresh function and mostly an immutable class (except + * for the create/add/save methods only used by the ktab command). + */ + + // Source, null if using the default one. Note that the default name + // is maintained in snapshot, this field is never "resolved". + private final File file; + + // Set up JavaxSecurityAuthKerberosAccess in SharedSecrets + static { + SharedSecrets.setJavaxSecurityAuthKerberosAccess( + new JavaxSecurityAuthKerberosAccessImpl()); + } + + private KeyTab(File file) { + this.file = file; + } + + /** + * Returns a {@code KeyTab} instance from a {@code File} object. + *

+ * The result of this method is never null. This method only associates + * the returned {@code KeyTab} object with the file and does not read it. + * @param file the keytab {@code File} object, must not be null + * @return the keytab instance + * @throws NullPointerException if the {@code file} argument is null + */ + public static KeyTab getInstance(File file) { + if (file == null) { + throw new NullPointerException("file must be non null"); + } + return new KeyTab(file); + } + + /** + * Returns the default {@code KeyTab} instance. + *

+ * The result of this method is never null. This method only associates + * the returned {@code KeyTab} object with the default keytab file and + * does not read it. + * @return the default keytab instance. + */ + public static KeyTab getInstance() { + return new KeyTab(null); + } + + //Takes a snapshot of the keytab content + private sun.security.krb5.internal.ktab.KeyTab takeSnapshot() { + return sun.security.krb5.internal.ktab.KeyTab.getInstance(file); + } + + /** + * Returns fresh keys for the given Kerberos principal. + *

+ * Implementation of this method should make sure the returned keys match + * the latest content of the keytab file. The result is a newly created + * copy that can be modified by the caller without modifying the keytab + * object. The caller should {@link KerberosKey#destroy() destroy} the + * result keys after they are used. + *

+ * Please note that the keytab file can be created after the + * {@code KeyTab} object is instantiated and its content may change over + * time. Therefore, an application should call this method only when it + * needs to use the keys. Any previous result from an earlier invocation + * could potentially be expired. + *

+ * If there is any error (say, I/O error or format error) + * during the reading process of the KeyTab file, a saved result should be + * returned. If there is no saved result (say, this is the first time this + * method is called, or, all previous read attempts failed), an empty array + * should be returned. This can make sure the result is not drastically + * changed during the (probably slow) update of the keytab file. + *

+ * Each time this method is called and the reading of the file succeeds + * with no exception (say, I/O error or file format error), + * the result should be saved for {@code principal}. The implementation can + * also save keys for other principals having keys in the same keytab object + * if convenient. + *

+ * Any unsupported key read from the keytab is ignored and not included + * in the result. + * + * @param principal the Kerberos principal, must not be null. + * @return the keys (never null, may be empty) + * @throws NullPointerException if the {@code principal} + * argument is null + * @throws SecurityException if a security manager exists and the read + * access to the keytab file is not permitted + */ + public KerberosKey[] getKeys(KerberosPrincipal principal) { + try { + EncryptionKey[] keys = takeSnapshot().readServiceKeys( + new PrincipalName(principal.getName())); + KerberosKey[] kks = new KerberosKey[keys.length]; + for (int i=0; i + * The caller can use the result to determine if it should fallback to + * another mechanism to read the keys. + * @return true if the keytab file exists; false otherwise. + * @throws SecurityException if a security manager exists and the read + * access to the keytab file is not permitted + */ + public boolean exists() { + return !takeSnapshot().isMissing(); + } + + public String toString() { + return file == null ? "Default keytab" : file.toString(); + } + + /** + * Returns a hashcode for this KeyTab. + * + * @return a hashCode() for the KeyTab + */ + public int hashCode() { + return Objects.hash(file); + } + + /** + * Compares the specified Object with this KeyTab for equality. + * Returns true if the given object is also a + * KeyTab and the two + * KeyTab instances are equivalent. + * + * @param other the Object to compare to + * @return true if the specified object is equal to this KeyTab + */ + public boolean equals(Object other) { + if (other == this) + return true; + + if (! (other instanceof KeyTab)) { + return false; + } + + KeyTab otherKtab = (KeyTab) other; + return Objects.equals(otherKtab.file, file); + } +} diff --git a/src/share/classes/sun/misc/JavaxSecurityAuthKerberosAccess.java b/src/share/classes/sun/misc/JavaxSecurityAuthKerberosAccess.java new file mode 100644 index 0000000000000000000000000000000000000000..9c8f9f2d3d20a8e66f27db2e8a61bac0606010ca --- /dev/null +++ b/src/share/classes/sun/misc/JavaxSecurityAuthKerberosAccess.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011, 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.misc; + +import javax.security.auth.kerberos.KeyTab; +import sun.security.krb5.EncryptionKey; +import sun.security.krb5.PrincipalName; + +/** + * An unsafe tunnel to get non-public access to classes in the + * javax.security.auth.kerberos package. + */ +public interface JavaxSecurityAuthKerberosAccess { + /** + * Returns keys for a principal in a keytab. + * @return the keys, never null, can be empty. + */ + public EncryptionKey[] keyTabGetEncryptionKeys( + KeyTab ktab, PrincipalName principal); +} diff --git a/src/share/classes/sun/misc/SharedSecrets.java b/src/share/classes/sun/misc/SharedSecrets.java index 0bd39b4a5d42f0b12478143bad17dce21db3ba11..969da9a744fe8dfadf558e8e220f1856fc5c52f5 100644 --- a/src/share/classes/sun/misc/SharedSecrets.java +++ b/src/share/classes/sun/misc/SharedSecrets.java @@ -29,6 +29,7 @@ import java.util.jar.JarFile; import java.io.Console; import java.io.FileDescriptor; import java.security.ProtectionDomain; +import javax.security.auth.kerberos.KeyTab; import java.security.AccessController; @@ -51,6 +52,7 @@ public class SharedSecrets { private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess; private static JavaSecurityProtectionDomainAccess javaSecurityProtectionDomainAccess; private static JavaSecurityAccess javaSecurityAccess; + private static JavaxSecurityAuthKerberosAccess javaxSecurityAuthKerberosAccess; public static JavaUtilJarAccess javaUtilJarAccess() { if (javaUtilJarAccess == null) { @@ -139,4 +141,16 @@ public class SharedSecrets { } return javaSecurityAccess; } + + public static void setJavaxSecurityAuthKerberosAccess + (JavaxSecurityAuthKerberosAccess jsaka) { + javaxSecurityAuthKerberosAccess = jsaka; + } + + public static JavaxSecurityAuthKerberosAccess + getJavaxSecurityAuthKerberosAccess() { + if (javaxSecurityAuthKerberosAccess == null) + unsafe.ensureClassInitialized(KeyTab.class); + return javaxSecurityAuthKerberosAccess; + } } diff --git a/src/share/classes/sun/rmi/log/ReliableLog.java b/src/share/classes/sun/rmi/log/ReliableLog.java index 30ea3f1d4647b4e5cc067a38f47cc7c1fca0d190..fe512c2d87c1273c6e851365e65c675cc19b52fa 100644 --- a/src/share/classes/sun/rmi/log/ReliableLog.java +++ b/src/share/classes/sun/rmi/log/ReliableLog.java @@ -380,9 +380,7 @@ public class ReliableLog { } catch (IOException e) { throw e; } catch (Exception e) { - throw new IOException("snapshot failed with exception of type: " + - e.getClass().getName() + - ", message was: " + e.getMessage()); + throw new IOException("snapshot failed", e); } lastSnapshot = System.currentTimeMillis(); } finally { diff --git a/src/share/classes/sun/rmi/server/Activation.java b/src/share/classes/sun/rmi/server/Activation.java index 119910ed5ccb0760a8629e2547f32d5d1aab6e17..919edb7fb657ab45cfb99212f015eceb9e685a7f 100644 --- a/src/share/classes/sun/rmi/server/Activation.java +++ b/src/share/classes/sun/rmi/server/Activation.java @@ -30,6 +30,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.ObjectInputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; @@ -98,6 +99,7 @@ import java.util.MissingResourceException; import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import sun.rmi.log.LogHandler; import sun.rmi.log.ReliableLog; import sun.rmi.registry.RegistryImpl; @@ -147,10 +149,10 @@ public class Activation implements Serializable { /** maps activation id to its respective group id */ private Map idTable = - new HashMap(); + new ConcurrentHashMap<>(); /** maps group id to its GroupEntry groups */ private Map groupTable = - new HashMap(); + new ConcurrentHashMap<>(); private byte majorVersion = MAJOR_VERSION; private byte minorVersion = MINOR_VERSION; @@ -236,9 +238,11 @@ public class Activation implements Serializable { groupSemaphore = getInt("sun.rmi.activation.groupThrottle", 3); groupCounter = 0; Runtime.getRuntime().addShutdownHook(shutdownHook); + + // Use array size of 0, since the value from calling size() + // may be out of date by the time toArray() is called. ActivationGroupID[] gids = - groupTable.keySet().toArray( - new ActivationGroupID[groupTable.size()]); + groupTable.keySet().toArray(new ActivationGroupID[0]); synchronized (startupLock = new Object()) { // all the remote methods briefly synchronize on startupLock @@ -274,6 +278,23 @@ public class Activation implements Serializable { } } + /** + * Previous versions used HashMap instead of ConcurrentHashMap. + * Replace any HashMaps found during deserialization with + * ConcurrentHashMaps. + */ + private void readObject(ObjectInputStream ois) + throws IOException, ClassNotFoundException + { + ois.defaultReadObject(); + if (! (groupTable instanceof ConcurrentHashMap)) { + groupTable = new ConcurrentHashMap<>(groupTable); + } + if (! (idTable instanceof ConcurrentHashMap)) { + idTable = new ConcurrentHashMap<>(idTable); + } + } + private static class SystemRegistryImpl extends RegistryImpl { private static final String NAME = ActivationSystem.class.getName(); @@ -488,9 +509,7 @@ public class Activation implements Serializable { ActivationGroupID id = new ActivationGroupID(systemStub); GroupEntry entry = new GroupEntry(id, desc); // table insertion must take place before log update - synchronized (groupTable) { - groupTable.put(id, entry); - } + groupTable.put(id, entry); addLogRecord(new LogRegisterGroup(id, desc)); return id; } @@ -515,11 +534,7 @@ public class Activation implements Serializable { // remove entry before unregister so state is updated before // logged - synchronized (groupTable) { - GroupEntry entry = getGroupEntry(id); - groupTable.remove(id); - entry.unregisterGroup(true); - } + removeGroupEntry(id).unregisterGroup(true); } public ActivationDesc setActivationDesc(ActivationID id, @@ -637,12 +652,7 @@ public class Activation implements Serializable { unexport(system); // destroy all child processes (groups) - GroupEntry[] groupEntries; - synchronized (groupTable) { - groupEntries = groupTable.values(). - toArray(new GroupEntry[groupTable.size()]); - } - for (GroupEntry groupEntry : groupEntries) { + for (GroupEntry groupEntry : groupTable.values()) { groupEntry.shutdown(); } @@ -693,10 +703,8 @@ public class Activation implements Serializable { } // destroy all child processes (groups) quickly - synchronized (groupTable) { - for (GroupEntry groupEntry : groupTable.values()) { - groupEntry.shutdownFast(); - } + for (GroupEntry groupEntry : groupTable.values()) { + groupEntry.shutdownFast(); } } } @@ -708,33 +716,54 @@ public class Activation implements Serializable { private ActivationGroupID getGroupID(ActivationID id) throws UnknownObjectException { - synchronized (idTable) { - ActivationGroupID groupID = idTable.get(id); - if (groupID != null) { - return groupID; - } + ActivationGroupID groupID = idTable.get(id); + if (groupID != null) { + return groupID; } throw new UnknownObjectException("unknown object: " + id); } /** - * Returns the group entry for the group id. Throws - * UnknownGroupException if the group is not registered. + * Returns the group entry for the group id, optionally removing it. + * Throws UnknownGroupException if the group is not registered. */ - private GroupEntry getGroupEntry(ActivationGroupID id) + private GroupEntry getGroupEntry(ActivationGroupID id, boolean rm) throws UnknownGroupException { if (id.getClass() == ActivationGroupID.class) { - synchronized (groupTable) { - GroupEntry entry = groupTable.get(id); - if (entry != null && !entry.removed) { - return entry; - } + GroupEntry entry; + if (rm) { + entry = groupTable.remove(id); + } else { + entry = groupTable.get(id); + } + if (entry != null && !entry.removed) { + return entry; } } throw new UnknownGroupException("group unknown"); } + /** + * Returns the group entry for the group id. Throws + * UnknownGroupException if the group is not registered. + */ + private GroupEntry getGroupEntry(ActivationGroupID id) + throws UnknownGroupException + { + return getGroupEntry(id, false); + } + + /** + * Removes and returns the group entry for the group id. Throws + * UnknownGroupException if the group is not registered. + */ + private GroupEntry removeGroupEntry(ActivationGroupID id) + throws UnknownGroupException + { + return getGroupEntry(id, true); + } + /** * Returns the group entry for the object's id. Throws * UnknownObjectException if the object is not registered or the @@ -744,11 +773,9 @@ public class Activation implements Serializable { throws UnknownObjectException { ActivationGroupID gid = getGroupID(id); - synchronized (groupTable) { - GroupEntry entry = groupTable.get(gid); - if (entry != null) { - return entry; - } + GroupEntry entry = groupTable.get(gid); + if (entry != null && !entry.removed) { + return entry; } throw new UnknownObjectException("object's group removed"); } @@ -882,9 +909,7 @@ public class Activation implements Serializable { } // table insertion must take place before log update - synchronized (idTable) { - idTable.put(id, groupID); - } + idTable.put(id, groupID); if (addRecord) { addLogRecord(new LogRegisterObject(id, desc)); @@ -901,10 +926,8 @@ public class Activation implements Serializable { restartSet.remove(id); } - // table insertion must take place before log update - synchronized (idTable) { - idTable.remove(id); - } + // table removal must take place before log update + idTable.remove(id); if (addRecord) { addLogRecord(new LogUnregisterObject(id)); } @@ -919,9 +942,7 @@ public class Activation implements Serializable { objects.entrySet()) { ActivationID id = entry.getKey(); - synchronized (idTable) { - idTable.remove(id); - } + idTable.remove(id); ObjectEntry objEntry = entry.getValue(); objEntry.removed = true; } diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java b/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java index 7a7156d9b1cad0b1b093e8c1ed3cb9f9c65d8e38..1df500bc5242a8bd217bb818a499c4659b1efde6 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5AcceptCredential.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2011, 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 @@ -29,7 +29,6 @@ import org.ietf.jgss.*; import sun.security.jgss.GSSCaller; import sun.security.jgss.spi.*; import sun.security.krb5.*; -import javax.security.auth.kerberos.*; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.security.AccessController; @@ -43,40 +42,23 @@ import javax.security.auth.DestroyFailedException; * @since 1.4 */ public class Krb5AcceptCredential - extends KerberosKey implements Krb5CredElement { private static final long serialVersionUID = 7714332137352567952L; private Krb5NameElement name; - /** - * We cache an EncryptionKey representation of this key because many - * Krb5 operation require a key in that form. At some point we might do - * away with EncryptionKey altogether and use the base class - * KerberosKey everywhere. - */ - private EncryptionKey[] krb5EncryptionKeys; + private Krb5Util.ServiceCreds screds; - private Krb5AcceptCredential(Krb5NameElement name, KerberosKey[] keys) { + private Krb5AcceptCredential(Krb5NameElement name, Krb5Util.ServiceCreds creds) { /* * Initialize this instance with the data from the acquired * KerberosKey. This class needs to be a KerberosKey too * hence we can't just store a reference. */ - super(keys[0].getPrincipal(), - keys[0].getEncoded(), - keys[0].getKeyType(), - keys[0].getVersionNumber()); this.name = name; - // Cache this for later use by the sun.security.krb5 package. - krb5EncryptionKeys = new EncryptionKey[keys.length]; - for (int i = 0; i < keys.length; i++) { - krb5EncryptionKeys[i] = new EncryptionKey(keys[i].getEncoded(), - keys[i].getKeyType(), - new Integer(keys[i].getVersionNumber())); - } + this.screds = creds; } static Krb5AcceptCredential getInstance(final GSSCaller caller, Krb5NameElement name) @@ -86,12 +68,12 @@ public class Krb5AcceptCredential name.getKrb5PrincipalName().getName()); final AccessControlContext acc = AccessController.getContext(); - KerberosKey[] keys; + Krb5Util.ServiceCreds creds = null; try { - keys = AccessController.doPrivileged( - new PrivilegedExceptionAction() { - public KerberosKey[] run() throws Exception { - return Krb5Util.getKeys( + creds = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Krb5Util.ServiceCreds run() throws Exception { + return Krb5Util.getServiceCreds( caller == GSSCaller.CALLER_UNKNOWN ? GSSCaller.CALLER_ACCEPT: caller, serverPrinc, acc); }}); @@ -103,17 +85,17 @@ public class Krb5AcceptCredential throw ge; } - if (keys == null || keys.length == 0) + if (creds == null) throw new GSSException(GSSException.NO_CRED, -1, - "Failed to find any Kerberos Key"); + "Failed to find any Kerberos credentails"); if (name == null) { - String fullName = keys[0].getPrincipal().getName(); + String fullName = creds.getName(); name = Krb5NameElement.getInstance(fullName, Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL); } - return new Krb5AcceptCredential(name, keys); + return new Krb5AcceptCredential(name, creds); } /** @@ -171,7 +153,7 @@ public class Krb5AcceptCredential } EncryptionKey[] getKrb5EncryptionKeys() { - return krb5EncryptionKeys; + return screds.getEKeys(); } /** @@ -193,13 +175,6 @@ public class Krb5AcceptCredential * destroy in the base class. */ public void destroy() throws DestroyFailedException { - if (krb5EncryptionKeys != null) { - for (int i = 0; i < krb5EncryptionKeys.length; i++) { - krb5EncryptionKeys[i].destroy(); - } - krb5EncryptionKeys = null; - } - - super.destroy(); + screds.destroy(); } } diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java index d5b172f92497ecd41b6b2faffb50b64fc0da1356..22552a71c3fff910153721985e9b98ffe667da5a 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2011, 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 @@ -28,6 +28,7 @@ package sun.security.jgss.krb5; import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.kerberos.KerberosKey; import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.KeyTab; import javax.security.auth.Subject; import javax.security.auth.login.LoginException; import java.security.AccessControlContext; @@ -38,7 +39,13 @@ import sun.security.krb5.Credentials; import sun.security.krb5.EncryptionKey; import sun.security.krb5.KrbException; import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Objects; +import java.util.Set; +import sun.misc.SharedSecrets; +import sun.security.krb5.PrincipalName; /** * Utilities for obtaining and converting Kerberos tickets. * @@ -75,7 +82,7 @@ public class Krb5Util { // 1. Try to find service ticket in acc subject Subject accSubj = Subject.getSubject(acc); - KerberosTicket ticket = (KerberosTicket) SubjectComber.find(accSubj, + KerberosTicket ticket = SubjectComber.find(accSubj, serverPrincipal, clientPrincipal, KerberosTicket.class); if (ticket != null) { @@ -87,7 +94,7 @@ public class Krb5Util { // 2. Try to get ticket from login try { loginSubj = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); - ticket = (KerberosTicket) SubjectComber.find(loginSubj, + ticket = SubjectComber.find(loginSubj, serverPrincipal, clientPrincipal, KerberosTicket.class); if (ticket != null) { return ticket; // found it @@ -102,13 +109,13 @@ public class Krb5Util { // Try to get TGT to acquire service ticket // 3. Try to get TGT from acc subject - KerberosTicket tgt = (KerberosTicket) SubjectComber.find(accSubj, + KerberosTicket tgt = SubjectComber.find(accSubj, tgsPrincipal, clientPrincipal, KerberosTicket.class); boolean fromAcc; if (tgt == null && loginSubj != null) { // 4. Try to get TGT from login subject - tgt = (KerberosTicket) SubjectComber.find(loginSubj, + tgt = SubjectComber.find(loginSubj, tgsPrincipal, clientPrincipal, KerberosTicket.class); fromAcc = false; } else { @@ -145,14 +152,14 @@ public class Krb5Util { // Try to get ticket from acc's Subject Subject accSubj = Subject.getSubject(acc); - KerberosTicket ticket = (KerberosTicket) + KerberosTicket ticket = SubjectComber.find(accSubj, serverPrincipal, clientPrincipal, KerberosTicket.class); // Try to get ticket from Subject obtained from GSSUtil if (ticket == null && !GSSUtil.useSubjectCredsOnly(caller)) { Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); - ticket = (KerberosTicket) SubjectComber.find(subject, + ticket = SubjectComber.find(subject, serverPrincipal, clientPrincipal, KerberosTicket.class); } return ticket; @@ -182,37 +189,152 @@ public class Krb5Util { return subject; } + // A special KerberosKey, used as keys read from a KeyTab object. + // Each time new keys are read from KeyTab objects in the private + // credentials set, old ones are removed and new ones added. + public static class KeysFromKeyTab extends KerberosKey { + public KeysFromKeyTab(KerberosKey key) { + super(key.getPrincipal(), key.getEncoded(), + key.getKeyType(), key.getVersionNumber()); + } + } + /** - * Retrieves the keys for the specified server principal from - * the Subject in the specified AccessControlContext. - * If the ticket can not be found in the Subject, and if - * useSubjectCredsOnly is false, then obtain keys from - * a LoginContext. + * Credentials of a service, the private secret to authenticate its + * identity, which can be: + * 1. Some KerberosKeys (generated from password) + * 2. A KeyTab (for a typical service) + * 3. A TGT (for a user2user service. Not supported yet) * - * NOTE: This method is used by JSSE Kerberos Cipher Suites + * Note that some creds can coexist. For example, a user2user service + * can use its keytab (or keys) if the client can successfully obtain a + * normal service ticket, otherwise, it can uses the TGT (actually, the + * session key of the TGT) if the client can only acquire a service ticket + * of ENC-TKT-IN-SKEY style. */ - public static KerberosKey[] getKeys(GSSCaller caller, + public static class ServiceCreds { + private KerberosPrincipal kp; + private List ktabs; + private List kk; + private Subject subj; + //private KerberosTicket tgt; // user2user, not supported yet + + private static ServiceCreds getInstance( + Subject subj, String serverPrincipal) { + + ServiceCreds sc = new ServiceCreds(); + sc.subj = subj; + + for (KerberosPrincipal p: subj.getPrincipals(KerberosPrincipal.class)) { + if (serverPrincipal == null || + p.getName().equals(serverPrincipal)) { + sc.kp = p; + serverPrincipal = p.getName(); + break; + } + } + if (sc.kp == null) { + // Compatibility with old behavior: even when there is no + // KerberosPrincipal, we can find one from KerberosKeys + List keys = SubjectComber.findMany( + subj, null, null, KerberosKey.class); + if (!keys.isEmpty()) { + sc.kp = keys.get(0).getPrincipal(); + serverPrincipal = sc.kp.getName(); + if (DEBUG) { + System.out.println(">>> ServiceCreds: no kp?" + + " find one from kk: " + serverPrincipal); + } + } else { + return null; + } + } + sc.ktabs = SubjectComber.findMany( + subj, null, null, KeyTab.class); + sc.kk = SubjectComber.findMany( + subj, serverPrincipal, null, KerberosKey.class); + if (sc.ktabs.isEmpty() && sc.kk.isEmpty()) { + return null; + } + return sc; + } + + public String getName() { + return kp.getName(); + } + + public KerberosKey[] getKKeys() { + if (ktabs.isEmpty()) { + return kk.toArray(new KerberosKey[kk.size()]); + } else { + List keys = new ArrayList<>(); + for (KeyTab ktab: ktabs) { + for (KerberosKey k: ktab.getKeys(kp)) { + keys.add(k); + } + } + // Compatibility: also add keys to privCredSet. Remove old + // ones first, only remove those from keytab. + if (!subj.isReadOnly()) { + Set pcs = subj.getPrivateCredentials(); + synchronized (pcs) { + Iterator iterator = pcs.iterator(); + while (iterator.hasNext()) { + Object obj = iterator.next(); + if (obj instanceof KeysFromKeyTab) { + KerberosKey key = (KerberosKey)obj; + if (Objects.equals(key.getPrincipal(), kp)) { + iterator.remove(); + } + } + } + } + for (KerberosKey key: keys) { + subj.getPrivateCredentials().add(new KeysFromKeyTab(key)); + } + } + return keys.toArray(new KerberosKey[keys.size()]); + } + } + + public EncryptionKey[] getEKeys() { + KerberosKey[] kkeys = getKKeys(); + EncryptionKey[] ekeys = new EncryptionKey[kkeys.length]; + for (int i=0; i kkeys = (List)SubjectComber.findMany( - accSubj, serverPrincipal, null, KerberosKey.class); - - if (kkeys == null && !GSSUtil.useSubjectCredsOnly(caller)) { - Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); - kkeys = (List) SubjectComber.findMany(subject, - serverPrincipal, null, KerberosKey.class); + ServiceCreds sc = null; + if (accSubj != null) { + sc = ServiceCreds.getInstance(accSubj, serverPrincipal); } - - int len; - if (kkeys != null && (len = kkeys.size()) > 0) { - KerberosKey[] keys = new KerberosKey[len]; - kkeys.toArray(keys); - return keys; - } else { - return null; + if (sc == null && !GSSUtil.useSubjectCredsOnly(caller)) { + Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); + sc = ServiceCreds.getInstance(subject, serverPrincipal); } + return sc; } public static KerberosTicket credsToTicket(Credentials serviceCreds) { @@ -247,4 +369,17 @@ public class Krb5Util { kerbTicket.getRenewTill(), kerbTicket.getClientAddresses()); } + + /** + * A helper method to get EncryptionKeys from a javax..KeyTab + * @param ktab the javax..KeyTab class + * @param cname the PrincipalName + * @return the EKeys, never null, might be empty + */ + public static EncryptionKey[] keysFromJavaxKeyTab( + KeyTab ktab, PrincipalName cname) { + return SharedSecrets.getJavaxSecurityAuthKerberosAccess(). + keyTabGetEncryptionKeys(ktab, cname); + } + } diff --git a/src/share/classes/sun/security/jgss/krb5/SubjectComber.java b/src/share/classes/sun/security/jgss/krb5/SubjectComber.java index e33867dedc87988dc9a5f236d62d3e46e4316c84..c7269cb874f6919b193aaa636a984dd6ba921ca2 100644 --- a/src/share/classes/sun/security/jgss/krb5/SubjectComber.java +++ b/src/share/classes/sun/security/jgss/krb5/SubjectComber.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2011, 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 @@ -33,10 +33,11 @@ import java.util.Iterator; import java.util.ArrayList; import java.util.List; import java.util.Set; +import javax.security.auth.kerberos.KeyTab; /** - * This utility looks through the current Subject and retrieves a ticket or key - * for the desired client/server principals. + * This utility looks through the current Subject and retrieves private + * credentials for the desired client/server principals. * * @author Ram Marti * @since 1.4.2 @@ -52,58 +53,70 @@ class SubjectComber { private SubjectComber() { // Cannot create one of these } - static Object find(Subject subject, String serverPrincipal, - String clientPrincipal, Class credClass) { + static T find(Subject subject, String serverPrincipal, + String clientPrincipal, Class credClass) { - return findAux(subject, serverPrincipal, clientPrincipal, credClass, + return (T)findAux(subject, serverPrincipal, clientPrincipal, credClass, true); } - static Object findMany(Subject subject, String serverPrincipal, - String clientPrincipal, Class credClass) { + static List findMany(Subject subject, String serverPrincipal, + String clientPrincipal, Class credClass) { - return findAux(subject, serverPrincipal, clientPrincipal, credClass, + return (List)findAux(subject, serverPrincipal, clientPrincipal, credClass, false); } /** - * Find the ticket or key for the specified client/server principals + * Find private credentials for the specified client/server principals * in the subject. Returns null if the subject is null. * - * @return the ticket or key + * @return the private credentials */ - private static Object findAux(Subject subject, String serverPrincipal, - String clientPrincipal, Class credClass, boolean oneOnly) { + private static Object findAux(Subject subject, String serverPrincipal, + String clientPrincipal, Class credClass, boolean oneOnly) { if (subject == null) { return null; } else { - List answer = (oneOnly ? null : new ArrayList()); + List answer = (oneOnly ? null : new ArrayList()); - if (credClass == KerberosKey.class) { - // We are looking for a KerberosKey credentials for the - // serverPrincipal - Iterator iterator = - subject.getPrivateCredentials(KerberosKey.class).iterator(); + if (credClass == KeyTab.class) { // Principal un-related + // We are looking for credentials unrelated to serverPrincipal + Iterator iterator = + subject.getPrivateCredentials(credClass).iterator(); while (iterator.hasNext()) { - KerberosKey key = iterator.next(); - if (serverPrincipal == null || - serverPrincipal.equals(key.getPrincipal().getName())) { + T t = iterator.next(); + if (DEBUG) { + System.out.println("Found " + credClass.getSimpleName()); + } + if (oneOnly) { + return t; + } else { + answer.add(t); + } + } + } else if (credClass == KerberosKey.class) { + // We are looking for credentials for the serverPrincipal + Iterator iterator = + subject.getPrivateCredentials(credClass).iterator(); + while (iterator.hasNext()) { + T t = iterator.next(); + String name = ((KerberosKey)t).getPrincipal().getName(); + if (serverPrincipal == null || serverPrincipal.equals(name)) { if (DEBUG) { - System.out.println("Found key for " - + key.getPrincipal() + "(" + - key.getKeyType() + ")"); + System.out.println("Found " + + credClass.getSimpleName() + " for " + name); } if (oneOnly) { - return key; + return t; } else { if (serverPrincipal == null) { // Record name so that keys returned will all // belong to the same principal - serverPrincipal = - key.getPrincipal().getName(); + serverPrincipal = name; } - answer.add(key); + answer.add(t); } } } @@ -167,7 +180,7 @@ class SubjectComber { serverPrincipal = ticket.getServer().getName(); } - answer.add(ticket); + answer.add((T)ticket); } } } diff --git a/src/share/classes/sun/security/krb5/Config.java b/src/share/classes/sun/security/krb5/Config.java index 01d97eaf599a7e2e797b16da39480f4f7215311b..2ec4e12bd7138506b0771f9356c28336787ae5f0 100644 --- a/src/share/classes/sun/security/krb5/Config.java +++ b/src/share/classes/sun/security/krb5/Config.java @@ -110,7 +110,6 @@ public class Config { public static synchronized void refresh() throws KrbException { singleton = new Config(); - KeyTab.refresh(); KdcComm.initStatic(); } diff --git a/src/share/classes/sun/security/krb5/EncryptionKey.java b/src/share/classes/sun/security/krb5/EncryptionKey.java index 8ce51c0c659f7fbffc63443d5b680226adb81a74..1de032945ff7e76e0cfa02dcdcd579eb576fc22f 100644 --- a/src/share/classes/sun/security/krb5/EncryptionKey.java +++ b/src/share/classes/sun/security/krb5/EncryptionKey.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2011, 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 @@ -138,8 +138,7 @@ public class EncryptionKey * @returns an array of secret keys or null if none were found. */ public static EncryptionKey[] acquireSecretKeys(PrincipalName princ, - String keytab) - throws KrbException, IOException { + String keytab) { if (princ == null) throw new IllegalArgumentException( @@ -148,11 +147,6 @@ public class EncryptionKey // KeyTab getInstance(keytab) will call KeyTab.getInstance() // if keytab is null KeyTab ktab = KeyTab.getInstance(keytab); - - if (ktab == null) { - return null; - } - return ktab.readServiceKeys(princ); } diff --git a/src/share/classes/sun/security/krb5/KrbAsRep.java b/src/share/classes/sun/security/krb5/KrbAsRep.java index 16c7d87895ff7ed12be080011a3befb0370b2f75..29a7a5b4ccd00b43d50561690f2f367c6bbcae27 100644 --- a/src/share/classes/sun/security/krb5/KrbAsRep.java +++ b/src/share/classes/sun/security/krb5/KrbAsRep.java @@ -37,6 +37,8 @@ import sun.security.krb5.internal.crypto.EType; import sun.security.util.*; import java.io.IOException; import java.util.Objects; +import javax.security.auth.kerberos.KeyTab; +import sun.security.jgss.krb5.Krb5Util; /** * This class encapsulates a AS-REP message that the KDC sends to the @@ -90,29 +92,32 @@ class KrbAsRep extends KrbKdcRep { } /** - * Called by KrbAsReqBuilder to resolve a AS-REP message using keys. - * @param keys user provided keys, not null + * Called by KrbAsReqBuilder to resolve a AS-REP message using a keytab. + * @param ktab the keytab, not null * @param asReq the original AS-REQ sent, used to validate AS-REP + * @param cname the user principal name, used to locate keys in ktab */ - void decryptUsingKeys(EncryptionKey[] keys, KrbAsReq asReq) + void decryptUsingKeyTab(KeyTab ktab, KrbAsReq asReq, PrincipalName cname) throws KrbException, Asn1Exception, IOException { EncryptionKey dkey = null; int encPartKeyType = rep.encPart.getEType(); Integer encPartKvno = rep.encPart.kvno; - try { - dkey = EncryptionKey.findKey(encPartKeyType, encPartKvno, keys); - } catch (KrbException ke) { - if (ke.returnCode() == Krb5.KRB_AP_ERR_BADKEYVER) { - // Fallback to no kvno. In some cases, keytab is generated - // not by sysadmin but Java's ktab command - dkey = EncryptionKey.findKey(encPartKeyType, keys); + try { + dkey = EncryptionKey.findKey(encPartKeyType, encPartKvno, + Krb5Util.keysFromJavaxKeyTab(ktab, cname)); + } catch (KrbException ke) { + if (ke.returnCode() == Krb5.KRB_AP_ERR_BADKEYVER) { + // Fallback to no kvno. In some cases, keytab is generated + // not by sysadmin but Java's ktab command + dkey = EncryptionKey.findKey(encPartKeyType, + Krb5Util.keysFromJavaxKeyTab(ktab, cname)); + } + } + if (dkey == null) { + throw new KrbException(Krb5.API_INVALID_ARG, + "Cannot find key for type/kvno to decrypt AS REP - " + + EType.toString(encPartKeyType) + "/" + encPartKvno); } - } - if (dkey == null) { - throw new KrbException(Krb5.API_INVALID_ARG, - "Cannot find key for type/kvno to decrypt AS REP - " + - EType.toString(encPartKeyType) + "/" + encPartKvno); - } decrypt(dkey, asReq); } diff --git a/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java b/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java index 940d31107e500438442ca67fec7adacca20a694f..dd9b8dcfb92b374b36c25109da631c4060c4c7e3 100644 --- a/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java +++ b/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2011, 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 @@ -27,6 +27,8 @@ package sun.security.krb5; import java.io.IOException; import java.util.Arrays; +import javax.security.auth.kerberos.KeyTab; +import sun.security.jgss.krb5.Krb5Util; import sun.security.krb5.internal.HostAddresses; import sun.security.krb5.internal.KDCOptions; import sun.security.krb5.internal.KRBError; @@ -42,13 +44,16 @@ import sun.security.krb5.internal.crypto.EType; * 1. Gather information to create AS-REQ * 2. Create and send AS-REQ * 3. Receive AS-REP and KRB-ERROR (-KRB_ERR_RESPONSE_TOO_BIG) and parse them - * 4. Emit credentials and secret keys (for JAAS storeKey=true) + * 4. Emit credentials and secret keys (for JAAS storeKey=true with password) * * This class does not: * 1. Deal with real communications (KdcComm does it, and TGS-REQ) * a. Name of KDCs for a realm * b. Server availability, timeout, UDP or TCP * d. KRB_ERR_RESPONSE_TOO_BIG + * 2. Stores its own copy of password, this means: + * a. Do not change/wipe it before Builder finish + * b. Builder will not wipe it for you * * With this class: * 1. KrbAsReq has only one constructor @@ -70,19 +75,17 @@ public final class KrbAsReqBuilder { private HostAddresses addresses; // Secret source: can't be changed once assigned, only one (of the two - // sources) can be set and should be non-null - private EncryptionKey[] keys; - private char[] password; + // sources) can be set to non-null + private final char[] password; + private final KeyTab ktab; // Used to create a ENC-TIMESTAMP in the 2nd AS-REQ - private EncryptionKey pakey; private PAData[] paList; // PA-DATA from both KRB-ERROR and AS-REP. // Used by getKeys() only. // Only AS-REP should be enough per RFC, // combined in case etypes are different. // The generated and received: - int[] eTypes; private KrbAsReq req; private KrbAsRep rep; @@ -94,7 +97,7 @@ public final class KrbAsReqBuilder { private State state; // Called by other constructors - private KrbAsReqBuilder(PrincipalName cname) + private void init(PrincipalName cname) throws KrbException { if (cname.getRealm() == null) { cname.setRealm(Config.getInstance().getDefaultRealm()); @@ -114,14 +117,11 @@ public final class KrbAsReqBuilder { * This argument will neither be modified nor stored by the method. * @throws KrbException */ - public KrbAsReqBuilder(PrincipalName cname, EncryptionKey[] keys) + public KrbAsReqBuilder(PrincipalName cname, KeyTab ktab) throws KrbException { - this(cname); - this.keys = new EncryptionKey[keys.length]; - for (int i=0; i map = new HashMap<>(); + + // KeyTab file does not exist. Note: a missing keytab is still valid + private boolean isMissing = false; + + // KeyTab file is invalid, possibly an I/O error or a file format error. + private boolean isValid = true; + + private final String tabName; + private long lastModified; + private int kt_vno; + private Vector entries = new Vector<>(); - private KeyTab(String filename) throws IOException, RealmException { - init(filename); + /** + * Constructs a KeyTab object. + * + * If there is any I/O error or format errot during the loading, the + * isValid flag is set to false, and all half-read entries are dismissed. + * @param filename path name for the keytab file, must not be null + */ + private KeyTab(String filename) { + tabName = filename; + try { + lastModified = new File(tabName).lastModified(); + KeyTabInputStream kis = + new KeyTabInputStream(new FileInputStream(filename)); + load(kis); + kis.close(); + } catch (FileNotFoundException e) { + entries.clear(); + isMissing = true; + } catch (Exception ioe) { + entries.clear(); + isValid = false; + } } + /** + * Read a keytab file. Returns a new object and save it into cache when + * new content (modified since last read) is available. If keytab file is + * invalid, the old object will be returned. This is a safeguard for + * partial-written keytab files or non-stable network. Please note that + * a missing keytab is valid, which is equivalent to an empty keytab. + * + * @param s file name of keytab, must not be null + * @return the keytab object, can be invalid, but never null. + */ + private synchronized static KeyTab getInstance0(String s) { + long lm = new File(s).lastModified(); + KeyTab old = map.get(s); + if (old != null && old.isValid() && old.lastModified == lm) { + return old; + } + KeyTab ktab = new KeyTab(s); + if (ktab.isValid()) { // A valid new keytab + map.put(s, ktab); + return ktab; + } else if (old != null) { // An existing old one + return old; + } else { + return ktab; // first read is invalid + } + } + + /** + * Gets a KeyTab object. + * @param s the key tab file name. + * @return the KeyTab object, never null. + */ public static KeyTab getInstance(String s) { - name = parse(s); - if (name == null) { + if (s == null) { return getInstance(); + } else { + return getInstance0(s); } - return getInstance(new File(name)); } /** - * Gets the single instance of KeyTab class. + * Gets a KeyTab object. * @param file the key tab file. - * @return single instance of KeyTab; - * return null if error occurs while reading data out of the file. + * @return the KeyTab object, never null. */ public static KeyTab getInstance(File file) { - try { - if (!(file.exists())) { - singleton = null; - } else { - String fname = file.getAbsolutePath(); - // Since this class deals with file I/O operations, - // we want only one class instance existing. - if (singleton != null) { - File kfile = new File(singleton.name); - String kname = kfile.getAbsolutePath(); - if (kname.equalsIgnoreCase(fname)) { - if (DEBUG) { - System.out.println("KeyTab instance already exists"); - } - } - } else { - singleton = new KeyTab(fname); - } - } - } catch (Exception e) { - singleton = null; - if (DEBUG) { - System.out.println("Could not obtain an instance of KeyTab" + - e.getMessage()); - } + if (file == null) { + return getInstance(); + } else { + return getInstance0(file.getPath()); } - return singleton; } /** - * Gets the single instance of KeyTab class. - * @return single instance of KeyTab; return null if default keytab file - * does not exist, or error occurs while reading data from the file. + * Gets the default KeyTab object. + * @return the KeyTab object, never null. */ public static KeyTab getInstance() { - try { - name = getDefaultKeyTab(); - if (name != null) { - singleton = getInstance(new File(name)); - } - } catch (Exception e) { - singleton = null; - if (DEBUG) { - System.out.println("Could not obtain an instance of KeyTab" + - e.getMessage()); - } - } - return singleton; + return getInstance(getDefaultTabName()); + } + + public boolean isMissing() { + return isMissing; + } + + public boolean isValid() { + return isValid; } /** * The location of keytab file will be read from the configuration file * If it is not specified, consider user.home as the keytab file's * default location. + * @return never null */ - private static String getDefaultKeyTab() { - if (name != null) { - return name; + private static String getDefaultTabName() { + if (defaultTabName != null) { + return defaultTabName; } else { String kname = null; try { @@ -145,7 +192,7 @@ public class KeyTab implements KeyTabConstants { StringTokenizer st = new StringTokenizer(keytab_names, " "); while (st.hasMoreTokens()) { kname = parse(st.nextToken()); - if (kname != null) { + if (new File(kname).exists()) { break; } } @@ -165,19 +212,20 @@ public class KeyTab implements KeyTabConstants { new sun.security.action.GetPropertyAction("user.dir")); } - if (user_home != null) { - kname = user_home + File.separator + "krb5.keytab"; - } + kname = user_home + File.separator + "krb5.keytab"; } + defaultTabName = kname; return kname; } } + /** + * Parses some common keytab name formats + * @param name never null + * @return never null + */ private static String parse(String name) { - String kname = null; - if (name == null) { - return null; - } + String kname; if ((name.length() >= 5) && (name.substring(0, 5).equalsIgnoreCase("FILE:"))) { kname = name.substring(5); @@ -194,18 +242,6 @@ public class KeyTab implements KeyTabConstants { return kname; } - private synchronized void init(String filename) - throws IOException, RealmException { - - if (filename != null) { - KeyTabInputStream kis = - new KeyTabInputStream(new FileInputStream(filename)); - load(kis); - kis.close(); - name = filename; - } - } - private void load(KeyTabInputStream kis) throws IOException, RealmException { @@ -234,14 +270,13 @@ public class KeyTab implements KeyTabConstants { * etypes that have been configured for use. If there are multiple * keys with same etype, the one with the highest kvno is returned. * @param service the PrincipalName of the requested service - * @return an array containing all the service keys + * @return an array containing all the service keys, never null */ public EncryptionKey[] readServiceKeys(PrincipalName service) { KeyTabEntry entry; EncryptionKey key; int size = entries.size(); ArrayList keys = new ArrayList<>(size); - for (int i = size-1; i >= 0; i--) { entry = entries.elementAt(i); if (entry.service.match(service)) { @@ -260,10 +295,7 @@ public class KeyTab implements KeyTabConstants { } } } - size = keys.size(); - if (size == 0) - return null; EncryptionKey[] retVal = keys.toArray(new EncryptionKey[size]); // Sort keys according to default_tkt_enctypes @@ -328,10 +360,13 @@ public class KeyTab implements KeyTabConstants { return false; } - public static String tabName() { - return name; + public String tabName() { + return tabName; } + /////////////////// THE WRITE SIDE /////////////////////// + /////////////// only used by ktab tool ////////////////// + /** * Adds a new entry in the key table. * @param service the service which will have a new entry in the key table. @@ -394,7 +429,7 @@ public class KeyTab implements KeyTabConstants { */ public synchronized static KeyTab create() throws IOException, RealmException { - String dname = getDefaultKeyTab(); + String dname = getDefaultTabName(); return create(dname); } @@ -408,8 +443,7 @@ public class KeyTab implements KeyTabConstants { new KeyTabOutputStream(new FileOutputStream(name)); kos.writeVersion(KRB5_KT_VNO); kos.close(); - singleton = new KeyTab(name); - return singleton; + return new KeyTab(name); } /** @@ -417,7 +451,7 @@ public class KeyTab implements KeyTabConstants { */ public synchronized void save() throws IOException { KeyTabOutputStream kos = - new KeyTabOutputStream(new FileOutputStream(name)); + new KeyTabOutputStream(new FileOutputStream(tabName)); kos.writeVersion(kt_vno); for (int i = 0; i < entries.size(); i++) { kos.writeEntry(entries.elementAt(i)); @@ -490,13 +524,4 @@ public class KeyTab implements KeyTabConstants { kos.write16(KRB5_KT_VNO); kos.close(); } - - public static void refresh() { - if (singleton != null) { - if (DEBUG) { - System.out.println("Refreshing Keytab"); - } - singleton = null; - } - } } diff --git a/src/share/classes/sun/security/ssl/ServerHandshaker.java b/src/share/classes/sun/security/ssl/ServerHandshaker.java index ff1c8d7d5c99e551594e29bd33b08c47495e74ff..125de5f3f50119d52304b941e6117470a10000a5 100644 --- a/src/share/classes/sun/security/ssl/ServerHandshaker.java +++ b/src/share/classes/sun/security/ssl/ServerHandshaker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2011, 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 @@ -1285,11 +1285,12 @@ final class ServerHandshaker extends Handshaker { // check permission to access and use the secret key of the // Kerberized "host" service - if (kerberosKeys != null) { - + if (kerberosKeys != null && kerberosKeys.length > 0) { if (debug != null && Debug.isOn("handshake")) { - System.out.println("Using Kerberos key: " + - kerberosKeys[0]); + for (SecretKey k: kerberosKeys) { + System.out.println("Using Kerberos key: " + + k); + } } String serverPrincipal = diff --git a/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java b/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java index 94d1e8e75be8775528f3bbef053e702d6e79a6f4..652268e166f6316502de3a76d6e81e97a48b26ed 100644 --- a/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java +++ b/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2011, 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 @@ -40,7 +40,7 @@ import sun.security.krb5.PrincipalName; import sun.security.ssl.Krb5Proxy; /** - * An implementatin of Krb5Proxy that simply delegates to the appropriate + * An implementation of Krb5Proxy that simply delegates to the appropriate * Kerberos APIs. */ public class Krb5ProxyImpl implements Krb5Proxy { @@ -62,7 +62,7 @@ public class Krb5ProxyImpl implements Krb5Proxy { @Override public SecretKey[] getServerKeys(AccessControlContext acc) throws LoginException { - return Krb5Util.getKeys(GSSCaller.CALLER_SSL_SERVER, null, acc); + return Krb5Util.getServiceCreds(GSSCaller.CALLER_SSL_SERVER, null, acc).getKKeys(); } @Override diff --git a/src/windows/classes/java/lang/ProcessEnvironment.java b/src/windows/classes/java/lang/ProcessEnvironment.java index b5a4383019e185a81c478614beede6ea62e00089..89e468999998a911ddadd821848ffca9d1fad636 100644 --- a/src/windows/classes/java/lang/ProcessEnvironment.java +++ b/src/windows/classes/java/lang/ProcessEnvironment.java @@ -143,7 +143,7 @@ final class ProcessEnvironment extends HashMap public void remove() { i.remove();} }; } - private static Map.Entry checkedEntry (Object o) { + private static Map.Entry checkedEntry(Object o) { Map.Entry e = (Map.Entry) o; nonNullString(e.getKey()); nonNullString(e.getValue()); @@ -285,7 +285,7 @@ final class ProcessEnvironment extends HashMap return (Map) theEnvironment.clone(); } - // Only for use by Runtime.exec(...String[]envp...) + // Only for use by ProcessBuilder.environment(String[] envp) static Map emptyEnvironment(int capacity) { return new ProcessEnvironment(capacity); } @@ -299,19 +299,46 @@ final class ProcessEnvironment extends HashMap Collections.sort(list, entryComparator); StringBuilder sb = new StringBuilder(size()*30); - for (Map.Entry e : list) - sb.append(e.getKey()) - .append('=') - .append(e.getValue()) - .append('\u0000'); - // Ensure double NUL termination, - // even if environment is empty. - if (sb.length() == 0) + int cmp = -1; + + // Some versions of MSVCRT.DLL require SystemRoot to be set. + // So, we make sure that it is always set, even if not provided + // by the caller. + final String SYSTEMROOT = "SystemRoot"; + + for (Map.Entry e : list) { + String key = e.getKey(); + String value = e.getValue(); + if (cmp < 0 && (cmp = nameComparator.compare(key, SYSTEMROOT)) > 0) { + // Not set, so add it here + addToEnvIfSet(sb, SYSTEMROOT); + } + addToEnv(sb, key, value); + } + if (cmp < 0) { + // Got to end of list and still not found + addToEnvIfSet(sb, SYSTEMROOT); + } + if (sb.length() == 0) { + // Environment was empty and SystemRoot not set in parent sb.append('\u0000'); + } + // Block is double NUL terminated sb.append('\u0000'); return sb.toString(); } + // add the environment variable to the child, if it exists in parent + private static void addToEnvIfSet(StringBuilder sb, String name) { + String s = getenv(name); + if (s != null) + addToEnv(sb, name, s); + } + + private static void addToEnv(StringBuilder sb, String name, String val) { + sb.append(name).append('=').append(val).append('\u0000'); + } + static String toEnvironmentBlock(Map map) { return map == null ? null : ((ProcessEnvironment)map).toEnvironmentBlock(); diff --git a/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java b/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java index f6c53f81f1ebe2fb538604a116ec556fb98fe4d6..41354c88a74badaa62451cef202a9e1d6d2d928a 100644 --- a/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java +++ b/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2011, 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 @@ -30,12 +30,15 @@ package sun.security.krb5.internal.tools; +import java.io.File; import sun.security.krb5.*; import sun.security.krb5.internal.*; import sun.security.krb5.internal.ccache.*; import java.io.IOException; import java.util.Arrays; +import javax.security.auth.kerberos.KerberosPrincipal; import sun.security.util.Password; +import javax.security.auth.kerberos.KeyTab; /** * Kinit tool for obtaining Kerberos v5 tickets. @@ -153,7 +156,6 @@ public class Kinit { System.out.println("Principal is " + principal); } char[] psswd = options.password; - EncryptionKey[] skeys = null; boolean useKeytab = options.useKeytabFile(); if (!useKeytab) { if (princName == null) { @@ -186,17 +188,9 @@ public class Kinit { } } - // assert princName and principal are nonnull - skeys = EncryptionKey.acquireSecretKeys(principal, ktabName); - - if (skeys == null || skeys.length == 0) { - String msg = "No supported key found in keytab"; - if (princName != null) { - msg += " for principal " + princName; - } - throw new KrbException(msg); - } - builder = new KrbAsReqBuilder(principal, skeys); + builder = new KrbAsReqBuilder(principal, ktabName == null + ? KeyTab.getInstance() + : KeyTab.getInstance(new File(ktabName))); } KDCOptions opt = new KDCOptions(); diff --git a/src/windows/classes/sun/security/krb5/internal/tools/Klist.java b/src/windows/classes/sun/security/krb5/internal/tools/Klist.java index ad5c8aa34f06563f980440eeba87b5c663affb9e..6cbc8a3fa61f5506ab0ede6c9503307742f0c23b 100644 --- a/src/windows/classes/sun/security/krb5/internal/tools/Klist.java +++ b/src/windows/classes/sun/security/krb5/internal/tools/Klist.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2011, 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 @@ -95,16 +95,15 @@ public class Klist { } break; case 'k': - if (klist.name == null) { - klist.target = KeyTab.getInstance(); - klist.name = KeyTab.tabName(); - } else klist.target = KeyTab.getInstance(klist.name); - if (klist.target != null) { - klist.displayTab(); - } else { + try { + KeyTab ktab = KeyTab.getInstance(klist.name); + klist.target = ktab; + klist.name = ktab.tabName(); + } catch (Exception e) { klist.displayMessage("KeyTab"); System.exit(-1); } + klist.displayTab(); break; default: if (klist.name != null) { @@ -295,9 +294,10 @@ public class Klist { void displayMessage(String target) { if (name == null) { - name = ""; + System.out.println("Default " + target + " not found."); + } else { + System.out.println(target + " " + name + " not found."); } - System.out.println(target + " " + name + " not found."); } /** * Reformats the date from the form - diff --git a/src/windows/classes/sun/security/krb5/internal/tools/Ktab.java b/src/windows/classes/sun/security/krb5/internal/tools/Ktab.java index 216c3a30c1273b908801649438ce18d7bbc04a45..fe29462add57f4a46e2fbdcab057456902459ce5 100644 --- a/src/windows/classes/sun/security/krb5/internal/tools/Ktab.java +++ b/src/windows/classes/sun/security/krb5/internal/tools/Ktab.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2011, 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 @@ -321,7 +321,7 @@ public class Ktab { * Lists key table name and entries in it. */ void listKt() { - System.out.println("Keytab name: " + KeyTab.tabName()); + System.out.println("Keytab name: " + table.tabName()); KeyTabEntry[] entries = table.getEntries(); if ((entries != null) && (entries.length > 0)) { String[][] output = new String[entries.length+1][showTime?3:2]; diff --git a/test/java/lang/ProcessBuilder/Basic.java b/test/java/lang/ProcessBuilder/Basic.java index 22916929561f687084fe60dbf44ea144a5220d32..677735474a83f3b69a81b0f0aafe90ae2daf1a6b 100644 --- a/test/java/lang/ProcessBuilder/Basic.java +++ b/test/java/lang/ProcessBuilder/Basic.java @@ -26,7 +26,7 @@ * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689 * 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313 * 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958 - * 4947220 7018606 + * 4947220 7018606 7034570 * @summary Basic tests for Process and Environment Variable code * @run main/othervm/timeout=300 Basic * @author Martin Buchholz @@ -1440,11 +1440,12 @@ public class Basic { // Check for sort order of environment variables on Windows. //---------------------------------------------------------------- try { + String systemRoot = "SystemRoot=" + System.getenv("SystemRoot"); // '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~' String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=", - "+=+", "_=_", "~=~"}; + "+=+", "_=_", "~=~", systemRoot}; String output = nativeEnv(envp); - String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n_=_\n~=~\n"; + String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n"; // On Windows, Java must keep the environment sorted. // Order is random on Unix, so this test does the sort. if (! Windows.is()) @@ -1452,6 +1453,21 @@ public class Basic { equal(output, expected); } catch (Throwable t) { unexpected(t); } + //---------------------------------------------------------------- + // Test Runtime.exec(...envp...) + // and check SystemRoot gets set automatically on Windows + //---------------------------------------------------------------- + try { + if (Windows.is()) { + String systemRoot = "SystemRoot=" + System.getenv("SystemRoot"); + String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=", + "+=+", "_=_", "~=~"}; + String output = nativeEnv(envp); + String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n"; + equal(output, expected); + } + } catch (Throwable t) { unexpected(t); } + //---------------------------------------------------------------- // System.getenv() must be consistent with System.getenv(String) //---------------------------------------------------------------- diff --git a/test/sun/security/krb5/auto/Context.java b/test/sun/security/krb5/auto/Context.java index 676042ba993dd04fe9fa46a66d81d9ee95ec6ab5..1739f876f08714b4c626ab550a63856b027110e6 100644 --- a/test/sun/security/krb5/auto/Context.java +++ b/test/sun/security/krb5/auto/Context.java @@ -44,6 +44,7 @@ import com.sun.security.jgss.InquireType; import com.sun.security.jgss.AuthorizationDataEntry; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import javax.security.auth.kerberos.KeyTab; /** * Context of a JGSS subject, encapsulating Subject and GSSContext. @@ -107,15 +108,19 @@ public class Context { return out; } + public static Context fromUserPass( + String user, char[] pass, boolean storeKey) throws Exception { + return fromUserPass(null, user, pass, storeKey); + } /** * Logins with a username and a password, using Krb5LoginModule directly * @param storeKey true if key should be saved, used on acceptor side */ - public static Context fromUserPass(String user, char[] pass, boolean storeKey) - throws Exception { + public static Context fromUserPass(Subject s, + String user, char[] pass, boolean storeKey) throws Exception { Context out = new Context(); out.name = user; - out.s = new Subject(); + out.s = s == null ? new Subject() : s; Krb5LoginModule krb5 = new Krb5LoginModule(); Map map = new HashMap<>(); Map shared = new HashMap<>(); @@ -198,12 +203,25 @@ public class Context { * @throws java.lang.Exception */ public void startAsServer(final Oid mech) throws Exception { + startAsServer(null, mech); + } + + /** + * Starts as a server with the specified service name + * @param name the service name + * @param mech GSS mech + * @throws java.lang.Exception + */ + public void startAsServer(final String name, final Oid mech) throws Exception { doAs(new Action() { @Override public byte[] run(Context me, byte[] dummy) throws Exception { GSSManager m = GSSManager.getInstance(); me.x = (ExtendedGSSContext)m.createContext(m.createCredential( - null, + name == null ? null : + (name.indexOf('@') < 0 ? + m.createName(name, null) : + m.createName(name, GSSName.NT_HOSTBASED_SERVICE)), GSSCredential.INDEFINITE_LIFETIME, mech, GSSCredential.ACCEPT_ONLY)); @@ -229,6 +247,14 @@ public class Context { return x; } + /** + * Accesses the internal subject. + * @return the subject + */ + public Subject s() { + return s; + } + /** * Disposes the GSSContext within * @throws org.ietf.jgss.GSSException @@ -297,7 +323,7 @@ public class Context { } catch (Exception e) { ;// Don't care } - System.out.println("====================================="); + System.out.println("====== Private Credentials Set ======"); for (Object o : s.getPrivateCredentials()) { System.out.println(" " + o.getClass()); if (o instanceof KerberosTicket) { @@ -315,6 +341,8 @@ public class Context { for (Object k : map.keySet()) { System.out.println(" " + k + ": " + map.get(k)); } + } else { + System.out.println(" " + o); } } if (x != null && x instanceof ExtendedGSSContext) { diff --git a/test/sun/security/krb5/auto/DynamicKeytab.java b/test/sun/security/krb5/auto/DynamicKeytab.java new file mode 100644 index 0000000000000000000000000000000000000000..256476b0b5e8783545ed136d0779a3e0c742ab40 --- /dev/null +++ b/test/sun/security/krb5/auto/DynamicKeytab.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011, 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. + * + * 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. + */ + +/* + * @test + * @bug 6894072 + * @run main/othervm DynamicKeytab + * @summary always refresh keytab + */ + +import java.io.File; +import java.io.FileOutputStream; +import org.ietf.jgss.GSSException; +import sun.security.jgss.GSSUtil; +import sun.security.krb5.KrbException; +import sun.security.krb5.internal.Krb5; + +public class DynamicKeytab { + + Context c, s; + public static void main(String[] args) + throws Exception { + new DynamicKeytab().go(); + } + + void go() throws Exception { + OneKDC k = new OneKDC(null); + k.writeJAASConf(); + + new File(OneKDC.KTAB).delete(); + + + // Starts with no keytab + c = Context.fromJAAS("client"); + s = Context.fromJAAS("com.sun.security.jgss.krb5.accept"); + + // Test 1: read new key 1 from keytab + k.addPrincipal(OneKDC.SERVER, "pass1".toCharArray()); + k.writeKtab(OneKDC.KTAB); + connect(); + + // Test 2: service key cached, find 1 in keytab (now contains 1 and 2) + k.addPrincipal(OneKDC.SERVER, "pass2".toCharArray()); + k.appendKtab(OneKDC.KTAB); + connect(); + + // Test 3: re-login. Now find 2 in keytab + c = Context.fromJAAS("client"); + connect(); + + // Test 4: re-login, KDC use 3 this time. + c = Context.fromJAAS("client"); + // Put 3 and 4 into keytab but keep the real key back to 3. + k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray()); + k.appendKtab(OneKDC.KTAB); + k.addPrincipal(OneKDC.SERVER, "pass4".toCharArray()); + k.appendKtab(OneKDC.KTAB); + k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray()); + connect(); + + // Test 5: invalid keytab file, should ignore + new FileOutputStream(OneKDC.KTAB).write("BADBADBAD".getBytes()); + connect(); + + // Test 6: delete keytab file, identical to revoke all + new File(OneKDC.KTAB).delete(); + try { + connect(); + throw new Exception("Should not success"); + } catch (GSSException gsse) { + System.out.println(gsse); + KrbException ke = (KrbException)gsse.getCause(); + // KrbApReq.authenticate(*) if (dkey == null)... + // This should have been Krb5.KRB_AP_ERR_NOKEY + if (ke.returnCode() != Krb5.API_INVALID_ARG) { + throw new Exception("Not expected failure code: " + + ke.returnCode()); + } + } + + // Test 7: 3 revoked, should fail (now contains only 5) + k.addPrincipal(OneKDC.SERVER, "pass5".toCharArray()); + k.writeKtab(OneKDC.KTAB); // overwrite keytab, which means + // old key is revoked + try { + connect(); + throw new Exception("Should not success"); + } catch (GSSException gsse) { + System.out.println(gsse); + KrbException ke = (KrbException)gsse.getCause(); + if (ke.returnCode() != Krb5.KRB_AP_ERR_BADKEYVER) { + throw new Exception("Not expected failure code: " + + ke.returnCode()); + } + } + + // Test 8: an empty KDC means revoke all + KDC.create("EMPTY.REALM").writeKtab(OneKDC.KTAB); + try { + connect(); + throw new Exception("Should not success"); + } catch (GSSException gsse) { + System.out.println(gsse); + KrbException ke = (KrbException)gsse.getCause(); + // KrbApReq.authenticate(*) if (dkey == null)... + // This should have been Krb5.KRB_AP_ERR_NOKEY + if (ke.returnCode() != Krb5.API_INVALID_ARG) { + throw new Exception("Not expected failure code: " + + ke.returnCode()); + } + } + } + + void connect() throws Exception { + Thread.sleep(2000); // make sure ktab timestamp is different + c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID); + s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); + Context.handshake(c, s); + } +} diff --git a/test/sun/security/krb5/auto/KDC.java b/test/sun/security/krb5/auto/KDC.java index 8d01dd346cdf3a2bd39c538719064e1e6105f41b..45a3d530c4b57a0b1a4b34f787393aac9e82e1e5 100644 --- a/test/sun/security/krb5/auto/KDC.java +++ b/test/sun/security/krb5/auto/KDC.java @@ -228,7 +228,33 @@ public class KDC { } /** - * Write all principals' keys from multiple KDCsinto one keytab file. + * Writes or appends KDC keys into a keytab. See doc for writeMultiKtab. + * @param append true if append, otherwise, overwrite. + */ + private static void writeKtab0(String tab, boolean append, KDC... kdcs) + throws IOException, KrbException { + KeyTab ktab = append ? KeyTab.getInstance(tab) : KeyTab.create(tab); + for (KDC kdc: kdcs) { + for (String name : kdc.passwords.keySet()) { + char[] pass = kdc.passwords.get(name); + int kvno = 0; + if (Character.isDigit(pass[pass.length-1])) { + kvno = pass[pass.length-1] - '0'; + } + ktab.addEntry(new PrincipalName(name, + name.indexOf('/') < 0 ? + PrincipalName.KRB_NT_UNKNOWN : + PrincipalName.KRB_NT_SRV_HST), + pass, + kvno, + true); + } + } + ktab.save(); + } + + /** + * Writes all principals' keys from multiple KDCs into one keytab file. * Note that the keys for the krbtgt principals will not be written. *

* Attention: This method references krb5.conf settings. If you need to @@ -252,17 +278,16 @@ public class KDC { */ public static void writeMultiKtab(String tab, KDC... kdcs) throws IOException, KrbException { - KeyTab ktab = KeyTab.create(tab); - for (KDC kdc: kdcs) { - for (String name : kdc.passwords.keySet()) { - ktab.addEntry(new PrincipalName(name, - name.indexOf('/') < 0 ? - PrincipalName.KRB_NT_UNKNOWN : - PrincipalName.KRB_NT_SRV_HST), - kdc.passwords.get(name), -1, true); - } - } - ktab.save(); + writeKtab0(tab, false, kdcs); + } + + /** + * Appends all principals' keys from multiple KDCs to one keytab file. + * See writeMultiKtab for details. + */ + public static void appendMultiKtab(String tab, KDC... kdcs) + throws IOException, KrbException { + writeKtab0(tab, true, kdcs); } /** @@ -272,6 +297,13 @@ public class KDC { KDC.writeMultiKtab(tab, this); } + /** + * Appends keys in this KDC to a ktab. + */ + public void appendKtab(String tab) throws IOException, KrbException { + KDC.appendMultiKtab(tab, this); + } + /** * Adds a new principal to this realm with a given password. * @param user the principal's name. For a service principal, use the diff --git a/test/sun/security/krb5/auto/KeyTabCompat.java b/test/sun/security/krb5/auto/KeyTabCompat.java new file mode 100644 index 0000000000000000000000000000000000000000..f6763510fd261233e0dff38df26572917066c4b4 --- /dev/null +++ b/test/sun/security/krb5/auto/KeyTabCompat.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2011, 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. + * + * 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. + */ + +/* + * @test + * @bug 6894072 + * @compile -XDignore.symbol.file KeyTabCompat.java + * @run main/othervm KeyTabCompat + * @summary always refresh keytab + */ + +import javax.security.auth.kerberos.KerberosKey; +import sun.security.jgss.GSSUtil; + +/* + * There are 2 compat issues to check: + * + * 1. If there is only KerberosKeys in private credential set and no + * KerberosPrincipal. JAAS login should go on. + * 2. Even if KeyTab is used, user can still get KerberosKeys from + * private credentials set. + */ +public class KeyTabCompat { + + public static void main(String[] args) + throws Exception { + OneKDC kdc = new OneKDC("aes128-cts"); + kdc.writeJAASConf(); + kdc.addPrincipal(OneKDC.SERVER, "pass1".toCharArray()); + kdc.writeKtab(OneKDC.KTAB); + + Context c, s; + + // Part 1 + c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); + s = Context.fromUserPass(OneKDC.USER2, OneKDC.PASS2, true); + + s.s().getPrincipals().clear(); + + c.startAsClient(OneKDC.USER2, GSSUtil.GSS_KRB5_MECH_OID); + s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); + + Context.handshake(c, s); + + // Part 2 + c = Context.fromJAAS("client"); + s = Context.fromJAAS("server"); + + c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID); + s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); + s.status(); + + if (s.s().getPrivateCredentials(KerberosKey.class).size() != 1) { + throw new Exception("There should be one KerberosKey"); + } + + Thread.sleep(2000); // make sure ktab timestamp is different + + kdc.addPrincipal(OneKDC.SERVER, "pass2".toCharArray()); + kdc.writeKtab(OneKDC.KTAB); + + Context.handshake(c, s); + s.status(); + + if (s.s().getPrivateCredentials(KerberosKey.class).size() != 1) { + throw new Exception("There should be only one KerberosKey"); + } + + } +} diff --git a/test/sun/security/krb5/auto/LoginModuleOptions.java b/test/sun/security/krb5/auto/LoginModuleOptions.java index a6dd33b80298782084c53b7d2ebaeb81c5b876c6..2c77451d74875b63079f8533eecda8a10389369e 100644 --- a/test/sun/security/krb5/auto/LoginModuleOptions.java +++ b/test/sun/security/krb5/auto/LoginModuleOptions.java @@ -28,7 +28,6 @@ * @summary Krb5LoginModule a little too restrictive, and the doc is not clear. */ import com.sun.security.auth.module.Krb5LoginModule; -import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.security.auth.Subject; @@ -36,7 +35,6 @@ import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; public class LoginModuleOptions { @@ -61,10 +59,12 @@ public class LoginModuleOptions { // 1. ccache -> keytab login(null, "useTicketCache", "true", "ticketCache", "krbcc_non_exists", "useKeyTab", "true", "principal", "dummy"); + // 2. keytab -> shared login(null, "useKeyTab", "true", "principal", "dummy", "keyTab", "ktab_non_exist", "tryFirstPass", "true", NAME, OneKDC.USER, PWD, OneKDC.PASS); + // 3. shared -> callback // 3.1. useFirstPass, no callback boolean failed = false; diff --git a/test/sun/security/krb5/auto/SSL.java b/test/sun/security/krb5/auto/SSL.java index 7bd4601481e4abdf7b66c91c9ec650d070a0ba77..eca535051fc88726f03f8acec15862daeedf53e9 100644 --- a/test/sun/security/krb5/auto/SSL.java +++ b/test/sun/security/krb5/auto/SSL.java @@ -48,7 +48,7 @@ import sun.security.krb5.internal.ktab.KeyTab; public class SSL { private static String krb5Cipher; - private static final int LOOP_LIMIT = 1; + private static final int LOOP_LIMIT = 3; private static int loopCount = 0; private static volatile String server; private static volatile int port; @@ -98,13 +98,13 @@ public class SSL { fos.close(); f.deleteOnExit(); - final Context c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); + Context c; final Context s = Context.fromJAAS("ssl"); - c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID); + // There's no keytab file when server starts. s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); - new Thread(new Runnable() { + Thread server = new Thread(new Runnable() { public void run() { try { s.doAs(new JsseServerAction(), null); @@ -112,12 +112,57 @@ public class SSL { e.printStackTrace(); } } - }).start(); + }); + server.setDaemon(true); + server.start(); // Warm the server Thread.sleep(2000); + // Now create the keytab + + /* + // Add 3 versions of keys into keytab + KeyTab ktab = KeyTab.create(OneKDC.KTAB); + PrincipalName service = new PrincipalName( + "host/" + server, PrincipalName.KRB_NT_SRV_HST); + ktab.addEntry(service, "pass1".toCharArray(), 1); + ktab.addEntry(service, "pass2".toCharArray(), 2); + ktab.addEntry(service, "pass3".toCharArray(), 3); + ktab.save(); + + // and use the middle one as the real key + kdc.addPrincipal("host/" + server, "pass2".toCharArray()); + */ + c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); + c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID); c.doAs(new JsseClientAction(), null); + + // Add another version of key, make sure it can be loaded + Thread.sleep(2000); + ktab = KeyTab.getInstance(OneKDC.KTAB); + ktab.addEntry(service, "pass4".toCharArray(), 4, true); + ktab.save(); + kdc.addPrincipal("host/" + server, "pass4".toCharArray()); + + c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); + c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID); + c.doAs(new JsseClientAction(), null); + + // Revoke the old key + /*Thread.sleep(2000); + ktab = KeyTab.create(OneKDC.KTAB); + ktab.addEntry(service, "pass5".toCharArray(), 5, false); + ktab.save(); + + c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); + c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID); + try { + c.doAs(new JsseClientAction(), null); + throw new Exception("Should fail this time."); + } catch (SSLException e) { + // Correct behavior. + }*/ } // Following codes copied from @@ -126,6 +171,7 @@ public class SSL { public byte[] run(Context s, byte[] input) throws Exception { SSLSocketFactory sslsf = (SSLSocketFactory) SSLSocketFactory.getDefault(); + System.out.println("Connecting " + server + ":" + port); SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(server, port); // Enable only a KRB5 cipher suite. @@ -154,6 +200,9 @@ public class SSL { System.out.println("Server is: " + peer.toString()); sslSocket.close(); + // This line should not be needed. It's the server's duty to + // forget the old key + //sslSocket.getSession().invalidate(); return null; } } @@ -165,6 +214,7 @@ public class SSL { SSLServerSocket sslServerSocket = (SSLServerSocket) sslssf.createServerSocket(0); // any port port = sslServerSocket.getLocalPort(); + System.out.println("Listening on " + port); // Enable only a KRB5 cipher suite. String enabledSuites[] = {krb5Cipher}; diff --git a/test/sun/security/krb5/auto/TwoPrinces.java b/test/sun/security/krb5/auto/TwoPrinces.java new file mode 100644 index 0000000000000000000000000000000000000000..30f16e96a2558f16e57bb3b70349a0aa147b172e --- /dev/null +++ b/test/sun/security/krb5/auto/TwoPrinces.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2011, 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. + * + * 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. + */ + +/* + * @test + * @bug 6894072 + * @compile -XDignore.symbol.file TwoPrinces.java + * @run main/othervm TwoPrinces + * @summary always refresh keytab + */ + +import java.io.File; +import java.io.FileOutputStream; +import sun.security.jgss.GSSUtil; +import sun.security.krb5.Config; + +public class TwoPrinces { + + public static void main(String[] args) + throws Exception { + + KDC k1 = KDC.create("R1"); + k1.addPrincipal("u1", "hello".toCharArray()); + k1.addPrincipalRandKey("krbtgt/R1"); + k1.addPrincipalRandKey("host/same.host"); + + KDC k2 = KDC.create("R2"); + k2.addPrincipal("u2", "hello".toCharArray()); + k2.addPrincipalRandKey("krbtgt/R2"); + k2.addPrincipalRandKey("host/same.host"); + + System.setProperty("java.security.krb5.conf", "krb5.conf"); + + // R1 is the default realm now + KDC.saveConfig("krb5.conf", k1, k2); + Config.refresh(); + + k1.writeKtab("ktab1"); + k2.writeKtab("ktab2"); + + // A JAAS config file with 2 Krb5LoginModules, after commit, the + // subject with have principals and keytabs from both sides + System.setProperty("java.security.auth.login.config", "jaas.conf"); + File f = new File("jaas.conf"); + FileOutputStream fos = new FileOutputStream(f); + fos.write(( + "me {\n" + + " com.sun.security.auth.module.Krb5LoginModule required" + + " isInitiator=true principal=\"host/same.host@R1\"" + + " useKeyTab=true keyTab=ktab1 storeKey=true;\n" + + " com.sun.security.auth.module.Krb5LoginModule required" + + " isInitiator=true principal=\"host/same.host@R2\"" + + " useKeyTab=true keyTab=ktab2 storeKey=true;\n" + + "};\n" + ).getBytes()); + fos.close(); + + /* + * This server side context will be able to act as services in both + * realms. Please note that we still don't support a single instance + * of server to accept connections from two realms at the same time. + * Therefore, we must call startAsServer in a given realm to start + * working there. The same Subject never changes anyway. + */ + Context s = Context.fromJAAS("me"); + + // Default realm still R1 + s.startAsServer("host@same.host", GSSUtil.GSS_KRB5_MECH_OID); + Context c1 = Context.fromUserPass("u1", "hello".toCharArray(), false); + c1.startAsClient("host@same.host", GSSUtil.GSS_KRB5_MECH_OID); + Context.handshake(c1, s); + + KDC.saveConfig("krb5.conf", k2, k1); + Config.refresh(); + + // Default realm now R2 + s.startAsServer("host@same.host", GSSUtil.GSS_KRB5_MECH_OID); + Context c2 = Context.fromUserPass("u2", "hello".toCharArray(), false); + c2.startAsClient("host@same.host", GSSUtil.GSS_KRB5_MECH_OID); + Context.handshake(c2, s); + } +} diff --git a/test/sun/security/krb5/ktab/KeyTabIndex.java b/test/sun/security/krb5/ktab/KeyTabIndex.java index f0cd88e6a651a03c84b485b2f582311b929638e3..26eb5621939358c0e3c107d10b3382c69b06717c 100644 --- a/test/sun/security/krb5/ktab/KeyTabIndex.java +++ b/test/sun/security/krb5/ktab/KeyTabIndex.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2011, 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 @@ -44,7 +44,6 @@ public class KeyTabIndex { KeyTab.getInstance("ktab").getClass(); } }; - KeyTab.refresh(); for (int i=0; i<10; i++) { new Thread(t).start(); }