Home » geronimo-2.2-source-release » org.apache.geronimo.yoko » [javadoc | source]

    1   /**
    2     * Licensed to the Apache Software Foundation (ASF) under one or more
    3     * contributor license agreements.  See the NOTICE file distributed with
    4     * this work for additional information regarding copyright ownership.
    5     * The ASF licenses this file to You under the Apache License, Version 2.0
    6     * (the "License"); you may not use this file except in compliance with
    7     * the License.  You may obtain a copy of the License at
    8     *
    9     *     http://www.apache.org/licenses/LICENSE-2.0
   10     *
   11     * Unless required by applicable law or agreed to in writing, software
   12     * distributed under the License is distributed on an "AS IS" BASIS,
   13     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14     * See the License for the specific language governing permissions and
   15     * limitations under the License.
   16     */
   17   package org.apache.geronimo.yoko;
   18   
   19   import java.io.IOException;
   20   import java.net.ConnectException;
   21   import java.net.InetAddress;
   22   import java.net.ServerSocket;
   23   import java.net.Socket;
   24   import java.security.cert.Certificate;
   25   import java.util.Arrays;
   26   
   27   import javax.net.ssl.HandshakeCompletedEvent;
   28   import javax.net.ssl.HandshakeCompletedListener;
   29   import javax.net.ssl.SSLServerSocket;
   30   import javax.net.ssl.SSLServerSocketFactory;
   31   import javax.net.ssl.SSLSocket;
   32   import javax.net.ssl.SSLSocketFactory;
   33   
   34   import org.slf4j.Logger;
   35   import org.slf4j.LoggerFactory;
   36   import org.apache.geronimo.corba.ORBConfiguration;
   37   import org.apache.geronimo.corba.security.config.ConfigUtil;
   38   import org.apache.geronimo.corba.security.config.ssl.SSLCipherSuiteDatabase;
   39   import org.apache.geronimo.corba.security.config.ssl.SSLConfig;
   40   import org.apache.geronimo.corba.security.config.tss.TSSCompoundSecMechListConfig;
   41   import org.apache.geronimo.corba.security.config.tss.TSSConfig;
   42   import org.apache.geronimo.corba.security.config.tss.TSSSSLTransportConfig;
   43   import org.apache.geronimo.corba.security.config.tss.TSSTransportMechConfig;
   44   import org.apache.geronimo.corba.util.Util;
   45   import org.apache.yoko.orb.OB.IORDump;
   46   import org.apache.yoko.orb.OCI.IIOP.ConnectionHelper;
   47   import org.apache.yoko.orb.OCI.ProfileInfo;
   48   import org.apache.yoko.orb.OCI.ProfileInfoHolder;
   49   import org.omg.CORBA.ORB;
   50   import org.omg.CORBA.Policy;
   51   import org.omg.CSIIOP.EstablishTrustInClient;
   52   import org.omg.CSIIOP.EstablishTrustInTarget;
   53   import org.omg.CSIIOP.NoProtection;
   54   import org.omg.CSIIOP.TAG_CSI_SEC_MECH_LIST;
   55   import org.omg.IIOP.ProfileBody_1_0;
   56   import org.omg.IIOP.ProfileBody_1_0Helper;
   57   import org.omg.IOP.IOR;
   58   
   59   
   60   /**
   61    * Socket factory instance used to interface openejb2
   62    * with the Yoko ORB.  Also enables the ORB for
   63    * SSL-type connections.
   64    * @version $Revision: 505035 $ $Date: 2007-02-08 16:01:06 -0500 (Thu, 08 Feb 2007) $
   65    */
   66   public class SocketFactory implements ConnectionHelper {
   67   
   68       private final static Logger log = LoggerFactory.getLogger(SocketFactory.class);
   69   
   70       // The initialized SSLSocketFactory obtained from the Geronimo KeystoreManager.
   71       private SSLSocketFactory socketFactory = null;
   72       // The initialized SSLServerSocketFactory obtained from the Geronimo KeystoreManager.
   73       private SSLServerSocketFactory serverSocketFactory = null;
   74       // The initialized SSLConfig we use to retrieve the SSL socket factories.
   75       private SSLConfig sslConfig = null;
   76       // The set of cypher suites we use with the SSL connection.
   77       private String[] cipherSuites;
   78       // indicates whether client authentication is supported by this transport.
   79       private boolean clientAuthSupported;
   80       // indicates whether client authentication is required by this transport.
   81       private boolean clientAuthRequired;
   82       // supports and requires values used to retrieve the cipher suites.
   83       int supports = NoProtection.value;
   84       int requires = NoProtection.value;
   85       // the orb we're attached to
   86       private ORB orb;
   87   
   88       public SocketFactory() {
   89       }
   90   
   91       /**
   92        * Initialize the socket factory instance.
   93        *
   94        * @param orb        The hosting ORB.
   95        * @param configName The initialization parameter passed to the socket factor.
   96        *                   This contains the abstract name of our configurator,
   97        *                   which we retrieve from a registry.
   98        */
   99       public void init(ORB orb, String configName) {
  100           this.orb = orb;
  101           clientAuthSupported = false;
  102           clientAuthRequired = false;
  103   
  104           // retrieve the configuration from the config adapter registry.
  105           ORBConfiguration config = Util.getRegisteredORB(configName);
  106           if (config == null) {
  107               throw new RuntimeException("Unable to resolve ORB configuration " + configName);
  108           }
  109           // get the configuration from the hosting bean and decode what needs to be implemented.
  110           sslConfig = config.getSslConfig();
  111           TSSConfig tssConfig = config.getTssConfig();
  112   
  113           TSSTransportMechConfig transportMech = tssConfig.getTransport_mech();
  114           // if we have a transport mech defined, this is the configuration for any listeners we end up
  115           // creating.
  116           if (transportMech != null) {
  117               if (transportMech instanceof TSSSSLTransportConfig) {
  118                   TSSSSLTransportConfig transportConfig = (TSSSSLTransportConfig) transportMech;
  119                   supports = transportConfig.getSupports();
  120                   requires = transportConfig.getRequires();
  121               }
  122           }
  123   
  124           // now set our listener creation flags based on the supports and requires values from the
  125           // TSS config.
  126           if ((supports & EstablishTrustInClient.value) != 0) {
  127               clientAuthSupported = true;
  128   
  129               if ((requires & EstablishTrustInClient.value) != 0) {
  130                   clientAuthRequired = true;
  131               }
  132           }
  133   
  134           if ((supports & EstablishTrustInTarget.value) != 0) {
  135               clientAuthSupported = true;
  136   
  137               if ((requires & EstablishTrustInTarget.value) != 0) {
  138                   clientAuthRequired = true;
  139               }
  140           }
  141   
  142           if (log.isDebugEnabled()) {
  143               log.debug("Creating Yoko SocketFactor for GBean " + configName);
  144               log.debug("   SUPPORTS: " + ConfigUtil.flags(supports));
  145               log.debug("   REQUIRES: " + ConfigUtil.flags(requires));
  146           }
  147       }
  148   
  149       /**
  150        * Create a client socket of the appropriate
  151        * type using the provided IOR and Policy information.
  152        *
  153        * @param ior      The target IOR of the connection.
  154        * @param policies Policies in effect for this ORB.
  155        * @param address  The target address of the connection.
  156        * @param port     The connection port.
  157        *
  158        * @return A Socket (either plain or SSL) configured for connection
  159        *         to the target.
  160        * @exception IOException
  161        * @exception ConnectException
  162        */
  163       public Socket createSocket(IOR ior, Policy[] policies, InetAddress address, int port) throws IOException {
  164           if (log.isDebugEnabled()) {
  165               log.debug("SocketFactory attempting to create socket for address: " + address + " port: " + port);
  166               log.debug("Policies: " + Arrays.asList(policies));
  167               log.debug(IORDump.PrintObjref(orb, ior));
  168           }
  169   
  170           try {
  171               ProfileInfoHolder holder = new ProfileInfoHolder();
  172               // we need to extract the profile information from the IOR to see if this connection has
  173               // any transport-level security defined.
  174               if (org.apache.yoko.orb.OCI.IIOP.Util.extractProfileInfo(ior, holder)) {
  175                   ProfileInfo profileInfo = holder.value;
  176                   for (int i = 0; i < profileInfo.components.length; i++) {
  177                       // we're lookoing for the security mechanism items.
  178                       if (profileInfo.components[i].tag == TAG_CSI_SEC_MECH_LIST.value) {
  179                           try {
  180                               // decode and pull the transport information.
  181                               TSSCompoundSecMechListConfig config = TSSCompoundSecMechListConfig.decodeIOR(Util.getCodec(), profileInfo.components[i]);
  182                               if (log.isDebugEnabled()) {
  183                                   log.debug("looking at tss: " + config);
  184                               }
  185                               for (int j = 0; j < config.size(); j++) {
  186                                   TSSTransportMechConfig transport_mech = config.mechAt(j).getTransport_mech();
  187                                   if (transport_mech instanceof TSSSSLTransportConfig) {
  188                                       TSSSSLTransportConfig transportConfig = (TSSSSLTransportConfig) transport_mech;
  189   
  190                                       int supports = transportConfig.getSupports();
  191                                       int requires = transportConfig.getRequires();
  192                                       // override the port and hostname with what's configured here.
  193                                       int sslPort = transportConfig.getPort(); 
  194                                       String sslHost = transportConfig.getHostname(); 
  195   
  196                                       if (log.isDebugEnabled()) {
  197                                           log.debug("IOR to target " + sslHost + ":" + sslPort);
  198                                           log.debug("   SUPPORTS: " + ConfigUtil.flags(supports));
  199                                           log.debug("   REQUIRES: " + ConfigUtil.flags(requires));
  200                                       }
  201   
  202                                       // TLS is configured.  If this is explicitly noprotection, then
  203                                       // just go create a plain socket using the configured port. 
  204                                       if ((NoProtection.value & requires) == NoProtection.value) {
  205                                           break;
  206                                       }
  207                                       // we need SSL, so create an SSLSocket for this connection.
  208                                       return createSSLSocket(sslHost, sslPort, requires, supports);
  209                                   }
  210                               }
  211                           } catch (Exception e) {
  212                               // do nothing
  213                           }
  214                       }
  215                   }
  216               }
  217   
  218               //SSL not needed, look in the profile for host/port
  219               String host = address.getHostName();
  220   
  221               // the Yoko ORB will use both the primary and secondary targets for connetions, which
  222               // sometimes gets us into trouble, forcing us to use an SSL target when we really need to
  223               // use the plain socket connection.  Therefore, we will ignore what's passed to us,
  224               // and extract the primary port information directly from the profile.
  225               for (int i = 0; i < ior.profiles.length; i++) {
  226                   if (ior.profiles[i].tag == org.omg.IOP.TAG_INTERNET_IOP.value) {
  227                       try {
  228                           //
  229                           // Get the IIOP profile body
  230                           //
  231                           byte[] data = ior.profiles[i].profile_data;
  232                           ProfileBody_1_0 body = ProfileBody_1_0Helper.extract(Util.getCodec().decode_value(data, ProfileBody_1_0Helper.type()));
  233   
  234                           //
  235                           // Create new connector for this profile
  236                           //
  237                           if (body.port < 0) {
  238                               port = 0xffff + (int) body.port + 1;
  239                           } else {
  240                               port = (int) body.port;
  241                           }
  242                           log.debug("set port: " + port);
  243                        } catch (org.omg.IOP.CodecPackage.FormatMismatch e) {
  244                           // just keep the original port.
  245                           log.debug("could not set port: ", e);
  246                           break;
  247                       } catch (org.omg.IOP.CodecPackage.TypeMismatch e) {
  248                           // just keep the original port.
  249                           log.debug("could not set port: ", e);
  250                           break;
  251                       }
  252   
  253                   }
  254               }
  255   
  256   
  257               // if security is not required, just create a plain Socket.
  258               if (log.isDebugEnabled()) log.debug("Created plain endpoint to " + host + ":" + port);
  259               return new Socket(host, port);
  260   
  261           } catch (IOException ex) {
  262               log.error("Exception creating a client socket to "  + address.getHostName() + ":" + port, ex);
  263               throw ex;
  264           }
  265       }
  266   
  267       /**
  268        * Create a loopback connection to the hosting
  269        * ORB.
  270        *
  271        * @param address The address information for the server.
  272        * @param port    The target port.
  273        *
  274        * @return An appropriately configured socket based on the
  275        *         listener characteristics.
  276        * @exception IOException
  277        * @exception ConnectException
  278        */
  279       public Socket createSelfConnection(InetAddress address, int port) throws IOException {
  280           try {
  281               // the requires information tells us whether we created a plain or SSL listener.  We need to create one
  282               // of the matching type.
  283   
  284               if ((NoProtection.value & requires) == NoProtection.value) {
  285                   if (log.isDebugEnabled()) log.debug("Created plain endpoint to " + address.getHostName() + ":" + port);
  286                   return new Socket(address, port);
  287               }
  288               else {
  289                   return createSSLSocket(address.getHostName(), port, requires, supports);
  290               }
  291           } catch (IOException ex) {
  292               log.error("Exception creating a client socket to "  + address.getHostName() + ":" + port, ex);
  293               throw ex;
  294           }
  295       }
  296   
  297       /**
  298        * Create a server socket listening on the given port.
  299        *
  300        * @param port    The target listening port.
  301        * @param backlog The desired backlog value.
  302        *
  303        * @return An appropriate server socket for this connection.
  304        * @exception IOException
  305        * @exception ConnectException
  306        */
  307       public ServerSocket createServerSocket(int port, int backlog)  throws IOException {
  308           try {
  309               // if no protection is required, just create a plain socket.
  310               if ((NoProtection.value & requires) == NoProtection.value) {
  311                   if (log.isDebugEnabled()) log.debug("Created plain server socket for port " + port);
  312                   return new ServerSocket(port, backlog);
  313               }
  314               else {
  315                   // SSL is required.  Create one from the SSLServerFactory retrieved from the config.  This will
  316                   // require additional QOS configuration after creation.
  317                   SSLServerSocket serverSocket = (SSLServerSocket)getServerSocketFactory().createServerSocket(port, backlog);
  318                   configureServerSocket(serverSocket);
  319                   return serverSocket;
  320               }
  321           } catch (IOException ex) {
  322               log.error("Exception creating a server socket for port "  + port, ex);
  323               throw ex;
  324           }
  325       }
  326   
  327       /**
  328        * Create a server socket for this connection.
  329        *
  330        * @param port    The target listener port.
  331        * @param backlog The requested backlog value for the connection.
  332        * @param address The host address information we're publishing under.
  333        *
  334        * @return An appropriately configured ServerSocket for this
  335        *         connection.
  336        * @exception IOException
  337        * @exception ConnectException
  338        */
  339       public ServerSocket createServerSocket(int port, int backlog, InetAddress address) throws IOException {
  340           try {
  341               // if no protection is required, just create a plain socket.
  342               if ((NoProtection.value & requires) == NoProtection.value) {
  343                   if (log.isDebugEnabled()) log.debug("Created plain server socket for port " + port);
  344                   return new ServerSocket(port, backlog, address);
  345               }
  346               else {
  347                   // SSL is required.  Create one from the SSLServerFactory retrieved from the config.  This will
  348                   // require additional QOS configuration after creation.
  349                   SSLServerSocket serverSocket = (SSLServerSocket)getServerSocketFactory().createServerSocket(port, backlog, address);
  350                   configureServerSocket(serverSocket);
  351                   return serverSocket;
  352               }
  353           } catch (IOException ex) {
  354               log.error("Exception creating a client socket to "  + address.getHostName() + ":" + port, ex);
  355               throw ex;
  356           }
  357       }
  358   
  359       /**
  360        * On-demand creation of an SSL socket factory, using the provided
  361        * Geronimo SSLConfig information.
  362        *
  363        * @return The SSLSocketFactory this connection should be using to create
  364        *         secure connections.
  365        * @throws java.io.IOException if we can't get a socket factory
  366        */
  367       private SSLSocketFactory getSocketFactory() throws IOException {
  368           // first use?
  369           if (socketFactory == null) {
  370               // the SSLConfig is optional, so if it's not there, use the default SSLSocketFactory.
  371               if (sslConfig == null) {
  372                   socketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
  373               }
  374               else {
  375                   // ask the SSLConfig bean to create a factory for us.
  376                   try {
  377                       socketFactory = sslConfig.createSSLFactory(Thread.currentThread().getContextClassLoader());
  378                   } catch (Exception e) {
  379                       log.error("Unable to create client SSL socket factory", e);
  380                       throw (IOException)new IOException("Unable to create client SSL socket factory: " + e.getMessage()).initCause(e);
  381                   }
  382               }
  383           }
  384           return socketFactory;
  385       }
  386   
  387       /**
  388        * On-demand creation of an SSL server socket factory, using the provided
  389        * Geronimo SSLConfig information.
  390        *
  391        * @return The SSLServerSocketFactory this connection should be using to create
  392        *         secure connections.
  393        * @throws java.io.IOException if we can't get a server socket factory
  394        */
  395       private SSLServerSocketFactory getServerSocketFactory() throws IOException {
  396           // first use?
  397           if (serverSocketFactory == null) {
  398               // the SSLConfig is optional, so if it's not there, use the default SSLSocketFactory.
  399               if (sslConfig == null) {
  400                   serverSocketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
  401               }
  402               else {
  403                   try {
  404                       serverSocketFactory = sslConfig.createSSLServerFactory(Thread.currentThread().getContextClassLoader());
  405                   } catch (Exception e) {
  406                       log.error("Unable to create server SSL socket factory", e);
  407                       throw (IOException)new IOException("Unable to create server SSL socket factory: " + e.getMessage()).initCause(e);
  408                   }
  409               }
  410               // we have a socket factory....now get our cipher suite set based on our requirements and what's
  411               // available from the factory.
  412               if (cipherSuites == null) {
  413                   cipherSuites = SSLCipherSuiteDatabase.getCipherSuites(requires, supports, serverSocketFactory.getSupportedCipherSuites());
  414               }
  415               // There's a bit of a timing problem with server-side ORBs.  Part of the ORB shutdown is to
  416               // establish a self-connection to shutdown the acceptor threads.  This requires a client
  417               // SSL socket factory.  Unfortunately, if this is occurring during server shutdown, the
  418               // FileKeystoreManager will get a NullPointerException because some name queries fail because
  419               // things are getting shutdown.  Therefore, if we need the server factory, assume we'll also
  420               // need the client factory to shutdown, and request it now.
  421               getSocketFactory();
  422           }
  423           return serverSocketFactory;
  424       }
  425   
  426   
  427       /**
  428        * Set the server socket configuration to our required
  429        * QOS values.
  430        *
  431        * A small experiment shows that setting either (want, need) parameter to either true or false sets the
  432        * other parameter to false.
  433        *
  434        * @param serverSocket
  435        *               The newly created SSLServerSocket.
  436        *
  437        * @throws IOException if server socket can't be configured
  438        */
  439       private void configureServerSocket(SSLServerSocket serverSocket) throws IOException {
  440           // set the authentication value and cipher suite info.
  441           serverSocket.setEnabledCipherSuites(cipherSuites);
  442           if (clientAuthRequired) {
  443               serverSocket.setNeedClientAuth(true);
  444           } else if (clientAuthSupported) {
  445               serverSocket.setWantClientAuth(true);
  446           } else {
  447               serverSocket.setNeedClientAuth(false); //could set want with the same effect
  448           }
  449           serverSocket.setSoTimeout(60 * 1000);
  450   
  451           if (log.isDebugEnabled()) {
  452               log.debug("Created SSL server socket on port " + serverSocket.getLocalPort());
  453               log.debug("    client authentication " + (clientAuthSupported ? "SUPPORTED" : "UNSUPPORTED"));
  454               log.debug("    client authentication " + (clientAuthRequired ? "REQUIRED" : "OPTIONAL"));
  455               log.debug("    cipher suites:");
  456   
  457               for (int i = 0; i < cipherSuites.length; i++) {
  458                   log.debug("    " + cipherSuites[i]);
  459               }
  460           }
  461       }
  462   
  463       /**
  464        * Create an SSL client socket using the IOR-encoded
  465        * security characteristics.
  466        * Setting want/need client auth on a client socket has no effect so all we can do is use the right host, port, ciphers
  467        *
  468        * @param host     The target host name.
  469        * @param port     The target connection port.
  470        *
  471        * @return An appropriately configured client SSLSocket.
  472        * @exception IOException if ssl socket can't be obtained and configured.
  473        */
  474       private Socket createSSLSocket(String host, int port, int requires, int supports) throws IOException {
  475           SSLSocketFactory factory = getSocketFactory();
  476           SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
  477   
  478           socket.setSoTimeout(60 * 1000);
  479   
  480           // get a set of cipher suites appropriate for this connections requirements.
  481           // We request this for each connection, since the outgoing IOR's requirements may be different from
  482           // our server listener requirements.
  483           String[] iorSuites = SSLCipherSuiteDatabase.getCipherSuites(requires, supports, factory.getSupportedCipherSuites());
  484           socket.setEnabledCipherSuites(iorSuites);
  485           if (log.isDebugEnabled()) {
  486               log.debug("Created SSL socket to " + host + ":" + port);
  487               log.debug("    cipher suites:");
  488   
  489               for (int i = 0; i < iorSuites.length; i++) {
  490                   log.debug("    " + iorSuites[i]);
  491               }
  492               socket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
  493   
  494                   public void handshakeCompleted(HandshakeCompletedEvent handshakeCompletedEvent) {
  495                       Certificate[] certs = handshakeCompletedEvent.getLocalCertificates();
  496                       if (certs != null) {
  497                           log.debug("handshake returned local certs count: " + certs.length);
  498                           for (int i = 0; i < certs.length; i++) {
  499                               Certificate cert = certs[i];
  500                               log.debug("cert: " + cert.toString());
  501                           }
  502                       } else {
  503                           log.debug("handshake returned no local certs");
  504                       }
  505                   }
  506               });
  507           }
  508           return socket;
  509       }
  510   }

Home » geronimo-2.2-source-release » org.apache.geronimo.yoko » [javadoc | source]