/* * JBoss, Home of Professional Open Source. * Copyright 2016, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.wildfly.iiop.openjdk; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Properties; import javax.net.ssl.SSLContext; import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.OperationStepHandler; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PersistentResourceDefinition; import org.jboss.as.controller.PropertiesAttributeDefinition; import org.jboss.as.controller.registry.Resource; import org.jboss.as.naming.InitialContext; import org.jboss.as.network.SocketBinding; import org.jboss.as.server.AbstractDeploymentChainStep; import org.jboss.as.server.DeploymentProcessorTarget; import org.jboss.as.server.deployment.Phase; import org.jboss.dmr.ModelNode; import org.jboss.dmr.Property; import org.jboss.metadata.ejb.jboss.IORASContextMetaData; import org.jboss.metadata.ejb.jboss.IORSASContextMetaData; import org.jboss.metadata.ejb.jboss.IORSecurityConfigMetaData; import org.jboss.metadata.ejb.jboss.IORTransportConfigMetaData; import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; import org.omg.CORBA.ORB; import org.omg.PortableServer.IdAssignmentPolicyValue; import org.omg.PortableServer.LifespanPolicyValue; import org.omg.PortableServer.POA; import org.wildfly.iiop.openjdk.csiv2.CSIV2IORToSocketInfo; import org.wildfly.iiop.openjdk.csiv2.ElytronSASClientInterceptor; import org.wildfly.iiop.openjdk.deployment.IIOPDependencyProcessor; import org.wildfly.iiop.openjdk.deployment.IIOPMarkerProcessor; import org.wildfly.iiop.openjdk.logging.IIOPLogger; import org.wildfly.iiop.openjdk.naming.jndi.JBossCNCtxFactory; import org.wildfly.iiop.openjdk.rmi.DelegatingStubFactoryFactory; import org.wildfly.iiop.openjdk.security.NoSSLSocketFactory; import org.wildfly.iiop.openjdk.security.LegacySSLSocketFactory; import org.wildfly.iiop.openjdk.security.SSLSocketFactory; import org.wildfly.iiop.openjdk.service.CorbaNamingService; import org.wildfly.iiop.openjdk.service.CorbaORBService; import org.wildfly.iiop.openjdk.service.CorbaPOAService; import org.wildfly.iiop.openjdk.service.IORSecConfigMetaDataService; import org.wildfly.security.auth.client.AuthenticationContext; import org.wildfly.security.manager.WildFlySecurityManager; import com.sun.corba.se.impl.orbutil.ORBConstants; import org.jboss.as.controller.AbstractBoottimeAddStepHandler; /** * <p> * This class implements a {@code ModelAddOperationHandler} that installs the IIOP subsystem services: * <ul> * <li>{@code CorbaORBService}: responsible for configuring and starting the CORBA {@code ORB}.</li> * <li>{@code CorbaPOAService}: responsible for creating and activating CORBA {@code POA}s.</li> * <li>{@code CorbaNamingService}: responsible for creating and starting the CORBA naming service.</li> * </ul> * After the {@code ORB} is created, we create and activate the "RootPOA" and then use this {@code POA} to create the * {@code POA}s required by the other services. * </p> * * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a> * @author <a href="mailto:tadamski@redhat.com">Tomasz Adamski</a> */ public class IIOPSubsystemAdd extends AbstractBoottimeAddStepHandler { private static final String SSL_CONTEXT_CAPABILITY = "org.wildfly.security.ssl-context"; private static final String AUTH_CONTEXT_CAPABILITY = "org.wildfly.security.authentication-context"; public IIOPSubsystemAdd(final Collection<? extends AttributeDefinition> attributes) { super(attributes); } private static final ServiceName SECURITY_DOMAIN_SERVICE_NAME = ServiceName.JBOSS.append("security").append( "security-domain"); @Override protected void performBoottime(final OperationContext context, final ModelNode operation, final ModelNode model) throws OperationFailedException { // This needs to run after all child resources so that they can detect a fresh state context.addStep(new OperationStepHandler() { @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { final Resource resource = context.readResource(PathAddress.EMPTY_ADDRESS); ModelNode node = Resource.Tools.readModel(resource); launchServices(context, node); // Rollback handled by the parent step context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER); } }, OperationContext.Stage.RUNTIME); } @Override protected void populateModel(final OperationContext context, final ModelNode operation, final Resource resource) throws OperationFailedException { super.populateModel(context, operation, resource); final ModelNode model = resource.getModel(); ConfigValidator.validateConfig(context, model); } protected void launchServices(final OperationContext context, final ModelNode model) throws OperationFailedException { IIOPLogger.ROOT_LOGGER.activatingSubsystem(); // set the ORBUseDynamicStub system property. WildFlySecurityManager.setPropertyPrivileged("org.jboss.com.sun.CORBA.ORBUseDynamicStub", "true"); // we set the same stub factory to both the static and dynamic stub factory. As there is no way to dynamically change // the userDynamicStubs's property at runtime it is possible for the ORB class's <clinit> method to be // called before this property is set. // TODO: investigate a better way to handle this com.sun.corba.se.spi.orb.ORB.getPresentationManager().setStubFactoryFactory(true, new DelegatingStubFactoryFactory()); com.sun.corba.se.spi.orb.ORB.getPresentationManager().setStubFactoryFactory(false, new DelegatingStubFactoryFactory()); // setup naming. InitialContext.addUrlContextFactory("corbaloc", JBossCNCtxFactory.INSTANCE); InitialContext.addUrlContextFactory("corbaname", JBossCNCtxFactory.INSTANCE); InitialContext.addUrlContextFactory("IOR", JBossCNCtxFactory.INSTANCE); InitialContext.addUrlContextFactory("iiopname", JBossCNCtxFactory.INSTANCE); InitialContext.addUrlContextFactory("iiop", JBossCNCtxFactory.INSTANCE); context.addStep(new AbstractDeploymentChainStep() { public void execute(DeploymentProcessorTarget processorTarget) { processorTarget.addDeploymentProcessor(IIOPExtension.SUBSYSTEM_NAME, Phase.DEPENDENCIES, Phase.DEPENDENCIES_JDKORB, new IIOPDependencyProcessor()); processorTarget.addDeploymentProcessor(IIOPExtension.SUBSYSTEM_NAME, Phase.PARSE, Phase.PARSE_JDKORB, new IIOPMarkerProcessor()); } }, OperationContext.Stage.RUNTIME); // get the configured ORB properties. Properties props = this.getConfigurationProperties(context, model); // setup the ORB initializers using the configured properties. this.setupInitializers(props); // setup the SSL socket factories, if necessary. final boolean sslConfigured = this.setupSSLFactories(props); // create the service that initializes and starts the CORBA ORB. CorbaORBService orbService = new CorbaORBService(props); final ServiceBuilder<ORB> builder = context.getServiceTarget().addService(CorbaORBService.SERVICE_NAME, orbService); org.jboss.as.server.Services.addServerExecutorDependency(builder, orbService.getExecutorInjector()); // if a security domain has been specified, add a dependency to the domain service. String securityDomain = props.getProperty(Constants.SECURITY_SECURITY_DOMAIN); if (securityDomain != null) builder.addDependency(SECURITY_DOMAIN_SERVICE_NAME.append(securityDomain)); // add dependencies to the ssl context services if needed. final String serverSSLContextName = props.getProperty(Constants.SERVER_SSL_CONTEXT); if (serverSSLContextName != null) { ServiceName serverContextServiceName = context.getCapabilityServiceName(SSL_CONTEXT_CAPABILITY, serverSSLContextName, SSLContext.class); builder.addDependency(serverContextServiceName); } final String clientSSLContextName = props.getProperty(Constants.CLIENT_SSL_CONTEXT); if (clientSSLContextName != null) { ServiceName clientContextServiceName = context.getCapabilityServiceName(SSL_CONTEXT_CAPABILITY, clientSSLContextName, SSLContext.class); builder.addDependency(clientContextServiceName); } // if an authentication context has ben specified, add a dependency to its service. final String authContext = props.getProperty(Constants.ORB_INIT_AUTH_CONTEXT); if (authContext != null) { ServiceName authContextServiceName = context.getCapabilityServiceName(AUTH_CONTEXT_CAPABILITY, authContext, AuthenticationContext.class); builder.addDependency(authContextServiceName); } // inject the socket bindings that specify IIOP and IIOP/SSL ports. String socketBinding = props.getProperty(Constants.ORB_SOCKET_BINDING); builder.addDependency(SocketBinding.JBOSS_BINDING_NAME.append(socketBinding), SocketBinding.class, orbService.getIIOPSocketBindingInjector()); String sslSocketBinding = props.getProperty(Constants.ORB_SSL_SOCKET_BINDING); if(sslSocketBinding != null) { builder.addDependency(SocketBinding.JBOSS_BINDING_NAME.append(sslSocketBinding), SocketBinding.class, orbService.getIIOPSSLSocketBindingInjector()); } // create the IOR security config metadata service. final IORSecurityConfigMetaData securityConfigMetaData = this.createIORSecurityConfigMetaData(context, model, sslConfigured); final IORSecConfigMetaDataService securityConfigMetaDataService = new IORSecConfigMetaDataService(securityConfigMetaData); context.getServiceTarget() .addService(IORSecConfigMetaDataService.SERVICE_NAME, securityConfigMetaDataService) .setInitialMode(ServiceController.Mode.ACTIVE).install(); builder.addDependency(IORSecConfigMetaDataService.SERVICE_NAME); // set the initial mode and install the service. builder.setInitialMode(ServiceController.Mode.ACTIVE).install(); // create the service the initializes the Root POA. CorbaPOAService rootPOAService = new CorbaPOAService("RootPOA", "poa"); context.getServiceTarget().addService(CorbaPOAService.ROOT_SERVICE_NAME, rootPOAService) .addDependency(CorbaORBService.SERVICE_NAME, ORB.class, rootPOAService.getORBInjector()) .setInitialMode(ServiceController.Mode.ACTIVE).install(); // create the service the initializes the interface repository POA. final CorbaPOAService irPOAService = new CorbaPOAService("IRPOA", "irpoa", IdAssignmentPolicyValue.USER_ID, null, null, LifespanPolicyValue.PERSISTENT, null, null, null); context.getServiceTarget() .addService(CorbaPOAService.INTERFACE_REPOSITORY_SERVICE_NAME, irPOAService) .addDependency(CorbaPOAService.ROOT_SERVICE_NAME, POA.class, irPOAService.getParentPOAInjector()) .setInitialMode(ServiceController.Mode.ACTIVE).install(); // create the service that initializes the naming service POA. final CorbaPOAService namingPOAService = new CorbaPOAService("Naming", null, IdAssignmentPolicyValue.USER_ID, null, null, LifespanPolicyValue.PERSISTENT, null, null, null); context.getServiceTarget() .addService(CorbaPOAService.SERVICE_NAME.append("namingpoa"), namingPOAService) .addDependency(CorbaPOAService.ROOT_SERVICE_NAME, POA.class, namingPOAService.getParentPOAInjector()) .setInitialMode(ServiceController.Mode.ACTIVE).install(); // create the CORBA naming service. final CorbaNamingService namingService = new CorbaNamingService(props); context .getServiceTarget() .addService(CorbaNamingService.SERVICE_NAME, namingService) .addDependency(CorbaORBService.SERVICE_NAME, ORB.class, namingService.getORBInjector()) .addDependency(CorbaPOAService.ROOT_SERVICE_NAME, POA.class, namingService.getRootPOAInjector()) .addDependency(CorbaPOAService.SERVICE_NAME.append("namingpoa"), POA.class, namingService.getNamingPOAInjector()) .setInitialMode(ServiceController.Mode.ACTIVE).install(); configureClientSecurity(props); } /** * <p> * Obtains the subsystem configuration properties from the specified {@code ModelNode}, using default values for undefined * properties. If the property has a IIOP equivalent, it is translated into its IIOP counterpart before being added to * the returned {@code Properties} object. * </p> * * @param model the {@code ModelNode} that contains the subsystem configuration properties. * @return a {@code Properties} instance containing all configured subsystem properties. * @throws OperationFailedException if an error occurs while resolving the properties. */ protected Properties getConfigurationProperties(OperationContext context, ModelNode model) throws OperationFailedException { Properties props = new Properties(); getResourceProperties(props, IIOPRootDefinition.INSTANCE, context, model); // check if the node contains a list of generic properties. ModelNode configNode = model.get(Constants.CONFIGURATION); if (configNode.hasDefined(Constants.PROPERTIES)) { for (Property property : configNode.get(Constants.PROPERTIES).get(Constants.PROPERTY) .asPropertyList()) { String name = property.getName(); String value = property.getValue().get(Constants.PROPERTY_VALUE).asString(); props.setProperty(name, value); } } return props; } private void getResourceProperties(final Properties properties, PersistentResourceDefinition resource, OperationContext context, ModelNode model) throws OperationFailedException { for (AttributeDefinition attrDefinition : resource.getAttributes()) { if(attrDefinition instanceof PropertiesAttributeDefinition){ PropertiesAttributeDefinition pad=(PropertiesAttributeDefinition)attrDefinition; ModelNode resolvedModelAttribute = attrDefinition.resolveModelAttribute(context, model); if(resolvedModelAttribute.isDefined()) { for (final Property prop : resolvedModelAttribute.asPropertyList()) { properties.setProperty(prop.getName(), prop.getValue().asString()); } } continue; } ModelNode resolvedModelAttribute = attrDefinition.resolveModelAttribute(context, model); //FIXME if (resolvedModelAttribute.isDefined()) { String name = attrDefinition.getName(); String value = resolvedModelAttribute.asString(); String openjdkProperty = PropertiesMap.PROPS_MAP.get(name); if (openjdkProperty != null) { name = openjdkProperty; } properties.setProperty(name, value); } } } /** * <p> * Sets up the ORB initializers according to what has been configured in the subsystem. * </p> * * @param props the subsystem configuration properties. */ private void setupInitializers(Properties props) { List<String> orbInitializers = new ArrayList<String>(); // check which groups of initializers are to be installed. String installSecurity = (String) props.remove(Constants.ORB_INIT_SECURITY); if (installSecurity.equalsIgnoreCase(Constants.CLIENT)) { orbInitializers.addAll(Arrays.asList(IIOPInitializer.SECURITY_CLIENT.getInitializerClasses())); } else if (installSecurity.equalsIgnoreCase(Constants.IDENTITY)) { orbInitializers.addAll(Arrays.asList(IIOPInitializer.SECURITY_IDENTITY.getInitializerClasses())); } else if (installSecurity.equalsIgnoreCase(Constants.ELYTRON)) { final String authContext = props.getProperty(Constants.ORB_INIT_AUTH_CONTEXT); ElytronSASClientInterceptor.setAuthenticationContextName(authContext); orbInitializers.addAll(Arrays.asList(IIOPInitializer.SECURITY_ELYTRON.getInitializerClasses())); } String installTransaction = (String) props.remove(Constants.ORB_INIT_TRANSACTIONS); if (installTransaction.equalsIgnoreCase(Constants.FULL)) { orbInitializers.addAll(Arrays.asList(IIOPInitializer.TRANSACTIONS.getInitializerClasses())); } else if (installTransaction.equalsIgnoreCase(Constants.SPEC)) { orbInitializers.addAll(Arrays.asList(IIOPInitializer.SPEC_TRANSACTIONS.getInitializerClasses())); } // add the standard opendk initializer plus all configured initializers. for (String initializerClass : orbInitializers) { props.setProperty(Constants.ORB_INITIALIZER_PREFIX + initializerClass, ""); } } /** * <p> * Sets up the SSL domain socket factories if SSL support has been enabled. * </p> * * @param props the subsystem configuration properties. * @return true if ssl has been configured * @throws OperationFailedException if the SSL setup has not been done correctly (SSL support has been turned on but no * security domain has been specified). */ private boolean setupSSLFactories(final Properties props) throws OperationFailedException { final boolean supportSSL = "true".equalsIgnoreCase(props.getProperty(Constants.SECURITY_SUPPORT_SSL)); final boolean sslConfigured; if (supportSSL) { // if the config is using Elytron supplied SSL contexts, install the SSLSocketFactory. final String serverSSLContextName = props.getProperty(Constants.SERVER_SSL_CONTEXT); final String clientSSLContextName = props.getProperty(Constants.CLIENT_SSL_CONTEXT); if (serverSSLContextName != null && clientSSLContextName != null) { SSLSocketFactory.setServerSSLContextName(serverSSLContextName); SSLSocketFactory.setClientSSLContextName(clientSSLContextName); props.setProperty(ORBConstants.SOCKET_FACTORY_CLASS_PROPERTY, SSLSocketFactory.class.getName()); } else { // if the config only has a legacy JSSE domain reference, install the LegacySSLSocketFactory. final String securityDomain = props.getProperty(Constants.SECURITY_SECURITY_DOMAIN); LegacySSLSocketFactory.setSecurityDomain(securityDomain); props.setProperty(ORBConstants.SOCKET_FACTORY_CLASS_PROPERTY, LegacySSLSocketFactory.class.getName()); } sslConfigured = true; } else { props.setProperty(ORBConstants.SOCKET_FACTORY_CLASS_PROPERTY, NoSSLSocketFactory.class.getName()); sslConfigured = false; } return sslConfigured; } private IORSecurityConfigMetaData createIORSecurityConfigMetaData(final OperationContext context, final ModelNode resourceModel, final boolean sslConfigured) throws OperationFailedException { final IORSecurityConfigMetaData securityConfigMetaData = new IORSecurityConfigMetaData(); final IORSASContextMetaData sasContextMetaData = new IORSASContextMetaData(); sasContextMetaData.setCallerPropagation(IIOPRootDefinition.CALLER_PROPAGATION.resolveModelAttribute(context, resourceModel).asString()); securityConfigMetaData.setSasContext(sasContextMetaData); final IORASContextMetaData asContextMetaData = new IORASContextMetaData(); asContextMetaData.setAuthMethod(IIOPRootDefinition.AUTH_METHOD.resolveModelAttribute(context, resourceModel).asString()); if (resourceModel.hasDefined(IIOPRootDefinition.REALM.getName())) { asContextMetaData.setRealm(IIOPRootDefinition.REALM.resolveModelAttribute(context, resourceModel).asString()); } asContextMetaData.setRequired(IIOPRootDefinition.REQUIRED.resolveModelAttribute(context, resourceModel).asBoolean()); securityConfigMetaData.setAsContext(asContextMetaData); final boolean serverRequiresSsl = IIOPRootDefinition.SERVER_REQUIRES_SSL.resolveModelAttribute(context, resourceModel).asBoolean(); final IORTransportConfigMetaData transportConfigMetaData = new IORTransportConfigMetaData(); final ModelNode integrityNode = IIOPRootDefinition.INTEGRITY.resolveModelAttribute(context, resourceModel); if(integrityNode.isDefined()){ transportConfigMetaData.setIntegrity(integrityNode.asString()); } else { transportConfigMetaData.setIntegrity(sslConfigured ? (serverRequiresSsl ? Constants.IOR_REQUIRED : Constants.IOR_SUPPORTED) : Constants.NONE); } final ModelNode confidentialityNode = IIOPRootDefinition.CONFIDENTIALITY.resolveModelAttribute(context, resourceModel); if(confidentialityNode.isDefined()){ transportConfigMetaData.setConfidentiality(confidentialityNode.asString()); } else { transportConfigMetaData.setConfidentiality(sslConfigured ? (serverRequiresSsl ? Constants.IOR_REQUIRED: Constants.IOR_SUPPORTED) : Constants.IOR_NONE); } final ModelNode establishTrustInTargetNode = IIOPRootDefinition.TRUST_IN_TARGET.resolveModelAttribute(context, resourceModel); if (establishTrustInTargetNode.isDefined()) { transportConfigMetaData.setEstablishTrustInTarget(confidentialityNode.asString()); } else { transportConfigMetaData.setEstablishTrustInTarget(sslConfigured ? Constants.IOR_SUPPORTED : Constants.NONE); } final ModelNode establishTrustInClientNode = IIOPRootDefinition.TRUST_IN_CLIENT.resolveModelAttribute(context, resourceModel); if(establishTrustInClientNode.isDefined()){ transportConfigMetaData.setEstablishTrustInClient(establishTrustInClientNode.asString()); } else { transportConfigMetaData.setEstablishTrustInClient(sslConfigured ? (serverRequiresSsl ? Constants.IOR_REQUIRED : Constants.IOR_SUPPORTED) : Constants.NONE); } transportConfigMetaData.setDetectMisordering(Constants.IOR_SUPPORTED); transportConfigMetaData.setDetectReplay(Constants.IOR_SUPPORTED); securityConfigMetaData.setTransportConfig(transportConfigMetaData); return securityConfigMetaData; } private void configureClientSecurity(final Properties props) { final boolean clientRequiresSSL = Boolean.getBoolean(props.getProperty(Constants.SECURITY_CLIENT_REQUIRES_SSL)); CSIV2IORToSocketInfo.setClientRequiresSSL(clientRequiresSSL); } }