1 /*
   2  * Copyright (c) 2014, 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  */
  53 public class FileHandlerPatternExceptions {
  54 
  55     /**
  56      * We will test null/empty pattern in two configurations.
  57      * UNSECURE: No security manager.
  58      * SECURE: With the security manager present - and the required
  59      *         permissions granted.
  60      */
  61     public static enum TestCase {
  62         UNSECURE, SECURE;
  63         public void run(Properties propertyFile) throws Exception {
  64             System.out.println("Running test case: " + name());
  65             Configure.setUp(this, propertyFile);
  66             test(this.name() + " " + propertyFile.getProperty("test.name"));
  67         }
  68     }
  69 
  70 
  71     private static final String PREFIX =
  72             "FileHandler-" + UUID.randomUUID() + ".log";
  73     private static final String userDir = System.getProperty("user.dir", ".");
  74     private static final boolean userDirWritable = Files.isWritable(Paths.get(userDir));
  75 
  76     private static final List<Properties> properties;
  77     static {
  78         Properties props1 = new Properties();
  79         Properties props2 = new Properties();
  80         props1.setProperty("test.name", "with count=1");
  81         props1.setProperty(FileHandler.class.getName() + ".pattern", "");
  82         props1.setProperty(FileHandler.class.getName() + ".count", "1");
  83         props2.setProperty("test.name", "with count=2");
  84         props2.setProperty(FileHandler.class.getName() + ".pattern", "");
  85         props2.setProperty(FileHandler.class.getName() + ".count", "2");
  86         properties = Collections.unmodifiableList(Arrays.asList(
  87                     props1,
  88                     props2));
  89     }
  90 
  91     public static void main(String... args) throws Exception {
  92 
  93 
  94         if (args == null || args.length == 0) {
  95             args = new String[] {
  96                 TestCase.UNSECURE.name(),
  97                 TestCase.SECURE.name(),
  98             };
  99         }
 100 
 101         try {
 102             for (String testName : args) {
 103                 for (Properties propertyFile : properties) {
 104                     TestCase test = TestCase.valueOf(testName);
 105                     test.run(propertyFile);
 106                 }
 107             }
 108         } finally {
 109             if (userDirWritable) {
 110                 Configure.doPrivileged(() -> {
 111                     // cleanup - delete files that have been created
 112                     try {
 113                         Files.list(Paths.get(userDir))
 114                             .filter((f) -> f.toString().contains(PREFIX))
 115                             .forEach((f) -> {
 116                                 try {
 117                                     System.out.println("deleting " + f);
 118                                     Files.delete(f);
 119                                 } catch(Throwable t) {
 120                                     System.err.println("Failed to delete " + f + ": " + t);
 121                                 }
 122                             });
 123                     } catch(Throwable t) {
 124                         System.err.println("Cleanup failed to list files: " + t);
 125                         t.printStackTrace();
 126                     }
 127                 });
 128             }
 129         }
 130     }
 131 
 132     static class Configure {
 133         static Policy policy = null;
 134         static final AtomicBoolean allowAll = new AtomicBoolean(false);
 135         static void setUp(TestCase test, Properties propertyFile) {
 136             switch (test) {
 137                 case SECURE:
 138                     if (policy == null && System.getSecurityManager() != null) {
 139                         throw new IllegalStateException("SecurityManager already set");
 140                     } else if (policy == null) {
 141                         policy = new SimplePolicy(TestCase.SECURE, allowAll);
 142                         Policy.setPolicy(policy);
 143                         System.setSecurityManager(new SecurityManager());
 144                     }
 145                     if (System.getSecurityManager() == null) {
 146                         throw new IllegalStateException("No SecurityManager.");
 147                     }
 148                     if (policy == null) {
 149                         throw new IllegalStateException("policy not configured");
 150                     }
 151                     break;
 152                 case UNSECURE:
 153                     if (System.getSecurityManager() != null) {
 154                         throw new IllegalStateException("SecurityManager already set");
 155                     }
 156                     break;
 157                 default:
 158                     new InternalError("No such testcase: " + test);
 159             }
 160             doPrivileged(() -> {
 161                 try {
 162                     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
 163                     propertyFile.store(bytes, propertyFile.getProperty("test.name"));
 164                     ByteArrayInputStream bais = new ByteArrayInputStream(bytes.toByteArray());
 165                     LogManager.getLogManager().readConfiguration(bais);
 166                 } catch (IOException ex) {
 167                     throw new RuntimeException(ex);
 168                 }
 169             });
 170         }
 171         static void doPrivileged(Runnable run) {
 172             allowAll.set(true);
 173             try {
 174                 run.run();
 175             } finally {
 176                 allowAll.set(false);
 177             }
 178         }
 179     }
 180 
 181     @FunctionalInterface
 182     public static interface FileHandlerSupplier {
 183         public FileHandler test() throws Exception;
 184     }
 185 
 186     private static void checkException(Class<? extends Exception> type, FileHandlerSupplier test) {
 187         Throwable t = null;
 188         FileHandler f = null;
 189         try {
 190             f = test.test();
 191         } catch (Throwable x) {
 192             t = x;
 193         }
 194         try {
 195             if (type != null && t == null) {
 196                 throw new RuntimeException("Expected " + type.getName() + " not thrown");
 197             } else if (type != null && t != null) {
 198                 if (type.isInstance(t)) {
 199                     System.out.println("Recieved expected exception: " + t);
 200                 } else {
 201                     throw new RuntimeException("Exception type mismatch: "
 202                         + type.getName() + " expected, "
 203                         + t.getClass().getName() + " received.", t);
 204                 }
 205             } else if (t != null) {
 206                 throw new RuntimeException("Unexpected exception received: " + t, t);
 207             }
 208         } finally {
 209             if (f != null) {
 210                 // f should always be null when an exception is expected,
 211                 // but in case the test doesn't behave as expected we will
 212                 // want to close f.
 213                 try { f.close(); } catch (Throwable x) {};
 214             }
 215         }
 216     }
 217 
 218     public static void test(String name) throws Exception {
 219         System.out.println("Testing: " + name);
 220         checkException(RuntimeException.class, () -> new FileHandler());
 221         checkException(IllegalArgumentException.class, () -> new FileHandler(""));
 222         checkException(NullPointerException.class, () -> new FileHandler(null));
 223 
 224         checkException(IllegalArgumentException.class, () -> new FileHandler("", true));
 225         checkException(IllegalArgumentException.class, () -> new FileHandler("", false));
 226         checkException(NullPointerException.class, () -> new FileHandler(null, true));
 227         checkException(NullPointerException.class, () -> new FileHandler(null, false));
 228 
 229         checkException(IllegalArgumentException.class, () -> new FileHandler("", 1, 1));
 230         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, 0, 0));
 231         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, -1, 1));
 232         checkException(IllegalArgumentException.class, () -> new FileHandler("", 0, 0));
 233         checkException(IllegalArgumentException.class, () -> new FileHandler("", -1, 1));
 234 
 235         checkException(IllegalArgumentException.class, () -> new FileHandler("", 1, 1, true));
 236         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, 0, 0, true));
 237         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, -1, 1, true));
 238         checkException(IllegalArgumentException.class, () -> new FileHandler("", 0, 0, true));
 239         checkException(IllegalArgumentException.class, () -> new FileHandler("", -1, 1, true));
 240 
 241         checkException(IllegalArgumentException.class, () -> new FileHandler("", 1, 1, false));
 242         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, 0, 0, false));
 243         checkException(IllegalArgumentException.class, () -> new FileHandler(PREFIX, -1, 1, false));
 244         checkException(IllegalArgumentException.class, () -> new FileHandler("", 0, 0, false));
 245         checkException(IllegalArgumentException.class, () -> new FileHandler("", -1, 1, false));
 246 
 247         final Class<? extends Exception> expectedException =
 248                 System.getSecurityManager() != null ? AccessControlException.class : null;
 249 
 250         if (userDirWritable || expectedException != null) {
 251             // These calls will create files in user.dir in the UNSECURE case.
 252             // The file name contain a random UUID (PREFIX) which identifies them
 253             // and allow us to remove them cleanly at the end (see finally block
 254             // in main()).
 255             checkException(expectedException,
 256                            () -> new FileHandler(PREFIX, 0, 1, true));
 257             checkException(expectedException,
 258                            () -> new FileHandler(PREFIX, 1, 2, true));
 259             checkException(expectedException,
 260                            () -> new FileHandler(PREFIX, 0, 1, false));
 261             checkException(expectedException,
 262                            () -> new FileHandler(PREFIX, 1, 2, false));
 263         }
 264     }
 265 
 266 
 267     final static class PermissionsBuilder {
 268         final Permissions perms;
 269         public PermissionsBuilder() {
 270             this(new Permissions());
 271         }
 272         public PermissionsBuilder(Permissions perms) {
 273             this.perms = perms;
 274         }
 275         public PermissionsBuilder add(Permission p) {
 276             perms.add(p);
 277             return this;
 278         }
 279         public PermissionsBuilder addAll(PermissionCollection col) {
 280             if (col != null) {
 281                 for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
 282                     perms.add(e.nextElement());
 283                 }
 284             }
 285             return this;
 286         }
 287         public Permissions toPermissions() {
 288             final PermissionsBuilder builder = new PermissionsBuilder();
 289             builder.addAll(perms);
 290             return builder.perms;
 291         }
 292     }
 293 
 294     public static class SimplePolicy extends Policy {
 295 
 296         final Permissions permissions;
 297         final Permissions allPermissions;
 298         final AtomicBoolean allowAll;
 299         public SimplePolicy(TestCase test, AtomicBoolean allowAll) {
 300             this.allowAll = allowAll;
 301             // we don't actually need any permission to create our
 302             // FileHandlers because we're passing invalid parameters
 303             // which will make the creation fail...
 304             permissions = new Permissions();
 305 
 306             // these are used for configuring the test itself...
 307             allPermissions = new Permissions();
 308             allPermissions.add(new java.security.AllPermission());
 309 
 310         }
 311 
 312         @Override
 313         public boolean implies(ProtectionDomain domain, Permission permission) {
 314             if (allowAll.get()) return allPermissions.implies(permission);
 315             return permissions.implies(permission);
 316         }
 317 
 318         @Override
 319         public PermissionCollection getPermissions(CodeSource codesource) {
 320             return new PermissionsBuilder().addAll(allowAll.get()
 321                     ? allPermissions : permissions).toPermissions();
 322         }
 323 
 324         @Override
 325         public PermissionCollection getPermissions(ProtectionDomain domain) {
 326             return new PermissionsBuilder().addAll(allowAll.get()
 327                     ? allPermissions : permissions).toPermissions();
 328         }
 329     }
 330 
 331 }