Logo Search packages:      
Sourcecode: zeroc-ice-java version File versions  Download package

Instance.java

// **********************************************************************
//
// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
//
// This copy of Ice is licensed to you under the terms described in the
// ICE_LICENSE file included in this distribution.
//
// **********************************************************************

package IceSSL;

class Instance
{
    Instance(Ice.Communicator communicator)
    {
      _logger = communicator.getLogger();
      _facade = Ice.Util.getProtocolPluginFacade(communicator);
      _securityTraceLevel = communicator.getProperties().getPropertyAsIntWithDefault("IceSSL.Trace.Security", 0);
      _securityTraceCategory = "Security";
      _initialized = false;
      _trustManager = new TrustManager(communicator);

      // 
      // Register the endpoint factory. We have to do this now, rather than
      // in initialize, because the communicator may need to interpret
      // proxies before the plugin is fully initialized.
      //
      _facade.addEndpointFactory(new EndpointFactoryI(this));
    }

    void
    initialize()
    {
      if(_initialized)
      {   
          return;
      }

      final String prefix = "IceSSL.";
      Ice.Properties properties = communicator().getProperties();

      //
      // Parse the cipher list.
      //
      String ciphers = properties.getProperty(prefix + "Ciphers");
      if(ciphers.length() > 0)
      {
          parseCiphers(ciphers);
      }

      //
      // Select protocols.
      //
      String protocols = properties.getProperty(prefix + "Protocols");
      if(protocols.length() > 0)
      {
          java.util.ArrayList l = new java.util.ArrayList();
          String[] arr = protocols.split("[ \t,]+");
          for(int i = 0; i < arr.length; ++i)
          {
            String s = arr[i].toLowerCase();
            if(s.equals("ssl3") || s.equals("sslv3"))
            {
                l.add("SSLv3");
            }
            else if(s.equals("tls") || s.equals("tls1") || s.equals("tlsv1"))
            {
                l.add("TLSv1");
            }
            else
            {
                Ice.PluginInitializationException e = new Ice.PluginInitializationException();
                e.reason = "IceSSL: unrecognized protocol `" + arr[i] + "'";
                throw e;
            }
          }
          _protocols = new String[l.size()];
          l.toArray(_protocols);
      }

      //
      // CheckCertName determines whether we compare the name in a peer's
      // certificate against its hostname.
      //
      _checkCertName = properties.getPropertyAsIntWithDefault(prefix + "CheckCertName", 0) > 0;

      //
      // VerifyDepthMax establishes the maximum length of a peer's certificate
      // chain, including the peer's certificate. A value of 0 means there is
      // no maximum.
      //
      _verifyDepthMax = properties.getPropertyAsIntWithDefault(prefix + "VerifyDepthMax", 2);

      //
      // If the user doesn't supply an SSLContext, we need to create one based
      // on property settings.
      //
      if(_context == null)
      {
          try
          {
            //
            // Check for a default directory. We look in this directory for
            // files mentioned in the configuration.
            //
            _defaultDir = properties.getProperty(prefix + "DefaultDir");

            //
            // We need a SecureRandom object.
            //
            // NOTE: The JDK recommends obtaining a SecureRandom object like this:
            //
            // java.security.SecureRandom rand = java.security.SecureRandom.getInstance("SHA1PRNG");
            //
            // However, there is a bug (6202721) which causes it to always use /dev/random,
            // which can lead to long delays at program startup. The workaround is to use
            // the default constructor.
            //
            java.security.SecureRandom rand = new java.security.SecureRandom();

            //
            // Check for seed data for the random number generator.
            //
            final String seedFiles = properties.getProperty(prefix + "Random");
            if(seedFiles.length() > 0)
            {
                byte[] seed = null;
                int start = 0;
                final String[] arr = seedFiles.split(java.io.File.pathSeparator);
                for(int i = 0; i < arr.length; ++i)
                {
                  Ice.StringHolder seedFile = new Ice.StringHolder(arr[i]);
                  if(!checkPath(seedFile, false))
                  {
                      Ice.PluginInitializationException e = new Ice.PluginInitializationException();
                      e.reason = "IceSSL: random seed file not found:\n" + arr[i];
                      throw e;
                  }
                  java.io.File f = new java.io.File(seedFile.value);
                  int num = (int)f.length();
                  if(seed == null)
                  {
                      seed = new byte[num];
                  }
                  else
                  {
                      byte[] tmp = new byte[seed.length + num];
                      System.arraycopy(seed, 0, tmp, 0, seed.length);
                      start = seed.length;
                      seed = tmp;
                  }
                  try
                  {
                      java.io.FileInputStream in = new java.io.FileInputStream(f);
                      in.read(seed, start, num);
                      in.close();
                  }
                  catch(java.io.IOException ex)
                  {
                      Ice.PluginInitializationException e = new Ice.PluginInitializationException();
                      e.reason = "IceSSL: error while reading random seed file:\n" + arr[i];
                      e.initCause(ex);
                      throw e;
                  }
                }
                rand.setSeed(seed);
            }

            //
            // We call nextInt() in order to force the object to perform any time-consuming
            // initialization tasks now.
            //
            rand.nextInt();

            //
            // The keystore holds private keys and associated certificates.
            //
            Ice.StringHolder keystorePath = new Ice.StringHolder(properties.getProperty(prefix + "Keystore"));

            //
            // The password for the keys.
            //
            final String password = properties.getProperty(prefix + "Password");

            //
            // The password for the keystore.
            //
            final String keystorePassword = properties.getProperty(prefix + "KeystorePassword");

            //
            // The default keystore type value is "JKS", but it can also be "PKCS12".
            //
            final String defaultType = java.security.KeyStore.getDefaultType();
            final String keystoreType = properties.getPropertyWithDefault(prefix + "KeystoreType", defaultType);

            //
            // The alias of the key to use in authentication.
            //
            final String alias = properties.getProperty(prefix + "Alias");

            //
            // The truststore holds the certificates of trusted CAs.
            //
            Ice.StringHolder truststorePath = new Ice.StringHolder(properties.getProperty(prefix + "Truststore"));

            //
            // The password for the truststore.
            //
            final String truststorePassword = properties.getProperty(prefix + "TruststorePassword");

            //
            // The truststore type defaults to "JKS", but it can also be "PKCS12".
            //
            String truststoreType = properties.getPropertyWithDefault(prefix + "TruststoreType",
                                                        java.security.KeyStore.getDefaultType());

            //
            // Collect the key managers.
            //
            javax.net.ssl.KeyManager[] keyManagers = null;
            if(keystorePath.value.length() > 0)
            {
                if(!checkPath(keystorePath, false))
                {
                  Ice.PluginInitializationException e = new Ice.PluginInitializationException();
                  e.reason = "IceSSL: keystore file not found:\n" + keystorePath.value;
                  throw e;
                }
                java.security.KeyStore keys = java.security.KeyStore.getInstance(keystoreType);
                try
                {
                  char[] passwordChars = null;
                  if(keystorePassword.length() > 0)
                  {
                      passwordChars = keystorePassword.toCharArray();
                  }

                  java.io.BufferedInputStream bis =
                      new java.io.BufferedInputStream(new java.io.FileInputStream(keystorePath.value));
                  keys.load(bis, passwordChars);
                }
                catch(java.io.IOException ex)
                {
                  Ice.PluginInitializationException e = new Ice.PluginInitializationException();
                  e.reason = "IceSSL: unable to load keystore:\n" + keystorePath.value;
                  e.initCause(ex);
                  throw e;
                }

                String algorithm = javax.net.ssl.KeyManagerFactory.getDefaultAlgorithm();
                javax.net.ssl.KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance(algorithm);
                kmf.init(keys, password.toCharArray());
                keyManagers = kmf.getKeyManagers();

                //
                // If the user selected a specific alias, we need to wrap the key managers
                // in order to return the desired alias.
                //
                if(alias.length() > 0)
                {
                  if(!keys.isKeyEntry(alias))
                  {
                      Ice.PluginInitializationException e = new Ice.PluginInitializationException();
                      e.reason = "IceSSL: keystore does not contain an entry with alias `" + alias + "'";
                      throw e;
                  }

                  for(int i = 0; i < keyManagers.length; ++i)
                  {
                      keyManagers[i] = new X509KeyManagerI((javax.net.ssl.X509KeyManager)keyManagers[i], alias);
                  }
                }
            }

            //
            // Collect the trust managers.
            //
            javax.net.ssl.TrustManager[] trustManagers = null;
            if(truststorePath.value.length() > 0)
            {
                if(!checkPath(truststorePath, false))
                {
                  Ice.PluginInitializationException e = new Ice.PluginInitializationException();
                  e.reason = "IceSSL: truststore file not found:\n" + truststorePath.value;
                  throw e;
                }
                java.security.KeyStore ts = java.security.KeyStore.getInstance(truststoreType);
                try
                {
                  char[] passwordChars = null;
                  if(truststorePassword.length() > 0)
                  {
                      passwordChars = truststorePassword.toCharArray();
                  }

                  java.io.BufferedInputStream bis =
                      new java.io.BufferedInputStream(new java.io.FileInputStream(truststorePath.value));
                  ts.load(bis, passwordChars);
                }
                catch(java.io.IOException ex)
                {
                  Ice.PluginInitializationException e = new Ice.PluginInitializationException();
                  e.reason = "IceSSL: unable to load truststore:\n" + truststorePath.value;
                  e.initCause(ex);
                  throw e;
                }

                String algorithm = javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm();
                javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance(algorithm);
                tmf.init(ts);
                trustManagers = tmf.getTrustManagers();
            }

            //
            // The default TrustManager implementation in IBM's JDK does not accept
            // anonymous ciphers, so we have to install our own.
            //
            if(trustManagers == null)
            {
                trustManagers = new javax.net.ssl.TrustManager[1];
                trustManagers[0] = new X509TrustManagerI(null);
            }
            else
            {
                for(int i = 0; i < trustManagers.length; ++i)
                {
                  trustManagers[i] = new X509TrustManagerI((javax.net.ssl.X509TrustManager)trustManagers[i]);
                }
            }

            //
            // Initialize the SSL context.
            //
            _context = javax.net.ssl.SSLContext.getInstance("SSL");
            _context.init(keyManagers, trustManagers, rand);
          }
          catch(java.security.GeneralSecurityException ex)
          {
            Ice.PluginInitializationException e = new Ice.PluginInitializationException();
            e.reason = "IceSSL: unable to initialize context";
            e.initCause(ex);
            throw e;
          }
      }

      _initialized = true;
    }

    void
    context(javax.net.ssl.SSLContext context)
    {
      if(_initialized)
      {
          Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
          ex.reason = "IceSSL: plugin is already initialized";
          throw ex;
      }

      _context = context;
    }

    javax.net.ssl.SSLContext
    context()
    {
      return _context;
    }

    void
    setCertificateVerifier(CertificateVerifier verifier)
    {
      _verifier = verifier;
    }

    Ice.Communicator
    communicator()
    {
      return _facade.getCommunicator();
    }

    String
    defaultHost()
    {
      return _facade.getDefaultHost();
    }

    int
    networkTraceLevel()
    {
      return _facade.getNetworkTraceLevel();
    }

    String
    networkTraceCategory()
    {
      return _facade.getNetworkTraceCategory();
    }

    int
    securityTraceLevel()
    {
      return _securityTraceLevel;
    }

    String
    securityTraceCategory()
    {
      return _securityTraceCategory;
    }

    boolean
    initialized()
    {
      return _initialized;
    }

    String[]
    filterCiphers(String[] supportedCiphers, String[] defaultCiphers)
    {
      java.util.LinkedList result = new java.util.LinkedList();
      if(_allCiphers)
      {
          for(int i = 0; i < supportedCiphers.length; ++i)
          {
            result.add(supportedCiphers[i]);
          }
      }
      else if(!_noCiphers)
      {
          for(int i = 0; i < defaultCiphers.length; ++i)
          {
            result.add(defaultCiphers[i]);
          }
      }

      if(_ciphers != null)
      {
          for(int i = 0; i < _ciphers.length; ++i)
          {
            CipherExpression ce = (CipherExpression)_ciphers[i];
            if(ce.not)
            {
                java.util.Iterator e = result.iterator();
                while(e.hasNext())
                {
                  String cipher = (String)e.next();
                  if(ce.cipher != null)
                  {
                      if(ce.cipher.equals(cipher))
                      {
                        e.remove();
                      }
                  }
                  else
                  {
                      assert(ce.re != null);
                      java.util.regex.Matcher m = ce.re.matcher(cipher);
                      if(m.find())
                      {
                        e.remove();
                      }
                  }
                }
            }
            else
            {
                if(ce.cipher != null)
                {
                  result.add(0, ce.cipher);
                }
                else
                {
                  assert(ce.re != null);
                  for(int j = 0; j < supportedCiphers.length; ++j)
                  {
                      java.util.regex.Matcher m = ce.re.matcher(supportedCiphers[j]);
                      if(m.find())
                      {
                        result.add(0, supportedCiphers[j]);
                      }
                  }
                }
            }
          }
      }

      String[] arr = new String[result.size()];
      result.toArray(arr);
      return arr;
    }

    String[]
    protocols()
    {
      return _protocols;
    }

    void
    traceConnection(javax.net.ssl.SSLSocket fd, boolean incoming)
    {
      javax.net.ssl.SSLSession session = fd.getSession();
      String msg = "SSL summary for " + (incoming ? "incoming" : "outgoing") + " connection\n" +
          "cipher = " + session.getCipherSuite() + "\n" +
          "protocol = " + session.getProtocol() + "\n" +
          IceInternal.Network.fdToString(fd);
      _logger.trace(_securityTraceCategory, msg);
    }

    void
    verifyPeer(ConnectionInfo info, javax.net.ssl.SSLSocket fd, String address, boolean incoming)
    {
      if(_verifyDepthMax > 0 && info.certs != null && info.certs.length > _verifyDepthMax)
      {
          String msg = (incoming ? "incoming" : "outgoing") + " connection rejected:\n" +
            "length of peer's certificate chain (" + info.certs.length + ") exceeds maximum of " +
            _verifyDepthMax + "\n" +
            IceInternal.Network.fdToString(fd);
          if(_securityTraceLevel >= 1)
          {
            _logger.trace(_securityTraceCategory, msg);
          }
          Ice.SecurityException ex = new Ice.SecurityException();
          ex.reason = msg;
          throw ex;
      }

      //
      // Extract the IP addresses and the DNS names from the subject
      // alternative names.
      //
      if(info.certs != null)
      {
          try
          {
            java.util.Collection subjectAltNames =
                ((java.security.cert.X509Certificate)info.certs[0]).getSubjectAlternativeNames();
            java.util.ArrayList ipAddresses = new java.util.ArrayList();
            java.util.ArrayList dnsNames = new java.util.ArrayList();
            if(subjectAltNames != null)
            {
                java.util.Iterator i = subjectAltNames.iterator();
                while(i.hasNext())
                {
                  java.util.List l = (java.util.List)i.next();
                  assert(!l.isEmpty());
                  Integer n = (Integer)l.get(0);
                  if(n.intValue() == 7)
                  {
                      ipAddresses.add((String)l.get(1));
                  }
                  else if(n.intValue() == 2)
                  {
                      dnsNames.add(((String)l.get(1)).toLowerCase());
                  }
                }
            }

            //
            // Compare the peer's address against the dnsName and ipAddress values.
            // This is only relevant for an outgoing connection.
            //
            if(address.length() > 0)
            {
                boolean certNameOK = ipAddresses.contains(address);
                if(!certNameOK)
                {
                  certNameOK = dnsNames.contains(address.toLowerCase());
                }

                //
                // Log a message if the name comparison fails. If CheckCertName is defined,
                // we also raise an exception to abort the connection. Don't log a message if
                // CheckCertName is not defined and a verifier is present.
                //
                if(!certNameOK && (_checkCertName || (_securityTraceLevel >= 1 && _verifier == null)))
                {
                  StringBuffer sb = new StringBuffer();
                  sb.append("IceSSL: ");
                  if(!_checkCertName)
                  {
                      sb.append("ignoring ");
                  }
                  sb.append("certificate validation failure:\npeer certificate does not contain `" +
                          address + "' in its subjectAltName extension");
                  if(!dnsNames.isEmpty())
                  {
                      sb.append("\nDNS names found in certificate: ");
                      for(int j = 0; j < dnsNames.size(); ++j)
                      {
                        if(j > 0)
                        {
                            sb.append(", ");
                        }
                        sb.append(dnsNames.get(j).toString());
                      }
                  }
                  if(!ipAddresses.isEmpty())
                  {
                      sb.append("\nIP addresses found in certificate: ");
                      for(int j = 0; j < ipAddresses.size(); ++j)
                      {
                        if(j > 0)
                        {
                            sb.append(", ");
                        }
                        sb.append(ipAddresses.get(j).toString());
                      }
                  }
                  if(_securityTraceLevel >= 1)
                  {
                      _logger.trace(_securityTraceCategory, sb.toString());
                  }
                  if(_checkCertName)
                  {
                      Ice.SecurityException ex = new Ice.SecurityException();
                      ex.reason = sb.toString();
                      throw ex;
                  }
                }
            }
          }
          catch(java.security.cert.CertificateParsingException ex)
          {
            assert(false);
          }
      }

      if(!_trustManager.verify(info))
      {
          String msg = (incoming ? "incoming" : "outgoing") + " connection rejected by trust manager\n" +
            IceInternal.Network.fdToString(fd);
          if(_securityTraceLevel >= 1)
          {
            _logger.trace(_securityTraceCategory, msg);
          }
          Ice.SecurityException ex = new Ice.SecurityException();
          ex.reason = msg;
          throw ex;
      }

      if(_verifier != null && !_verifier.verify(info))
      {
          String msg = (incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier\n" +
            IceInternal.Network.fdToString(fd);

          if(_securityTraceLevel > 0)
          {
            _logger.trace(_securityTraceCategory, msg);
          }

          Ice.SecurityException ex = new Ice.SecurityException();
          ex.reason = msg;
          throw ex;
      }
    }

    private void
    parseCiphers(String ciphers)
    {
      java.util.ArrayList cipherList = new java.util.ArrayList();
      String[] expr = ciphers.split("[ \t]+");
      for(int i = 0; i < expr.length; ++i)
      {
          if(expr[i].equals("ALL"))
          {
            if(i != 0)
            {
                Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
                ex.reason = "IceSSL: `ALL' must be first in cipher list `" + ciphers + "'";
                throw ex;
            }
            _allCiphers = true;
          }
          else if(expr[i].equals("NONE"))
          {
            if(i != 0)
            {
                Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
                ex.reason = "IceSSL: `NONE' must be first in cipher list `" + ciphers + "'";
                throw ex;
            }
            _noCiphers = true;
          }
          else
          {
            CipherExpression ce = new CipherExpression();
            String exp = expr[i];
            if(exp.charAt(0) == '!')
            {
                ce.not = true;
                if(exp.length() > 1)
                {
                  exp = exp.substring(1);
                }
                else
                {
                  Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
                  ex.reason = "IceSSL: invalid cipher expression `" + exp + "'";
                  throw ex;
                }
            }

            if(exp.charAt(0) == '(')
            {
                if(!exp.endsWith(")"))
                {
                  Ice.PluginInitializationException ex = new Ice.PluginInitializationException();
                  ex.reason = "IceSSL: invalid cipher expression `" + exp + "'";
                  throw ex;
                }

                try
                {
                  ce.re = java.util.regex.Pattern.compile(exp.substring(1, exp.length() - 2));
                }
                catch(java.util.regex.PatternSyntaxException ex)
                {
                  Ice.PluginInitializationException e = new Ice.PluginInitializationException();
                  e.reason = "IceSSL: invalid cipher expression `" + exp + "'";
                  e.initCause(ex);
                  throw e;
                }
            }
            else
            {
                ce.cipher = exp;
            }

            cipherList.add(ce);
          }
      }
      _ciphers = new CipherExpression[cipherList.size()];
      cipherList.toArray(_ciphers);
    }

    private boolean
    checkPath(Ice.StringHolder path, boolean dir)
    {
      //
      // Check if file exists. If not, try prepending the default
      // directory and check again. If the file is found, the
      // string argument is modified and true is returned. Otherwise
      // false is returned.
      //
      java.io.File f = new java.io.File(path.value);
      if(f.exists())
      {
          return dir ? f.isDirectory() : f.isFile();
      }

      if(_defaultDir.length() > 0)
      {
          String s = _defaultDir + java.io.File.separator + path.value;
          f = new java.io.File(s);
          if(f.exists() && ((!dir && f.isFile()) || (dir && f.isDirectory())))
          {
            path.value = s;
            return true;
          }
      }

      return false;
    }

    private static class CipherExpression
    {
      boolean not;
      String cipher;
      java.util.regex.Pattern re;
    }

    private Ice.Logger _logger;
    private IceInternal.ProtocolPluginFacade _facade;
    private int _securityTraceLevel;
    private String _securityTraceCategory;
    private boolean _initialized;
    private javax.net.ssl.SSLContext _context;
    private String _defaultDir;
    private CipherExpression[] _ciphers;
    private boolean _allCiphers;
    private boolean _noCiphers;
    private String[] _protocols;
    private boolean _checkCertName;
    private int _verifyDepthMax;
    private CertificateVerifier _verifier;
    private TrustManager _trustManager;
}

Generated by  Doxygen 1.6.0   Back to index