From 032cabff13965a7b6eef4aa269ff9b4d25faef6f Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Wed, 30 Aug 2006 20:30:10 +0000
Subject: [PATCH] Provide a mechanism for managing file permissions.  On UNIX-based systems where the use of exec is allowed, it will use the underlying chmod utility to set file permissions.  On other systems, if Java 6 is available then the new methods in the java.io.File class will be used.  If neither option is available, then it will not be possible to manage file permissions.

---
 opends/src/server/org/opends/server/util/StaticUtils.java         |   37 +
 opends/src/server/org/opends/server/types/FilePermission.java     | 1043 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 opends/src/server/org/opends/server/messages/UtilityMessages.java |  101 +++++
 opends/src/server/org/opends/server/util/ServerConstants.java     |   12 
 4 files changed, 1,193 insertions(+), 0 deletions(-)

diff --git a/opends/src/server/org/opends/server/messages/UtilityMessages.java b/opends/src/server/org/opends/server/messages/UtilityMessages.java
index 246c66b..b3dafb2 100644
--- a/opends/src/server/org/opends/server/messages/UtilityMessages.java
+++ b/opends/src/server/org/opends/server/messages/UtilityMessages.java
@@ -1515,6 +1515,76 @@
 
 
   /**
+   * The message ID for the message that will be used if an attempt is made to
+   * set the permissions of a file that does not exist.  This takes a single
+   * argument, which is the path to the specified file.
+   */
+  public static final int MSGID_FILEPERM_SET_NO_SUCH_FILE =
+       CATEGORY_MASK_UTIL | SEVERITY_MASK_MILD_ERROR | 141;
+
+
+
+  /**
+   * The message ID for the message that will be used if an error occurs while
+   * trying to execute the chmod command.  This takes two arguments, which are
+   * the path to the file and a message explaining the problem that occurred.
+   */
+  public static final int MSGID_FILEPERM_CANNOT_EXEC_CHMOD =
+       CATEGORY_MASK_UTIL | SEVERITY_MASK_MILD_ERROR | 142;
+
+
+
+  /**
+   * The message ID for the message that will be used if an exception is thrown
+   * while attempting to update file permissions.  This takes a single argument,
+   * which is the path to the file being updated.
+   */
+  public static final int MSGID_FILEPERM_SET_JAVA_EXCEPTION =
+       CATEGORY_MASK_UTIL | SEVERITY_MASK_SEVERE_ERROR | 143;
+
+
+
+  /**
+   * The message ID for the message that will be used if at least one attempt
+   * to update file permissions failed, but at least one attempt was successful.
+   * This takes a single argument, which is the path to the file being updated.
+   */
+  public static final int MSGID_FILEPERM_SET_JAVA_FAILED_ALTERED =
+       CATEGORY_MASK_UTIL | SEVERITY_MASK_SEVERE_ERROR | 144;
+
+
+
+  /**
+   * The message ID for the message that will be used if all attempts to update
+   * file permissions failed.  This takes a single argument, which is the path
+   * to the file being updated.
+   */
+  public static final int MSGID_FILEPERM_SET_JAVA_FAILED_UNALTERED =
+       CATEGORY_MASK_UTIL | SEVERITY_MASK_SEVERE_ERROR | 145;
+
+
+
+  /**
+   * The message ID for the message that will be used if an invalid UNIX mode
+   * string is provided.  This takes a single argument, which is the provided
+   * mode string.
+   */
+  public static final int MSGID_FILEPERM_INVALID_UNIX_MODE_STRING =
+       CATEGORY_MASK_UTIL | SEVERITY_MASK_MILD_ERROR | 146;
+
+
+
+  /**
+   * The message ID for the message that will be used if an attempt is made to
+   * use the exec method when the server has been configured to disallow that
+   * capability.
+   */
+  public static final int MSGID_EXEC_DISABLED =
+       CATEGORY_MASK_UTIL | SEVERITY_MASK_MILD_ERROR | 147;
+
+
+
+  /**
    * Associates a set of generic messages with the message IDs defined in this
    * class.
    */
@@ -1539,6 +1609,12 @@
                     "a valid hexadecimal digit.");
 
 
+    registerMessage(MSGID_EXEC_DISABLED,
+                    "The %s command will not be allowed because the " +
+                    "Directory Server has been configured to refuse the use " +
+                    "of the exec method.");
+
+
     registerMessage(MSGID_LDIF_INVALID_LEADING_SPACE,
                     "Unable to parse line %d (\"%s\") from the LDIF source " +
                     "because the line started with a space but there were no " +
@@ -2007,6 +2083,31 @@
                     "password-reset");
     registerMessage(MSGID_ACCTNOTTYPE_PASSWORD_CHANGED,
                     "password-changed");
+
+
+    registerMessage(MSGID_FILEPERM_SET_NO_SUCH_FILE,
+                    "Unable to set permissions for file %s because it does " +
+                    "not exist.");
+    registerMessage(MSGID_FILEPERM_CANNOT_EXEC_CHMOD,
+                    "Unable to execute the chmod command to set file " +
+                    "permissions on %s:  %s.");
+    registerMessage(MSGID_FILEPERM_SET_JAVA_EXCEPTION,
+                    "One or more exceptions were thrown in the process of " +
+                    "updating the file permissions for %s.  Some of the " +
+                    "permissions for the file may have been altered.");
+    registerMessage(MSGID_FILEPERM_SET_JAVA_FAILED_ALTERED,
+                    "One or more updates to the file permissions for %s " +
+                    "failed, but at least one update was successful.  Some " +
+                    "of the permissions for the file may have been altered.");
+    registerMessage(MSGID_FILEPERM_SET_JAVA_FAILED_UNALTERED,
+                    "All of the attempts to update the file permissions for " +
+                    "%s failed.  The file should be left with its original " +
+                    "permissions.");
+    registerMessage(MSGID_FILEPERM_INVALID_UNIX_MODE_STRING,
+                    "The provided string %s does not represent a valid UNIX " +
+                    "file mode.  UNIX file modes must be a three-character " +
+                    "string in which each character is a numeric digit " +
+                    "between zero and seven.");
   }
 }
 
diff --git a/opends/src/server/org/opends/server/types/FilePermission.java b/opends/src/server/org/opends/server/types/FilePermission.java
new file mode 100644
index 0000000..a48f3c4
--- /dev/null
+++ b/opends/src/server/org/opends/server/types/FilePermission.java
@@ -0,0 +1,1043 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2006 Sun Microsystems, Inc.
+ */
+package org.opends.server.types;
+
+
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+import org.opends.server.core.DirectoryException;
+import org.opends.server.core.DirectoryServer;
+
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.Debug.*;
+import static org.opends.server.messages.MessageHandler.*;
+import static org.opends.server.messages.UtilityMessages.*;
+import static org.opends.server.util.StaticUtils.*;
+
+
+
+/**
+ * This class provides a mechanism for setting file permissions in a
+ * more abstract manner than is provided by the underlying operating
+ * system and/or filesystem.  It uses a traditional UNIX-style rwx/ugo
+ * representation for the permissions and converts them as necessary
+ * to the scheme used by the underlying platform.  It does not provide
+ * any mechanism for getting file permissions, nor does it provide any
+ * way of dealing with file ownership or ACLs.
+ * <BR><BR>
+ * Note that the mechanism used to perform this work on UNIX systems
+ * is based on executing the <CODE>chmod</CODE> command on the
+ * underlying system.  This should be a safe operation because the
+ * Directory Server startup scripts should explicitly specify the PATH
+ * that should be used.  Nevertheless, it is possible to prevent the
+ * server from using the <CODE>Runtime.exec</CODE> method by setting
+ * the <CODE>org.opends.server.DisableExec</CODE> system property with
+ * a value of "true".
+ */
+public class FilePermission
+{
+  /**
+   * The fully-qualified name of this class for debugging purposes.
+   */
+  private static final String CLASS_NAME =
+       "org.opends.server.types.FilePermission";
+
+
+
+  /**
+   * The bitmask that should be used for indicating whether a file is
+   * readable by its owner.
+   */
+  public static final int OWNER_READABLE = 0x0100;
+
+
+
+  /**
+   * The bitmask that should be used for indicating whether a file is
+   * writable by its owner.
+   */
+  public static final int OWNER_WRITABLE = 0x0080;
+
+
+
+  /**
+   * The bitmask that should be used for indicating whether a file is
+   * executable by its owner.
+   */
+  public static final int OWNER_EXECUTABLE = 0x0040;
+
+
+
+  /**
+   * The bitmask that should be used for indicating whether a file is
+   * readable by members of its group.
+   */
+  public static final int GROUP_READABLE = 0x0020;
+
+
+
+  /**
+   * The bitmask that should be used for indicating whether a file is
+   * writable by members of its group.
+   */
+  public static final int GROUP_WRITABLE = 0x0010;
+
+
+
+  /**
+   * The bitmask that should be used for indicating whether a file is
+   * executable by members of its group.
+   */
+  public static final int GROUP_EXECUTABLE = 0x0008;
+
+
+
+  /**
+   * The bitmask that should be used for indicating whether a file is
+   * readable by users other than the owner or group members.
+   */
+  public static final int OTHER_READABLE = 0x0004;
+
+
+
+  /**
+   * The bitmask that should be used for indicating whether a file is
+   * writable by users other than the owner or group members.
+   */
+  public static final int OTHER_WRITABLE = 0x0002;
+
+
+
+  /**
+   * The bitmask that should be used for indicating whether a file is
+   * executable by users other than the owner or group members.
+   */
+  public static final int OTHER_EXECUTABLE = 0x0001;
+
+
+
+  // Indicates whether to allow the use of exec for setting file
+  // permissions.
+  private static boolean allowExec;
+
+  // The method that may be used to specify whether a file is
+  // executable by its owner (and optionally others).
+  private static Method setExecutableMethod;
+
+  // The method that may be used to specify whether a file is readable
+  // by its owner (and optionally others).
+  private static Method setReadableMethod;
+
+  // The method that may be used to specify whether a file is wriable
+  // by its owner (and optionally others).
+  private static Method setWritableMethod;
+
+  // The encoded representation for this file permission.
+  private int encodedPermission;
+
+
+
+  static
+  {
+    // Iterate through the available methods and see if any of the
+    // Java 6 methods for dealing with permissions are available.
+    try
+    {
+      setExecutableMethod = null;
+      setReadableMethod   = null;
+      setWritableMethod   = null;
+
+      for (Method m : File.class.getMethods())
+      {
+        String  name     = m.getName();
+        Class[] argTypes = m.getParameterTypes();
+
+        if (name.equals("setExecutable") && (argTypes.length == 2))
+        {
+          setExecutableMethod = m;
+        }
+        else if (name.equals("setReadable") && (argTypes.length == 2))
+        {
+          setReadableMethod = m;
+        }
+        else if (name.equals("setWritable") && (argTypes.length == 2))
+        {
+          setWritableMethod = m;
+        }
+      }
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "<static>", e);
+    }
+
+
+    // Determine whether we should disable the ability to execute
+    // commands on the underlying system even if it could provide more
+    // control and capability.
+    allowExec = mayUseExec();
+  }
+
+
+
+  /**
+   * Creates a new file permission object with the provided encoded
+   * representation.
+   *
+   * @param  encodedPermission  The encoded representation for this
+   *                            file permission.
+   */
+  public FilePermission(int encodedPermission)
+  {
+    this.encodedPermission = encodedPermission;
+  }
+
+
+
+  /**
+   * Creates a new file permission with the specified rights for the
+   * file owner.  Users other than the owner will not have any rights.
+   *
+   * @param  ownerReadable    Indicates whether the owner should have
+   *                          the read permission.
+   * @param  ownerWritable    Indicates whether the owner should have
+   *                          the write permission.
+   * @param  ownerExecutable  Indicates whether the owner should have
+   *                          the execute permission.
+   */
+  public FilePermission(boolean ownerReadable, boolean ownerWritable,
+                        boolean ownerExecutable)
+  {
+    encodedPermission = 0x0000;
+
+    if (ownerReadable)
+    {
+      encodedPermission |= OWNER_READABLE;
+    }
+
+    if (ownerWritable)
+    {
+      encodedPermission |= OWNER_WRITABLE;
+    }
+
+    if (ownerExecutable)
+    {
+      encodedPermission |= OWNER_EXECUTABLE;
+    }
+  }
+
+
+
+  /**
+   * Creates a new file permission with the specified rights for the
+   * file owner, group members, and other users.
+   *
+   * @param  ownerReadable    Indicates whether the owner should have
+   *                          the read permission.
+   * @param  ownerWritable    Indicates whether the owner should have
+   *                          the write permission.
+   * @param  ownerExecutable  Indicates whether the owner should have
+   *                          the execute permission.
+   * @param  groupReadable    Indicates whether members of the file's
+   *                          group should have the read permission.
+   * @param  groupWritable    Indicates whether members of the file's
+   *                          group should have the write permission.
+   * @param  groupExecutable  Indicates whether members of the file's
+   *                          group should have the execute
+   *                          permission.
+   * @param  otherReadable    Indicates whether other users should
+   *                          have the read permission.
+   * @param  otherWritable    Indicates whether other users should
+   *                          have the write permission.
+   * @param  otherExecutable  Indicates whether other users should
+   *                          have the execute permission.
+   */
+  public FilePermission(boolean ownerReadable, boolean ownerWritable,
+                        boolean ownerExecutable,
+                        boolean groupReadable, boolean groupWritable,
+                        boolean groupExecutable,
+                        boolean otherReadable, boolean otherWritable,
+                        boolean otherExecutable)
+  {
+    encodedPermission = 0x0000;
+
+    if (ownerReadable)
+    {
+      encodedPermission |= OWNER_READABLE;
+    }
+
+    if (ownerWritable)
+    {
+      encodedPermission |= OWNER_WRITABLE;
+    }
+
+    if (ownerExecutable)
+    {
+      encodedPermission |= OWNER_EXECUTABLE;
+    }
+
+    if (groupReadable)
+    {
+      encodedPermission |= GROUP_READABLE;
+    }
+
+    if (groupWritable)
+    {
+      encodedPermission |= GROUP_WRITABLE;
+    }
+
+    if (groupExecutable)
+    {
+      encodedPermission |= GROUP_EXECUTABLE;
+    }
+
+    if (otherReadable)
+    {
+      encodedPermission |= OTHER_READABLE;
+    }
+
+    if (otherWritable)
+    {
+      encodedPermission |= OTHER_WRITABLE;
+    }
+
+    if (otherExecutable)
+    {
+      encodedPermission |= OTHER_EXECUTABLE;
+    }
+  }
+
+
+
+  /**
+   * Indicates whether this file permission includes the owner read
+   * permission.
+   *
+   * @return  <CODE>true</CODE> if this file permission includes the
+   *          owner read permission, or <CODE>false</CODE> if not.
+   */
+  public boolean isOwnerReadable()
+  {
+    return ((encodedPermission & OWNER_READABLE) == OWNER_READABLE);
+  }
+
+
+
+  /**
+   * Indicates whether this file permission includes the owner write
+   * permission.
+   *
+   * @return  <CODE>true</CODE> if this file permission includes the
+   *          owner write permission, or <CODE>false</CODE> if not.
+   */
+  public boolean isOwnerWritable()
+  {
+    return ((encodedPermission & OWNER_WRITABLE) == OWNER_WRITABLE);
+  }
+
+
+
+  /**
+   * Indicates whether this file permission includes the owner execute
+   * permission.
+   *
+   * @return  <CODE>true</CODE> if this file permission includes the
+   *          owner execute permission, or <CODE>false</CODE> if not.
+   */
+  public boolean isOwnerExecutable()
+  {
+    return ((encodedPermission & OWNER_EXECUTABLE) ==
+            OWNER_EXECUTABLE);
+  }
+
+
+
+  /**
+   * Indicates whether this file permission includes the group read
+   * permission.
+   *
+   * @return  <CODE>true</CODE> if this file permission includes the
+   *          group read permission, or <CODE>false</CODE> if not.
+   */
+  public boolean isGroupReadable()
+  {
+    return ((encodedPermission & GROUP_READABLE) == GROUP_READABLE);
+  }
+
+
+
+  /**
+   * Indicates whether this file permission includes the group write
+   * permission.
+   *
+   * @return  <CODE>true</CODE> if this file permission includes the
+   *          group write permission, or <CODE>false</CODE> if not.
+   */
+  public boolean isGroupWritable()
+  {
+    return ((encodedPermission & GROUP_WRITABLE) == GROUP_WRITABLE);
+  }
+
+
+
+  /**
+   * Indicates whether this file permission includes the group execute
+   * permission.
+   *
+   * @return  <CODE>true</CODE> if this file permission includes the
+   *          group execute permission, or <CODE>false</CODE> if not.
+   */
+  public boolean isGroupExecutable()
+  {
+    return ((encodedPermission & GROUP_EXECUTABLE) ==
+            GROUP_EXECUTABLE);
+  }
+
+
+
+  /**
+   * Indicates whether this file permission includes the other read
+   * permission.
+   *
+   * @return  <CODE>true</CODE> if this file permission includes the
+   *          other read permission, or <CODE>false</CODE> if not.
+   */
+  public boolean isOtherReadable()
+  {
+    return ((encodedPermission & OTHER_READABLE) == OTHER_READABLE);
+  }
+
+
+
+  /**
+   * Indicates whether this file permission includes the other write
+   * permission.
+   *
+   * @return  <CODE>true</CODE> if this file permission includes the
+   *          other write permission, or <CODE>false</CODE> if not.
+   */
+  public boolean isOtherWritable()
+  {
+    return ((encodedPermission & OTHER_WRITABLE) == OTHER_WRITABLE);
+  }
+
+
+
+  /**
+   * Indicates whether this file permission includes the other execute
+   * permission.
+   *
+   * @return  <CODE>true</CODE> if this file permission includes the
+   *          other execute permission, or <CODE>false</CODE> if not.
+   */
+  public boolean isOtherExecutable()
+  {
+    return ((encodedPermission & OTHER_EXECUTABLE) ==
+            OTHER_EXECUTABLE);
+  }
+
+
+
+  /**
+   * Indicates whether the there is a mechanism available for setting
+   * permissions in the underlying filesystem on the current platform.
+   *
+   * @return  <CODE>true</CODE> if there is a mechanism available for
+   *          setting file permissions on the underlying system (e.g.,
+   *          if the server is running in a Java 6 environment, or if
+   *          this is a UNIX-based system and the use of exec is
+   *          allowed), or <CODE>false</CODE> if no such mechanism is
+   *          available.
+   */
+  public static boolean canSetPermissions()
+  {
+    if ((setReadableMethod != null) && (setWritableMethod != null) &&
+        (setExecutableMethod != null))
+    {
+      // It's a Java 6 environment, so we can always use that
+      // mechanism.
+      return true;
+    }
+
+    OperatingSystem os = DirectoryServer.getOperatingSystem();
+    if (allowExec && (os != null) && OperatingSystem.isUNIXBased(os))
+    {
+      // It's a UNIX-based system and we can exec the chmod utility.
+      return true;
+    }
+
+    // We have no way to set file permissions on this system.
+    return false;
+  }
+
+
+
+  /**
+   * Attempts to set the given permissions on the specified file.  If
+   * the underlying platform does not allow the full level of
+   * granularity specified in the permissions, then an attempt will be
+   * made to set them as closely as possible to the provided
+   * permissions, erring on the side of security.
+   *
+   * @param  f  The file to which the permissions should be applied.
+   * @param  p  The permissions to apply to the file.
+   *
+   * @return  <CODE>true</CODE> if the permissions (or the nearest
+   *          equivalent) were successfully applied to the specified
+   *          file, or <CODE>false</CODE> if was not possible to set
+   *          the permissions on the current platform.
+   *
+   * @throws  FileNotFoundException  If the specified file does not
+   *                                 exist.
+   *
+   * @throws  DirectoryException  If a problem occurs while trying to
+   *                              set the file permissions.
+   */
+  public static boolean setPermissions(File f, FilePermission p)
+         throws FileNotFoundException, DirectoryException
+  {
+    if (! f.exists())
+    {
+      int    msgID   = MSGID_FILEPERM_SET_NO_SUCH_FILE;
+      String message = getMessage(msgID, f.getAbsolutePath());
+      throw new FileNotFoundException(message);
+    }
+
+
+    // If it's a UNIX-based system, then try using the chmod command
+    // to set the permissions.  Otherwise (or if that fails), then try
+    // to use the Java 6 API.
+    OperatingSystem os = DirectoryServer.getOperatingSystem();
+    if (allowExec && (os != null) && OperatingSystem.isUNIXBased(os))
+    {
+      return setUsingUNIX(f, p);
+    }
+
+    // FIXME -- Consider using cacls on Windows.
+
+    if ((setReadableMethod != null) && (setWritableMethod != null) &&
+        (setExecutableMethod != null))
+    {
+      return setUsingJava(f, p);
+    }
+
+
+    // We have no way to set file permissions on this system.
+    return false;
+  }
+
+
+
+  /**
+   * Attempts to set the specified permissions for the given file
+   * using the UNIX chmod command.
+   *
+   * @param  f  The file to which the permissions should be applied.
+   * @param  p  The permissions to apply to the file.
+   *
+   * @return  <CODE>true</CODE> if the permissions were successfully
+   *          updated, or <CODE>false</CODE> if not.
+   *
+   * @throws  DirectoryException  If an error occurs while trying to
+   *                              execute the chmod command.
+   */
+  private static boolean setUsingUNIX(File f, FilePermission p)
+          throws DirectoryException
+  {
+    String[] arguments =
+    {
+      toUNIXMode(p),
+      f.getAbsolutePath()
+    };
+
+    ArrayList<String> outputLines = new ArrayList<String>(1);
+    int exitCode;
+    try
+    {
+      exitCode = exec("chmod", arguments, null, null, outputLines);
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "getUsingUNIX", e);
+
+      int    msgID   = MSGID_FILEPERM_CANNOT_EXEC_CHMOD;
+      String message = getMessage(msgID, f.getAbsolutePath(),
+                                  String.valueOf(e));
+      throw new DirectoryException(ResultCode.OTHER, message, msgID,
+                                   e);
+    }
+
+    return (exitCode == 0);
+  }
+
+
+
+  /**
+   * Attempts to set the specified permissions for the given file
+   * using the Java 6 <CODE>FILE</CODE> API.  Only the "owner" and
+   * "other" permissions will be preserved, since Java doesn't provide
+   * a way to set the group permissions directly.
+   *
+   * @param  f  The file to which the permissions should be applied.
+   * @param  p  The permissions to apply to the file.
+   *
+   * @return  <CODE>true</CODE> if the permissions were successfully
+   *          updated, or <CODE>false</CODE> if not.
+   *
+   * @throws  DirectoryException  If a problem occurs while attempting
+   *                              to update permissions.
+   */
+  private static boolean setUsingJava(File f, FilePermission p)
+          throws DirectoryException
+  {
+    // NOTE:  Due to a very nasty behavior of the Java 6 API, if you
+    //        want to want to grant a permission for the owner but not
+    //        for anyone else, then you *must* remove it for everyone
+    //        first, and then add it only for the owner.  Otherwise,
+    //        the other permissions will be left unchanged and if they
+    //        had it before then they will still have it.
+
+    boolean anySuccessful   = false;
+    boolean anyFailed       = false;
+    boolean exceptionThrown = false;
+
+    // Take away read permission from everyone if necessary.
+    if (p.isOwnerReadable() && (! p.isOtherReadable()))
+    {
+      try
+      {
+        Boolean b =
+             (Boolean) setReadableMethod.invoke(f, false, false);
+        if (b.booleanValue())
+        {
+          anySuccessful = true;
+        }
+        else
+        {
+          anyFailed = true;
+        }
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "setUsingJava", e);
+        exceptionThrown = true;
+      }
+    }
+
+    // Grant the appropriate read permission.
+    try
+    {
+      boolean ownerOnly =
+           (p.isOwnerReadable() != p.isOtherReadable());
+
+      Boolean b = (Boolean)
+                  setReadableMethod.invoke(f, p.isOwnerReadable(),
+                                           ownerOnly);
+      if (b.booleanValue())
+      {
+        anySuccessful = true;
+      }
+      else
+      {
+        anyFailed = true;
+      }
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "setUsingJava", e);
+      exceptionThrown = true;
+    }
+
+
+    // Take away write permission from everyone if necessary.
+    if (p.isOwnerWritable() && (! p.isOtherWritable()))
+    {
+      try
+      {
+        Boolean b =
+             (Boolean) setWritableMethod.invoke(f, false, false);
+        if (b.booleanValue())
+        {
+          anySuccessful = true;
+        }
+        else
+        {
+          anyFailed = true;
+        }
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "setUsingJava", e);
+        exceptionThrown = true;
+      }
+    }
+
+    // Grant the appropriate write permission.
+    try
+    {
+      boolean ownerOnly =
+           (p.isOwnerWritable() != p.isOtherWritable());
+
+      Boolean b = (Boolean)
+                  setWritableMethod.invoke(f, p.isOwnerWritable(),
+                                           ownerOnly);
+      if (b.booleanValue())
+      {
+        anySuccessful = true;
+      }
+      else
+      {
+        anyFailed = true;
+      }
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "setUsingJava", e);
+      exceptionThrown = true;
+    }
+
+
+    // Take away execute permission from everyone if necessary.
+    if (p.isOwnerExecutable() && (! p.isOtherExecutable()))
+    {
+      try
+      {
+        Boolean b =
+             (Boolean) setExecutableMethod.invoke(f, false, false);
+        if (b.booleanValue())
+        {
+          anySuccessful = true;
+        }
+        else
+        {
+          anyFailed = true;
+        }
+      }
+      catch (Exception e)
+      {
+        assert debugException(CLASS_NAME, "setUsingJava", e);
+        exceptionThrown = true;
+      }
+    }
+
+    // Grant the appropriate execute permission.
+    try
+    {
+      boolean ownerOnly =
+           (p.isOwnerExecutable() != p.isOtherExecutable());
+
+      Boolean b = (Boolean)
+                  setExecutableMethod.invoke(f, p.isOwnerExecutable(),
+                                             ownerOnly);
+      if (b.booleanValue())
+      {
+        anySuccessful = true;
+      }
+      else
+      {
+        anyFailed = true;
+      }
+    }
+    catch (Exception e)
+    {
+      assert debugException(CLASS_NAME, "setUsingJava", e);
+      exceptionThrown = true;
+    }
+
+
+    if (exceptionThrown)
+    {
+      // If an exception was thrown, we can't be sure whether or not
+      // any permissions were updated.
+      int    msgID   = MSGID_FILEPERM_SET_JAVA_EXCEPTION;
+      String message = getMessage(msgID, f.getAbsolutePath());
+      throw new DirectoryException(ResultCode.OTHER, message,
+                                   msgID);
+    }
+    else if (anyFailed)
+    {
+      if (anySuccessful)
+      {
+        // Some of the file permissions may have been altered.
+        int    msgID   = MSGID_FILEPERM_SET_JAVA_FAILED_ALTERED;
+        String message = getMessage(msgID, f.getAbsolutePath());
+        throw new DirectoryException(ResultCode.OTHER, message,
+                                     msgID);
+      }
+      else
+      {
+        // The file permissions should have been left intact.
+        int    msgID   = MSGID_FILEPERM_SET_JAVA_FAILED_UNALTERED;
+        String message = getMessage(msgID, f.getAbsolutePath());
+        throw new DirectoryException(ResultCode.OTHER, message,
+                                     msgID);
+      }
+    }
+    else
+    {
+      return anySuccessful;
+    }
+  }
+
+
+
+  /**
+   * Retrieves a three-character string that is the UNIX mode for the
+   * provided file permission.  Each character of the string will be a
+   * numeric digit from zero through seven.
+   *
+   * @param  p  The permission to retrieve as a UNIX mode string.
+   *
+   * @return  The UNIX mode string for the provided permission.
+   */
+  public static String toUNIXMode(FilePermission p)
+  {
+    StringBuilder buffer = new StringBuilder(3);
+    toUNIXMode(buffer, p);
+    return buffer.toString();
+  }
+
+
+
+  /**
+   * Appends a three-character string that is the UNIX mode for the
+   * provided file permission to the given buffer.  Each character of
+   * the string will be anumeric digit from zero through seven.
+   *
+   * @param  buffer  The buffer to which the mode string should be
+   *                 appended.
+   * @param  p       The permission to retrieve as a UNIX mode string.
+   */
+  public static void toUNIXMode(StringBuilder buffer,
+                                FilePermission p)
+  {
+    byte modeByte = 0x00;
+    if (p.isOwnerReadable())
+    {
+      modeByte |= 0x04;
+    }
+    if (p.isOwnerWritable())
+    {
+      modeByte |= 0x02;
+    }
+    if (p.isOwnerExecutable())
+    {
+      modeByte |= 0x01;
+    }
+    buffer.append(String.valueOf(modeByte));
+
+    modeByte = 0x00;
+    if (p.isGroupReadable())
+    {
+      modeByte |= 0x04;
+    }
+    if (p.isGroupWritable())
+    {
+      modeByte |= 0x02;
+    }
+    if (p.isGroupExecutable())
+    {
+      modeByte |= 0x01;
+    }
+    buffer.append(String.valueOf(modeByte));
+
+    modeByte = 0x00;
+    if (p.isOtherReadable())
+    {
+      modeByte |= 0x04;
+    }
+    if (p.isOtherWritable())
+    {
+      modeByte |= 0x02;
+    }
+    if (p.isOtherExecutable())
+    {
+      modeByte |= 0x01;
+    }
+    buffer.append(String.valueOf(modeByte));
+  }
+
+
+
+  /**
+   * Decodes the provided string as a UNIX mode and retrieves the
+   * corresponding file permission.  The mode string must contain
+   * three digits between zero and seven.
+   *
+   * @param  modeString  The string representation of the UNIX mode to
+   *                     decode.
+   *
+   * @return  The file permission that is equivalent to the given UNIX
+   *          mode.
+   *
+   * @throws  DirectoryException  If the provided string is not a
+   *                              valid three-digit UNIX mode.
+   */
+  public static FilePermission decodeUNIXMode(String modeString)
+         throws DirectoryException
+  {
+    if ((modeString == null) || (modeString.length() != 3))
+    {
+      int    msgID   = MSGID_FILEPERM_INVALID_UNIX_MODE_STRING;
+      String message = getMessage(msgID, String.valueOf(modeString));
+      throw new DirectoryException(ResultCode.OTHER, message, msgID);
+    }
+
+    int encodedPermission = 0x0000;
+    switch (modeString.charAt(0))
+    {
+      case '0':
+        break;
+      case '1':
+        encodedPermission |= OWNER_EXECUTABLE;
+        break;
+      case '2':
+        encodedPermission |= OWNER_WRITABLE;
+        break;
+      case '3':
+        encodedPermission |= OWNER_WRITABLE | OWNER_EXECUTABLE;
+        break;
+      case '4':
+        encodedPermission |= OWNER_READABLE;
+        break;
+      case '5':
+         encodedPermission |= OWNER_READABLE | OWNER_EXECUTABLE;
+        break;
+      case '6':
+        encodedPermission |= OWNER_READABLE | OWNER_WRITABLE;
+        break;
+      case '7':
+        encodedPermission |= OWNER_READABLE | OWNER_WRITABLE |
+                             OWNER_EXECUTABLE;
+        break;
+      default:
+      int    msgID   = MSGID_FILEPERM_INVALID_UNIX_MODE_STRING;
+      String message = getMessage(msgID, String.valueOf(modeString));
+      throw new DirectoryException(ResultCode.OTHER, message, msgID);
+    }
+
+    switch (modeString.charAt(1))
+    {
+      case '0':
+        break;
+      case '1':
+        encodedPermission |= GROUP_EXECUTABLE;
+        break;
+      case '2':
+        encodedPermission |= GROUP_WRITABLE;
+        break;
+      case '3':
+        encodedPermission |= GROUP_WRITABLE | GROUP_EXECUTABLE;
+        break;
+      case '4':
+        encodedPermission |= GROUP_READABLE;
+        break;
+      case '5':
+         encodedPermission |= GROUP_READABLE | GROUP_EXECUTABLE;
+        break;
+      case '6':
+        encodedPermission |= GROUP_READABLE | GROUP_WRITABLE;
+        break;
+      case '7':
+        encodedPermission |= GROUP_READABLE | GROUP_WRITABLE |
+                             GROUP_EXECUTABLE;
+        break;
+      default:
+      int    msgID   = MSGID_FILEPERM_INVALID_UNIX_MODE_STRING;
+      String message = getMessage(msgID, String.valueOf(modeString));
+      throw new DirectoryException(ResultCode.OTHER, message, msgID);
+    }
+
+    switch (modeString.charAt(2))
+    {
+      case '0':
+        break;
+      case '1':
+        encodedPermission |= OTHER_EXECUTABLE;
+        break;
+      case '2':
+        encodedPermission |= OTHER_WRITABLE;
+        break;
+      case '3':
+        encodedPermission |= OTHER_WRITABLE | OTHER_EXECUTABLE;
+        break;
+      case '4':
+        encodedPermission |= OTHER_READABLE;
+        break;
+      case '5':
+         encodedPermission |= OTHER_READABLE | OTHER_EXECUTABLE;
+        break;
+      case '6':
+        encodedPermission |= OTHER_READABLE | OTHER_WRITABLE;
+        break;
+      case '7':
+        encodedPermission |= OTHER_READABLE | OTHER_WRITABLE |
+                             OTHER_EXECUTABLE;
+        break;
+      default:
+      int    msgID   = MSGID_FILEPERM_INVALID_UNIX_MODE_STRING;
+      String message = getMessage(msgID, String.valueOf(modeString));
+      throw new DirectoryException(ResultCode.OTHER, message, msgID);
+    }
+
+    return new FilePermission(encodedPermission);
+  }
+
+
+
+  /**
+   * Retrieves a string representation of this file permission.
+   *
+   * @return  A string representation of this file permission.
+   */
+  public String toString()
+  {
+    StringBuilder buffer = new StringBuilder();
+    toString(buffer);
+    return buffer.toString();
+  }
+
+
+
+  /**
+   * Appends a string representation of this file permission to the
+   * given buffer.
+   *
+   * @param  buffer  The buffer to which the data should be appended.
+   */
+  public void toString(StringBuilder buffer)
+  {
+    buffer.append("FilePermission(");
+    toUNIXMode(buffer, this);
+    buffer.append(")");
+  }
+}
+
diff --git a/opends/src/server/org/opends/server/util/ServerConstants.java b/opends/src/server/org/opends/server/util/ServerConstants.java
index b33e39b..c64a154 100644
--- a/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -1993,5 +1993,17 @@
    * The full unit that should be used for a time specified in weeks.
    */
   public static final String TIME_UNIT_WEEKS_FULL = "weeks";
+
+
+
+  /**
+   * The name of the system property that can be used to indicate whether
+   * components should be allowed to use the <CODE>Runtime.exec</CODE> method.
+   * If this property is set and the value is anything other than "false",
+   * "off", "no", or "0", then components should not allow the use of the
+   * <CODE>exec</CODE> method.
+   */
+  public static final String PROPERTY_DISABLE_EXEC =
+       "org.opends.server.DisableExec";
 }
 
diff --git a/opends/src/server/org/opends/server/util/StaticUtils.java b/opends/src/server/org/opends/server/util/StaticUtils.java
index 98ca5db..52f090c 100644
--- a/opends/src/server/org/opends/server/util/StaticUtils.java
+++ b/opends/src/server/org/opends/server/util/StaticUtils.java
@@ -2024,6 +2024,32 @@
 
 
   /**
+   * Indicates whether the use of the exec method will be allowed on this
+   * system.  It will be allowed by default, but that capability will be removed
+   * if the org.opends.server.DisableExec system property is set and has any
+   * value other than "false", "off", "no", or "0".
+   *
+   * @return  <CODE>true</CODE> if the use of the exec method should be allowed,
+   *          or <CODE>false</CODE> if it should not be allowed.
+   */
+  public static boolean mayUseExec()
+  {
+    assert debugEnter(CLASS_NAME, "mayUseExec");
+
+    String s = System.getProperty(PROPERTY_DISABLE_EXEC);
+    if (s == null)
+    {
+      return true;
+    }
+
+    s = toLowerCase(s);
+    return (s.equals("false") || s.equals("off") || s.equals("no") ||
+            s.equals("0"));
+  }
+
+
+
+  /**
    * Executes the specified command on the system and captures its output.  This
    * will not return until the specified process has completed.
    *
@@ -2056,6 +2082,17 @@
     assert debugEnter(CLASS_NAME, "exec", String.valueOf(command),
                       String.valueOf(args));
 
+
+    // See whether we'll allow the use of exec on this system.  If not, then
+    // throw an exception.
+    if (! mayUseExec())
+    {
+      int    msgID   = MSGID_EXEC_DISABLED;
+      String message = getMessage(msgID, String.valueOf(command));
+      throw new SecurityException(message);
+    }
+
+
     ArrayList<String> commandAndArgs = new ArrayList<String>();
     commandAndArgs.add(command);
     if ((args != null) && (args.length > 0))

--
Gitblit v1.10.0