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