From a791a43de44115e6e7f9de49a88cc2d452eb91a4 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Sun, 29 Jul 2007 21:30:06 +0000
Subject: [PATCH] Implement support for restricting the set of tasks that can be invoked in the server.  A new configuration attribute, ds-cfg-allowed-task, is now available in the cn=config entry, and any attempt to invoke a task whose fully-qualified class name is not included in this attribute will be rejected.

---
 opends/resource/schema/02-config.ldif                                                      |    5 
 opends/src/server/org/opends/server/core/DirectoryServer.java                              |   31 ++++
 opends/src/server/org/opends/server/messages/BackendMessages.java                          |   13 +
 opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java           |   64 +++++++++
 opends/src/server/org/opends/server/backends/task/TaskScheduler.java                       |    7 +
 opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AllowedTaskTestCase.java |  233 +++++++++++++++++++++++++++++++++
 opends/resource/config/config.ldif                                                         |   12 +
 opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml                  |   25 +++
 opends/src/server/org/opends/server/core/CoreConfigManager.java                            |    2 
 9 files changed, 391 insertions(+), 1 deletions(-)

diff --git a/opends/resource/config/config.ldif b/opends/resource/config/config.ldif
index 5998b95..a5caa71 100644
--- a/opends/resource/config/config.ldif
+++ b/opends/resource/config/config.ldif
@@ -46,6 +46,18 @@
 ds-cfg-bind-with-dn-requires-password: true
 ds-cfg-reject-unauthenticated-requests: false
 ds-cfg-default-password-policy: cn=Default Password Policy,cn=Password Policies,cn=config
+ds-cfg-allowed-task: org.opends.server.tasks.AddSchemaFileTask
+ds-cfg-allowed-task: org.opends.server.tasks.BackupTask
+ds-cfg-allowed-task: org.opends.server.tasks.DisconnectClientTask
+ds-cfg-allowed-task: org.opends.server.tasks.EnterLockdownModeTask
+ds-cfg-allowed-task: org.opends.server.tasks.ExportTask
+ds-cfg-allowed-task: org.opends.server.tasks.ImportTask
+ds-cfg-allowed-task: org.opends.server.tasks.InitializeTargetTask
+ds-cfg-allowed-task: org.opends.server.tasks.InitializeTask
+ds-cfg-allowed-task: org.opends.server.tasks.LeaveLockdownModeTask
+ds-cfg-allowed-task: org.opends.server.tasks.RebuildTask
+ds-cfg-allowed-task: org.opends.server.tasks.RestoreTask
+ds-cfg-allowed-task: org.opends.server.tasks.ShutdownTask
 
 dn: cn=Access Control Handler,cn=config
 objectClass: top
diff --git a/opends/resource/schema/02-config.ldif b/opends/resource/schema/02-config.ldif
index e67d9eb..a8d3197 100644
--- a/opends/resource/schema/02-config.ldif
+++ b/opends/resource/schema/02-config.ldif
@@ -1530,6 +1530,8 @@
 attributeTypes: ( 1.3.6.1.4.1.26027.1.1.455
   NAME 'ds-task-disconnect-notify-client' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
   SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.456 NAME 'ds-cfg-allowed-task'
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
   MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1744,7 +1746,8 @@
   ds-cfg-proxied-authorization-identity-mapper-dn $ ds-cfg-writability-mode $
   ds-cfg-reject-unauthenticated-requests  $
   ds-cfg-bind-with-dn-requires-password $ ds-cfg-lookthrough-limit $
-  ds-cfg-smtp-server ) X-ORIGIN 'OpenDS Directory Server' )
+  ds-cfg-smtp-server $ ds-cfg-allowed-task )
+  X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.41 NAME 'ds-cfg-root-dn' SUP top
   AUXILIARY MAY ds-cfg-alternate-bind-dn X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.42 NAME 'ds-cfg-root-dse'
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
index f9901c5..e57546f 100644
--- a/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
+++ b/opends/src/admin/defn/org/opends/server/admin/std/GlobalConfiguration.xml
@@ -501,5 +501,30 @@
     </adm:profile>
   </adm:property>
 
+  <adm:property name="allowed-task" mandatory="false" multi-valued="true">
+    <adm:synopsis>
+      Specifies the fully-qualified name of a Java class that may be invoked in
+      the server.  Any attempt to invoke a task not included in the list of
+      allowed tasks will be rejected.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:alias>
+        <adm:synopsis>
+          If no values are defined, then the server will not allow any tasks to
+          be invoked.
+        </adm:synopsis>
+      </adm:alias>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:oid>1.3.6.1.4.1.26027.1.1.454</ldap:oid>
+        <ldap:name>ds-cfg-allowed-task</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+
 </adm:managed-object>
 
diff --git a/opends/src/server/org/opends/server/backends/task/TaskScheduler.java b/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
index bcd30ef..2fd6d42 100644
--- a/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
+++ b/opends/src/server/org/opends/server/backends/task/TaskScheduler.java
@@ -1829,6 +1829,13 @@
     }
 
     String taskClassName = value.getStringValue();
+    if (! DirectoryServer.getAllowedTasks().contains(taskClassName))
+    {
+      int    msgID   = MSGID_TASKSCHED_NOT_ALLOWED_TASK;
+      String message = getMessage(msgID, taskClassName);
+      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
+                                   msgID);
+    }
 
 
     // Try to load the specified class.
diff --git a/opends/src/server/org/opends/server/core/CoreConfigManager.java b/opends/src/server/org/opends/server/core/CoreConfigManager.java
index 1ac0fcf..e712717 100644
--- a/opends/src/server/org/opends/server/core/CoreConfigManager.java
+++ b/opends/src/server/org/opends/server/core/CoreConfigManager.java
@@ -251,6 +251,8 @@
       }
     }
     DirectoryServer.setMailServerPropertySets(mailServerProperties);
+
+    DirectoryServer.setAllowedTasks(globalConfig.getAllowedTask());
   }
 
 
diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index 10e3e66..865a256 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -522,6 +522,9 @@
   // The error logger that will be used during the Directory Server startup.
   private TextErrorLogPublisher startupErrorLogPublisher;
 
+  // The set of allowed task classes.
+  private Set<String> allowedTasks;
+
   // The fully-qualified name of the configuration handler class.
   private String configClass;
 
@@ -718,6 +721,7 @@
          new CopyOnWriteArrayList<ExportTaskListener>();
     directoryServer.importTaskListeners =
          new CopyOnWriteArrayList<ImportTaskListener>();
+    directoryServer.allowedTasks = new LinkedHashSet<String>(0);
   }
 
 
@@ -7392,6 +7396,33 @@
 
 
   /**
+   * Retrieves a set containing the names of the allowed tasks that may be
+   * invoked in the server.
+   *
+   * @return  A set containing the names of the allowed tasks that may be
+   *          invoked in the server.
+   */
+  public static Set<String> getAllowedTasks()
+  {
+    return directoryServer.allowedTasks;
+  }
+
+
+
+  /**
+   * Specifies the set of allowed tasks that may be invoked in the server.
+   *
+   * @param  allowedTasks  A set containing the names of the allowed tasks that
+   *                       may be invoked in the server.
+   */
+  public static void setAllowedTasks(Set<String> allowedTasks)
+  {
+    directoryServer.allowedTasks = allowedTasks;
+  }
+
+
+
+  /**
    * Registers the provided backup task listener with the Directory Server.
    *
    * @param  listener  The backup task listener to register with the Directory
diff --git a/opends/src/server/org/opends/server/messages/BackendMessages.java b/opends/src/server/org/opends/server/messages/BackendMessages.java
index 493f138..942417a 100644
--- a/opends/src/server/org/opends/server/messages/BackendMessages.java
+++ b/opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -3227,6 +3227,16 @@
 
 
   /**
+   * The message ID for the message that will be used if an attempt is made to
+   * schedule a task that is not included in the set of allowed tasks.  This
+   * takes a single argument, which is the name of the target class.
+   */
+  public static final int MSGID_TASKSCHED_NOT_ALLOWED_TASK =
+       CATEGORY_MASK_BACKEND | SEVERITY_MASK_SEVERE_ERROR | 298;
+
+
+
+  /**
    * Associates a set of generic messages with the message IDs defined in this
    * class.
    */
@@ -4238,6 +4248,9 @@
                     "The provided task entry contains multiple values for " +
                     "the %s attribute, which is used to specify the task " +
                     "class name, but only a single value is allowed");
+    registerMessage(MSGID_TASKSCHED_NOT_ALLOWED_TASK,
+                    "The Directory Server is not configured to allow task " +
+                    "%s to be invoked");
     registerMessage(MSGID_TASKSCHED_CANNOT_LOAD_CLASS,
                     "An error occurred while attempting to load class %s " +
                     "specified in attribute %s of the provided task entry:  " +
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AllowedTaskTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AllowedTaskTestCase.java
new file mode 100644
index 0000000..ccdb0e2
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/AllowedTaskTestCase.java
@@ -0,0 +1,233 @@
+/*
+ * 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 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.tasks;
+
+
+
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeClass;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.backends.task.Task;
+import org.opends.server.backends.task.TaskBackend;
+import org.opends.server.backends.task.TaskState;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.extensions.GetConnectionIDExtendedOperation;
+import org.opends.server.protocols.ldap.LDAPResultCode;
+import org.opends.server.tools.LDAPModify;
+import org.opends.server.tools.dsconfig.DSConfig;
+import org.opends.server.types.DN;
+
+import static org.testng.Assert.*;
+
+import static org.opends.server.util.ServerConstants.*;
+
+
+
+/**
+ * Tests the ability of the server to control the set of tasks that are allowed
+ * to be executed.
+ */
+public class AllowedTaskTestCase
+       extends TasksTestCase
+{
+  /**
+   * Make sure that the Directory Server is running.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @BeforeClass()
+  public void startServer()
+         throws Exception
+  {
+    TestCaseUtils.startServer();
+  }
+
+
+
+  /**
+   * Tests the function of the ds-cfg-allowed-task configuration attribute
+   * using a dummy task.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAllowedTask()
+         throws Exception
+  {
+    // Try to add the dummy task and expect it to fail because it's not allowed.
+    String path = TestCaseUtils.createTempFile(
+      "dn: ds-task-id=testAllowedTask 1,cn=Scheduled Tasks,cn=Tasks",
+      "changetype: add",
+      "objectClass: top",
+      "objectClass: ds-task",
+      "ds-task-id: testAllowedTask 1",
+      "ds-task-class-name: org.opends.server.tasks.DummyTask");
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-f", path
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
+                 LDAPResultCode.UNWILLING_TO_PERFORM);
+
+
+    // Update the set of allowed tasks to include the dummy task.
+    args = new String[]
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "set-global-configuration-prop",
+      "--add", "allowed-task:org.opends.server.tasks.DummyTask"
+    };
+    assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+
+    // Now verify that we can add the task and have it complete successfully.
+    path = TestCaseUtils.createTempFile(
+      "dn: ds-task-id=testAllowedTask 2,cn=Scheduled Tasks,cn=Tasks",
+      "changetype: add",
+      "objectClass: top",
+      "objectClass: ds-task",
+      "ds-task-id: testAllowedTask 2",
+      "ds-task-class-name: org.opends.server.tasks.DummyTask");
+
+    args = new String[]
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-f", path
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
+                 LDAPResultCode.SUCCESS);
+
+    Task task = getCompletedTask(DN.decode(
+         "ds-task-id=testAllowedTask 2,cn=Scheduled Tasks,cn=Tasks"));
+    assertNotNull(task);
+    assertEquals(task.getTaskState(), TaskState.COMPLETED_SUCCESSFULLY);
+
+
+    // Remove the task class from the set of allowed tasks and verify that we
+    // can no longer schedule the task.
+    args = new String[]
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "set-global-configuration-prop",
+      "--remove", "allowed-task:org.opends.server.tasks.DummyTask"
+    };
+    assertEquals(DSConfig.main(args, false, System.out, System.err), 0);
+
+
+    // Now verify that we can add the task and have it complete successfully.
+    path = TestCaseUtils.createTempFile(
+      "dn: ds-task-id=testAllowedTask 3,cn=Scheduled Tasks,cn=Tasks",
+      "changetype: add",
+      "objectClass: top",
+      "objectClass: ds-task",
+      "ds-task-id: testAllowedTask 3",
+      "ds-task-class-name: org.opends.server.tasks.DummyTask");
+
+    args = new String[]
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
+      "-D", "cn=Directory Manager",
+      "-w", "password",
+      "-f", path
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, System.out, System.err),
+                 LDAPResultCode.UNWILLING_TO_PERFORM);
+  }
+
+
+
+  /**
+   * Retrieves the specified task from the server, waiting for it to finish all
+   * the running its going to do before returning.
+   *
+   * @param  taskEntryDN  The DN of the entry for the task to retrieve.
+   *
+   * @return  The requested task entry.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  private Task getCompletedTask(DN taskEntryDN)
+          throws Exception
+  {
+    TaskBackend taskBackend =
+         (TaskBackend) DirectoryServer.getBackend(DN.decode("cn=tasks"));
+    Task task = taskBackend.getScheduledTask(taskEntryDN);
+    if (task == null)
+    {
+      long stopWaitingTime = System.currentTimeMillis() + 10000L;
+      while ((task == null) && (System.currentTimeMillis() < stopWaitingTime))
+      {
+        Thread.sleep(10);
+        task = taskBackend.getScheduledTask(taskEntryDN);
+      }
+    }
+
+    if (task == null)
+    {
+      throw new AssertionError("There is no such task " +
+                               taskEntryDN.toString());
+    }
+
+    if (! TaskState.isDone(task.getTaskState()))
+    {
+      long stopWaitingTime = System.currentTimeMillis() + 20000L;
+      while ((! TaskState.isDone(task.getTaskState())) &&
+             (System.currentTimeMillis() < stopWaitingTime))
+      {
+        Thread.sleep(10);
+      }
+    }
+
+    if (! TaskState.isDone(task.getTaskState()))
+    {
+      throw new AssertionError("Task " + taskEntryDN.toString() +
+                               " did not complete in a timely manner.");
+    }
+
+    return task;
+  }
+}
+
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java
new file mode 100644
index 0000000..05a6916
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/tasks/DummyTask.java
@@ -0,0 +1,64 @@
+/*
+ * 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 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.tasks;
+
+
+
+import org.opends.server.backends.task.Task;
+import org.opends.server.backends.task.TaskState;
+import org.opends.server.types.DirectoryException;
+
+
+
+/**
+ * This class provides an implementation of a Directory Server task always
+ * completes successfully.  It is intended only for testing purposes.
+ */
+public class DummyTask
+       extends Task
+{
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void initializeTask()
+         throws DirectoryException
+  {
+    // No implementation is required.
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  protected TaskState runTask()
+  {
+    return TaskState.COMPLETED_SUCCESSFULLY;
+  }
+}
+

--
Gitblit v1.10.0