/* * Copyright (c) 2014, 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 8015081 * @summary javax.security.auth.Subject.toString() throws NPE */ import java.io.File; import java.io.FileInputStream; import java.io.ObjectInputStream; import java.io.IOException; import java.lang.Exception; import java.security.Principal; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.TreeSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.management.remote.JMXPrincipal; import javax.security.auth.Subject; import javax.security.auth.x500.X500Principal; import javax.security.auth.kerberos.KerberosPrincipal; public class SubjectNullTests { // Value templates for the constructor private static Principal[] princVals = { new X500Principal("CN=Tom Sawyer, ST=Missouri, C=US"), new JMXPrincipal("Huckleberry Finn"), new KerberosPrincipal("mtwain/author@LITERATURE.US") }; private static String[] pubVals = {"tsawyer", "hfinn", "mtwain"}; private static String[] privVals = {"th3R!v3r", "oNth3R4ft", "5Cl3M3nz"}; // Templates for collection-based modifiers for the Subject private static Principal[] tmplAddPrincs = { new X500Principal("CN=John Doe, O=Bogus Corp."), new KerberosPrincipal("jdoe/admin@BOGUSCORP.COM") }; private static String[] tmplAddPubVals = {"jdoe", "djoe"}; private static String[] tmplAddPrvVals = {"b4dpa55w0rd", "pass123"}; /** * Construct a subject, and optionally place a null in any one * of the three Sets used to initialize a Subject's values */ private static Subject makeSubj(boolean nullPrinc, boolean nullPub, boolean nullPriv) { Set setPrinc = new HashSet(Arrays.asList(princVals)); Set setPubCreds = new HashSet(Arrays.asList(pubVals)); Set setPrvCreds = new HashSet(Arrays.asList(privVals)); if (nullPrinc) { setPrinc.add(null); } if (nullPub) { setPubCreds.add(null); } if (nullPriv) { setPrvCreds.add(null); } return (new Subject(false, setPrinc, setPubCreds, setPrvCreds)); } /** * Provide a simple interface for abstracting collection-on-collection * functions */ public interface Function { boolean execCollection(Set subjSet, Collection actorData); } public static final Function methAdd = new Function() { public boolean execCollection(Set subjSet, Collection actorData) { return subjSet.addAll((Collection)actorData); } }; public static final Function methContains = new Function() { public boolean execCollection(Set subjSet, Collection actorData) { return subjSet.containsAll(actorData); } }; public static final Function methRemove = new Function() { public boolean execCollection(Set subjSet, Collection actorData) { return subjSet.removeAll(actorData); } }; public static final Function methRetain = new Function() { public boolean execCollection(Set subjSet, Collection actorData) { return subjSet.retainAll(actorData); } }; /** * Run a test using a specified Collection method upon a Subject's * SecureSet fields. This method expects NullPointerExceptions * to be thrown, and throws RuntimeException when the operation * succeeds */ private static void nullTestCollection(Function meth, Set subjSet, Collection actorData) { try { meth.execCollection(subjSet, actorData); throw new RuntimeException("Failed to throw NullPointerException"); } catch (NullPointerException npe) { System.out.println("Caught expected NullPointerException [PASS]"); } } /** * Run a test using a specified Collection method upon a Subject's * SecureSet fields. This method expects the function and arguments * passed in to complete without exception. It returns false * if either an exception occurs or the result of the operation is * false. */ private static boolean validTestCollection(Function meth, Set subjSet, Collection actorData) { boolean result = false; try { result = meth.execCollection(subjSet, actorData); } catch (Exception exc) { System.out.println("Caught exception " + exc); } return result; } /** * Deserialize an object from a file. * * @param type The {@code Class} that the serialized file is supposed * to contain. * @param fileName The name of the file to be deserialized. * * @return An object of the type specified in the {@code type} parameter */ private static T deserializeFile(Class type, String fileName) throws IOException, ClassNotFoundException { File fileObj = new File(System.getProperty("test.src", "."), fileName); FileInputStream fis = new FileInputStream(fileObj); ObjectInputStream ois = new ObjectInputStream(fis); T newObj = (T)ois.readObject(); ois.close(); fis.close(); return newObj; } private static void testCTOR() { System.out.println("------ constructor ------"); try { // Case 1: Create a subject with a null principal // Expected result: NullPointerException Subject mtSubj = makeSubj(true, false, false); throw new RuntimeException( "constructor [principal w/ null]: Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println("constructor [principal w/ null]: " + "NullPointerException [PASS]"); } try { // Case 2: Create a subject with a null public credential element // Expected result: NullPointerException Subject mtSubj = makeSubj(false, true, false); throw new RuntimeException( "constructor [pub cred w/ null]: Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println("constructor [pub cred w/ null]: " + "NullPointerException [PASS]"); } try { // Case 3: Create a subject with a null private credential element // Expected result: NullPointerException Subject mtSubj = makeSubj(false, false, true); throw new RuntimeException( "constructor [priv cred w/ null]: Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println("constructor [priv cred w/ null]: " + "NullPointerException [PASS]"); } // Case 4: Create a new subject using the principals, public // and private credentials from another well-formed subject // Expected result: Successful construction Subject srcSubj = makeSubj(false, false, false); Subject mtSubj = new Subject(false, srcSubj.getPrincipals(), srcSubj.getPublicCredentials(), srcSubj.getPrivateCredentials()); System.out.println("Construction from another well-formed Subject's " + "principals/creds [PASS]"); } private static void testDeserialize() throws Exception { System.out.println("------ deserialize -----"); Subject subj = null; Set prin = null; // Case 1: positive deserialization test of a Subject // Expected result: well-formed Subject subj = deserializeFile(Subject.class, "SubjNoNull.bin"); System.out.println("Positive deserialization test (Subject) passed"); // Case 2: positive deserialization test of a SecureSet // Expected result: well-formed Set prin = deserializeFile(Set.class, "PrinNoNull.bin"); System.out.println("Positive deserialization test (SecureSet) passed"); System.out.println( "* Testing deserialization with null-poisoned objects"); // Case 3: deserialization test of a null-poisoned Subject // Expected result: NullPointerException try { subj = deserializeFile(Subject.class, "SubjWithNull.bin"); throw new RuntimeException("Failed to throw NullPointerException"); } catch (NullPointerException npe) { System.out.println("Caught expected NullPointerException [PASS]"); } // Case 4: deserialization test of a null-poisoned SecureSet // Expected result: NullPointerException try { prin = deserializeFile(Set.class, "PrinWithNull.bin"); throw new RuntimeException("Failed to throw NullPointerException"); } catch (NullPointerException npe) { System.out.println("Caught expected NullPointerException [PASS]"); } } private static void testAdd() { System.out.println("------ add() ------"); // Create a well formed subject Subject mtSubj = makeSubj(false, false, false); try { // Case 1: Attempt to add null values to principal // Expected result: NullPointerException mtSubj.getPrincipals().add(null); throw new RuntimeException( "PRINCIPAL add(null): Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println( "PRINCIPAL add(null): NullPointerException [PASS]"); } try { // Case 2: Attempt to add null into the public creds // Expected result: NullPointerException mtSubj.getPublicCredentials().add(null); throw new RuntimeException( "PUB CRED add(null): Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println( "PUB CRED add(null): NullPointerException [PASS]"); } try { // Case 3: Attempt to add null into the private creds // Expected result: NullPointerException mtSubj.getPrivateCredentials().add(null); throw new RuntimeException( "PRIV CRED add(null): Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println( "PRIV CRED add(null): NullPointerException [PASS]"); } } private static void testRemove() { System.out.println("------ remove() ------"); // Create a well formed subject Subject mtSubj = makeSubj(false, false, false); try { // Case 1: Attempt to remove null values from principal // Expected result: NullPointerException mtSubj.getPrincipals().remove(null); throw new RuntimeException( "PRINCIPAL remove(null): Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println( "PRINCIPAL remove(null): NullPointerException [PASS]"); } try { // Case 2: Attempt to remove null from the public creds // Expected result: NullPointerException mtSubj.getPublicCredentials().remove(null); throw new RuntimeException( "PUB CRED remove(null): Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println( "PUB CRED remove(null): NullPointerException [PASS]"); } try { // Case 3: Attempt to remove null from the private creds // Expected result: NullPointerException mtSubj.getPrivateCredentials().remove(null); throw new RuntimeException( "PRIV CRED remove(null): Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println( "PRIV CRED remove(null): NullPointerException [PASS]"); } } private static void testContains() { System.out.println("------ contains() ------"); // Create a well formed subject Subject mtSubj = makeSubj(false, false, false); try { // Case 1: Attempt to check for null values in principals // Expected result: NullPointerException mtSubj.getPrincipals().contains(null); throw new RuntimeException( "PRINCIPAL contains(null): Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println( "PRINCIPAL contains(null): NullPointerException [PASS]"); } try { // Case 2: Attempt to check for null in public creds // Expected result: NullPointerException mtSubj.getPublicCredentials().contains(null); throw new RuntimeException( "PUB CRED contains(null): Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println( "PUB CRED contains(null): NullPointerException [PASS]"); } try { // Case 3: Attempt to check for null in private creds // Expected result: NullPointerException mtSubj.getPrivateCredentials().contains(null); throw new RuntimeException( "PRIV CRED contains(null): Failed to throw NPE"); } catch (NullPointerException npe) { System.out.println( "PRIV CRED contains(null): NullPointerException [PASS]"); } } private static void testAddAll() { // Create a well formed subject and additional collections Subject mtSubj = makeSubj(false, false, false); Set morePrincs = new HashSet<>(Arrays.asList(tmplAddPrincs)); Set morePubVals = new HashSet<>(Arrays.asList(tmplAddPubVals)); Set morePrvVals = new HashSet<>(Arrays.asList(tmplAddPrvVals)); // Run one success test for each Subject family to verify the // overloaded method works as intended. Set setPrin = mtSubj.getPrincipals(); Set setPubCreds = mtSubj.getPublicCredentials(); Set setPrvCreds = mtSubj.getPrivateCredentials(); int prinOrigSize = setPrin.size(); int pubOrigSize = setPubCreds.size(); int prvOrigSize = setPrvCreds.size(); System.out.println("------ addAll() -----"); // Add the new members, then check the resulting size of the // Subject attributes to verify they've increased by the proper // amounts. if ((validTestCollection(methAdd, setPrin, morePrincs) != true) || (setPrin.size() != prinOrigSize + morePrincs.size())) { throw new RuntimeException("Failed addAll() on principals"); } if ((validTestCollection(methAdd, setPubCreds, morePubVals) != true) || (setPubCreds.size() != pubOrigSize + morePubVals.size())) { throw new RuntimeException("Failed addAll() on public creds"); } if ((validTestCollection(methAdd, setPrvCreds, morePrvVals) != true) || (setPrvCreds.size() != prvOrigSize + morePrvVals.size())) { throw new RuntimeException("Failed addAll() on private creds"); } System.out.println("Positive addAll() test passed"); // Now add null elements into each container, then retest morePrincs.add(null); morePubVals.add(null); morePrvVals.add(null); System.out.println("* Testing addAll w/ null values on Principals"); nullTestCollection(methAdd, mtSubj.getPrincipals(), null); nullTestCollection(methAdd, mtSubj.getPrincipals(), morePrincs); System.out.println("* Testing addAll w/ null values on Public Creds"); nullTestCollection(methAdd, mtSubj.getPublicCredentials(), null); nullTestCollection(methAdd, mtSubj.getPublicCredentials(), morePubVals); System.out.println("* Testing addAll w/ null values on Private Creds"); nullTestCollection(methAdd, mtSubj.getPrivateCredentials(), null); nullTestCollection(methAdd, mtSubj.getPrivateCredentials(), morePrvVals); } private static void testRemoveAll() { // Create a well formed subject and additional collections Subject mtSubj = makeSubj(false, false, false); Set remPrincs = new HashSet<>(); Set remPubVals = new HashSet<>(); Set remPrvVals = new HashSet<>(); remPrincs.add(new KerberosPrincipal("mtwain/author@LITERATURE.US")); remPubVals.add("mtwain"); remPrvVals.add("5Cl3M3nz"); // Run one success test for each Subject family to verify the // overloaded method works as intended. Set setPrin = mtSubj.getPrincipals(); Set setPubCreds = mtSubj.getPublicCredentials(); Set setPrvCreds = mtSubj.getPrivateCredentials(); int prinOrigSize = setPrin.size(); int pubOrigSize = setPubCreds.size(); int prvOrigSize = setPrvCreds.size(); System.out.println("------ removeAll() -----"); // Remove the specified members, then check the resulting size of the // Subject attributes to verify they've decreased by the proper // amounts. if ((validTestCollection(methRemove, setPrin, remPrincs) != true) || (setPrin.size() != prinOrigSize - remPrincs.size())) { throw new RuntimeException("Failed removeAll() on principals"); } if ((validTestCollection(methRemove, setPubCreds, remPubVals) != true) || (setPubCreds.size() != pubOrigSize - remPubVals.size())) { throw new RuntimeException("Failed removeAll() on public creds"); } if ((validTestCollection(methRemove, setPrvCreds, remPrvVals) != true) || (setPrvCreds.size() != prvOrigSize - remPrvVals.size())) { throw new RuntimeException("Failed removeAll() on private creds"); } System.out.println("Positive removeAll() test passed"); // Now add null elements into each container, then retest remPrincs.add(null); remPubVals.add(null); remPrvVals.add(null); System.out.println("* Testing removeAll w/ null values on Principals"); nullTestCollection(methRemove, mtSubj.getPrincipals(), null); nullTestCollection(methRemove, mtSubj.getPrincipals(), remPrincs); System.out.println( "* Testing removeAll w/ null values on Public Creds"); nullTestCollection(methRemove, mtSubj.getPublicCredentials(), null); nullTestCollection(methRemove, mtSubj.getPublicCredentials(), remPubVals); System.out.println( "* Testing removeAll w/ null values on Private Creds"); nullTestCollection(methRemove, mtSubj.getPrivateCredentials(), null); nullTestCollection(methRemove, mtSubj.getPrivateCredentials(), remPrvVals); } private static void testContainsAll() { // Create a well formed subject and additional collections Subject mtSubj = makeSubj(false, false, false); Set testPrincs = new HashSet<>(Arrays.asList(princVals)); Set testPubVals = new HashSet<>(Arrays.asList(pubVals)); Set testPrvVals = new HashSet<>(Arrays.asList(privVals)); System.out.println("------ containsAll() -----"); // Run one success test for each Subject family to verify the // overloaded method works as intended. if ((validTestCollection(methContains, mtSubj.getPrincipals(), testPrincs) == false) && (validTestCollection(methContains, mtSubj.getPublicCredentials(), testPubVals) == false) && (validTestCollection(methContains, mtSubj.getPrivateCredentials(), testPrvVals) == false)) { throw new RuntimeException("Valid containsAll() check failed"); } System.out.println("Positive containsAll() test passed"); // Now let's add a null into each collection and watch the fireworks. testPrincs.add(null); testPubVals.add(null); testPrvVals.add(null); System.out.println( "* Testing containsAll w/ null values on Principals"); nullTestCollection(methContains, mtSubj.getPrincipals(), null); nullTestCollection(methContains, mtSubj.getPrincipals(), testPrincs); System.out.println( "* Testing containsAll w/ null values on Public Creds"); nullTestCollection(methContains, mtSubj.getPublicCredentials(), null); nullTestCollection(methContains, mtSubj.getPublicCredentials(), testPubVals); System.out.println( "* Testing containsAll w/ null values on Private Creds"); nullTestCollection(methContains, mtSubj.getPrivateCredentials(), null); nullTestCollection(methContains, mtSubj.getPrivateCredentials(), testPrvVals); } private static void testRetainAll() { // Create a well formed subject and additional collections Subject mtSubj = makeSubj(false, false, false); Set remPrincs = new HashSet<>(Arrays.asList(tmplAddPrincs)); Set remPubVals = new HashSet<>(Arrays.asList(tmplAddPubVals)); Set remPrvVals = new HashSet<>(Arrays.asList(tmplAddPrvVals)); // Add in values that exist within the Subject remPrincs.add(princVals[2]); remPubVals.add(pubVals[2]); remPrvVals.add(privVals[2]); // Run one success test for each Subject family to verify the // overloaded method works as intended. Set setPrin = mtSubj.getPrincipals(); Set setPubCreds = mtSubj.getPublicCredentials(); Set setPrvCreds = mtSubj.getPrivateCredentials(); int prinOrigSize = setPrin.size(); int pubOrigSize = setPubCreds.size(); int prvOrigSize = setPrvCreds.size(); System.out.println("------ retainAll() -----"); // Retain the specified members (those that exist in the Subject) // and validate the results. if (validTestCollection(methRetain, setPrin, remPrincs) == false || setPrin.size() != 1 || setPrin.contains(princVals[2]) == false) { throw new RuntimeException("Failed retainAll() on principals"); } if (validTestCollection(methRetain, setPubCreds, remPubVals) == false || setPubCreds.size() != 1 || setPubCreds.contains(pubVals[2]) == false) { throw new RuntimeException("Failed retainAll() on public creds"); } if (validTestCollection(methRetain, setPrvCreds, remPrvVals) == false || setPrvCreds.size() != 1 || setPrvCreds.contains(privVals[2]) == false) { throw new RuntimeException("Failed retainAll() on private creds"); } System.out.println("Positive retainAll() test passed"); // Now add null elements into each container, then retest remPrincs.add(null); remPubVals.add(null); remPrvVals.add(null); System.out.println("* Testing retainAll w/ null values on Principals"); nullTestCollection(methRetain, mtSubj.getPrincipals(), null); nullTestCollection(methRetain, mtSubj.getPrincipals(), remPrincs); System.out.println( "* Testing retainAll w/ null values on Public Creds"); nullTestCollection(methRetain, mtSubj.getPublicCredentials(), null); nullTestCollection(methRetain, mtSubj.getPublicCredentials(), remPubVals); System.out.println( "* Testing retainAll w/ null values on Private Creds"); nullTestCollection(methRetain, mtSubj.getPrivateCredentials(), null); nullTestCollection(methRetain, mtSubj.getPrivateCredentials(), remPrvVals); } private static void testIsEmpty() { Subject populatedSubj = makeSubj(false, false, false); Subject emptySubj = new Subject(); System.out.println("------ isEmpty() -----"); if (populatedSubj.getPrincipals().isEmpty()) { throw new RuntimeException( "Populated Subject Principals incorrectly returned empty"); } if (emptySubj.getPrincipals().isEmpty() == false) { throw new RuntimeException( "Empty Subject Principals incorrectly returned non-empty"); } System.out.println("isEmpty() test passed"); } private static void testSecureSetEquals() { System.out.println("------ SecureSet.equals() -----"); Subject subj = makeSubj(false, false, false); // Case 1: null comparison [expect false] if (subj.getPublicCredentials().equals(null) != false) { throw new RuntimeException( "equals(null) incorrectly returned true"); } // Case 2: Self-comparison [expect true] Set princs = subj.getPrincipals(); princs.equals(subj.getPrincipals()); // Case 3: Comparison with non-Set type [expect false] List listPrinc = new LinkedList(Arrays.asList(princVals)); if (subj.getPublicCredentials().equals(listPrinc) != false) { throw new RuntimeException( "equals([Non-Set]) incorrectly returned true"); } // Case 4: SecureSets of differing sizes [expect false] Subject subj1princ = new Subject(); Subject subj2princ = new Subject(); subj1princ.getPrincipals().add( new X500Principal("CN=Tom Sawyer, ST=Missouri, C=US")); subj1princ.getPrincipals().add( new X500Principal("CN=John Doe, O=Bogus Corp.")); subj2princ.getPrincipals().add( new X500Principal("CN=Tom Sawyer, ST=Missouri, C=US")); if (subj1princ.getPrincipals().equals( subj2princ.getPrincipals()) != false) { throw new RuntimeException( "equals([differing sizes]) incorrectly returned true"); } // Case 5: Content equality test [expect true] Set equalSet = new HashSet(Arrays.asList(princVals)); if (subj.getPrincipals().equals(equalSet) != true) { throw new RuntimeException( "equals([equivalent set]) incorrectly returned false"); } // Case 5: Content inequality test [expect false] // Note: to not fall into the size inequality check the two // sets need to have the same number of elements. Set inequalSet = new HashSet(Arrays.asList(tmplAddPrincs)); inequalSet.add(new JMXPrincipal("Samuel Clemens")); if (subj.getPrincipals().equals(inequalSet) != false) { throw new RuntimeException( "equals([equivalent set]) incorrectly returned false"); } System.out.println("SecureSet.equals() tests passed"); } private static void testToArray() { System.out.println("------ toArray() -----"); Subject subj = makeSubj(false, false, false); // Case 1: no-parameter toArray with equality comparison // Expected result: true List alSubj = Arrays.asList(subj.getPrincipals().toArray()); List alPrincs = Arrays.asList(princVals); if (alSubj.size() != alPrincs.size() || alSubj.containsAll(alPrincs) != true) { throw new RuntimeException( "Unexpected inequality on returned toArray()"); } // Case 2: generic-type toArray where passed array is of sufficient // size. // Expected result: returned Array is reference-equal to input param // and content equal to data used to construct the originating Subject. Principal[] pBlock = new Principal[3]; Principal[] pBlockRef = subj.getPrincipals().toArray(pBlock); alSubj = Arrays.asList(pBlockRef); if (pBlockRef != pBlock) { throw new RuntimeException( "Unexpected reference-inequality on returned toArray(T[])"); } else if (alSubj.size() != alPrincs.size() || alSubj.containsAll(alPrincs) != true) { throw new RuntimeException( "Unexpected content-inequality on returned toArray(T[])"); } // Case 3: generic-type toArray where passed array is of // insufficient size. // Expected result: returned Array is not reference-equal to // input param but is content equal to data used to construct the // originating Subject. pBlock = new Principal[1]; pBlockRef = subj.getPrincipals().toArray(pBlock); alSubj = Arrays.asList(pBlockRef); if (pBlockRef == pBlock) { throw new RuntimeException( "Unexpected reference-equality on returned toArray(T[])"); } else if (alSubj.size() != alPrincs.size() || alSubj.containsAll(alPrincs) != true) { throw new RuntimeException( "Unexpected content-inequality on returned toArray(T[])"); } System.out.println("toArray() tests passed"); } public static void main(String[] args) throws Exception { testCTOR(); testDeserialize(); testAdd(); testRemove(); testContains(); testAddAll(); testRemoveAll(); testContainsAll(); testRetainAll(); testIsEmpty(); testSecureSetEquals(); testToArray(); } }