1 /*
2  * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 import java.io.ByteArrayInputStream;
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.nio.file.Files;
27 import java.nio.file.Paths;
28 import java.security.AccessControlException;
29 import java.security.CodeSource;
30 import java.security.Permission;
31 import java.security.PermissionCollection;
32 import java.security.Permissions;
33 import java.security.Policy;
34 import java.security.ProtectionDomain;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.Enumeration;
38 import java.util.List;
39 import java.util.Properties;
40 import java.util.UUID;
41 import java.util.concurrent.atomic.AtomicBoolean;
42 import java.util.logging.FileHandler;
43 import java.util.logging.LogManager;
44 
45 /**
46  * @test
47  * @bug 8025690
48  * @summary tests that an empty or null pattern always result in an exception.
49  * @run main/othervm FileHandlerPatternExceptions UNSECURE
50  * @run main/othervm FileHandlerPatternExceptions SECURE
51  * @author danielfuchs
52  * @key randomness
53  */
54 public class FileHandlerPatternExceptions {
55 
56     /**
57      * We will test null/empty pattern in two configurations.
58      * UNSECURE: No security manager.
59      * SECURE: With the security manager present - and the required
60      *         permissions granted.
61      */
62     public static enum TestCase {
63         UNSECURE, SECURE;
run(Properties propertyFile)64         public void run(Properties propertyFile) throws Exception {
65             System.out.println("Running test case: " + name());
66             Configure.setUp(this, propertyFile);
67             test(this.name() + " " + propertyFile.getProperty("test.name"));
68         }
69     }
70 
71 
72     private static final String PREFIX =
73             "FileHandler-" + UUID.randomUUID() + ".log";
74     private static final String userDir = System.getProperty("user.dir", ".");
75     private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir));
76 
77     private static final List<Properties> properties;
78     static {
79         Properties props1 = new Properties();
80         Properties props2 = new Properties();
81         props1.setProperty("test.name", "with count=1");
FileHandler.class.getName()82         props1.setProperty(FileHandler.class.getName() + ".pattern", "");
FileHandler.class.getName()83         props1.setProperty(FileHandler.class.getName() + ".count", "1");
84         props2.setProperty("test.name", "with count=2");
FileHandler.class.getName()85         props2.setProperty(FileHandler.class.getName() + ".pattern", "");
FileHandler.class.getName()86         props2.setProperty(FileHandler.class.getName() + ".count", "2");
87         properties = Collections.unmodifiableList(Arrays.asList(
88                     props1,
89                     props2));
90     }
91 
main(String... args)92     public static void main(String... args) throws Exception {
93 
94 
95         if (args == null || args.length == 0) {
96             args = new String[] {
97                 TestCase.UNSECURE.name(),
98                 TestCase.SECURE.name(),
99             };
100         }
101 
102         try {
103             for (String testName : args) {
104                 for (Properties propertyFile : properties) {
105                     TestCase test = TestCase.valueOf(testName);
106                     test.run(propertyFile);
107                 }
108             }
109         } finally {
110             if (userDirWritable) {
111                 Configure.doPrivileged(() -> {
112                     // cleanup - delete files that have been created
113                     try {
114                         Files.list(Paths.get(userDir))
115                             .filter((f) -> f.toString().contains(PREFIX))
116                             .forEach((f) -> {
117                                 try {
118                                     System.out.println("deleting " + f);
119                                     Files.delete(f);
120                                 } catch(Throwable t) {
121                                     System.err.println("Failed to delete " + f + ": " + t);
122                                 }
123                             });
124                     } catch(Throwable t) {
125                         System.err.println("Cleanup failed to list files: " + t);
126                         t.printStackTrace();
127                     }
128                 });
129             }
130         }
131     }
132 
133     static class Configure {
134         static Policy policy = null;
135         static final AtomicBoolean allowAll = new AtomicBoolean(false);
setUp(TestCase test, Properties propertyFile)136         static void setUp(TestCase test, Properties propertyFile) {
137             switch (test) {
138                 case SECURE:
139                     if (policy == null && System.getSecurityManager() != null) {
140                         throw new IllegalStateException("SecurityManager already set");
141                     } else if (policy == null) {
142                         policy = new SimplePolicy(TestCase.SECURE, allowAll);
143                         Policy.setPolicy(policy);
144                         System.setSecurityManager(new SecurityManager());
145                     }
146                     if (System.getSecurityManager() == null) {
147                         throw new IllegalStateException("No SecurityManager.");
148                     }
149                     if (policy == null) {
150                         throw new IllegalStateException("policy not configured");
151                     }
152                     break;
153                 case UNSECURE:
154                     if (System.getSecurityManager() != null) {
155                         throw new IllegalStateException("SecurityManager already set");
156                     }
157                     break;
158                 default:
159                     new InternalError("No such testcase: " + test);
160             }
161             doPrivileged(() -> {
162                 try {
163                     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
164                     propertyFile.store(bytes, propertyFile.getProperty("test.name"));
165                     ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
166                     LogManager.getLogManager().readConfiguration(bais);
167                 } catch (IOException ex) {
168                     throw new RuntimeException(ex);
169                 }
170             });
171         }
doPrivileged(Runnable run)172         static void doPrivileged(Runnable run) {
173             allowAll.set(true);
174             try {
175                 run.run();
176             } finally {
177                 allowAll.set(false);
178             }
179         }
180     }
181 
182     @FunctionalInterface
183     public static interface FileHandlerSupplier {
test()184         public FileHandler test() throws Exception;
185     }
186 
checkException(Class<? extends Exception> type, FileHandlerSupplier test)187     private static void checkException(Class<? extends Exception> type, FileHandlerSupplier test) {
188         Throwable t = null;
189         FileHandler f = null;
190         try {
191             f = test.test();
192         } catch (Throwable x) {
193             t = x;
194         }
195         try {
196             if (type != null && t == null) {
197                 throw new RuntimeException("Expected " + type.getName() + " not thrown");
198             } else if (type != null && t != null) {
199                 if (type.isInstance(t)) {
200                     System.out.println("Recieved expected exception: " + t);
201                 } else {
202                     throw new RuntimeException("Exception type mismatch: "
203                         + type.getName() + " expected, "
204                         + t.getClass().getName() + " received.", t);
205                 }
206             } else if (t != null) {
207                 throw new RuntimeException("Unexpected exception received: " + t, t);
208             }
209         } finally {
210             if (f != null) {
211                 // f should always be null when an exception is expected,
212                 // but in case the test doesn't behave as expected we will
213                 // want to close f.
214                 try { f.close(); } catch (Throwable x) {};
215             }
216         }
217     }
218 
test(String name)219     public static void test(String name) throws Exception {
220         System.out.println("Testing: " + name);
221         checkException(RuntimeException.class, () -> new FileHandler());
222         checkException(IllegalArgumentException.class, () -> new FileHandler(""));
223         checkException(NullPointerException.class, () -> new FileHandler(null));
224 
225         checkException(IllegalArgumentException.class, () -> new FileHandler("", true));
226         checkException(IllegalArgumentException.class, () -> new FileHandler("", false));
227         checkException(NullPointerException.class, () -> new FileHandler(null, true));
228         checkException(NullPointerException.class, () -> new FileHandler(null, false));
229 
230         checkException(IllegalArgumentException.class, () -> new FileHandler("", 1, 1));
231         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, 0, 0));
232         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, -1, 1));
233         checkException(IllegalArgumentException.class, () -> new FileHandler("", 0, 0));
234         checkException(IllegalArgumentException.class, () -> new FileHandler("", -1, 1));
235 
236         checkException(IllegalArgumentException.class, () -> new FileHandler("", 1, 1, true));
237         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, 0, 0, true));
238         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, -1, 1, true));
239         checkException(IllegalArgumentException.class, () -> new FileHandler("", 0, 0, true));
240         checkException(IllegalArgumentException.class, () -> new FileHandler("", -1, 1, true));
241 
242         checkException(IllegalArgumentException.class, () -> new FileHandler("", 1, 1, false));
243         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, 0, 0, false));
244         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, -1, 1, false));
245         checkException(IllegalArgumentException.class, () -> new FileHandler("", 0, 0, false));
246         checkException(IllegalArgumentException.class, () -> new FileHandler("", -1, 1, false));
247 
248         final Class<? extends Exception> expectedException =
249                 System.getSecurityManager() != null ? AccessControlException.class : null;
250 
251         if (userDirWritable || expectedException != null) {
252             // These calls will create files in user.dir in the UNSECURE case.
253             // The file name contain a random UUID (PREFIX) which identifies them
254             // and allow us to remove them cleanly at the end (see finally block
255             // in main()).
256             checkException(expectedException,
257                            () -> new FileHandler(PREFIX, 0, 1, true));
258             checkException(expectedException,
259                            () -> new FileHandler(PREFIX, 1, 2, true));
260             checkException(expectedException,
261                            () -> new FileHandler(PREFIX, 0, 1, false));
262             checkException(expectedException,
263                            () -> new FileHandler(PREFIX, 1, 2, false));
264         }
265     }
266 
267 
268     static final class PermissionsBuilder {
269         final Permissions perms;
PermissionsBuilder()270         public PermissionsBuilder() {
271             this(new Permissions());
272         }
PermissionsBuilder(Permissions perms)273         public PermissionsBuilder(Permissions perms) {
274             this.perms = perms;
275         }
add(Permission p)276         public PermissionsBuilder add(Permission p) {
277             perms.add(p);
278             return this;
279         }
addAll(PermissionCollection col)280         public PermissionsBuilder addAll(PermissionCollection col) {
281             if (col != null) {
282                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
283                     perms.add(e.nextElement());
284                 }
285             }
286             return this;
287         }
toPermissions()288         public Permissions toPermissions() {
289             final PermissionsBuilder builder = new PermissionsBuilder();
290             builder.addAll(perms);
291             return builder.perms;
292         }
293     }
294 
295     public static class SimplePolicy extends Policy {
296 
297         static final Policy DEFAULT_POLICY = Policy.getPolicy();
298 
299         final Permissions permissions;
300         final Permissions allPermissions;
301         final AtomicBoolean allowAll;
SimplePolicy(TestCase test, AtomicBoolean allowAll)302         public SimplePolicy(TestCase test, AtomicBoolean allowAll) {
303             this.allowAll = allowAll;
304             // we don't actually need any permission to create our
305             // FileHandlers because we're passing invalid parameters
306             // which will make the creation fail...
307             permissions = new Permissions();
308 
309             // these are used for configuring the test itself...
310             allPermissions = new Permissions();
311             allPermissions.add(new java.security.AllPermission());
312 
313         }
314 
315         @Override
implies(ProtectionDomain domain, Permission permission)316         public boolean implies(ProtectionDomain domain, Permission permission) {
317             if (allowAll.get()) return allPermissions.implies(permission);
318             return permissions.implies(permission) || DEFAULT_POLICY.implies(domain, permission);
319         }
320 
321         @Override
getPermissions(CodeSource codesource)322         public PermissionCollection getPermissions(CodeSource codesource) {
323             return new PermissionsBuilder().addAll(allowAll.get()
324                     ? allPermissions : permissions).toPermissions();
325         }
326 
327         @Override
getPermissions(ProtectionDomain domain)328         public PermissionCollection getPermissions(ProtectionDomain domain) {
329             return new PermissionsBuilder().addAll(allowAll.get()
330                     ? allPermissions : permissions).toPermissions();
331         }
332     }
333 
334 }
335