mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

neil_a_wilson
30.30.2006 032cabff13965a7b6eef4aa269ff9b4d25faef6f
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.

This update also includes a mechanism that can be used to allow administrators
to preven the server from using exec to run commands on the underlying system.
Note that this is more a configuration option than an actual enforcement,
although it could be possible to configure the security manager to explicitly
forbid this.

OpenDS Issue Number: 601
1 files added
3 files modified
1193 ■■■■■ changed files
opends/src/server/org/opends/server/messages/UtilityMessages.java 101 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/FilePermission.java 1043 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/ServerConstants.java 12 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/StaticUtils.java 37 ●●●●● patch | view | raw | blame | history
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.");
  }
}
opends/src/server/org/opends/server/types/FilePermission.java
New file
@@ -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(")");
  }
}
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";
}
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))