All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.geronimo.yoko.SocketFactory Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
/**
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
  * The ASF licenses this file to You under the Apache License, Version 2.0
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
package org.apache.geronimo.yoko;

import java.io.IOException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.cert.Certificate;
import java.util.Arrays;

import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.geronimo.corba.ORBConfiguration;
import org.apache.geronimo.corba.security.config.ConfigUtil;
import org.apache.geronimo.corba.security.config.ssl.SSLCipherSuiteDatabase;
import org.apache.geronimo.corba.security.config.ssl.SSLConfig;
import org.apache.geronimo.corba.security.config.tss.TSSCompoundSecMechListConfig;
import org.apache.geronimo.corba.security.config.tss.TSSConfig;
import org.apache.geronimo.corba.security.config.tss.TSSSSLTransportConfig;
import org.apache.geronimo.corba.security.config.tss.TSSTransportMechConfig;
import org.apache.geronimo.corba.util.Util;
import org.apache.yoko.orb.OB.IORDump;
import org.apache.yoko.orb.OCI.IIOP.ConnectionHelper;
import org.apache.yoko.orb.OCI.ProfileInfo;
import org.apache.yoko.orb.OCI.ProfileInfoHolder;
import org.omg.CORBA.ORB;
import org.omg.CORBA.Policy;
import org.omg.CSIIOP.EstablishTrustInClient;
import org.omg.CSIIOP.EstablishTrustInTarget;
import org.omg.CSIIOP.NoProtection;
import org.omg.CSIIOP.TAG_CSI_SEC_MECH_LIST;
import org.omg.IIOP.ProfileBody_1_0;
import org.omg.IIOP.ProfileBody_1_0Helper;
import org.omg.IOP.IOR;


/**
 * Socket factory instance used to interface openejb2
 * with the Yoko ORB.  Also enables the ORB for
 * SSL-type connections.
 * @version $Revision: 505035 $ $Date: 2007-02-08 16:01:06 -0500 (Thu, 08 Feb 2007) $
 */
public class SocketFactory implements ConnectionHelper {

    private final static Logger log = LoggerFactory.getLogger(SocketFactory.class);

    // The initialized SSLSocketFactory obtained from the Geronimo KeystoreManager.
    private SSLSocketFactory socketFactory = null;
    // The initialized SSLServerSocketFactory obtained from the Geronimo KeystoreManager.
    private SSLServerSocketFactory serverSocketFactory = null;
    // The initialized SSLConfig we use to retrieve the SSL socket factories.
    private SSLConfig sslConfig = null;
    // The set of cypher suites we use with the SSL connection.
    private String[] cipherSuites;
    // indicates whether client authentication is supported by this transport.
    private boolean clientAuthSupported;
    // indicates whether client authentication is required by this transport.
    private boolean clientAuthRequired;
    // supports and requires values used to retrieve the cipher suites.
    int supports = NoProtection.value;
    int requires = NoProtection.value;
    // the orb we're attached to
    private ORB orb;

    public SocketFactory() {
    }

    /**
     * Initialize the socket factory instance.
     *
     * @param orb        The hosting ORB.
     * @param configName The initialization parameter passed to the socket factor.
     *                   This contains the abstract name of our configurator,
     *                   which we retrieve from a registry.
     */
    public void init(ORB orb, String configName) {
        this.orb = orb;
        clientAuthSupported = false;
        clientAuthRequired = false;

        // retrieve the configuration from the config adapter registry.
        ORBConfiguration config = Util.getRegisteredORB(configName);
        if (config == null) {
            throw new RuntimeException("Unable to resolve ORB configuration " + configName);
        }
        // get the configuration from the hosting bean and decode what needs to be implemented.
        sslConfig = config.getSslConfig();
        TSSConfig tssConfig = config.getTssConfig();

        TSSTransportMechConfig transportMech = tssConfig.getTransport_mech();
        // if we have a transport mech defined, this is the configuration for any listeners we end up
        // creating.
        if (transportMech != null) {
            if (transportMech instanceof TSSSSLTransportConfig) {
                TSSSSLTransportConfig transportConfig = (TSSSSLTransportConfig) transportMech;
                supports = transportConfig.getSupports();
                requires = transportConfig.getRequires();
            }
        }

        // now set our listener creation flags based on the supports and requires values from the
        // TSS config.
        if ((supports & EstablishTrustInClient.value) != 0) {
            clientAuthSupported = true;

            if ((requires & EstablishTrustInClient.value) != 0) {
                clientAuthRequired = true;
            }
        }

        if ((supports & EstablishTrustInTarget.value) != 0) {
            clientAuthSupported = true;

            if ((requires & EstablishTrustInTarget.value) != 0) {
                clientAuthRequired = true;
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("Creating Yoko SocketFactor for GBean " + configName);
            log.debug("   SUPPORTS: " + ConfigUtil.flags(supports));
            log.debug("   REQUIRES: " + ConfigUtil.flags(requires));
        }
    }

    /**
     * Create a client socket of the appropriate
     * type using the provided IOR and Policy information.
     *
     * @param ior      The target IOR of the connection.
     * @param policies Policies in effect for this ORB.
     * @param address  The target address of the connection.
     * @param port     The connection port.
     *
     * @return A Socket (either plain or SSL) configured for connection
     *         to the target.
     * @exception IOException
     * @exception ConnectException
     */
    public Socket createSocket(IOR ior, Policy[] policies, InetAddress address, int port) throws IOException {
        if (log.isDebugEnabled()) {
            log.debug("SocketFactory attempting to create socket for address: " + address + " port: " + port);
            log.debug("Policies: " + Arrays.asList(policies));
            log.debug(IORDump.PrintObjref(orb, ior));
        }

        try {
            ProfileInfoHolder holder = new ProfileInfoHolder();
            // we need to extract the profile information from the IOR to see if this connection has
            // any transport-level security defined.
            if (org.apache.yoko.orb.OCI.IIOP.Util.extractProfileInfo(ior, holder)) {
                ProfileInfo profileInfo = holder.value;
                for (int i = 0; i < profileInfo.components.length; i++) {
                    // we're lookoing for the security mechanism items.
                    if (profileInfo.components[i].tag == TAG_CSI_SEC_MECH_LIST.value) {
                        try {
                            // decode and pull the transport information.
                            TSSCompoundSecMechListConfig config = TSSCompoundSecMechListConfig.decodeIOR(Util.getCodec(), profileInfo.components[i]);
                            if (log.isDebugEnabled()) {
                                log.debug("looking at tss: " + config);
                            }
                            for (int j = 0; j < config.size(); j++) {
                                TSSTransportMechConfig transport_mech = config.mechAt(j).getTransport_mech();
                                if (transport_mech instanceof TSSSSLTransportConfig) {
                                    TSSSSLTransportConfig transportConfig = (TSSSSLTransportConfig) transport_mech;

                                    int supports = transportConfig.getSupports();
                                    int requires = transportConfig.getRequires();
                                    // override the port and hostname with what's configured here.
                                    int sslPort = transportConfig.getPort(); 
                                    String sslHost = transportConfig.getHostname(); 

                                    if (log.isDebugEnabled()) {
                                        log.debug("IOR to target " + sslHost + ":" + sslPort);
                                        log.debug("   SUPPORTS: " + ConfigUtil.flags(supports));
                                        log.debug("   REQUIRES: " + ConfigUtil.flags(requires));
                                    }

                                    // TLS is configured.  If this is explicitly noprotection, then
                                    // just go create a plain socket using the configured port. 
                                    if ((NoProtection.value & requires) == NoProtection.value) {
                                        break;
                                    }
                                    // we need SSL, so create an SSLSocket for this connection.
                                    return createSSLSocket(sslHost, sslPort, requires, supports);
                                }
                            }
                        } catch (Exception e) {
                            // do nothing
                        }
                    }
                }
            }

            //SSL not needed, look in the profile for host/port
            String host = address.getHostName();

            // the Yoko ORB will use both the primary and secondary targets for connetions, which
            // sometimes gets us into trouble, forcing us to use an SSL target when we really need to
            // use the plain socket connection.  Therefore, we will ignore what's passed to us,
            // and extract the primary port information directly from the profile.
            for (int i = 0; i < ior.profiles.length; i++) {
                if (ior.profiles[i].tag == org.omg.IOP.TAG_INTERNET_IOP.value) {
                    try {
                        //
                        // Get the IIOP profile body
                        //
                        byte[] data = ior.profiles[i].profile_data;
                        ProfileBody_1_0 body = ProfileBody_1_0Helper.extract(Util.getCodec().decode_value(data, ProfileBody_1_0Helper.type()));

                        //
                        // Create new connector for this profile
                        //
                        if (body.port < 0) {
                            port = 0xffff + (int) body.port + 1;
                        } else {
                            port = (int) body.port;
                        }
                        log.debug("set port: " + port);
                     } catch (org.omg.IOP.CodecPackage.FormatMismatch e) {
                        // just keep the original port.
                        log.debug("could not set port: ", e);
                        break;
                    } catch (org.omg.IOP.CodecPackage.TypeMismatch e) {
                        // just keep the original port.
                        log.debug("could not set port: ", e);
                        break;
                    }

                }
            }


            // if security is not required, just create a plain Socket.
            if (log.isDebugEnabled()) log.debug("Created plain endpoint to " + host + ":" + port);
            return new Socket(host, port);

        } catch (IOException ex) {
            log.error("Exception creating a client socket to "  + address.getHostName() + ":" + port, ex);
            throw ex;
        }
    }

    /**
     * Create a loopback connection to the hosting
     * ORB.
     *
     * @param address The address information for the server.
     * @param port    The target port.
     *
     * @return An appropriately configured socket based on the
     *         listener characteristics.
     * @exception IOException
     * @exception ConnectException
     */
    public Socket createSelfConnection(InetAddress address, int port) throws IOException {
        try {
            // the requires information tells us whether we created a plain or SSL listener.  We need to create one
            // of the matching type.

            if ((NoProtection.value & requires) == NoProtection.value) {
                if (log.isDebugEnabled()) log.debug("Created plain endpoint to " + address.getHostName() + ":" + port);
                return new Socket(address, port);
            }
            else {
                return createSSLSocket(address.getHostName(), port, requires, supports);
            }
        } catch (IOException ex) {
            log.error("Exception creating a client socket to "  + address.getHostName() + ":" + port, ex);
            throw ex;
        }
    }

    /**
     * Create a server socket listening on the given port.
     *
     * @param port    The target listening port.
     * @param backlog The desired backlog value.
     *
     * @return An appropriate server socket for this connection.
     * @exception IOException
     * @exception ConnectException
     */
    public ServerSocket createServerSocket(int port, int backlog)  throws IOException {
        try {
            // if no protection is required, just create a plain socket.
            if ((NoProtection.value & requires) == NoProtection.value) {
                if (log.isDebugEnabled()) log.debug("Created plain server socket for port " + port);
                return new ServerSocket(port, backlog);
            }
            else {
                // SSL is required.  Create one from the SSLServerFactory retrieved from the config.  This will
                // require additional QOS configuration after creation.
                SSLServerSocket serverSocket = (SSLServerSocket)getServerSocketFactory().createServerSocket(port, backlog);
                configureServerSocket(serverSocket);
                return serverSocket;
            }
        } catch (IOException ex) {
            log.error("Exception creating a server socket for port "  + port, ex);
            throw ex;
        }
    }

    /**
     * Create a server socket for this connection.
     *
     * @param port    The target listener port.
     * @param backlog The requested backlog value for the connection.
     * @param address The host address information we're publishing under.
     *
     * @return An appropriately configured ServerSocket for this
     *         connection.
     * @exception IOException
     * @exception ConnectException
     */
    public ServerSocket createServerSocket(int port, int backlog, InetAddress address) throws IOException {
        try {
            // if no protection is required, just create a plain socket.
            if ((NoProtection.value & requires) == NoProtection.value) {
                if (log.isDebugEnabled()) log.debug("Created plain server socket for port " + port);
                return new ServerSocket(port, backlog, address);
            }
            else {
                // SSL is required.  Create one from the SSLServerFactory retrieved from the config.  This will
                // require additional QOS configuration after creation.
                SSLServerSocket serverSocket = (SSLServerSocket)getServerSocketFactory().createServerSocket(port, backlog, address);
                configureServerSocket(serverSocket);
                return serverSocket;
            }
        } catch (IOException ex) {
            log.error("Exception creating a client socket to "  + address.getHostName() + ":" + port, ex);
            throw ex;
        }
    }

    /**
     * On-demand creation of an SSL socket factory, using the provided
     * Geronimo SSLConfig information.
     *
     * @return The SSLSocketFactory this connection should be using to create
     *         secure connections.
     * @throws java.io.IOException if we can't get a socket factory
     */
    private SSLSocketFactory getSocketFactory() throws IOException {
        // first use?
        if (socketFactory == null) {
            // the SSLConfig is optional, so if it's not there, use the default SSLSocketFactory.
            if (sslConfig == null) {
                socketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            }
            else {
                // ask the SSLConfig bean to create a factory for us.
                try {
                    socketFactory = sslConfig.createSSLFactory(Thread.currentThread().getContextClassLoader());
                } catch (Exception e) {
                    log.error("Unable to create client SSL socket factory", e);
                    throw (IOException)new IOException("Unable to create client SSL socket factory: " + e.getMessage()).initCause(e);
                }
            }
        }
        return socketFactory;
    }

    /**
     * On-demand creation of an SSL server socket factory, using the provided
     * Geronimo SSLConfig information.
     *
     * @return The SSLServerSocketFactory this connection should be using to create
     *         secure connections.
     * @throws java.io.IOException if we can't get a server socket factory
     */
    private SSLServerSocketFactory getServerSocketFactory() throws IOException {
        // first use?
        if (serverSocketFactory == null) {
            // the SSLConfig is optional, so if it's not there, use the default SSLSocketFactory.
            if (sslConfig == null) {
                serverSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
            }
            else {
                try {
                    serverSocketFactory = sslConfig.createSSLServerFactory(Thread.currentThread().getContextClassLoader());
                } catch (Exception e) {
                    log.error("Unable to create server SSL socket factory", e);
                    throw (IOException)new IOException("Unable to create server SSL socket factory: " + e.getMessage()).initCause(e);
                }
            }
            // we have a socket factory....now get our cipher suite set based on our requirements and what's
            // available from the factory.
            if (cipherSuites == null) {
                cipherSuites = SSLCipherSuiteDatabase.getCipherSuites(requires, supports, serverSocketFactory.getSupportedCipherSuites());
            }
            // There's a bit of a timing problem with server-side ORBs.  Part of the ORB shutdown is to
            // establish a self-connection to shutdown the acceptor threads.  This requires a client
            // SSL socket factory.  Unfortunately, if this is occurring during server shutdown, the
            // FileKeystoreManager will get a NullPointerException because some name queries fail because
            // things are getting shutdown.  Therefore, if we need the server factory, assume we'll also
            // need the client factory to shutdown, and request it now.
            getSocketFactory();
        }
        return serverSocketFactory;
    }


    /**
     * Set the server socket configuration to our required
     * QOS values.
     *
     * A small experiment shows that setting either (want, need) parameter to either true or false sets the
     * other parameter to false.
     *
     * @param serverSocket
     *               The newly created SSLServerSocket.
     *
     * @throws IOException if server socket can't be configured
     */
    private void configureServerSocket(SSLServerSocket serverSocket) throws IOException {
        // set the authentication value and cipher suite info.
        serverSocket.setEnabledCipherSuites(cipherSuites);
        if (clientAuthRequired) {
            serverSocket.setNeedClientAuth(true);
        } else if (clientAuthSupported) {
            serverSocket.setWantClientAuth(true);
        } else {
            serverSocket.setNeedClientAuth(false); //could set want with the same effect
        }
        serverSocket.setSoTimeout(60 * 1000);

        if (log.isDebugEnabled()) {
            log.debug("Created SSL server socket on port " + serverSocket.getLocalPort());
            log.debug("    client authentication " + (clientAuthSupported ? "SUPPORTED" : "UNSUPPORTED"));
            log.debug("    client authentication " + (clientAuthRequired ? "REQUIRED" : "OPTIONAL"));
            log.debug("    cipher suites:");

            for (int i = 0; i < cipherSuites.length; i++) {
                log.debug("    " + cipherSuites[i]);
            }
        }
    }

    /**
     * Create an SSL client socket using the IOR-encoded
     * security characteristics.
     * Setting want/need client auth on a client socket has no effect so all we can do is use the right host, port, ciphers
     *
     * @param host     The target host name.
     * @param port     The target connection port.
     *
     * @return An appropriately configured client SSLSocket.
     * @exception IOException if ssl socket can't be obtained and configured.
     */
    private Socket createSSLSocket(String host, int port, int requires, int supports) throws IOException {
        SSLSocketFactory factory = getSocketFactory();
        SSLSocket socket = (SSLSocket) factory.createSocket(host, port);

        socket.setSoTimeout(60 * 1000);

        // get a set of cipher suites appropriate for this connections requirements.
        // We request this for each connection, since the outgoing IOR's requirements may be different from
        // our server listener requirements.
        String[] iorSuites = SSLCipherSuiteDatabase.getCipherSuites(requires, supports, factory.getSupportedCipherSuites());
        socket.setEnabledCipherSuites(iorSuites);
        if (log.isDebugEnabled()) {
            log.debug("Created SSL socket to " + host + ":" + port);
            log.debug("    cipher suites:");

            for (int i = 0; i < iorSuites.length; i++) {
                log.debug("    " + iorSuites[i]);
            }
            socket.addHandshakeCompletedListener(new HandshakeCompletedListener() {

                public void handshakeCompleted(HandshakeCompletedEvent handshakeCompletedEvent) {
                    Certificate[] certs = handshakeCompletedEvent.getLocalCertificates();
                    if (certs != null) {
                        log.debug("handshake returned local certs count: " + certs.length);
                        for (int i = 0; i < certs.length; i++) {
                            Certificate cert = certs[i];
                            log.debug("cert: " + cert.toString());
                        }
                    } else {
                        log.debug("handshake returned no local certs");
                    }
                }
            });
        }
        return socket;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy