Update the server to provide a lockdown mode. This is a mode in which the
server will only allow client connections over loopback interfaces and will
reject requests from non-root users. This can be used in cases where it would
be helpful for the server to be online to address a problem, but there might be
security risks in having it fully available (e.g., the server detects a
malformed access control rule on startup, and we don't want to allow normal
access to the server since that rule might be intended to prevent users from
seeing sensitive information and not having it interpreted properly could be
dangerous).
This mode is designed so that server components like the access control
subsystem can place the server in this mode if a problem is detected, but it
also includes tasks that can be used to manually place the server into and out
of the lockdown mode. These tasks will only be allowed to be invoked by root
users over a loopback connection.
OpenDS Issue Number: 1758
3 files added
7 files modified
| | |
| | | // to allow it. |
| | | if ((simplePassword == null) || (simplePassword.value().length == 0)) |
| | | { |
| | | // If the server is in lockdown mode, then fail. |
| | | if (DirectoryServer.lockdownMode()) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | int msgID = MSGID_BIND_REJECTED_LOCKDOWN_MODE; |
| | | setAuthFailureReason(msgID, getMessage(msgID)); |
| | | |
| | | processingStopTime = System.currentTimeMillis(); |
| | | logBindResponse(this); |
| | | break bindProcessing; |
| | | } |
| | | |
| | | // If there is a bind DN, then see whether that is acceptable. |
| | | if (DirectoryServer.bindWithDNRequiresPassword() && |
| | | ((bindDN != null) && (! bindDN.isNullDN()))) |
| | |
| | | setResultCode(ResultCode.SUCCESS); |
| | | |
| | | boolean isRoot = DirectoryServer.isRootDN(userEntry.getDN()); |
| | | if (DirectoryServer.lockdownMode() && (! isRoot)) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | int msgID = MSGID_BIND_REJECTED_LOCKDOWN_MODE; |
| | | setAuthFailureReason(msgID, getMessage(msgID)); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | |
| | | authInfo = new AuthenticationInfo(userEntry, simplePassword, |
| | | isRoot); |
| | | |
| | |
| | | saslHandler.processSASLBind(this); |
| | | |
| | | |
| | | // If the server is operating in lockdown mode, then we will need to |
| | | // ensure that the authentication was successful and performed as a |
| | | // root user to continue. |
| | | if (DirectoryServer.lockdownMode()) |
| | | { |
| | | ResultCode resultCode = getResultCode(); |
| | | if (resultCode != ResultCode.SASL_BIND_IN_PROGRESS) |
| | | { |
| | | if ((resultCode != ResultCode.SUCCESS) || |
| | | (saslAuthUserEntry == null) || |
| | | (! DirectoryServer.isRootDN(saslAuthUserEntry.getDN()))) |
| | | { |
| | | setResultCode(ResultCode.INVALID_CREDENTIALS); |
| | | |
| | | int msgID = MSGID_BIND_REJECTED_LOCKDOWN_MODE; |
| | | setAuthFailureReason(msgID, getMessage(msgID)); |
| | | |
| | | break bindProcessing; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // Create the password policy state object. |
| | | String userDNString; |
| | | if (saslAuthUserEntry == null) |
| | |
| | | // Indicates whether the server is currently online. |
| | | private boolean isRunning; |
| | | |
| | | // Indicates whether the server is currently in "lockdown mode". |
| | | private boolean lockdownMode; |
| | | |
| | | // Indicates whether the server should send a response to operations that have |
| | | // been abandoned. |
| | | private boolean notifyAbandonedOperations; |
| | |
| | | isBootstrapped = false; |
| | | isRunning = false; |
| | | shuttingDown = false; |
| | | lockdownMode = false; |
| | | serverErrorResultCode = ResultCode.OTHER; |
| | | |
| | | operatingSystem = OperatingSystem.forName(System.getProperty("os.name")); |
| | |
| | | |
| | | //Reject or accept the unauthenticated requests based on the configuration |
| | | // settings. |
| | | if(directoryServer.rejectUnauthenticatedRequests && |
| | | if ((directoryServer.rejectUnauthenticatedRequests || |
| | | directoryServer.lockdownMode) && |
| | | !clientConnection.getAuthenticationInfo().isAuthenticated()) |
| | | { |
| | | switch(operation.getOperationType()) |
| | |
| | | case SEARCH: |
| | | case MODIFY: |
| | | case MODIFY_DN: |
| | | int msgID = MSGID_REJECT_UNAUTHENTICATED_OPERATION; |
| | | String message = getMessage(msgID); |
| | | throw new DirectoryException( |
| | | ResultCode.UNWILLING_TO_PERFORM,message,msgID); |
| | | if (directoryServer.lockdownMode) |
| | | { |
| | | int msgID = MSGID_REJECT_OPERATION_IN_LOCKDOWN_MODE; |
| | | String message = getMessage(msgID); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_REJECT_UNAUTHENTICATED_OPERATION; |
| | | String message = getMessage(msgID); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, |
| | | message, msgID); |
| | | } |
| | | |
| | | case EXTENDED: |
| | | ExtendedOperation extOp = (ExtendedOperation) operation; |
| | | String requestOID = extOp.getRequestOID(); |
| | | if (!((requestOID != null) && |
| | | requestOID.equals(OID_START_TLS_REQUEST))) |
| | | { |
| | | msgID = MSGID_REJECT_UNAUTHENTICATED_OPERATION; |
| | | message = getMessage(msgID); |
| | | throw new DirectoryException( |
| | | ResultCode.UNWILLING_TO_PERFORM,message,msgID); |
| | | if (directoryServer.lockdownMode) |
| | | { |
| | | int msgID = MSGID_REJECT_OPERATION_IN_LOCKDOWN_MODE; |
| | | String message = getMessage(msgID); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_REJECT_UNAUTHENTICATED_OPERATION; |
| | | String message = getMessage(msgID); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, |
| | | message, msgID); |
| | | } |
| | | } |
| | | break; |
| | | |
| | |
| | | { |
| | | synchronized (directoryServer.establishedConnections) |
| | | { |
| | | if (directoryServer.lockdownMode) |
| | | { |
| | | InetAddress remoteAddress = clientConnection.getRemoteAddress(); |
| | | if ((remoteAddress != null) && (! remoteAddress.isLoopbackAddress())) |
| | | { |
| | | return -1; |
| | | } |
| | | } |
| | | |
| | | if ((directoryServer.maxAllowedConnections > 0) && |
| | | (directoryServer.currentConnections >= |
| | | directoryServer.maxAllowedConnections)) |
| | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the Directory Server is currently configured to operate |
| | | * in the lockdown mode, in which all non-root requests will be rejected and |
| | | * all connection attempts from non-loopback clients will be rejected. |
| | | * |
| | | * @return {@code true} if the Directory Server is currently configured to |
| | | * operate in the lockdown mode, or {@code false} if not. |
| | | */ |
| | | public static boolean lockdownMode() |
| | | { |
| | | return directoryServer.lockdownMode; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Specifies whether the server should operate in lockdown mode. |
| | | * |
| | | * @param lockdownMode Indicates whether the Directory Server should operate |
| | | * in lockdown mode. |
| | | */ |
| | | public static void setLockdownMode(boolean lockdownMode) |
| | | { |
| | | directoryServer.lockdownMode = lockdownMode; |
| | | |
| | | if (lockdownMode) |
| | | { |
| | | int msgID = MSGID_DIRECTORY_SERVER_ENTERING_LOCKDOWN_MODE; |
| | | String message = getMessage(msgID); |
| | | logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.NOTICE, message, |
| | | msgID); |
| | | |
| | | sendAlertNotification(directoryServer, ALERT_TYPE_ENTERING_LOCKDOWN_MODE, |
| | | msgID, message); |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_DIRECTORY_SERVER_LEAVING_LOCKDOWN_MODE; |
| | | String message = getMessage(msgID); |
| | | logError(ErrorLogCategory.CORE_SERVER, ErrorLogSeverity.NOTICE, message, |
| | | msgID); |
| | | |
| | | sendAlertNotification(directoryServer, ALERT_TYPE_LEAVING_LOCKDOWN_MODE, |
| | | msgID, message); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the DN of the configuration entry with which this alert generator |
| | | * is associated. |
| | | * |
| | |
| | | alerts.put(ALERT_TYPE_SERVER_SHUTDOWN, ALERT_DESCRIPTION_SERVER_SHUTDOWN); |
| | | alerts.put(ALERT_TYPE_UNCAUGHT_EXCEPTION, |
| | | ALERT_DESCRIPTION_UNCAUGHT_EXCEPTION); |
| | | alerts.put(ALERT_TYPE_ENTERING_LOCKDOWN_MODE, |
| | | ALERT_DESCRIPTION_ENTERING_LOCKDOWN_MODE); |
| | | alerts.put(ALERT_TYPE_LEAVING_LOCKDOWN_MODE, |
| | | ALERT_DESCRIPTION_LEAVING_LOCKDOWN_MODE); |
| | | |
| | | return alerts; |
| | | } |
| | |
| | | */ |
| | | public static final int MSGID_ERROR_STARTING_CONNECTION_HANDLERS = |
| | | CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 615; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if a bind is rejected ' |
| | | * because the server is in lockdown mode and the client was not a root user. |
| | | * This does not take any arguments. |
| | | */ |
| | | public static final int MSGID_BIND_REJECTED_LOCKDOWN_MODE = |
| | | CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 616; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used as the alert message |
| | | * string when the server enters lockdown mode. It does not take any |
| | | * arguments. |
| | | */ |
| | | public static final int MSGID_DIRECTORY_SERVER_ENTERING_LOCKDOWN_MODE = |
| | | CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_WARNING | 617; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be as used the alert message |
| | | * string when the server leaves lockdown mode. It does not take any |
| | | * arguments. |
| | | */ |
| | | public static final int MSGID_DIRECTORY_SERVER_LEAVING_LOCKDOWN_MODE = |
| | | CATEGORY_MASK_CORE | SEVERITY_MASK_NOTICE | 618; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if an unauthorized client |
| | | * tries to submit a request with the server in lockdown mode. |
| | | */ |
| | | public static final int MSGID_REJECT_OPERATION_IN_LOCKDOWN_MODE = |
| | | CATEGORY_MASK_CORE | SEVERITY_MASK_NOTICE | 619; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Associates a set of generic messages with the message IDs defined |
| | | * in this class. |
| | |
| | | "contained a control with OID %s that was marked " + |
| | | "critical but this control is not supported for the bind " + |
| | | "operation"); |
| | | registerMessage(MSGID_BIND_REJECTED_LOCKDOWN_MODE, |
| | | "Unable to process the non-root bind because the server " + |
| | | "is in lockdown mode"); |
| | | registerMessage(MSGID_BIND_DN_BUT_NO_PASSWORD, |
| | | "Unable to process the simple bind request because it " + |
| | | "contained a bind DN but no password, which is forbidden " + |
| | |
| | | registerMessage(MSGID_ERROR_STARTING_CONNECTION_HANDLERS, |
| | | "Could not start connection handlers"); |
| | | |
| | | |
| | | registerMessage(MSGID_REJECT_OPERATION_IN_LOCKDOWN_MODE, |
| | | "Rejecting the requested operation because the server " + |
| | | "is in lockdown mode and will only accept requests from " + |
| | | "root users over loopback connections"); |
| | | registerMessage(MSGID_DIRECTORY_SERVER_ENTERING_LOCKDOWN_MODE, |
| | | "The Directory Server is entering lockdown mode, in " + |
| | | "which clients will only be allowed to connect via a " + |
| | | "loopback address, and only root users will be allowed " + |
| | | "to process operations"); |
| | | registerMessage(MSGID_DIRECTORY_SERVER_LEAVING_LOCKDOWN_MODE, |
| | | "The Directory Server is leaving lockdown mode and will " + |
| | | "resume normal operation"); |
| | | } |
| | | } |
| | | |
| | |
| | | "The attempt to register this connection with the " + |
| | | "Directory Server was rejected. This may indicate that " + |
| | | "the server already has the maximum allowed number of " + |
| | | "concurrent connections established"); |
| | | "concurrent connections established, or that it is in a " + |
| | | "restricted access mode"); |
| | | |
| | | |
| | | registerMessage(MSGID_LDAP_CONNHANDLER_DESCRIPTION_LISTEN_ADDRESS, |
| | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if a nonroot user attempts |
| | | * to place the server in lockdown mode. It does not take any arguments. |
| | | */ |
| | | public static final int MSGID_TASK_ENTERLOCKDOWN_NOT_ROOT = |
| | | CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 21; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if a client not connected |
| | | * via a loopback address attempts to place the server in lockdown mode. It |
| | | * does not take any arguments. |
| | | */ |
| | | public static final int MSGID_TASK_ENTERLOCKDOWN_NOT_LOOPBACK = |
| | | CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 22; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if a nonroot user attempts |
| | | * to remove the server from lockdown mode. It does not take any arguments. |
| | | */ |
| | | public static final int MSGID_TASK_LEAVELOCKDOWN_NOT_ROOT = |
| | | CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 23; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if a client not connected |
| | | * via a loopback address attempts to remove the server from lockdown mode. |
| | | * It does not take any arguments. |
| | | */ |
| | | public static final int MSGID_TASK_LEAVELOCKDOWN_NOT_LOOPBACK = |
| | | CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 24; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Associates a set of generic messages with the message IDs defined in this |
| | | * class. |
| | | */ |
| | |
| | | |
| | | registerMessage(MSGID_TASK_INITIALIZE_INVALID_DN, |
| | | "Invalid DN provided with the Initialize task"); |
| | | |
| | | |
| | | registerMessage(MSGID_TASK_ENTERLOCKDOWN_NOT_ROOT, |
| | | "Only root users may place the server in lockdown mode"); |
| | | registerMessage(MSGID_TASK_ENTERLOCKDOWN_NOT_LOOPBACK, |
| | | "Only root users connected from a loopback address may " + |
| | | "place the server in lockdown mode"); |
| | | |
| | | registerMessage(MSGID_TASK_LEAVELOCKDOWN_NOT_ROOT, |
| | | "Only root users may cause the server to leave lockdown " + |
| | | "mode"); |
| | | registerMessage(MSGID_TASK_LEAVELOCKDOWN_NOT_LOOPBACK, |
| | | "Only root users connected from a loopback address may " + |
| | | "cause the server to leave lockdown mode"); |
| | | } |
| | | } |
| | | |
| | |
| | | .accept(); |
| | | LDAPClientConnection clientConnection = |
| | | new LDAPClientConnection(this, clientChannel); |
| | | |
| | | // Check to see if the core server rejected the |
| | | // connection (e.g., already too many connections |
| | | // established). |
| | | if (clientConnection.getConnectionID() < 0) { |
| | | // The connection will have already been closed. |
| | | iterator.remove(); |
| | | continue; |
| | | } |
| | | |
| | | InetAddress clientAddr = clientConnection |
| | | .getRemoteAddress(); |
| | | // Check to see if the client is on the denied list. |
| | |
| | | continue; |
| | | } |
| | | |
| | | |
| | | // Check to see if the core server rejected the |
| | | // connection (e.g., already too many connections |
| | | // established). |
| | | if (clientConnection.getConnectionID() < 0) { |
| | | // The connection will have already been closed. |
| | | iterator.remove(); |
| | | continue; |
| | | } |
| | | |
| | | // If we've gotten here, then we'll take the |
| | | // connection so invoke the post-connect plugins and |
| | | // register the client connection with a request |
| New file |
| | |
| | | /* |
| | | * 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 java.net.InetAddress; |
| | | |
| | | import org.opends.server.backends.task.Task; |
| | | import org.opends.server.backends.task.TaskState; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.ResultCode; |
| | | |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | import static org.opends.server.messages.TaskMessages.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class provides an implementation of a Directory Server task that can be |
| | | * used to place the server in lockdown mode. |
| | | */ |
| | | public class EnterLockdownModeTask |
| | | extends Task |
| | | { |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void initializeTask() |
| | | throws DirectoryException |
| | | { |
| | | // If the client connection is available, then make sure it is authorized |
| | | // as a root user. |
| | | Operation operation = getOperation(); |
| | | if (operation != null) |
| | | { |
| | | DN authzDN = operation.getAuthorizationDN(); |
| | | if ((authzDN == null) || (! DirectoryServer.isRootDN(authzDN))) |
| | | { |
| | | int msgID = MSGID_TASK_ENTERLOCKDOWN_NOT_ROOT; |
| | | String message = getMessage(msgID); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message, |
| | | msgID); |
| | | } |
| | | |
| | | InetAddress clientAddress = |
| | | operation.getClientConnection().getRemoteAddress(); |
| | | if ((clientAddress != null) && (! clientAddress.isLoopbackAddress())) |
| | | { |
| | | int msgID = MSGID_TASK_ENTERLOCKDOWN_NOT_LOOPBACK; |
| | | String message = getMessage(msgID); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message, |
| | | msgID); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | protected TaskState runTask() |
| | | { |
| | | DirectoryServer.setLockdownMode(true); |
| | | return TaskState.COMPLETED_SUCCESSFULLY; |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | /* |
| | | * 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 java.net.InetAddress; |
| | | |
| | | import org.opends.server.backends.task.Task; |
| | | import org.opends.server.backends.task.TaskState; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.DN; |
| | | import org.opends.server.types.Operation; |
| | | import org.opends.server.types.ResultCode; |
| | | |
| | | import static org.opends.server.messages.MessageHandler.*; |
| | | import static org.opends.server.messages.TaskMessages.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * This class provides an implementation of a Directory Server task that can be |
| | | * used bring the server out of lockdown mode. |
| | | */ |
| | | public class LeaveLockdownModeTask |
| | | extends Task |
| | | { |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public void initializeTask() |
| | | throws DirectoryException |
| | | { |
| | | // If the client connection is available, then make sure it is authorized |
| | | // as a root user. |
| | | Operation operation = getOperation(); |
| | | if (operation != null) |
| | | { |
| | | DN authzDN = operation.getAuthorizationDN(); |
| | | if ((authzDN == null) || (! DirectoryServer.isRootDN(authzDN))) |
| | | { |
| | | int msgID = MSGID_TASK_LEAVELOCKDOWN_NOT_ROOT; |
| | | String message = getMessage(msgID); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message, |
| | | msgID); |
| | | } |
| | | |
| | | InetAddress clientAddress = |
| | | operation.getClientConnection().getRemoteAddress(); |
| | | if ((clientAddress != null) && (! clientAddress.isLoopbackAddress())) |
| | | { |
| | | int msgID = MSGID_TASK_LEAVELOCKDOWN_NOT_LOOPBACK; |
| | | String message = getMessage(msgID); |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message, |
| | | msgID); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | protected TaskState runTask() |
| | | { |
| | | DirectoryServer.setLockdownMode(false); |
| | | return TaskState.COMPLETED_SUCCESSFULLY; |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | /** |
| | | * The description for the alert type that will be used for the alert |
| | | * notification generated upon entering lockdown mode. |
| | | */ |
| | | public static final String ALERT_DESCRIPTION_ENTERING_LOCKDOWN_MODE = |
| | | "This alert type will be used to notify administrators that the " + |
| | | "Directory Server is entering lockdown mode, in which only root " + |
| | | "users will be allowed to perform operations and only over the " + |
| | | "loopback address."; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The alert type that will be used when the Directory Server enters lockdown |
| | | * mode. |
| | | */ |
| | | public static final String ALERT_TYPE_ENTERING_LOCKDOWN_MODE = |
| | | "org.opends.server.EnteringLockdownMode"; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The description for the alert type that will be used for the alert |
| | | * notification generated upon leaving lockdown mode. |
| | | */ |
| | | public static final String ALERT_DESCRIPTION_LEAVING_LOCKDOWN_MODE = |
| | | "This alert type will be used to notify administrators that the " + |
| | | "Directory Server is leaving lockdown mode."; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The alert type that will be used when the Directory Server leaves lockdown |
| | | * mode. |
| | | */ |
| | | public static final String ALERT_TYPE_LEAVING_LOCKDOWN_MODE = |
| | | "org.opends.server.LeavingLockdownMode"; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The description for the alert type that will be used for the alert |
| | | * notification generated if the server detects that the configuration has |
| | | * been manually edited with the server online and those edits would have been |
| | | * lost by an online config change. |
| New file |
| | |
| | | /* |
| | | * 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 java.net.InetAddress; |
| | | |
| | | import org.testng.annotations.Test; |
| | | import org.testng.annotations.AfterClass; |
| | | import org.testng.annotations.BeforeClass; |
| | | |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.backends.SchemaTestMatchingRule; |
| | | 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.tools.LDAPSearch; |
| | | import org.opends.server.tools.LDAPModify; |
| | | import org.opends.server.types.DN; |
| | | |
| | | import static org.testng.Assert.*; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Tests the enter and leave lockdown mode tasks. |
| | | */ |
| | | public class LockdownModeTaskTestCase |
| | | 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(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Make sure that no matter what, when these tests are done the server is no |
| | | * longer in lockdown mode. |
| | | */ |
| | | @AfterClass() |
| | | public void disableLockdownMode() |
| | | { |
| | | DirectoryServer.setLockdownMode(false); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Test to ensure that the enter and leave lockdown tasks work as expected. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | public void testLockdownModeTasks() |
| | | throws Exception |
| | | { |
| | | // Add a test user that has the bypass-acl privilege but isn't a root user. |
| | | TestCaseUtils.initializeTestBackend(true); |
| | | TestCaseUtils.addEntry( |
| | | "dn: cn=Admin,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "cn: Admin", |
| | | "sn: Admin", |
| | | "userPassword: password", |
| | | "ds-privilege-name: bypass-acl"); |
| | | |
| | | |
| | | // Make sure that the server isn't currently in lockdown mode. |
| | | assertFalse(DirectoryServer.lockdownMode()); |
| | | |
| | | |
| | | // Make sure that we can retrieve the server's root DSE over an |
| | | // unauthenticated client connection. |
| | | InetAddress localAddress = InetAddress.getLocalHost(); |
| | | String localIP = localAddress.getHostAddress(); |
| | | boolean isLoopback = localAddress.isLoopbackAddress(); |
| | | String[] args = |
| | | { |
| | | "-h", localIP, |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-b", "", |
| | | "-s", "base", |
| | | "(objectClass=*)" |
| | | }; |
| | | assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0); |
| | | |
| | | |
| | | // Create a file that holds the LDIF for putting the server in lockdown |
| | | // mode. |
| | | String taskFile = TestCaseUtils.createTempFile( |
| | | "dn: ds-task-id=Enter Lockdown Mode,cn=Scheduled Tasks,cn=tasks", |
| | | "changetype: add", |
| | | "objectClass: top", |
| | | "objectClass: ds-task", |
| | | "ds-task-id: Enter Lockdown Mode", |
| | | "ds-task-class-name: org.opends.server.tasks.EnterLockdownModeTask"); |
| | | |
| | | DN taskDN = DN.decode( |
| | | "ds-task-id=Enter Lockdown Mode,cn=Scheduled Tasks,cn=tasks"); |
| | | |
| | | |
| | | // Ensure that we can't put the server in lockdown mode as a non-root user. |
| | | args = new String[] |
| | | { |
| | | "-h", "127.0.0.1", |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-D", "cn=Admin,o=test", |
| | | "-w", "password", |
| | | "-f", taskFile |
| | | }; |
| | | assertFalse(LDAPModify.mainModify(args, false, null, System.err) == 0); |
| | | |
| | | |
| | | // If the local address isn't a loopback address, then verify that we can't |
| | | // put the server in lockdown mode using it. |
| | | if (! isLoopback) |
| | | { |
| | | args = new String[] |
| | | { |
| | | "-h", localIP, |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-D", "cn=Directory Manager", |
| | | "-w", "password", |
| | | "-f", taskFile |
| | | }; |
| | | assertFalse(LDAPModify.mainModify(args, false, null, System.err) == 0); |
| | | } |
| | | |
| | | |
| | | // Verify that we can put the server in lockdown mode as a root user over |
| | | // a loopback address. |
| | | args = new String[] |
| | | { |
| | | "-h", "127.0.0.1", |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-D", "cn=Directory Manager", |
| | | "-w", "password", |
| | | "-f", taskFile |
| | | }; |
| | | assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0); |
| | | Task task = getCompletedTask(taskDN); |
| | | assertNotNull(task); |
| | | assertEquals(task.getTaskState(), TaskState.COMPLETED_SUCCESSFULLY); |
| | | assertTrue(DirectoryServer.lockdownMode()); |
| | | |
| | | |
| | | // If the local IP isn't the loopback address, then verify that we can't |
| | | // connect using it even as a root user. |
| | | if (! isLoopback) |
| | | { |
| | | args = new String[] |
| | | { |
| | | "-h", localIP, |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-D", "cn=Directory Manager", |
| | | "-w", "password", |
| | | "-b", "", |
| | | "-s", "base", |
| | | "(objectClass=*)" |
| | | }; |
| | | assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0); |
| | | } |
| | | |
| | | |
| | | // Make sure that we can no longer retrieve the server's root DSE over an |
| | | // unauthenticated connection. In this case, we'll make sure to use a |
| | | // loopback connection. |
| | | args = new String[] |
| | | { |
| | | "-h", "127.0.0.1", |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-b", "", |
| | | "-s", "base", |
| | | "(objectClass=*)" |
| | | }; |
| | | assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0); |
| | | |
| | | |
| | | // Make sure that we can no longer retrieve the server's root DSE over an |
| | | // authenticated connection. In this case, we'll make sure to use a |
| | | // loopback connection. |
| | | args = new String[] |
| | | { |
| | | "-h", "127.0.0.1", |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-D", "cn=Admin,o=test", |
| | | "-w", "password", |
| | | "-b", "", |
| | | "-s", "base", |
| | | "(objectClass=*)" |
| | | }; |
| | | assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0); |
| | | |
| | | |
| | | // Make sure that we can retrieve the server's root DSE over a |
| | | // root-authenticated loopback connection. |
| | | args = new String[] |
| | | { |
| | | "-h", "127.0.0.1", |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-D", "cn=Directory Manager", |
| | | "-w", "password", |
| | | "-b", "", |
| | | "-s", "base", |
| | | "(objectClass=*)" |
| | | }; |
| | | assertEquals(LDAPSearch.mainSearch(args, false, null, null), 0); |
| | | |
| | | |
| | | // Use another task to take the server out of lockdown mode and make sure it |
| | | // works. |
| | | taskFile = TestCaseUtils.createTempFile( |
| | | "dn: ds-task-id=Leave Lockdown Mode,cn=Scheduled Tasks,cn=tasks", |
| | | "changetype: add", |
| | | "objectClass: top", |
| | | "objectClass: ds-task", |
| | | "ds-task-id: Leave Lockdown Mode", |
| | | "ds-task-class-name: org.opends.server.tasks.LeaveLockdownModeTask"); |
| | | |
| | | taskDN = DN.decode( |
| | | "ds-task-id=Leave Lockdown Mode,cn=Scheduled Tasks,cn=tasks"); |
| | | |
| | | args = new String[] |
| | | { |
| | | "-h", "127.0.0.1", |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-D", "cn=Directory Manager", |
| | | "-w", "password", |
| | | "-f", taskFile |
| | | }; |
| | | assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0); |
| | | task = getCompletedTask(taskDN); |
| | | assertNotNull(task); |
| | | assertEquals(task.getTaskState(), TaskState.COMPLETED_SUCCESSFULLY); |
| | | assertFalse(DirectoryServer.lockdownMode()); |
| | | |
| | | |
| | | // Make sure that we can once again retrieve the server's root DSE over an |
| | | // anonymous connection. |
| | | args = new String[] |
| | | { |
| | | "-h", localIP, |
| | | "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), |
| | | "-b", "", |
| | | "-s", "base", |
| | | "(objectClass=*)" |
| | | }; |
| | | assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 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; |
| | | } |
| | | } |
| | | |