/* * 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 * * * Copyright 2006-2010 Sun Microsystems, Inc. */ package org.opends.server.protocols.jmx; import org.opends.messages.Message; import java.util.*; import javax.management.remote.JMXAuthenticator; import javax.security.auth.Subject; import org.opends.server.api.plugin.PluginResult; import org.opends.server.core.BindOperationBasis; import org.opends.server.core.DirectoryServer; import org.opends.server.core.PluginConfigManager; import org.opends.messages.CoreMessages; import org.opends.server.protocols.ldap.LDAPResultCode; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.messages.ProtocolMessages.*; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.types.*; /** * A <code>RMIAuthenticator</code> manages authentication for the secure * RMI connectors. It receives authentication requests from clients as a * SASL/PLAIN challenge and relies on a SASL server plus the local LDAP * authentication accept or reject the user being connected. */ public class RmiAuthenticator implements JMXAuthenticator { /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); /** * The client authencation mode. <code>true</code> indicates that the * client will be authenticated by its certificate (SSL protocol). * <code>true</code> indicate , that we have to perform an lDAP * authentication */ private boolean needClientCertificate = false; /** * Indicate if the we are in the finalized phase. * * @see JmxConnectionHandler */ private boolean finalizedPhase = false; /** * The JMX Client connection to be used to perform the bind (auth) * call. */ private JmxConnectionHandler jmxConnectionHandler; /** * Constructs a <code>RmiAuthenticator</code>. * * @param jmxConnectionHandler * The jmxConnectionHandler associated to this RmiAuthenticator */ public RmiAuthenticator(JmxConnectionHandler jmxConnectionHandler) { this.jmxConnectionHandler = jmxConnectionHandler; } /** * Set that we are in the finalized phase. * * @param finalizedPhase Set to true, it indicates that we are in * the finalized phase that that we other connection should be accepted. * * @see JmxConnectionHandler */ public synchronized void setFinalizedPhase(boolean finalizedPhase) { this.finalizedPhase = finalizedPhase; } /** * Authenticates a RMI client. The credentials received are composed of * a SASL/PLAIN authentication id and a password. * * @param credentials * the SASL/PLAIN credentials to validate * @return a <code>Subject</code> holding the principal(s) * authenticated */ public Subject authenticate(Object credentials) { // // If we are in the finalized phase, we should not accept // new connection if (finalizedPhase) { SecurityException se = new SecurityException(); throw se; } // // Credentials are null !!! if (credentials == null) { SecurityException se = new SecurityException(); throw se; } Object c[] = (Object[]) credentials; String authcID = (String) c[0]; String password = (String) c[1]; // // The authcID is used at forwarder level to identify the calling // client if (authcID == null) { if (debugEnabled()) { TRACER.debugVerbose("User name is Null"); } SecurityException se = new SecurityException(); throw se; } if (password == null) { if (debugEnabled()) { TRACER.debugVerbose("User password is Null "); } SecurityException se = new SecurityException(); throw se; } if (debugEnabled()) { TRACER.debugVerbose("UserName = %s", authcID); } // // Declare the client connection JmxClientConnection jmxClientConnection; // // Try to see if we have an Ldap Authentication // Which should be the case in the current implementation try { jmxClientConnection = bind(authcID, password); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } SecurityException se = new SecurityException(e.getMessage()); throw se; } // // If we've gotten here, then the authentication was // successful. We'll take the connection so // invoke the post-connect plugins. PluginConfigManager pluginManager = DirectoryServer .getPluginConfigManager(); PluginResult.PostConnect pluginResult = pluginManager .invokePostConnectPlugins(jmxClientConnection); if (!pluginResult.continueProcessing()) { jmxClientConnection.disconnect(pluginResult.getDisconnectReason(), pluginResult.sendDisconnectNotification(), pluginResult.getErrorMessage()); if (debugEnabled()) { TRACER.debugVerbose("Disconnect result from post connect plugins: " + "%s: %s ", pluginResult.getDisconnectReason(), pluginResult.getErrorMessage()); } SecurityException se = new SecurityException(); throw se; } // initialize a subject Subject s = new Subject(); // // Add the Principal. The current implementation doesn't use it s.getPrincipals().add(new OpendsJmxPrincipal(authcID)); // add the connection client object // this connection client is used at forwarder level to identify the // calling client s.getPrivateCredentials().add(new Credential(jmxClientConnection)); return s; } /** * Process bind operation. * * @param authcID * The LDAP user. * @param password * The Ldap password associated to the user. */ private JmxClientConnection bind(String authcID, String password) { ArrayList<Control> requestControls = new ArrayList<Control>(); // // We have a new client connection DN bindDN; try { bindDN = DN.decode(authcID); } catch (Exception e) { LDAPException ldapEx = new LDAPException( LDAPResultCode.INVALID_CREDENTIALS, CoreMessages.INFO_RESULT_INVALID_CREDENTIALS.get()); SecurityException se = new SecurityException(); se.initCause(ldapEx); throw se; } ByteString bindPW; if (password == null) { bindPW = null; } else { bindPW = ByteString.valueOf(password); } AuthenticationInfo authInfo = new AuthenticationInfo(); JmxClientConnection jmxClientConnection = new JmxClientConnection( jmxConnectionHandler, authInfo); BindOperationBasis bindOp = new BindOperationBasis(jmxClientConnection, jmxClientConnection.nextOperationID(), jmxClientConnection.nextMessageID(), requestControls, jmxConnectionHandler.getRMIConnector().getProtocolVersion(), ByteString.valueOf(authcID), bindPW); bindOp.run(); if (bindOp.getResultCode() == ResultCode.SUCCESS) { if (debugEnabled()) { TRACER.debugVerbose("User is authenticated"); } authInfo = bindOp.getAuthenticationInfo(); jmxClientConnection.setAuthenticationInfo(authInfo); // Check JMX_READ privilege. if (! jmxClientConnection.hasPrivilege(Privilege.JMX_READ, null)) { Message message = ERR_JMX_INSUFFICIENT_PRIVILEGES.get(); jmxClientConnection.disconnect(DisconnectReason.CONNECTION_REJECTED, false, message); throw new SecurityException(message.toString()); } return jmxClientConnection; } else { // // Set the initcause. LDAPException ldapEx = new LDAPException( LDAPResultCode.INVALID_CREDENTIALS, CoreMessages.INFO_RESULT_INVALID_CREDENTIALS.get()); SecurityException se = new SecurityException("return code: " + bindOp.getResultCode()); se.initCause(ldapEx); throw se; } } }