提交 697d0bd9 编写于 作者: V vinnie

Merge

/*
* 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;
......
......@@ -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.
*
* <p>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()}.
*
* <p>If there is a security manager, its
* {@link SecurityManager#checkExec checkExec}
* method is called with the first component of this object's
......
......@@ -544,6 +544,11 @@ public class Runtime {
* <p>If <tt>envp</tt> is <tt>null</tt>, the subprocess inherits the
* environment settings of the current process.
*
* <p>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.
*
* <p>{@link ProcessBuilder#start()} is now the preferred way to
* start a process with a modified environment.
*
......
......@@ -156,9 +156,11 @@ public abstract class AbstractSet<E> extends AbstractCollection<E> implements Se
* @throws UnsupportedOperationException if the <tt>removeAll</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
* @see #contains(Object)
......
......@@ -628,9 +628,11 @@ public class ArrayList<E> extends AbstractList<E>
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see Collection#contains(Object)
*/
......@@ -646,9 +648,11 @@ public class ArrayList<E> extends AbstractList<E>
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see Collection#contains(Object)
*/
......
......@@ -60,7 +60,8 @@ package java.util;
* but is not required to, throw the exception if the collection to be added
* is empty.
*
* <p>Some collection implementations have restrictions on the elements that
* <p><a name="optional-restrictions"/>
* 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<E> extends Iterable<E> {
* @return <tt>true</tt> 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
* (<a href="#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* collection does not permit null elements (optional)
* collection does not permit null elements
* (<a href="#optional-restrictions">optional</a>)
*/
boolean contains(Object o);
......@@ -279,9 +282,11 @@ public interface Collection<E> extends Iterable<E> {
* @param o element to be removed from this collection, if present
* @return <tt>true</tt> 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
* (<a href="#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* collection does not permit null elements (optional)
* collection does not permit null elements
* (<a href="#optional-restrictions">optional</a>)
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this collection
*/
......@@ -299,10 +304,13 @@ public interface Collection<E> extends Iterable<E> {
* 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
* (<a href="#optional-restrictions">optional</a>)
* @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
* (<a href="#optional-restrictions">optional</a>),
* or if the specified collection is null.
* @see #contains(Object)
*/
boolean containsAll(Collection<?> c);
......@@ -346,10 +354,13 @@ public interface Collection<E> extends Iterable<E> {
* 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
* (<a href="#optional-restrictions">optional</a>)
* @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
* (<a href="#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
* @see #contains(Object)
*/
......@@ -367,10 +378,13 @@ public interface Collection<E> extends Iterable<E> {
* 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
* (<a href="#optional-restrictions">optional</a>)
* @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
* (<a href="#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
* @see #contains(Object)
*/
......
......@@ -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)
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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.
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @since 1.5
*/
public static boolean disjoint(Collection<?> c1, Collection<?> c2) {
......
......@@ -351,9 +351,11 @@ public interface Deque<E> extends Queue<E> {
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements (optional)
* deque does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean removeFirstOccurrence(Object o);
......@@ -369,9 +371,11 @@ public interface Deque<E> extends Queue<E> {
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements (optional)
* deque does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean removeLastOccurrence(Object o);
......@@ -527,9 +531,11 @@ public interface Deque<E> extends Queue<E> {
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements (optional)
* deque does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
......@@ -542,9 +548,11 @@ public interface Deque<E> extends Queue<E> {
* @param o element whose presence in this deque is to be tested
* @return <tt>true</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* deque does not permit null elements (optional)
* deque does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean contains(Object o);
......
......@@ -134,9 +134,11 @@ public interface List<E> extends Collection<E> {
* @param o element whose presence in this list is to be tested
* @return <tt>true</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* list does not permit null elements (optional)
* list does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean contains(Object o);
......@@ -245,9 +247,11 @@ public interface List<E> extends Collection<E> {
* @param o element to be removed from this list, if present
* @return <tt>true</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* list does not permit null elements (optional)
* list does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this list
*/
......@@ -265,10 +269,13 @@ public interface List<E> extends Collection<E> {
* specified collection
* @throws ClassCastException if the types of one or more elements
* in the specified collection are incompatible with this
* list (optional)
* list
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #contains(Object)
*/
boolean containsAll(Collection<?> c);
......@@ -334,9 +341,11 @@ public interface List<E> extends Collection<E> {
* @throws UnsupportedOperationException if the <tt>removeAll</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
* @see #contains(Object)
......@@ -354,9 +363,11 @@ public interface List<E> extends Collection<E> {
* @throws UnsupportedOperationException if the <tt>retainAll</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
* @see #contains(Object)
......@@ -493,9 +504,11 @@ public interface List<E> extends Collection<E> {
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* list does not permit null elements (optional)
* list does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
int indexOf(Object o);
......@@ -510,9 +523,11 @@ public interface List<E> extends Collection<E> {
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* list does not permit null elements (optional)
* list does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
int lastIndexOf(Object o);
......
......@@ -144,9 +144,11 @@ public interface Map<K,V> {
* @return <tt>true</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key is null and this map
* does not permit null keys (optional)
* does not permit null keys
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean containsKey(Object key);
......@@ -162,9 +164,11 @@ public interface Map<K,V> {
* @return <tt>true</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified value is null and this
* map does not permit null values (optional)
* map does not permit null values
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean containsValue(Object value);
......@@ -187,9 +191,11 @@ public interface Map<K,V> {
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key is null and this map
* does not permit null keys (optional)
* does not permit null keys
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
V get(Object key);
......@@ -245,9 +251,11 @@ public interface Map<K,V> {
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this map
* @throws ClassCastException if the key is of an inappropriate type for
* this map (optional)
* this map
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key is null and this
* map does not permit null keys (optional)
* map does not permit null keys
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
V remove(Object key);
......@@ -466,4 +474,5 @@ public interface Map<K,V> {
* @see #equals(Object)
*/
int hashCode();
}
......@@ -110,9 +110,11 @@ public interface Set<E> extends Collection<E> {
* @param o element whose presence in this set is to be tested
* @return <tt>true</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* set does not permit null elements (optional)
* set does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
*/
boolean contains(Object o);
......@@ -236,9 +238,11 @@ public interface Set<E> extends Collection<E> {
* @param o object to be removed from this set, if present
* @return <tt>true</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null and this
* set does not permit null elements (optional)
* set does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws UnsupportedOperationException if the <tt>remove</tt> operation
* is not supported by this set
*/
......@@ -257,10 +261,13 @@ public interface Set<E> extends Collection<E> {
* specified collection
* @throws ClassCastException if the types of one or more elements
* in the specified collection are incompatible with this
* set (optional)
* set
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #contains(Object)
*/
boolean containsAll(Collection<?> c);
......@@ -302,9 +309,11 @@ public interface Set<E> extends Collection<E> {
* @throws UnsupportedOperationException if the <tt>retainAll</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
*/
......@@ -322,9 +331,11 @@ public interface Set<E> extends Collection<E> {
* @throws UnsupportedOperationException if the <tt>removeAll</tt> 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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
* @see #contains(Object)
......
......@@ -893,10 +893,13 @@ public class Vector<E>
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @since 1.2
*/
public synchronized boolean removeAll(Collection<?> c) {
......@@ -913,10 +916,13 @@ public class Vector<E>
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @since 1.2
*/
public synchronized boolean retainAll(Collection<?> c) {
......
......@@ -400,8 +400,10 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> 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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
* (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean removeFirstOccurrence(Object o);
......@@ -416,8 +418,10 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> 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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
* (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean removeLastOccurrence(Object o);
......@@ -591,8 +595,10 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* @param o element to be removed from this deque, if present
* @return <tt>true</tt> 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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
* (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
......@@ -604,8 +610,10 @@ public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {
* @param o object to be checked for containment in this deque
* @return <tt>true</tt> 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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
* (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
public boolean contains(Object o);
......
......@@ -303,8 +303,10 @@ public interface BlockingQueue<E> extends Queue<E> {
* @param o element to be removed from this queue, if present
* @return <tt>true</tt> 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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
* (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
......@@ -316,8 +318,10 @@ public interface BlockingQueue<E> extends Queue<E> {
* @param o object to be checked for containment in this queue
* @return <tt>true</tt> 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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
* (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
public boolean contains(Object o);
......
......@@ -103,9 +103,11 @@ public interface ConcurrentMap<K, V> extends Map<K, V> {
* @throws UnsupportedOperationException if the <tt>remove</tt> 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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object key, Object value);
......
......@@ -631,9 +631,11 @@ public class CopyOnWriteArrayList<E>
* @param c collection containing elements to be removed from this list
* @return <tt>true</tt> 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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="../Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
*/
......@@ -671,9 +673,11 @@ public class CopyOnWriteArrayList<E>
* @param c collection containing elements to be retained in this list
* @return <tt>true</tt> 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
* (<a href="../Collection.html#optional-restrictions">optional</a>)
* @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
* (<a href="../Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see #remove(Object)
*/
......
/*
* 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);
}
}
/*
* 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.<p>
*
* 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.<p>
*
* A Kerberos service using a keytab to read secret keys should use
* the {@link KeyTab} class, where latest keys can be read when needed.<p>
*
* It might be necessary for the application to be granted a
* {@link javax.security.auth.PrivateCredentialPermission
* PrivateCredentialPermission} if it needs to access the KerberosKey
......
/*
* 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.
* <p>
* 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.
* <p>
* 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}.
* <p>
* The keytab file format is described at
* <a href="http://www.ioplex.com/utilities/keytab.txt">
* http://www.ioplex.com/utilities/keytab.txt</a>.
*
* @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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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<kks.length; i++) {
Integer tmp = keys[i].getKeyVersionNumber();
kks[i] = new KerberosKey(
principal,
keys[i].getBytes(),
keys[i].getEType(),
tmp == null ? 0 : tmp.intValue());
keys[i].destroy();
}
return kks;
} catch (RealmException re) {
return new KerberosKey[0];
}
}
EncryptionKey[] getEncryptionKeys(PrincipalName principal) {
return takeSnapshot().readServiceKeys(principal);
}
/**
* Checks if the keytab file exists. Implementation of this method
* should make sure that the result matches the latest status of the
* keytab file.
* <p>
* 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 <code>KeyTab</code>
*/
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
* <code>KeyTab</code> and the two
* <code>KeyTab</code> 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);
}
}
/*
* 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);
}
......@@ -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;
}
}
......@@ -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 {
......
......@@ -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<ActivationID,ActivationGroupID> idTable =
new HashMap<ActivationID,ActivationGroupID>();
new ConcurrentHashMap<>();
/** maps group id to its GroupEntry groups */
private Map<ActivationGroupID,GroupEntry> groupTable =
new HashMap<ActivationGroupID,GroupEntry>();
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;
}
......
/*
* 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<KerberosKey[]>() {
public KerberosKey[] run() throws Exception {
return Krb5Util.getKeys(
creds = AccessController.doPrivileged(
new PrivilegedExceptionAction<Krb5Util.ServiceCreds>() {
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();
}
}
/*
* 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<KeyTab> ktabs;
private List<KerberosKey> 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<KerberosKey> 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<KerberosKey> 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<Object> pcs = subj.getPrivateCredentials();
synchronized (pcs) {
Iterator<Object> 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<ekeys.length; i++) {
ekeys[i] = new EncryptionKey(
kkeys[i].getEncoded(), kkeys[i].getKeyType(),
new Integer(kkeys[i].getVersionNumber()));
}
return ekeys;
}
public void destroy() {
kp = null;
ktabs = null;
kk = null;
}
}
/**
* Retrieves the ServiceCreds for the specified server principal from
* the Subject in the specified AccessControlContext. If not found, and if
* useSubjectCredsOnly is false, then obtain from a LoginContext.
*
* NOTE: This method is also used by JSSE Kerberos Cipher Suites
*/
public static ServiceCreds getServiceCreds(GSSCaller caller,
String serverPrincipal, AccessControlContext acc)
throws LoginException {
Subject accSubj = Subject.getSubject(acc);
List<KerberosKey> kkeys = (List<KerberosKey>)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<KerberosKey>) 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);
}
}
/*
* 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> T find(Subject subject, String serverPrincipal,
String clientPrincipal, Class<T> 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 <T> List<T> findMany(Subject subject, String serverPrincipal,
String clientPrincipal, Class<T> credClass) {
return findAux(subject, serverPrincipal, clientPrincipal, credClass,
return (List<T>)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 <T> Object findAux(Subject subject, String serverPrincipal,
String clientPrincipal, Class<T> credClass, boolean oneOnly) {
if (subject == null) {
return null;
} else {
List<Object> answer = (oneOnly ? null : new ArrayList<Object>());
List<T> answer = (oneOnly ? null : new ArrayList<T>());
if (credClass == KerberosKey.class) {
// We are looking for a KerberosKey credentials for the
// serverPrincipal
Iterator<KerberosKey> iterator =
subject.getPrivateCredentials(KerberosKey.class).iterator();
if (credClass == KeyTab.class) { // Principal un-related
// We are looking for credentials unrelated to serverPrincipal
Iterator<T> 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<T> 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);
}
}
}
......
......@@ -110,7 +110,6 @@ public class Config {
public static synchronized void refresh() throws KrbException {
singleton = new Config();
KeyTab.refresh();
KdcComm.initStatic();
}
......
/*
* 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);
}
......
......@@ -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);
}
......
/*
* 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<keys.length; i++) {
this.keys[i] = (EncryptionKey)keys[i].clone();
}
eTypes = EType.getDefaults("default_tkt_enctypes", keys);
init(cname);
this.ktab = ktab;
this.password = null;
}
/**
......@@ -137,30 +137,24 @@ public final class KrbAsReqBuilder {
*/
public KrbAsReqBuilder(PrincipalName cname, char[] pass)
throws KrbException {
this(cname);
init(cname);
this.password = pass.clone();
eTypes = EType.getDefaults("default_tkt_enctypes");
this.ktab = null;
}
/**
* Retrieves an array of secret keys for the client. This is useful if
* Retrieves an array of secret keys for the client. This is used when
* the client supplies password but need keys to act as an acceptor
* (in JAAS words, isInitiator=true and storeKey=true)
* @return original keys if initiated with keys, or generated keys if
* password. In latter case, PA-DATA from server might be used to
* generate keys. All "default_tkt_enctypes" keys will be generated,
* Never null.
* @return generated keys from password. PA-DATA from server might be used.
* All "default_tkt_enctypes" keys will be generated, Never null.
* @throws IllegalStateException if not constructed from a password
* @throws KrbException
*/
public EncryptionKey[] getKeys() throws KrbException {
checkState(State.REQ_OK, "Cannot get keys");
if (keys != null) {
EncryptionKey[] result = new EncryptionKey[keys.length];
for (int i=0; i<keys.length; i++) {
result[i] = (EncryptionKey)keys[i].clone();
}
return result;
} else {
if (password != null) {
int[] eTypes = EType.getDefaults("default_tkt_enctypes");
EncryptionKey[] result = new EncryptionKey[eTypes.length];
/*
......@@ -205,6 +199,8 @@ public final class KrbAsReqBuilder {
}
}
return result;
} else {
throw new IllegalStateException("Required password not provided");
}
}
......@@ -241,12 +237,22 @@ public final class KrbAsReqBuilder {
/**
* Build a KrbAsReq object from all info fed above. Normally this method
* will be called twice: initial AS-REQ and second with pakey
* @param key null (initial AS-REQ) or pakey (with preauth)
* @return the KrbAsReq object
* @throws KrbException
* @throws IOException
*/
private KrbAsReq build() throws KrbException, IOException {
return new KrbAsReq(pakey,
private KrbAsReq build(EncryptionKey key) throws KrbException, IOException {
int[] eTypes;
if (password != null) {
eTypes = EType.getDefaults("default_tkt_enctypes");
} else {
EncryptionKey[] ks = Krb5Util.keysFromJavaxKeyTab(ktab, cname);
eTypes = EType.getDefaults("default_tkt_enctypes",
ks);
for (EncryptionKey k: ks) k.destroy();
}
return new KrbAsReq(key,
options,
cname,
sname,
......@@ -263,9 +269,10 @@ public final class KrbAsReqBuilder {
* @throws Asn1Exception
* @throws IOException
*/
private KrbAsReqBuilder resolve() throws KrbException, Asn1Exception, IOException {
if (keys != null) {
rep.decryptUsingKeys(keys, req);
private KrbAsReqBuilder resolve()
throws KrbException, Asn1Exception, IOException {
if (ktab != null) {
rep.decryptUsingKeyTab(ktab, req, cname);
} else {
rep.decryptUsingPassword(password, req, cname);
}
......@@ -292,9 +299,10 @@ public final class KrbAsReqBuilder {
private KrbAsReqBuilder send() throws KrbException, IOException {
boolean preAuthFailedOnce = false;
KdcComm comm = new KdcComm(cname.getRealmAsString());
EncryptionKey pakey = null;
while (true) {
try {
req = build();
req = build(pakey);
rep = new KrbAsRep(comm.send(req.encoding()));
return this;
} catch (KrbException ke) {
......@@ -308,7 +316,10 @@ public final class KrbAsReqBuilder {
preAuthFailedOnce = true;
KRBError kerr = ke.getError();
if (password == null) {
pakey = EncryptionKey.findKey(kerr.getEType(), keys);
EncryptionKey[] ks = Krb5Util.keysFromJavaxKeyTab(ktab, cname);
pakey = EncryptionKey.findKey(kerr.getEType(), ks);
if (pakey != null) pakey = (EncryptionKey)pakey.clone();
for (EncryptionKey k: ks) k.destroy();
} else {
PAData.SaltAndParams snp = PAData.getSaltAndParams(
kerr.getEType(), kerr.getPA());
......@@ -317,7 +328,7 @@ public final class KrbAsReqBuilder {
// does not recommend this
pakey = EncryptionKey.acquireSecretKey(password,
snp.salt == null ? cname.getSalt() : snp.salt,
eTypes[0],
EType.getDefaults("default_tkt_enctypes")[0],
null);
} else {
pakey = EncryptionKey.acquireSecretKey(password,
......@@ -369,15 +380,8 @@ public final class KrbAsReqBuilder {
*/
public void destroy() {
state = State.DESTROYED;
if (keys != null) {
for (EncryptionKey k: keys) {
k.destroy();
}
keys = null;
}
if (password != null) {
Arrays.fill(password, (char)0);
password = null;
}
}
......
......@@ -40,6 +40,7 @@ import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
......@@ -50,92 +51,138 @@ import java.util.Vector;
* This class represents key table. The key table functions deal with storing
* and retrieving service keys for use in authentication exchanges.
*
* A KeyTab object is always constructed, if the file specified does not
* exist, it's still valid but empty. If there is an I/O error or file format
* error, it's invalid.
*
* The class is immutable on the read side (the write side is only used by
* the ktab tool).
*
* @author Yanni Zhang
*/
public class KeyTab implements KeyTabConstants {
int kt_vno;
private static KeyTab singleton = null;
private static final boolean DEBUG = Krb5.DEBUG;
private static String name;
private static String defaultTabName = null;
// Attention: Currently there is no way to remove a keytab from this map,
// this might lead to a memory leak.
private static Map<String,KeyTab> 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<KeyTabEntry> 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<EncryptionKey> 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;
}
}
}
/*
* 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 =
......
/*
* 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
......
......@@ -143,7 +143,7 @@ final class ProcessEnvironment extends HashMap<String,String>
public void remove() { i.remove();}
};
}
private static Map.Entry<String,String> checkedEntry (Object o) {
private static Map.Entry<String,String> checkedEntry(Object o) {
Map.Entry<String,String> e = (Map.Entry<String,String>) o;
nonNullString(e.getKey());
nonNullString(e.getValue());
......@@ -285,7 +285,7 @@ final class ProcessEnvironment extends HashMap<String,String>
return (Map<String,String>) theEnvironment.clone();
}
// Only for use by Runtime.exec(...String[]envp...)
// Only for use by ProcessBuilder.environment(String[] envp)
static Map<String,String> emptyEnvironment(int capacity) {
return new ProcessEnvironment(capacity);
}
......@@ -299,19 +299,46 @@ final class ProcessEnvironment extends HashMap<String,String>
Collections.sort(list, entryComparator);
StringBuilder sb = new StringBuilder(size()*30);
for (Map.Entry<String,String> 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<String,String> 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<String,String> map) {
return map == null ? null :
((ProcessEnvironment)map).toEnvironmentBlock();
......
/*
* 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();
......
/*
* 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 -
......
/*
* 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];
......
......@@ -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)
//----------------------------------------------------------------
......
......@@ -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<String, String> map = new HashMap<>();
Map<String, Object> 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) {
......
/*
* 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);
}
}
......@@ -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.
* <p>
* 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
......
/*
* 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");
}
}
}
......@@ -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;
......
......@@ -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};
......
/*
* 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);
}
}
/*
* 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();
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册