Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8184787

Granting extensions AllPermission is not enough since Java 1.6

    XMLWordPrintable

    Details

      Description

      FULL PRODUCT VERSION :
      java version "1.8.0_131"
      Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
      Java HotSpot(TM) Client VM (build 25.131-b11, mixed mode)

      But I also observe the problem with
      java version "1.6.0_45"
      Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
      Java HotSpot(TM) Client VM (build 20.45-b01, mixed mode, sharing)

      But I didn't have the problem with
      java version "1.5.0_22"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_22-b03)
      Java HotSpot(TM) Client VM (build 1.5.0_22-b03, mixed mode, sharing)

      nor with
      java version "1.4.2_19"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_19-b04)
      Java HotSpot(TM) Client VM (build 1.4.2_19-b04, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 10.0.15063]

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      I don't think there is any other relevant information. The problem is with Java and not an interaction with my computer.

      A DESCRIPTION OF THE PROBLEM :
      When I run an extension (in the JRE's lib/ext folder) and do any of several security-sensitive operations when using a SecurityManager it succeeds with Java 1.4 and 1.5 but fails with Java 1.6 and 1.8.0_101 and _131 The default grant of AllPermission. By updating rt.jar in the JREs with compiled source that comes with the JDK I was able to place a breakpoint at AccessControlContext.java line 450 in Java 1.8.0_131 and the equivalent in Java 1.5.0_22. I saw that the difference, and the reason for the failure, is that whereas 1.5 has a value for the variable 'context' that is an array of one SecurityDomain for the extension, Java 1.8 has two SecurityDomains in the array. The first is for the extension and passes the "implies" test at line 450 but the second is for the main application, which does not have AllPermission and so does not pass the "implies" test and causes an exception to be thrown. Thus the extension is not able to carry out its security-sensitive operation despite the grant of AllPermission to it.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run a Java application with the default SecurityManager and an extension that does a security-related action. Put a breakpoint in the extension and see it succeed with Java 1.4 or 1.5. See it fail with Java 1.6 or 1.8 (probably 1.7 also).

      I have a nice, simple Java program and extension that can be downloaded from Dropbox through the end of 2017 when I expect the account to be terminated.

      https://www.dropbox.com/sh/va8gjfbcifpggf4/AADlBz9oWdGeeg5Opyg8BNyta?dl=0

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      With default java.security, java.policy, and Security Manager I expect the extension to be able to do any security-sensitive operation, such as System.getProperty("user.home").
      ACTUAL -
      With Java 1.4 and 1.5 the actual result and the expected result are the same.
      With Java 1.6 and 1.8 an exception is thrown instead.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      It depends on which operation I try. Examples are:
      1 Error: java.security.AccessControlException - access denied ("java.lang.reflect.ReflectPermission" "suppressAccessChecks")
      2 Error: java.security.AccessControlException - access denied ("java.util.PropertyPermission" "java.home" "read")
      3 Error: java.security.AccessControlException - access denied ("java.util.PropertyPermission" "user.home" "read")
      7 Error: java.security.AccessControlException - access denied ("java.net.SocketPermission" "localhost:10492" "listen,resolve")

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      Here's source code for the extension. See also https://www.dropbox.com/sh/va8gjfbcifpggf4/AADlBz9oWdGeeg5Opyg8BNyta?dl=0

      /**
       * Copyright 2017 - 2017 by Worksoft.Inc
       *
       * Created by GLystad on 7/7/2017
       *
       * NOTE: This uses no classes or methods that were not present in Java 1.4.
       */

      package us.lystad.security;

      import javax.swing.*;
      import java.awt.*;
      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      import java.awt.event.WindowEvent;
      import java.io.*;
      import java.lang.reflect.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      import java.util.ArrayList;

      public class SecuTest extends JFrame {

        Button button1 = new Button("1 Toggle method accessibility.");
        Button button2 = new Button("2 getProperty(java.home/user.home)");
        Button button3 = new Button("3 OpenFile,write,close.");
        Button button4 = new Button("4 System.getSecurityManager()");
        Button button5 = new Button("5 Toggle SecurityManager there/not.");
        Button button6 = new Button("6 InputDialog");
        Button button7 = new Button("7 Listen on Socket.");
        TextArea label8 = new TextArea("");

        public static void main (String[] args) {
          try {
            new SecuTest("Security Test Application");
          } catch (Throwable xx) {
            System.out.println("Security Test error on init: " +xx.getClass().getName() +" - " +xx.getMessage());
          }
        }

        public SecuTest () {
          super("Security Test Extension");
          try {
            init(10, 400);
          } catch (Throwable xx) {
            System.out.println("Security Test Extension error on init: " +xx.getClass().getName() +" - " +xx.getMessage());
          }
        }

        public SecuTest (String title) {
          super(title);
          try {
            init(10, 0);
          } catch (Throwable xx) {
            System.out.println("Security Test App error on init: " +xx.getClass().getName() +" - " +xx.getMessage());
          }
        }

        private String button1_target_method () {
          return "hello";
        }

        private void init (int xPos, int yPos) {
          Thread runner = new Thread () {
              public void run () {
                label8.setEditable(false);

                Container pane = getContentPane();
                pane.setLayout(new GridLayout(4, 2, 0, 0));
                Color cream = new Color(0xFFFFDD);
                ButtonListener butler = new ButtonListener();

                pane.add(button1);
                pane.add(button2);
                pane.add(button3);
                pane.add(button4);
                pane.add(button5);
                pane.add(button6);
                pane.add(button7);
                pane.add(label8);

                button1.setBackground(cream);
                button1.addActionListener(butler);
                button2.setBackground(cream);
                button2.addActionListener(butler);
                button3.setBackground(cream);
                button3.addActionListener(butler);
                button4.setBackground(cream);
                button4.addActionListener(butler);
                button5.setBackground(cream);
                button5.addActionListener(butler);
                button6.setBackground(cream);
                button6.addActionListener(butler);
                button7.setBackground(cream);
                button7.addActionListener(butler);
                label8.setBackground(new Color(0xDDFFDD));
                setBounds(xPos, yPos, 700, 350);
                addWindowListener(new MyWindowListener()); // Defined below.
                setVisible(true);
              }};
          runner.start();
        }

        static public Object sendIfHandles (final Object cc, final String methodStr) {
          try {
            Class ccClass = cc.getClass();
            Method method = null;
            try {
              method = ccClass.getMethod(methodStr, (Class[]) null);
            } catch (Throwable yy) {
            }
            if (method == null) {
              method = getMethodTheHardWay(ccClass, methodStr, (Class[]) null);
            }
            boolean isAccessible = method.isAccessible();
            method.setAccessible(true);
            Object answer = method.invoke(cc, (Object[]) null);
            method.setAccessible(isAccessible);
            return answer;
          } catch (Throwable xx) {
          }
          return null;
        }

        static public Method getMethodTheHardWay (Class claB, String methodStr, final Class[] parmClasses) {
          Method current = null;
          while (claB != null) {
            Method[] methods = claB.getDeclaredMethods();
            METHOD_LOOP:
            for (int index = 0; index < methods.length; index++) {
              current = methods[index];
              if (current.getName().equals(methodStr)) {
                Class[] parmTypes = current.getParameterTypes();
                if ((parmClasses == null || parmClasses.length == 0)) {
                  if (parmTypes.length == 0) {
                    return current;
                  }
                } else if (parmTypes.length == parmClasses.length) {
                  for (int index2 = 0; index2 < parmTypes.length; index2++) {
                    if (parmTypes[index2] != parmClasses[index2]) {
                      continue METHOD_LOOP;
                    }
                  }
                  return current;
                }
              }
            }
            claB = claB.getSuperclass();
          }
          return null;
        }

        public class ButtonListener implements ActionListener {

          // This runs in the EDT in response to a button click.
          public void actionPerformed (ActionEvent evnt) {
            try {
              label8.setText("");
              Object source = evnt.getSource();
              if (!(source instanceof Button)) {
                return;
              }
              String text = ((Button)source).getLabel();
              if (text.startsWith("1")) { // TEST Class.forName
                try {
                  Class claB = SecuTest.class;
                  Method method = claB.getDeclaredMethod("button1_target_method", new Class[0]);
                  method.setAccessible(true);
                  method.setAccessible(false);
                  label8.setText("Done 1" +" - Method accessibility toggled.");
                } catch (Exception xx) {
                  label8.setText("1 Error: " +xx.getClass().getName() +" - " +xx.getMessage());
                }
              } else if (text.startsWith("2")) { // TEST System.getProperty(java.home/user.home)
                try {
                  String javaH = System.getProperty("java.home");
                  String userH = System.getProperty("user.home");
                  if (javaH == null) {javaH = "null";}
                  if (userH == null) {userH = "null";}
                  label8.setText("Done 2" +"\njava.home=" +javaH +"\nuser.home=" +userH);
                } catch (Exception xx) {
                  label8.setText("2 Error: " +xx.getClass().getName() +" - " +xx.getMessage());
                }
              } else if (text.startsWith("3")) { // TEST OpenFile,write,close.
                try {
                  String userH = System.getProperty("user.home");
                  File outFile = new File(userH + "/junk.log");
                  FileOutputStream fStream = new FileOutputStream(outFile);
                  PrintStream outStream = new PrintStream(fStream);
                  outStream.println("This is from button 3 of SecuTest program.");
                  label8.setText("Done 3: Wrote to " +userH + "/junk.log");
                } catch (Exception xx) {
                  label8.setText("3 Error: " +xx.getClass().getName() +" - " +xx.getMessage());
                }
              } else if (text.startsWith("4")) { // TEST System.getSecurityManager()
                try {
                  SecurityManager fosdic = System.getSecurityManager();
                  label8.setText("Done 4" +" " +(fosdic == null ? "{NULL}" : fosdic.getClass().getName()));
                } catch (Exception xx) {
                  label8.setText("4 Error: " +xx.getClass().getName() +" - " +xx.getMessage());
                }
              } else if (text.startsWith("5")) { // TEST Toggle SecurityManager there/not.
                try {
                  SecurityManager fosdic = System.getSecurityManager();
                  if (fosdic == null) {
                    System.setSecurityManager(new SecurityManager());
                    label8.setText("Done 5 installed.");
                  } else {
                    System.setSecurityManager(null);
                    label8.setText("Done 5 unInstalled.");
                  }
                } catch (Exception xx) {
                  label8.setText("5 Error: " +xx.getClass().getName() +" - " +xx.getMessage());
                }
              } else if (text.startsWith("6")) { // TEST InputDialog
                try {
                  String userInput = JOptionPane.showInputDialog(null, "Enter a string");
                  label8.setText("Done 6 User input: " +(userInput == null ? "{NULL}" : userInput));
                } catch (Exception xx) {
                  label8.setText("6 Error: " +xx.getClass().getName() +" - " +xx.getMessage());
                }
              } else if (text.startsWith("7")) { // TEST Listen on Socket.
                final int port = 10492;
                try {
                  final boolean[] success = new boolean[]{false, false};
                  final ArrayList errors = new ArrayList();
                  final ServerSocket ss = new ServerSocket(10492);
                  Thread socListener = new Thread ("SecuTestSocketListener") {
                      public void run () {
                        try {
                          Socket client = null;
                          try {
                            client = ss.accept();
                            success[0] = true;
                          } finally {
                            try {client.close();} catch (Exception yx) {};
                            try {ss.close();} catch (Exception xy) {};
                          }
                        } catch (SecurityException sec) {
                          synchronized(errors) {
                            errors.add(sec);
                          }
                        } catch (Exception yy) {
                        }
                      }};
                  Thread socTalker = new Thread ("SecuTestSocketTalker") {
                      public void run () {
                        try {
                          long startTime = System.currentTimeMillis(); // for debug only.
                          long endTime = System.currentTimeMillis() + 3000;
                          while (System.currentTimeMillis() < endTime && !success[1]) {
                            Socket socket = null;
                            try {
                              socket = new Socket("localhost", port);
                              success[1] = true;
                            } catch (SecurityException sec) {
                              synchronized(errors) {
                                errors.add(sec);
                              }
                              break;
                            } catch (Throwable xx) {
                              try { Thread.sleep(500);} catch (Exception ie) {}
                            } finally {
                              try {socket.close();} catch (Exception yy) {};
                            }
                            try {Thread.sleep(100);} catch (Throwable ignore) {}
                          }
                        } catch (Exception zz) {}
                      }};
                  socListener.start();
                  socTalker.start();
                  socListener.join(3500);
                  label8.setText("Done 7" +" OK?: listener=" +success[0] +" sender=" +success[1]);
                  for (int index = 0; index < errors.size(); index++) {
                    SecurityException error1 = (SecurityException)errors.get(index);
                    label8.append("\n* " +error1.getClass().getName() +" - " +error1.getMessage());
                  }
                } catch (Exception xx) {
                  label8.setText("7 Error: " +xx.getClass().getName() +" - " +xx.getMessage());
                }
              } else {
                label8.setText("Lable sans Number");
              }
            } catch (Throwable xx) {
              try {
                ByteArrayOutputStream baoStream = new ByteArrayOutputStream(20000);
                PrintStream stringStream = new PrintStream(baoStream);
                xx.printStackTrace(stringStream); //A PrintStream
                System.out.println(baoStream.toString());
                label8.setText(baoStream.toString());
              } catch (Throwable yy) {
                label8.setText(yy.getMessage() == null ? "ERROR" : yy.getMessage());
              }
            }
          }
        }

        

        public class MyWindowListener extends java.awt.event.WindowAdapter {
          public void windowClosing (WindowEvent e) {
            try {
              System.exit(0);
            } catch (Exception xx) {
              System.out.println("SecuTest Windowclosing Error on System.exit(0): " +xx.getClass().getName() +" - " +xx.getMessage());
            }
          }
        }

      }

      // $EXTENTS:$

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      You can give all permissions to the main application but that sort of defeats the purpose of the SecurityManager. You can also turn off the SecurityManager.

      I tried altering the rt.jar file just for my own use and that works great but giving it to customers violates the Java license agreement so that's out. I tried a Java Agent but it isn't given the class AccessControlAgent, or much else in the java.security package, so that didn't work. I just hope you at Oracle fix this soon because we're out of business with customers who use security managers till you do.

      Oh yes, another work around if you have no new Java features is to use Java 1.5.

        Attachments

          Activity

            People

            Assignee:
            Unassigned Unassigned
            Reporter:
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved: