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  * @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;
  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");
  82         props1.setProperty(FileHandler.class.getName() + ".pattern", "");
  83         props1.setProperty(FileHandler.class.getName() + ".count", "1");
  84         props2.setProperty("test.name", "with count=2");
  85         props2.setProperty(FileHandler.class.getName() + ".pattern", "");
  86         props2.setProperty(FileHandler.class.getName() + ".count", "2");
  87         properties = Collections.unmodifiableList(Arrays.asList(
  88                     props1,
  89                     props2));
  90     }
  91 
  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);
 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         }
 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 {
 184         public FileHandler test() throws Exception;
 185     }
 186 
 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 
 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     final static class PermissionsBuilder {
 269         final Permissions perms;
 270         public PermissionsBuilder() {
 271             this(new Permissions());
 272         }
 273         public PermissionsBuilder(Permissions perms) {
 274             this.perms = perms;
 275         }
 276         public PermissionsBuilder add(Permission p) {
 277             perms.add(p);
 278             return this;
 279         }
 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         }
 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         final Permissions permissions;
 298         final Permissions allPermissions;
 299         final AtomicBoolean allowAll;
 300         public SimplePolicy(TestCase test, AtomicBoolean allowAll) {
 301             this.allowAll = allowAll;
 302             // we don't actually need any permission to create our
 303             // FileHandlers because we're passing invalid parameters
 304             // which will make the creation fail...
 305             permissions = new Permissions();
 306 
 307             // these are used for configuring the test itself...
 308             allPermissions = new Permissions();
 309             allPermissions.add(new java.security.AllPermission());
 310 
 311         }
 312 
 313         @Override
 314         public boolean implies(ProtectionDomain domain, Permission permission) {
 315             if (allowAll.get()) return allPermissions.implies(permission);
 316             return permissions.implies(permission);
 317         }
 318 
 319         @Override
 320         public PermissionCollection getPermissions(CodeSource codesource) {
 321             return new PermissionsBuilder().addAll(allowAll.get()
 322                     ? allPermissions : permissions).toPermissions();
 323         }
 324 
 325         @Override
 326         public PermissionCollection getPermissions(ProtectionDomain domain) {
 327             return new PermissionsBuilder().addAll(allowAll.get()
 328                     ? allPermissions : permissions).toPermissions();
 329         }
 330     }
 331 
 332 }