diff options
100 files changed, 5942 insertions, 1465 deletions
diff --git a/config/PropertyNames.xml b/config/PropertyNames.xml index 0b718a9c058..f50ff305d7b 100644 --- a/config/PropertyNames.xml +++ b/config/PropertyNames.xml @@ -535,12 +535,15 @@ generated from the section label. <property name="Ciphers" /> <property name="DefaultDir" /> <property name="DH.[any]" /> + <property name="DHParams" /> <property name="EntropyDaemon" /> <property name="FindCert.[any]" /> <property name="ImportCert.[any]" /> <property name="InitOpenSSL" /> <property name="KeyFile" /> <property name="KeySet"/> + <property name="Keychain"/> + <property name="KeychainPassword"/> <property name="Keystore" /> <property name="KeystorePassword" /> <property name="KeystoreType" /> @@ -549,6 +552,8 @@ generated from the section label. <property name="PasswordRetryMax" /> <property name="PersistKeySet"/> <property name="Protocols" /> + <property name="ProtocolVersionMax" /> + <property name="ProtocolVersionMin" /> <property name="Random" /> <property name="Trace.Security" /> <property name="TrustOnly" /> diff --git a/cpp/config/Make.rules b/cpp/config/Make.rules index 03e1d2cbb27..54fa9d6c81a 100644 --- a/cpp/config/Make.rules +++ b/cpp/config/Make.rules @@ -204,6 +204,10 @@ ifeq ($(OPENSSL_LIBS),) endif OPENSSL_RPATH_LINK = $(if $(OPENSSL_HOME),$(call rpathlink,$(OPENSSL_HOME)/$(libsubdir))) +ifeq ($(SSL_OS_LIBS),) + SSL_OS_LIBS = $(OPENSSL_LIBS) +endif + BZIP2_FLAGS = $(if $(BZIP2_HOME),-I$(BZIP2_HOME)/include) ifeq ($(BZIP2_LIBS),) BZIP2_LIBS = $(if $(BZIP2_HOME),-L$(BZIP2_HOME)/$(libsubdir)) -lbz2 diff --git a/cpp/config/Make.rules.Darwin b/cpp/config/Make.rules.Darwin index 7f6cc37e2e4..d59297b43b1 100644 --- a/cpp/config/Make.rules.Darwin +++ b/cpp/config/Make.rules.Darwin @@ -15,8 +15,8 @@ OSX_TARGET_MIN_SDK_VERSION = 10.8 CXX = xcrun clang++ -CPPFLAGS += -pthread -CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden -Wall -Werror -mmacosx-version-min=$(OSX_TARGET_MIN_SDK_VERSION) +CPPFLAGS += -pthread +CXXFLAGS += -Wall -Werror -mmacosx-version-min=$(OSX_TARGET_MIN_SDK_VERSION) ifeq ($(OPTIMIZE),yes) # @@ -76,4 +76,6 @@ ICONV_LIB = -liconv ICEUTIL_OS_LIBS = ICE_OS_LIBS = -ldl +SSL_OS_LIBS = -framework Security -framework CoreFoundation + PLATFORM_HAS_READLINE := yes diff --git a/cpp/demo/Glacier2/callback/config.client b/cpp/demo/Glacier2/callback/config.client index 250ed196408..6d6b5365d62 100644 --- a/cpp/demo/Glacier2/callback/config.client +++ b/cpp/demo/Glacier2/callback/config.client @@ -49,3 +49,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password
\ No newline at end of file diff --git a/cpp/demo/Glacier2/callback/config.glacier2 b/cpp/demo/Glacier2/callback/config.glacier2 index ff9a3550c2c..b1e379eeb40 100644 --- a/cpp/demo/Glacier2/callback/config.glacier2 +++ b/cpp/demo/Glacier2/callback/config.glacier2 @@ -126,3 +126,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=glacier2.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Glacier2/chat/config.client b/cpp/demo/Glacier2/chat/config.client index 93312f8c547..64c7e27d6c3 100644 --- a/cpp/demo/Glacier2/chat/config.client +++ b/cpp/demo/Glacier2/chat/config.client @@ -43,6 +43,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/cpp/demo/Glacier2/chat/config.glacier2 b/cpp/demo/Glacier2/chat/config.glacier2 index de080939094..e6680be52a7 100644 --- a/cpp/demo/Glacier2/chat/config.glacier2 +++ b/cpp/demo/Glacier2/chat/config.glacier2 @@ -57,6 +57,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=glacier2.keychain +IceSSL.KeychainPassword=password # # Uncomment the properties below if you want run the demo with the diff --git a/cpp/demo/Ice/callback/config.client b/cpp/demo/Ice/callback/config.client index acd7e0f4b33..000b2146af1 100644 --- a/cpp/demo/Ice/callback/config.client +++ b/cpp/demo/Ice/callback/config.client @@ -50,3 +50,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/callback/config.server b/cpp/demo/Ice/callback/config.server index ad65f43d4d8..048f0045f62 100644 --- a/cpp/demo/Ice/callback/config.server +++ b/cpp/demo/Ice/callback/config.server @@ -44,3 +44,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/hello/config.client b/cpp/demo/Ice/hello/config.client index 6140a70ea4a..b791b95194a 100644 --- a/cpp/demo/Ice/hello/config.client +++ b/cpp/demo/Ice/hello/config.client @@ -53,6 +53,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/cpp/demo/Ice/hello/config.server b/cpp/demo/Ice/hello/config.server index ae02d1fdbd1..e576633d0b7 100644 --- a/cpp/demo/Ice/hello/config.server +++ b/cpp/demo/Ice/hello/config.server @@ -54,6 +54,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password # # Uncomment the properties below if you want run the demo with the diff --git a/cpp/demo/Ice/interleaved/config.client b/cpp/demo/Ice/interleaved/config.client index e3c81d95d64..181a670fe23 100755 --- a/cpp/demo/Ice/interleaved/config.client +++ b/cpp/demo/Ice/interleaved/config.client @@ -17,4 +17,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem - +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/interleaved/config.server b/cpp/demo/Ice/interleaved/config.server index 78ecfdadf53..b3afa959f38 100755 --- a/cpp/demo/Ice/interleaved/config.server +++ b/cpp/demo/Ice/interleaved/config.server @@ -29,3 +29,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/invoke/config.client b/cpp/demo/Ice/invoke/config.client index c9ab176fc28..bbf5b32c3c4 100644 --- a/cpp/demo/Ice/invoke/config.client +++ b/cpp/demo/Ice/invoke/config.client @@ -43,3 +43,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/invoke/config.server b/cpp/demo/Ice/invoke/config.server index 599bffc673b..afb8a12a7e1 100644 --- a/cpp/demo/Ice/invoke/config.server +++ b/cpp/demo/Ice/invoke/config.server @@ -44,3 +44,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/latency/config.client b/cpp/demo/Ice/latency/config.client index 74332ccceab..43061f18a12 100644 --- a/cpp/demo/Ice/latency/config.client +++ b/cpp/demo/Ice/latency/config.client @@ -29,6 +29,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/cpp/demo/Ice/latency/config.server b/cpp/demo/Ice/latency/config.server index 24219cabf08..e324e7659e5 100644 --- a/cpp/demo/Ice/latency/config.server +++ b/cpp/demo/Ice/latency/config.server @@ -27,6 +27,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/cpp/demo/Ice/nested/config.client b/cpp/demo/Ice/nested/config.client index 01c322a06f1..86de72eaed0 100644 --- a/cpp/demo/Ice/nested/config.client +++ b/cpp/demo/Ice/nested/config.client @@ -61,3 +61,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/nested/config.server b/cpp/demo/Ice/nested/config.server index d65b25550e6..92bce3de59a 100644 --- a/cpp/demo/Ice/nested/config.server +++ b/cpp/demo/Ice/nested/config.server @@ -55,3 +55,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password
\ No newline at end of file diff --git a/cpp/demo/Ice/nrvo/config.client b/cpp/demo/Ice/nrvo/config.client index 6a2ab473a5d..fc38babe618 100644 --- a/cpp/demo/Ice/nrvo/config.client +++ b/cpp/demo/Ice/nrvo/config.client @@ -12,4 +12,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem - +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/nrvo/config.server b/cpp/demo/Ice/nrvo/config.server index 053eecddce8..d0f78c0f18a 100644 --- a/cpp/demo/Ice/nrvo/config.server +++ b/cpp/demo/Ice/nrvo/config.server @@ -18,3 +18,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/properties/config.client b/cpp/demo/Ice/properties/config.client index 8b079dfc267..c0d74a6be27 100644 --- a/cpp/demo/Ice/properties/config.client +++ b/cpp/demo/Ice/properties/config.client @@ -49,3 +49,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/properties/config.server b/cpp/demo/Ice/properties/config.server index 3fae2c52d4d..2969290fd56 100644 --- a/cpp/demo/Ice/properties/config.server +++ b/cpp/demo/Ice/properties/config.server @@ -59,3 +59,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password diff --git a/cpp/demo/Ice/throughput/config.client b/cpp/demo/Ice/throughput/config.client index 0329afc63d2..8dd16d1c369 100644 --- a/cpp/demo/Ice/throughput/config.client +++ b/cpp/demo/Ice/throughput/config.client @@ -29,6 +29,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/cpp/demo/Ice/throughput/config.server b/cpp/demo/Ice/throughput/config.server index 85adf59767d..4d422916147 100644 --- a/cpp/demo/Ice/throughput/config.server +++ b/cpp/demo/Ice/throughput/config.server @@ -28,6 +28,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/cpp/demo/IceBox/hello/config.client b/cpp/demo/IceBox/hello/config.client index c6fb2622b1b..59af9979d56 100644 --- a/cpp/demo/IceBox/hello/config.client +++ b/cpp/demo/IceBox/hello/config.client @@ -43,3 +43,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password
\ No newline at end of file diff --git a/cpp/demo/IceBox/hello/config.service b/cpp/demo/IceBox/hello/config.service index 2b91e157f7f..b4a2ff5e2d3 100644 --- a/cpp/demo/IceBox/hello/config.service +++ b/cpp/demo/IceBox/hello/config.service @@ -43,3 +43,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=service.keychain +IceSSL.KeychainPassword=password
\ No newline at end of file diff --git a/cpp/demo/IceDiscovery/hello/config.client b/cpp/demo/IceDiscovery/hello/config.client index 7303f6c6797..7358239c4a6 100644 --- a/cpp/demo/IceDiscovery/hello/config.client +++ b/cpp/demo/IceDiscovery/hello/config.client @@ -21,6 +21,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/cpp/demo/IceDiscovery/hello/config.server b/cpp/demo/IceDiscovery/hello/config.server index d03024f20cf..bbacf2c65fa 100644 --- a/cpp/demo/IceDiscovery/hello/config.server +++ b/cpp/demo/IceDiscovery/hello/config.server @@ -29,6 +29,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password # # Uncomment the properties below if you want run the demo with the diff --git a/cpp/demo/IceGrid/secure/application.xml b/cpp/demo/IceGrid/secure/application.xml index 2f0b2d59374..c0d4d6e17ff 100644 --- a/cpp/demo/IceGrid/secure/application.xml +++ b/cpp/demo/IceGrid/secure/application.xml @@ -18,6 +18,8 @@ <property name="IceSSL.CertFile" value="server_cert.pem"/> <property name="IceSSL.KeyFile" value="server_key.pem"/> <property name="IceSSL.DefaultDir" value="certs"/> + <property name="IceSSL.Keychain" value="server.keychain"/> + <property name="IceSSL.KeychainPassword" value="password"/> <property name="Ice.Admin.Endpoints" value="ssl -h localhost"/> <property name="IceSSL.TrustOnly.Client" value="CN=Master;CN=Slave"/> diff --git a/cpp/demo/IceGrid/secure/config.glacier2 b/cpp/demo/IceGrid/secure/config.glacier2 index 83f7317aa22..a54d78c70a2 100644 --- a/cpp/demo/IceGrid/secure/config.glacier2 +++ b/cpp/demo/IceGrid/secure/config.glacier2 @@ -21,6 +21,8 @@ IceSSL.DefaultDir=certs IceSSL.CertAuthFile=ca_cert.pem IceSSL.CertFile=glacier2_cert.pem IceSSL.KeyFile=glacier2_key.pem +IceSSL.Keychain=glacier2.keychain +IceSSL.KeychainPassword=password # # Don't require certificates. This is useful for admin clients that don't diff --git a/cpp/demo/IceGrid/secure/config.master b/cpp/demo/IceGrid/secure/config.master index 2b5a672355b..551bc373cfb 100644 --- a/cpp/demo/IceGrid/secure/config.master +++ b/cpp/demo/IceGrid/secure/config.master @@ -40,6 +40,8 @@ IceSSL.DefaultDir=certs IceSSL.CertAuthFile=ca_cert.pem IceSSL.CertFile=master_cert.pem IceSSL.KeyFile=master_key.pem +IceSSL.Keychain=master.keychain +IceSSL.KeychainPassword=password # # Don't require certificates. This is useful for admin clients that don't diff --git a/cpp/demo/IceGrid/secure/config.node b/cpp/demo/IceGrid/secure/config.node index d4323c737d9..7b9864cea3e 100644 --- a/cpp/demo/IceGrid/secure/config.node +++ b/cpp/demo/IceGrid/secure/config.node @@ -25,6 +25,8 @@ IceSSL.DefaultDir=certs IceSSL.CertAuthFile=ca_cert.pem IceSSL.CertFile=node_cert.pem IceSSL.KeyFile=node_key.pem +IceSSL.Keychain=node.keychain +IceSSL.KeychainPassword=password IceSSL.TrustOnly.Client=CN="Server";CN="Master";CN="Slave" IceSSL.TrustOnly.Server=CN="Master";CN="Slave" diff --git a/cpp/demo/IceGrid/secure/config.slave b/cpp/demo/IceGrid/secure/config.slave index 0847f9c5207..682651e969a 100644 --- a/cpp/demo/IceGrid/secure/config.slave +++ b/cpp/demo/IceGrid/secure/config.slave @@ -45,6 +45,8 @@ IceSSL.DefaultDir=certs IceSSL.CertAuthFile=ca_cert.pem IceSSL.CertFile=slave_cert.pem IceSSL.KeyFile=slave_key.pem +IceSSL.Keychain=slave.keychain +IceSSL.KeychainPassword=password # # Don't require certificates. This is useful for admin clients that don't diff --git a/cpp/demo/IceGrid/secure/expect.py b/cpp/demo/IceGrid/secure/expect.py index 0d74ba486e9..14e62d4a8fa 100755 --- a/cpp/demo/IceGrid/secure/expect.py +++ b/cpp/demo/IceGrid/secure/expect.py @@ -29,6 +29,15 @@ Util.cleanDbDir("db/node") Util.cleanDbDir("certs") print("ok") +if sys.platform == "darwin": + sys.stdout.write("cleaning keychains... ") + for f in ["master", "slave", "node", "glacier2", "admin", "server"]: + try: + os.remove(f + ".keychain") + except: + pass + print("ok") + if Util.defaultHost: args = ' --IceGrid.Node.PropertiesOverride="Ice.Default.Host=127.0.0.1"' else: diff --git a/cpp/include/IceSSL/Config.h b/cpp/include/IceSSL/Config.h new file mode 100644 index 00000000000..f28999ee1f7 --- /dev/null +++ b/cpp/include/IceSSL/Config.h @@ -0,0 +1,16 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +#include <Ice/Config.h> + +#ifdef __APPLE__ +# define ICE_USE_SECURE_TRANSPORT 1 +#else +# define ICE_USE_OPENSSL 1 +#endif diff --git a/cpp/include/IceSSL/Plugin.h b/cpp/include/IceSSL/Plugin.h index b2456c2eab1..b16db92e40a 100644 --- a/cpp/include/IceSSL/Plugin.h +++ b/cpp/include/IceSSL/Plugin.h @@ -12,6 +12,7 @@ #include <IceUtil/Time.h> #include <Ice/Plugin.h> +#include <IceSSL/Config.h> #include <IceSSL/ConnectionInfo.h> #include <vector> @@ -32,22 +33,48 @@ # endif #endif +#ifdef ICE_USE_OPENSSL // -// SSL_CTX is the OpenSSL type that holds configuration settings for -// all SSL connections. +// Pointer to an opaque SSL session context object. ssl_ctx_st is the +// OpenSSL type that holds configuration settings for all SSL +// connections. // -typedef struct ssl_ctx_st SSL_CTX; +typedef struct ssl_ctx_st* ContextRef; // -// X509 is the OpenSSL type that represents a certificate. +// Pointer to an opaque certificate object. X509_st is the OpenSSL +// type that represents a certificate. // -typedef struct x509_st X509; -typedef struct X509_name_st X509NAME; +typedef struct x509_st* X509CertificateRef; // // EVP_PKEY is the OpenSSL type that represents a public key. // -typedef struct evp_pkey_st EVP_PKEY; +typedef struct evp_pkey_st* KeyRef; + + +#elif defined(ICE_USE_SECURE_TRANSPORT) + +// +// Pointer to an opaque SSL session context object. The SSL session context +// object references the state associated with a session. +// +struct SSLContext; +typedef struct SSLContext* ContextRef; + +// +// Pointer to an opanque certificate object. +// +struct OpaqueSecCertificateRef; +typedef struct OpaqueSecCertificateRef* X509CertificateRef; + +// +// Pointer to an opaque connection objecct. +// +struct OpaqueSecKeyRef; +typedef struct OpaqueSecKeyRef* KeyRef; + +#endif namespace IceSSL { @@ -127,14 +154,14 @@ public: ~PublicKey(); - EVP_PKEY* key() const; + KeyRef key() const; private: - PublicKey(EVP_PKEY*); + PublicKey(KeyRef); friend class Certificate; - EVP_PKEY* _key; + KeyRef _key; }; typedef IceUtil::Handle<PublicKey> PublicKeyPtr; @@ -155,11 +182,6 @@ class ICE_SSL_API DistinguishedName public: // - // Create a DistinguishedName using an OpenSSL value. - // - DistinguishedName(X509NAME*); - - // // Create a DistinguishedName from a string encoded using // the rules in RFC2253. // @@ -215,7 +237,7 @@ public: // Construct a certificate using a X509*. The Certificate assumes // ownership of the X509* struct. // - Certificate(X509*); + Certificate(X509CertificateRef); ~Certificate(); // @@ -240,10 +262,16 @@ public: PublicKeyPtr getPublicKey() const; // + // Verify that this certificate was signed by the given certificate + // public key. Returns true if signed, false otherwise. + // + bool verify(const CertificatePtr&) const; + + // // Verify that this certificate was signed by the given public // key. Returns true if signed, false otherwise. // - bool verify(const PublicKeyPtr&) const; + ICE_DEPRECATED_API bool verify(const PublicKeyPtr&) const; // // Return a string encoding of the certificate in PEM format. @@ -347,18 +375,18 @@ public: // for the lifetime of this object unless the caller increments its // reference count explicitly using X509_dup. // - X509* getCert() const; + X509CertificateRef getCert() const; private: - X509* _cert; + X509CertificateRef _cert; }; // // NativeConnectionInfo is an extension of IceSSL::ConnectionInfo that // provides access to native certificates. // -class NativeConnectionInfo : public ConnectionInfo +class ICE_SSL_API NativeConnectionInfo : public ConnectionInfo { public: @@ -375,7 +403,7 @@ typedef IceUtil::Handle<NativeConnectionInfo> NativeConnectionInfoPtr; // An application can customize the certificate verification process // by implementing the CertificateVerifier interface. // -class CertificateVerifier : public IceUtil::Shared +class ICE_SSL_API CertificateVerifier : public IceUtil::Shared { public: @@ -402,7 +430,7 @@ typedef IceUtil::Handle<CertificateVerifier> CertificateVerifierPtr; // IceSSL.DelayInit=1), configure the PasswordPrompt, then manually // initialize the plug-in. // -class PasswordPrompt : public IceUtil::Shared +class ICE_SSL_API PasswordPrompt : public IceUtil::Shared { public: @@ -415,11 +443,30 @@ public: }; typedef IceUtil::Handle<PasswordPrompt> PasswordPromptPtr; -class Plugin : public Ice::Plugin +class ICE_SSL_API Plugin : public Ice::Plugin { public: // + // Establish the certificate verifier object. This should be done + // before any connections are established. + // + virtual void setCertificateVerifier(const CertificateVerifierPtr&) = 0; + + // + // Establish the password prompt object. This must be done before + // the plug-in is initialized. + // + virtual void setPasswordPrompt(const PasswordPromptPtr&) = 0; +}; +typedef IceUtil::Handle<Plugin> PluginPtr; + +#ifdef ICE_USE_OPENSSL +class ICE_SSL_API OpenSSLPlugin : public Plugin +{ +public: + + // // Establish the OpenSSL context. This must be done before the // plug-in is initialized, therefore the application must define // the property Ice.InitPlugins=0, set the context, and finally @@ -431,27 +478,15 @@ public: // // Note that the plugin assumes ownership of the given context. // - virtual void setContext(SSL_CTX*) = 0; + virtual void setContext(ContextRef) = 0; // // Obtain the SSL context. Use caution when modifying this value. // Changes made to this value have no effect on existing connections. // - virtual SSL_CTX* getContext() = 0; - - // - // Establish the certificate verifier object. This should be done - // before any connections are established. - // - virtual void setCertificateVerifier(const CertificateVerifierPtr&) = 0; - - // - // Establish the password prompt object. This must be done before - // the plug-in is initialized. - // - virtual void setPasswordPrompt(const PasswordPromptPtr&) = 0; + virtual ContextRef getContext() = 0; }; -typedef IceUtil::Handle<Plugin> PluginPtr; +#endif } diff --git a/cpp/include/IceUtil/FileUtil.h b/cpp/include/IceUtil/FileUtil.h index 7fabce927b9..57bb8f369cd 100644 --- a/cpp/include/IceUtil/FileUtil.h +++ b/cpp/include/IceUtil/FileUtil.h @@ -22,6 +22,9 @@ namespace IceUtilInternal { +extern const ICE_UTIL_API std::string pathsep; +extern const ICE_UTIL_API std::string separator; + // // Detemine if path is an absolute path. // diff --git a/cpp/src/Ice/Network.h b/cpp/src/Ice/Network.h index f8928c11f7b..3bc5856aea6 100644 --- a/cpp/src/Ice/Network.h +++ b/cpp/src/Ice/Network.h @@ -168,7 +168,7 @@ public: { } - SOCKET fd() + SOCKET fd() const { return _fd; } diff --git a/cpp/src/Ice/PropertyNames.cpp b/cpp/src/Ice/PropertyNames.cpp index c9bc11dc890..c173ecfaa82 100644 --- a/cpp/src/Ice/PropertyNames.cpp +++ b/cpp/src/Ice/PropertyNames.cpp @@ -6,7 +6,7 @@ // ICE_LICENSE file included in this distribution. // // ********************************************************************** -// Generated by makeprops.py from file ./config/PropertyNames.xml, Wed Jun 4 19:01:29 2014 +// Generated by makeprops.py from file ../config/PropertyNames.xml, Thu Jun 5 21:19:31 2014 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -109,8 +109,7 @@ const IceInternal::Property IcePropsData[] = IceInternal::Property("Ice.IPv6", false, 0), IceInternal::Property("Ice.EventLog.Source", false, 0), IceInternal::Property("Ice.FactoryAssemblies", false, 0), - IceInternal::Property("Ice.GC", false, 0), - IceInternal::Property("Ice.GC.Interval", true, "Ice.GC"), + IceInternal::Property("Ice.CollectObjects", false, 0), IceInternal::Property("Ice.ImplicitContext", false, 0), IceInternal::Property("Ice.InitPlugins", false, 0), IceInternal::Property("Ice.LogFile", false, 0), @@ -155,7 +154,6 @@ const IceInternal::Property IcePropsData[] = IceInternal::Property("Ice.ThreadPool.Server.ThreadPriority", false, 0), IceInternal::Property("Ice.ThreadPriority", false, 0), IceInternal::Property("Ice.Trace.Admin.Properties", false, 0), - IceInternal::Property("Ice.Trace.GC", true, 0), IceInternal::Property("Ice.Trace.Locator", false, 0), IceInternal::Property("Ice.Trace.Network", false, 0), IceInternal::Property("Ice.Trace.Protocol", false, 0), @@ -841,12 +839,15 @@ const IceInternal::Property IceSSLPropsData[] = IceInternal::Property("IceSSL.Ciphers", false, 0), IceInternal::Property("IceSSL.DefaultDir", false, 0), IceInternal::Property("IceSSL.DH.*", false, 0), + IceInternal::Property("IceSSL.DHParams", false, 0), IceInternal::Property("IceSSL.EntropyDaemon", false, 0), IceInternal::Property("IceSSL.FindCert.*", false, 0), IceInternal::Property("IceSSL.ImportCert.*", false, 0), IceInternal::Property("IceSSL.InitOpenSSL", false, 0), IceInternal::Property("IceSSL.KeyFile", false, 0), IceInternal::Property("IceSSL.KeySet", false, 0), + IceInternal::Property("IceSSL.Keychain", false, 0), + IceInternal::Property("IceSSL.KeychainPassword", false, 0), IceInternal::Property("IceSSL.Keystore", false, 0), IceInternal::Property("IceSSL.KeystorePassword", false, 0), IceInternal::Property("IceSSL.KeystoreType", false, 0), @@ -855,6 +856,8 @@ const IceInternal::Property IceSSLPropsData[] = IceInternal::Property("IceSSL.PasswordRetryMax", false, 0), IceInternal::Property("IceSSL.PersistKeySet", false, 0), IceInternal::Property("IceSSL.Protocols", false, 0), + IceInternal::Property("IceSSL.ProtocolVersionMax", false, 0), + IceInternal::Property("IceSSL.ProtocolVersionMin", false, 0), IceInternal::Property("IceSSL.Random", false, 0), IceInternal::Property("IceSSL.Trace.Security", false, 0), IceInternal::Property("IceSSL.TrustOnly", false, 0), diff --git a/cpp/src/Ice/PropertyNames.h b/cpp/src/Ice/PropertyNames.h index 7c33f285caf..8637fb4f1cd 100644 --- a/cpp/src/Ice/PropertyNames.h +++ b/cpp/src/Ice/PropertyNames.h @@ -6,7 +6,7 @@ // ICE_LICENSE file included in this distribution. // // ********************************************************************** -// Generated by makeprops.py from file ./config/PropertyNames.xml, Wed Jun 4 19:01:29 2014 +// Generated by makeprops.py from file ../config/PropertyNames.xml, Thu Jun 5 21:19:31 2014 // IMPORTANT: Do not edit this file -- any edits made here will be lost! diff --git a/cpp/src/IceSSL/AcceptorI.cpp b/cpp/src/IceSSL/AcceptorI.cpp index 5780a9bae89..f835f6b8044 100644 --- a/cpp/src/IceSSL/AcceptorI.cpp +++ b/cpp/src/IceSSL/AcceptorI.cpp @@ -9,7 +9,9 @@ #include <IceSSL/AcceptorI.h> #include <IceSSL/Instance.h> +#include <IceSSL/SecureTransportTransceiverI.h> #include <IceSSL/TransceiverI.h> + #include <IceSSL/Util.h> #include <Ice/Communicator.h> @@ -144,7 +146,7 @@ IceSSL::AcceptorI::accept() // // The plug-in may not be initialized. // - if(!_instance->context()) + if(!_instance->initialized()) { PluginInitializationException ex(__FILE__, __LINE__); ex.reason = "IceSSL: plug-in is not initialized"; diff --git a/cpp/src/IceSSL/Certificate.cpp b/cpp/src/IceSSL/Certificate.cpp index 71ef9a9fd96..5c7f7bf08d7 100644 --- a/cpp/src/IceSSL/Certificate.cpp +++ b/cpp/src/IceSSL/Certificate.cpp @@ -16,12 +16,11 @@ #include <IceSSL/RFC2253.h> #include <Ice/Object.h> -#include <openssl/x509v3.h> -#include <openssl/pem.h> - -// Ignore OS X OpenSSL deprecation warnings -#ifdef __APPLE__ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#ifdef ICE_USE_OPENSSL +# include <openssl/x509v3.h> +# include <openssl/pem.h> +#elif defined(ICE_USE_SECURE_TRANSPORT) +# include <Security/Security.h> #endif #ifdef __SUNPRO_CC @@ -46,6 +45,270 @@ using namespace IceSSL; const char* IceSSL::CertificateReadException::_name = "IceSSL::CertificateReadException"; +#ifdef ICE_USE_SECURE_TRANSPORT +// +// Map a certificate OID to its alias +// +struct CertificateOID +{ + const char* name; + const char* alias; +}; + +const CertificateOID certificateOIDS[] = +{ + {"2.5.4.3", "CN"}, + {"2.5.4.4", "SN"}, + {"2.5.4.5", "DeviceSerialNumber"}, + {"2.5.4.6", "C"}, + {"2.5.4.7", "L"}, + {"2.5.4.8", "ST"}, + {"2.5.4.9", "STREET"}, + {"2.5.4.10", "O"}, + {"2.5.4.11", "OU"}, + {"2.5.4.12", "T"}, + {"2.5.4.42", "G"}, + {"2.5.4.43", "I"}, + {"1.2.840.113549.1.9.8", "unstructuredAddress"}, + {"1.2.840.113549.1.9.2", "unstructuredName"}, + {"1.2.840.113549.1.9.1", "emailAddress"}, + {"0.9.2342.19200300.100.1.25", "DC"} +}; + +const int certificateOIDSSize = sizeof(certificateOIDS) / sizeof(CertificateOID); + +string +certificateOIDAlias(const string& name) +{ + for(int i = 0; i < certificateOIDSSize; ++i) + { + if(name == certificateOIDS[i].name) + { + return certificateOIDS[i].alias; + } + } + return name; +} + +// +// Map alternative name alias to its types. +// +const char* certificateAlternativeNameTypes[] = {"", "Email Address", "DNS Name", "", "Directory Name", "", "URI", + "IP Address"}; +const int certificateAlternativeNameTypesSize = sizeof(certificateAlternativeNameTypes) / sizeof(char*); + +int +certificateAlternativeNameType(const string& alias) +{ + if(!alias.empty()) + { + for(int i = 0; i < certificateAlternativeNameTypesSize; ++i) + { + if(alias == certificateAlternativeNameTypes[i]) + { + return i; + } + } + } + return -1; // Not supported +} + +string +scapeX509Name(const string& name) +{ + ostringstream os; + for(string::const_iterator i = name.begin(); i != name.end(); ++i) + { + switch(*i) + { + case ',': + case '=': + case '+': + case '<': + case '>': + case '#': + case ';': + { + os << '\\'; + } + default: + { + break; + } + } + os << *i; + } + return os.str(); +} + +DistinguishedName +getX509Name(SecCertificateRef cert, CFTypeRef key) +{ + CFErrorRef err = 0; + assert(key == kSecOIDX509V1IssuerName || key == kSecOIDX509V1SubjectName); + CFArrayRef keys = CFArrayCreate(NULL, &key , 1, &kCFTypeArrayCallBacks); + CFDictionaryRef values = SecCertificateCopyValues(cert, keys, &err); + CFRelease(keys); + + if(err) + { + ostringstream os; + os << "certificate error:\n" << errorToString(err); + CFRelease(err); + + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + assert(values); + + CFArrayRef dn = (CFArrayRef)CFDictionaryGetValue((CFDictionaryRef)CFDictionaryGetValue(values, key), kSecPropertyKeyValue); + int size = CFArrayGetCount(dn); + list<pair<string, string> > rdnPairs; + for(int i = 0; i < size; ++i) + { + CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(dn, i); + rdnPairs.push_front(make_pair( + certificateOIDAlias(fromCFString((CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyLabel))), + scapeX509Name(fromCFString((CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyValue))))); + } + CFRelease(values); + return DistinguishedName(rdnPairs); +} + +vector<pair<int, string> > +getX509AltName(SecCertificateRef cert, CFTypeRef key) +{ + CFErrorRef err = 0; + assert(key == kSecOIDIssuerAltName || key == kSecOIDSubjectAltName); + CFArrayRef keys = CFArrayCreate(NULL, &key , 1, &kCFTypeArrayCallBacks); + CFDictionaryRef values = SecCertificateCopyValues(cert, keys, &err); + CFRelease(keys); + + if(err) + { + ostringstream os; + os << "certificate error:\n" << errorToString(err); + CFRelease(err); + + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + assert(values); + + vector<pair<int, string> > pairs; + CFDictionaryRef property = (CFDictionaryRef)CFDictionaryGetValue(values, key); + if(property) + { + CFArrayRef names = (CFArrayRef)CFDictionaryGetValue(property, kSecPropertyKeyValue); + int size = CFArrayGetCount(names); + + for(int i = 0; i < size; ++i) + { + CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(names, i); + + int type = certificateAlternativeNameType(fromCFString( + (CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyLabel))); + if(type != -1) + { + CFTypeRef v = (CFTypeRef)CFDictionaryGetValue(dict, kSecPropertyKeyValue); + CFStringRef t = (CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyType); + if(CFEqual(t, kSecPropertyTypeString) || CFEqual(t, kSecPropertyTypeTitle)) + { + pairs.push_back(make_pair(type, fromCFString((CFStringRef)v))); + } + else if(CFEqual(t, kSecPropertyTypeURL)) + { + pairs.push_back(make_pair(type, fromCFString(CFURLGetString((CFURLRef)v)))); + } + else if(CFEqual(t, kSecPropertyTypeSection)) + { + CFArrayRef section = (CFArrayRef)v; + ostringstream os; + for(int i = 0, count = CFArrayGetCount(section); i < count;) + { + CFDictionaryRef d = (CFDictionaryRef)CFArrayGetValueAtIndex(section, i); + + CFStringRef sectionLabel = (CFStringRef)CFDictionaryGetValue(d, kSecPropertyKeyLabel); + CFStringRef sectionValue = (CFStringRef)CFDictionaryGetValue(d, kSecPropertyKeyValue); + + os << certificateOIDAlias(fromCFString(sectionLabel)) + << "=" << fromCFString(sectionValue); + if(++i < count) + { + os << ","; + } + } + pairs.push_back(make_pair(type, os.str())); + } + } + } + } + CFRelease(values); + return pairs; +} + +IceUtil::Time +getX509Date(SecCertificateRef cert, CFTypeRef key) +{ + assert(key == kSecOIDX509V1ValidityNotAfter || key == kSecOIDX509V1ValidityNotBefore); + CFErrorRef err = 0; + const void* keyValues[] = { key }; + CFArrayRef keys = CFArrayCreate(NULL, keyValues , 1, &kCFTypeArrayCallBacks); + CFDictionaryRef values = SecCertificateCopyValues(cert, keys, &err); + CFRelease(keys); + + if(err) + { + ostringstream os; + os << "certificate error:\n" << errorToString(err); + CFRelease(err); + + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + assert(values); + + CFNumberRef date = (CFNumberRef)CFDictionaryGetValue( + (CFDictionaryRef)CFDictionaryGetValue(values, key), kSecPropertyKeyValue); + + CFAbsoluteTime seconds; + CFNumberGetValue(date, kCFNumberDoubleType, &seconds); + CFRelease(values); + return IceUtil::Time::secondsDouble(kCFAbsoluteTimeIntervalSince1970 + seconds); +} + +string +getX509String(SecCertificateRef cert, CFTypeRef key) +{ + assert(key == kSecOIDX509V1SerialNumber || key == kSecOIDX509V1Version); + CFErrorRef err = 0; + const void* keyValues[] = { key }; + CFArrayRef keys = CFArrayCreate(NULL, keyValues , 1, &kCFTypeArrayCallBacks); + CFDictionaryRef values = SecCertificateCopyValues(cert, keys, &err); + CFRelease(keys); + + if(err) + { + ostringstream os; + os << "certificate error:\n" << errorToString(err); + CFRelease(err); + + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + assert(values); + + string value = fromCFString((CFStringRef)CFDictionaryGetValue( + (CFDictionaryRef)CFDictionaryGetValue(values, key), kSecPropertyKeyValue)); + CFRelease(values); + return value; +} +#endif + CertificateReadException::CertificateReadException(const char* file, int line, const string& r) : Exception(file, line), reason(r) @@ -104,6 +367,8 @@ CertificateEncodingException::ice_throw() const throw *this; } +#ifdef ICE_USE_OPENSSL + namespace { @@ -137,7 +402,7 @@ ASMUtcTimeToIceUtilTime(const ASN1_UTCTIME* s) memset(&tm, '\0', sizeof tm); -#define g2(p) (((p)[0]-'0')*10+(p)[1]-'0') +# define g2(p) (((p)[0]-'0')*10+(p)[1]-'0') tm.tm_year = g2(s->data); if(tm.tm_year < 50) tm.tm_year += 100; @@ -158,7 +423,7 @@ ASMUtcTimeToIceUtilTime(const ASN1_UTCTIME* s) offset = -offset; } } -#undef g2 +# undef g2 // // If timegm was on all systems this code could be @@ -176,7 +441,7 @@ ASMUtcTimeToIceUtilTime(const ASN1_UTCTIME* s) } static string -convertX509NameToString(X509NAME* name) +convertX509NameToString(X509_NAME* name) { BIO* out = BIO_new(BIO_s_mem()); X509_NAME_print_ex(out, name, 0, XN_FLAG_RFC2253); @@ -272,6 +537,7 @@ convertGeneralNames(GENERAL_NAMES* gens) sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); return alt; } +#endif const char* ParseException::_name = "IceSSL::ParseException"; @@ -303,12 +569,6 @@ ParseException::ice_throw() const throw *this; } -DistinguishedName::DistinguishedName(X509NAME* name) : - _rdns(RFC2253::parseStrict(convertX509NameToString(name))) -{ - unescape(); -} - DistinguishedName::DistinguishedName(const string& dn) : _rdns(RFC2253::parseStrict(dn)) { @@ -395,17 +655,21 @@ DistinguishedName::unescape() } } -PublicKey::PublicKey(EVP_PKEY* key) : +PublicKey::PublicKey(KeyRef key) : _key(key) { } PublicKey::~PublicKey() { +#ifdef ICE_USE_SECURE_TRANSPORT + CFRelease(_key); +#else EVP_PKEY_free(_key); +#endif } -EVP_PKEY* +KeyRef PublicKey::key() const { return _key; @@ -414,7 +678,7 @@ PublicKey::key() const // // The caller is responsible for incrementing the reference count. // -Certificate::Certificate(X509* cert) : +Certificate::Certificate(X509CertificateRef cert) : _cert(cert) { assert(_cert != 0); @@ -422,12 +686,21 @@ Certificate::Certificate(X509* cert) : Certificate::~Certificate() { +#ifdef ICE_USE_SECURE_TRANSPORT + CFRelease(_cert); +#else X509_free(_cert); +#endif } CertificatePtr Certificate::load(const string& file) { +#ifdef ICE_USE_SECURE_TRANSPORT + SecCertificateRef cert = 0; + loadCertificate(&cert, 0, 0, 0, file); + return new Certificate(cert); +#else BIO *cert = BIO_new(BIO_s_file()); if(BIO_read_filename(cert, file.c_str()) <= 0) { @@ -435,7 +708,7 @@ Certificate::load(const string& file) throw CertificateReadException(__FILE__, __LINE__, "error opening file"); } - X509* x = PEM_read_bio_X509_AUX(cert, NULL, NULL, NULL); + X509CertificateRef x = PEM_read_bio_X509_AUX(cert, NULL, NULL, NULL); if(x == NULL) { BIO_free(cert); @@ -443,13 +716,47 @@ Certificate::load(const string& file) } BIO_free(cert); return new Certificate(x); +#endif } CertificatePtr Certificate::decode(const string& encoding) { +#ifdef ICE_USE_SECURE_TRANSPORT + CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const UInt8*>(encoding.c_str()), + encoding.size(), kCFAllocatorNull); + + SecExternalFormat format = kSecFormatUnknown; + SecExternalItemType type = kSecItemTypeCertificate; + + SecItemImportExportKeyParameters params; + memset(¶ms, 0, sizeof(params)); + params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; + + CFArrayRef items = 0; + + OSStatus err = SecItemImport(data, 0, &format, &type, 0, ¶ms, 0, &items); + + CFRelease(data); + + if(err != noErr) + { + ostringstream os; + os << "error decoding certificate:\n" << errorToString(err); + CertificateReadException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + SecKeychainItemRef item = (SecKeychainItemRef)CFArrayGetValueAtIndex(items, 0); + + CFRetain(item); + CFRelease(items); + + assert(SecCertificateGetTypeID() == CFGetTypeID(item)); + return new Certificate((SecCertificateRef)item); +#else BIO *cert = BIO_new_mem_buf(static_cast<void*>(const_cast<char*>(&encoding[0])), static_cast<int>(encoding.size())); - X509* x = PEM_read_bio_X509_AUX(cert, NULL, NULL, NULL); + X509CertificateRef x = PEM_read_bio_X509_AUX(cert, NULL, NULL, NULL); if(x == NULL) { BIO_free(cert); @@ -457,35 +764,179 @@ Certificate::decode(const string& encoding) } BIO_free(cert); return new Certificate(x); +#endif } bool Certificate::operator==(const Certificate& other) const { +#ifdef ICE_USE_SECURE_TRANSPORT + return CFEqual(_cert, other._cert); +#else return X509_cmp(_cert, other._cert) == 0; +#endif } bool Certificate::operator!=(const Certificate& other) const { +#ifdef ICE_USE_SECURE_TRANSPORT + return !CFEqual(_cert, other._cert); +#else return X509_cmp(_cert, other._cert) != 0; +#endif } PublicKeyPtr Certificate::getPublicKey() const { +#ifdef ICE_USE_SECURE_TRANSPORT + SecKeyRef key; + OSStatus err = SecCertificateCopyPublicKey(_cert, &key); + if(err != noErr) + { + ostringstream os; + os << "certificate error:\n" << errorToString(err); + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + return new PublicKey(key); +#else return new PublicKey(X509_get_pubkey(_cert)); +#endif +} + +bool +Certificate::verify(const CertificatePtr& cert) const +{ +#ifdef ICE_USE_SECURE_TRANSPORT + + bool valid = false; + + CFErrorRef error = 0; + CFDataRef issuer = 0; + CFDataRef subject = 0; + + issuer = SecCertificateCopyNormalizedIssuerContent(_cert, &error); + if(error) + { + ostringstream os; + os << "certificate error:\n" << errorToString(error); + CFRelease(error); + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + subject = SecCertificateCopyNormalizedSubjectContent(cert->getCert(), &error); + if(error) + { + ostringstream os; + os << "certificate error:\n" << errorToString(error); + CFRelease(error); + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + // + // The certificate issuer must match the CA subject. + // + valid = CFEqual(issuer, subject); + + CFRelease(issuer); + CFRelease(subject); + + if(valid) + { + SecPolicyRef policy = 0; + SecTrustRef trust = 0; + try + { + SecPolicyRef policy = SecPolicyCreateBasicX509(); + SecTrustResultType trustResult = kSecTrustResultInvalid; + SecTrustRef trust; + OSStatus err = SecTrustCreateWithCertificates(_cert, policy, &trust); + + if(err != noErr) + { + ostringstream os; + os << "certificate error:\n" << errorToString(err); + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + SecCertificateRef certs[1] = { cert->getCert() }; + + CFArrayRef anchorCertificates = CFArrayCreate(kCFAllocatorDefault, (const void**)&certs, 1, &kCFTypeArrayCallBacks); + err = SecTrustSetAnchorCertificates(trust, anchorCertificates); + CFRelease(anchorCertificates); + + if(err != noErr) + { + ostringstream os; + os << "certificate error:\n" << errorToString(err); + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + err = SecTrustEvaluate(trust, &trustResult); + if(err != noErr) + { + ostringstream os; + os << "certificate error:\n" << errorToString(err); + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + valid = trustResult == kSecTrustResultUnspecified; + + CFRelease(policy); + CFRelease(trust); + } + catch(...) + { + if(policy) + { + CFRelease(policy); + } + + if(trust) + { + CFRelease(trust); + } + throw; + } + } + return valid; +#else + return X509_verify(_cert, cert->getPublicKey()->key()) > 0; +#endif } +#ifndef ICE_USE_SECURE_TRANSPORT bool Certificate::verify(const PublicKeyPtr& key) const { return X509_verify(_cert, key->key()) > 0; } +#endif string Certificate::encode() const { +#ifdef ICE_USE_SECURE_TRANSPORT + CFDataRef exported; + OSStatus err = SecItemExport(_cert, kSecFormatPEMSequence, kSecItemPemArmour, 0, &exported); + if(err != noErr) + { + ostringstream os; + os << "error encoding certificate:\n" << errorToString(err); + CertificateEncodingException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + string data(reinterpret_cast<const char*>(CFDataGetBytePtr(exported)), CFDataGetLength(exported)); + CFRelease(exported); + return data; +#else BIO* out = BIO_new(BIO_s_mem()); int i = PEM_write_bio_X509_AUX(out, _cert); if(i <= 0) @@ -498,6 +949,7 @@ Certificate::encode() const string result = string(p->data, p->length); BIO_free(out); return result; +#endif } bool @@ -516,18 +968,29 @@ Certificate::checkValidity(const IceUtil::Time& now) const IceUtil::Time Certificate::getNotAfter() const { +#ifdef ICE_USE_SECURE_TRANSPORT + return getX509Date(_cert, kSecOIDX509V1ValidityNotAfter); +#else return ASMUtcTimeToIceUtilTime(X509_get_notAfter(_cert)); +#endif } IceUtil::Time Certificate::getNotBefore() const { +#ifdef ICE_USE_SECURE_TRANSPORT + return getX509Date(_cert, kSecOIDX509V1ValidityNotBefore); +#else return ASMUtcTimeToIceUtilTime(X509_get_notBefore(_cert)); +#endif } string Certificate::getSerialNumber() const { +#ifdef ICE_USE_SECURE_TRANSPORT + return getX509String(_cert, kSecOIDX509V1SerialNumber); +#else BIGNUM* bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(_cert), 0); char* dec = BN_bn2dec(bn); string result = dec; @@ -535,6 +998,7 @@ Certificate::getSerialNumber() const BN_free(bn); return result; +#endif } //string @@ -550,33 +1014,53 @@ Certificate::getSerialNumber() const DistinguishedName Certificate::getIssuerDN() const { - return DistinguishedName(X509_get_issuer_name(_cert)); +#ifdef ICE_USE_SECURE_TRANSPORT + return getX509Name(_cert, kSecOIDX509V1IssuerName); +#else + return DistinguishedName(RFC2253::parseStrict(convertX509NameToString(X509_get_issuer_name(_cert)))); +#endif } vector<pair<int, string> > Certificate::getIssuerAlternativeNames() { +#ifdef ICE_USE_SECURE_TRANSPORT + return getX509AltName(_cert, kSecOIDIssuerAltName); +#else return convertGeneralNames(reinterpret_cast<GENERAL_NAMES*>( X509_get_ext_d2i(_cert, NID_issuer_alt_name, 0, 0))); +#endif } DistinguishedName Certificate::getSubjectDN() const { - return DistinguishedName(X509_get_subject_name(_cert)); +#ifdef ICE_USE_SECURE_TRANSPORT + return getX509Name(_cert, kSecOIDX509V1SubjectName); +#else + return DistinguishedName(RFC2253::parseStrict(convertX509NameToString(X509_get_subject_name(_cert)))); +#endif } vector<pair<int, string> > Certificate::getSubjectAlternativeNames() { +#ifdef ICE_USE_SECURE_TRANSPORT + return getX509AltName(_cert, kSecOIDSubjectAltName); +#else return convertGeneralNames( reinterpret_cast<GENERAL_NAMES*>(X509_get_ext_d2i(_cert, NID_subject_alt_name, 0, 0))); +#endif } int Certificate::getVersion() const { +#ifdef ICE_USE_SECURE_TRANSPORT + return atoi(getX509String(_cert, kSecOIDX509V1Version).c_str()); +#else return static_cast<int>(X509_get_version(_cert)); +#endif } string @@ -588,11 +1072,10 @@ Certificate::toString() const os << "subject: " << string(getSubjectDN()) << "\n"; os << "notBefore: " << getNotBefore().toDateTime() << "\n"; os << "notAfter: " << getNotAfter().toDateTime(); - return os.str(); } -X509* +X509CertificateRef Certificate::getCert() const { return _cert; diff --git a/cpp/src/IceSSL/ConnectorI.cpp b/cpp/src/IceSSL/ConnectorI.cpp index fb9386d0a89..74db5e52cb6 100644 --- a/cpp/src/IceSSL/ConnectorI.cpp +++ b/cpp/src/IceSSL/ConnectorI.cpp @@ -9,6 +9,7 @@ #include <IceSSL/ConnectorI.h> #include <IceSSL/Instance.h> +#include <IceSSL/SecureTransportTransceiverI.h> #include <IceSSL/TransceiverI.h> #include <IceSSL/EndpointI.h> #include <IceSSL/Util.h> @@ -26,7 +27,7 @@ IceSSL::ConnectorI::connect() // // The plug-in may not be initialized. // - if(!_instance->context()) + if(!_instance->initialized()) { PluginInitializationException ex(__FILE__, __LINE__); ex.reason = "IceSSL: plug-in is not initialized"; diff --git a/cpp/src/IceSSL/EndpointI.cpp b/cpp/src/IceSSL/EndpointI.cpp index de769b1186e..93994804783 100644 --- a/cpp/src/IceSSL/EndpointI.cpp +++ b/cpp/src/IceSSL/EndpointI.cpp @@ -381,5 +381,5 @@ IceSSL::EndpointFactoryI::destroy() IceInternal::EndpointFactoryPtr IceSSL::EndpointFactoryI::clone(const IceInternal::ProtocolInstancePtr& instance) const { - return new EndpointFactoryI(new Instance(_instance->sharedInstance(), instance->type(), instance->protocol())); + return new EndpointFactoryI(new Instance(_instance->engine(), instance->type(), instance->protocol())); } diff --git a/cpp/src/IceSSL/Instance.cpp b/cpp/src/IceSSL/Instance.cpp index 44c4704b2bc..3d3f24e58d8 100644 --- a/cpp/src/IceSSL/Instance.cpp +++ b/cpp/src/IceSSL/Instance.cpp @@ -11,1143 +11,18 @@ #ifdef _WIN32 # include <winsock2.h> #endif - #include <IceSSL/Instance.h> -#include <IceSSL/Util.h> -#include <IceSSL/TrustManager.h> - -#include <Ice/Communicator.h> -#include <Ice/LocalException.h> -#include <Ice/Logger.h> -#include <Ice/LoggerUtil.h> #include <Ice/Properties.h> -#include <IceUtil/Mutex.h> -#include <IceUtil/MutexPtrLock.h> -#include <IceUtil/StringUtil.h> - -#include <openssl/rand.h> -#include <openssl/err.h> - -#include <IceUtil/DisableWarnings.h> - -// Ignore OS X OpenSSL deprecation warnings -#ifdef __APPLE__ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - using namespace std; using namespace Ice; using namespace IceSSL; IceUtil::Shared* IceSSL::upCast(IceSSL::Instance* p) { return p; } -IceUtil::Shared* IceSSL::upCast(IceSSL::SharedInstance* p) { return p; } - -namespace -{ - -IceUtil::Mutex* staticMutex = 0; -int instanceCount = 0; -IceUtil::Mutex* locks = 0; - -class Init -{ -public: - - Init() - { - staticMutex = new IceUtil::Mutex; - } - - ~Init() - { - delete staticMutex; - staticMutex = 0; - if(locks) - { - delete[] locks; - locks = 0; - } - } -}; - -Init init; - -} - -extern "C" -{ - -// -// OpenSSL mutex callback. -// -void -IceSSL_opensslLockCallback(int mode, int n, const char* /*file*/, int /*line*/) -{ - assert(locks); - if(mode & CRYPTO_LOCK) - { - locks[n].lock(); - } - else - { - locks[n].unlock(); - } -} - -// -// OpenSSL thread id callback. -// -unsigned long -IceSSL_opensslThreadIdCallback() -{ -#if defined(_WIN32) - return static_cast<unsigned long>(GetCurrentThreadId()); -#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(__osf1__) - // - // On some platforms, pthread_t is a pointer to a per-thread structure. - // - return reinterpret_cast<unsigned long>(pthread_self()); -#elif (defined(__linux) || defined(__sun) || defined(__hpux)) || defined(_AIX) - // - // On Linux, Solaris, HP-UX and AIX, pthread_t is an integer. - // - return static_cast<unsigned long>(pthread_self()); -#else -# error "Unknown platform" -#endif -} - -int -IceSSL_opensslPasswordCallback(char* buf, int size, int flag, void* userData) -{ - IceSSL::SharedInstance* p = reinterpret_cast<IceSSL::SharedInstance*>(userData); - string passwd = p->password(flag == 1); - int sz = static_cast<int>(passwd.size()); - if(sz > size) - { - sz = size - 1; - } - strncpy(buf, passwd.c_str(), sz); - buf[sz] = '\0'; - - for(string::iterator i = passwd.begin(); i != passwd.end(); ++i) - { - *i = '\0'; - } - - return sz; -} - -#ifndef OPENSSL_NO_DH -DH* -IceSSL_opensslDHCallback(SSL* ssl, int /*isExport*/, int keyLength) -{ - IceSSL::SharedInstance* p = reinterpret_cast<IceSSL::SharedInstance*>(SSL_CTX_get_ex_data(ssl->ctx, 0)); - return p->dhParams(keyLength); -} -#endif - -int -IceSSL_opensslVerifyCallback(int ok, X509_STORE_CTX* ctx) -{ - SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); - IceSSL::SharedInstance* p = reinterpret_cast<IceSSL::SharedInstance*>(SSL_CTX_get_ex_data(ssl->ctx, 0)); - return p->verifyCallback(ok, ssl, ctx); -} - -} - -static bool -passwordError() -{ - int reason = ERR_GET_REASON(ERR_peek_error()); - return (reason == PEM_R_BAD_BASE64_DECODE || - reason == PEM_R_BAD_DECRYPT || - reason == PEM_R_BAD_PASSWORD_READ || - reason == PEM_R_PROBLEMS_GETTING_PASSWORD); -} - -IceSSL::SharedInstance::SharedInstance(const CommunicatorPtr& communicator) : - _communicator(communicator), - _logger(communicator->getLogger()), - _initialized(false), - _ctx(0) -{ - __setNoDelete(true); - - // - // Initialize OpenSSL if necessary. - // - IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(staticMutex); - instanceCount++; - - if(instanceCount == 1) - { - PropertiesPtr properties = communicator->getProperties(); - - // - // The IceSSL.InitOpenSSL property specifies whether we should perform the global - // startup (and shutdown) tasks for the OpenSSL library. - // - // If an application uses multiple components that each depend on OpenSSL, the - // application should disable OpenSSL initialization in those components and - // perform the initialization itself. - // - _initOpenSSL = properties->getPropertyAsIntWithDefault("IceSSL.InitOpenSSL", 1) > 0; - if(_initOpenSSL) - { - // - // Create the mutexes and set the callbacks. - // - if(!locks) - { - locks = new IceUtil::Mutex[CRYPTO_num_locks()]; - CRYPTO_set_locking_callback(IceSSL_opensslLockCallback); - CRYPTO_set_id_callback(IceSSL_opensslThreadIdCallback); - } - - // - // Load human-readable error messages. - // - SSL_load_error_strings(); - - // - // Initialize the SSL library. - // - SSL_library_init(); - - // - // This is necessary to allow programs that use OpenSSL 0.9.x to - // load private key files generated by OpenSSL 1.x. - // - OpenSSL_add_all_algorithms(); - - // - // Initialize the PRNG. - // -#ifdef WINDOWS - RAND_screen(); // Uses data from the screen if possible. -#endif - char randFile[1024]; - if(RAND_file_name(randFile, sizeof(randFile))) // Gets the name of a default seed file. - { - RAND_load_file(randFile, 1024); - } - - string randFiles = properties->getProperty("IceSSL.Random"); - - if(!randFiles.empty()) - { - vector<string> files; -#ifdef _WIN32 - const string sep = ";"; -#else - const string sep = ":"; -#endif - string defaultDir = properties->getProperty("IceSSL.DefaultDir"); - - if(!IceUtilInternal::splitString(randFiles, sep, files)) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: invalid value for IceSSL.Random:\n" + randFiles; - throw ex; - } - for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) - { - string file = *p; - if(!checkPath(file, defaultDir, false)) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: entropy data file not found:\n" + file; - throw ex; - } - if(!RAND_load_file(file.c_str(), 1024)) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: unable to load entropy data from " + file; - throw ex; - } - } - } -#ifndef _WIN32 - // - // The Entropy Gathering Daemon (EGD) is not available on Windows. - // The file should be a Unix domain socket for the daemon. - // - string entropyDaemon = properties->getProperty("IceSSL.EntropyDaemon"); - if(!entropyDaemon.empty()) - { - if(RAND_egd(entropyDaemon.c_str()) <= 0) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: EGD failure using file " + entropyDaemon; - throw ex; - } - } -#endif - if(!RAND_status()) - { - communicator->getLogger()->warning("IceSSL: insufficient data to initialize PRNG"); - } - } - else - { - if(!properties->getProperty("IceSSL.Random").empty()) - { - _logger->warning("IceSSL: ignoring IceSSL.Random because OpenSSL initialization is disabled"); - } -#ifndef _WIN32 - else if(!properties->getProperty("IceSSL.EntropyDaemon").empty()) - { - _logger->warning("IceSSL: ignoring IceSSL.EntropyDaemon because OpenSSL initialization is disabled"); - } -#endif - } - } - - _securityTraceLevel = communicator->getProperties()->getPropertyAsInt("IceSSL.Trace.Security"); - _securityTraceCategory = "Security"; - _trustManager = new TrustManager(communicator); - - __setNoDelete(false); -} - -IceSSL::SharedInstance::~SharedInstance() -{ - // - // Clean up OpenSSL resources. - // - IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(staticMutex); - - if(--instanceCount == 0 && _initOpenSSL) - { - // - // NOTE: We can't destroy the locks here: threads which might have called openssl methods - // might access openssl locks upon termination (from DllMain/THREAD_DETACHED). Instead, - // we release the locks in the ~Init() static destructor. See bug #4156. - // - //CRYPTO_set_locking_callback(0); - //CRYPTO_set_id_callback(0); - //delete[] locks; - //locks = 0; - - CRYPTO_cleanup_all_ex_data(); - RAND_cleanup(); - ERR_free_strings(); - EVP_cleanup(); - } -} - -void -IceSSL::SharedInstance::initialize() -{ - if(_initialized) - { - return; - } - - try - { - const string propPrefix = "IceSSL."; - PropertiesPtr properties = communicator()->getProperties(); - - // - // CheckCertName determines whether we compare the name in a peer's - // certificate against its hostname. - // - _checkCertName = properties->getPropertyAsIntWithDefault(propPrefix + "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(propPrefix + "VerifyDepthMax", 2); - - // - // VerifyPeer determines whether certificate validation failures abort a connection. - // - _verifyPeer = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyPeer", 2); - - // - // Protocols selects which protocols to enable. - // - const int protocols = parseProtocols(properties->getPropertyAsList(propPrefix + "Protocols")); - - // - // Create an SSL context if the application hasn't supplied one. - // - if(!_ctx) - { - _ctx = SSL_CTX_new(getMethod(protocols)); - if(!_ctx) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: unable to create SSL context:\n" + sslErrors(); - throw ex; - } - - // - // Check for a default directory. We look in this directory for - // files mentioned in the configuration. - // - string defaultDir = properties->getProperty(propPrefix + "DefaultDir"); - - // - // If the configuration defines a password, or the application has supplied - // a password prompt object, then register a password callback. Otherwise, - // let OpenSSL use its default behavior. - // - { - // TODO: Support quoted value? - string password = properties->getProperty(propPrefix + "Password"); - if(!password.empty() || _prompt) - { - SSL_CTX_set_default_passwd_cb(_ctx, IceSSL_opensslPasswordCallback); - SSL_CTX_set_default_passwd_cb_userdata(_ctx, this); - _password = password; - } - } - - int passwordRetryMax = properties->getPropertyAsIntWithDefault(propPrefix + "PasswordRetryMax", 3); - - // - // Establish the location of CA certificates. - // - { - string caFile = properties->getProperty(propPrefix + "CertAuthFile"); - string caDir = properties->getPropertyWithDefault(propPrefix + "CertAuthDir", defaultDir); - const char* file = 0; - const char* dir = 0; - if(!caFile.empty()) - { - if(!checkPath(caFile, defaultDir, false)) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: CA certificate file not found:\n" + caFile; - throw ex; - } - file = caFile.c_str(); - } - if(!caDir.empty()) - { - if(!checkPath(caDir, defaultDir, true)) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: CA certificate directory not found:\n" + caDir; - throw ex; - } - dir = caDir.c_str(); - } - if(file || dir) - { - // - // The certificate may be stored in an encrypted file, so handle - // password retries. - // - int count = 0; - int err = 0; - while(count < passwordRetryMax) - { - ERR_clear_error(); - err = SSL_CTX_load_verify_locations(_ctx, file, dir); - if(err) - { - break; - } - ++count; - } - if(err == 0) - { - string msg = "IceSSL: unable to establish CA certificates"; - if(passwordError()) - { - msg += ":\ninvalid password"; - } - else - { - string err = sslErrors(); - if(!err.empty()) - { - msg += ":\n" + err; - } - } - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; - } - } - } - - // - // Establish the certificate chains and private keys. One RSA certificate and - // one DSA certificate are allowed. - // - { -#ifdef _WIN32 - const string sep = ";"; -#else - const string sep = ":"; -#endif - string certFile = properties->getProperty(propPrefix + "CertFile"); - string keyFile = properties->getProperty(propPrefix + "KeyFile"); - vector<string>::size_type numCerts = 0; - if(!certFile.empty()) - { - vector<string> files; - if(!IceUtilInternal::splitString(certFile, sep, files) || files.size() > 2) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: invalid value for " + propPrefix + "CertFile:\n" + certFile; - throw ex; - } - numCerts = files.size(); - for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) - { - string file = *p; - if(!checkPath(file, defaultDir, false)) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: certificate file not found:\n" + file; - throw ex; - } - // - // The certificate may be stored in an encrypted file, so handle - // password retries. - // - int count = 0; - int err = 0; - while(count < passwordRetryMax) - { - ERR_clear_error(); - err = SSL_CTX_use_certificate_chain_file(_ctx, file.c_str()); - if(err) - { - break; - } - ++count; - } - if(err == 0) - { - string msg = "IceSSL: unable to load certificate chain from file " + file; - if(passwordError()) - { - msg += ":\ninvalid password"; - } - else - { - string err = sslErrors(); - if(!err.empty()) - { - msg += ":\n" + err; - } - } - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; - } - } - } - if(keyFile.empty()) - { - keyFile = certFile; // Assume the certificate file also contains the private key. - } - if(!keyFile.empty()) - { - vector<string> files; - if(!IceUtilInternal::splitString(keyFile, sep, files) || files.size() > 2) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: invalid value for " + propPrefix + "KeyFile:\n" + keyFile; - throw ex; - } - if(files.size() != numCerts) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: " + propPrefix + "KeyFile does not agree with " + propPrefix + "CertFile"; - throw ex; - } - for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) - { - string file = *p; - if(!checkPath(file, defaultDir, false)) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: key file not found:\n" + file; - throw ex; - } - // - // The private key may be stored in an encrypted file, so handle - // password retries. - // - int count = 0; - int err = 0; - while(count < passwordRetryMax) - { - ERR_clear_error(); - err = SSL_CTX_use_PrivateKey_file(_ctx, file.c_str(), SSL_FILETYPE_PEM); - if(err) - { - break; - } - ++count; - } - if(err == 0) - { - string msg = "IceSSL: unable to load private key from file " + file; - if(passwordError()) - { - msg += ":\ninvalid password"; - } - else - { - string err = sslErrors(); - if(!err.empty()) - { - msg += ":\n" + err; - } - } - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; - } - } - if(!SSL_CTX_check_private_key(_ctx)) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: unable to validate private key(s):\n" + sslErrors(); - throw ex; - } - } - } - - // - // Diffie Hellman configuration. - // - { -#ifndef OPENSSL_NO_DH - _dhParams = new DHParams; - SSL_CTX_set_options(_ctx, SSL_OP_SINGLE_DH_USE); - SSL_CTX_set_tmp_dh_callback(_ctx, IceSSL_opensslDHCallback); -#endif - // - // Properties have the following form: - // - // ...DH.<keyLength>=file - // - const string dhPrefix = propPrefix + "DH."; - PropertyDict d = properties->getPropertiesForPrefix(dhPrefix); - if(!d.empty()) - { -#ifdef OPENSSL_NO_DH - _logger->warning("IceSSL: OpenSSL is not configured for Diffie Hellman"); -#else - for(PropertyDict::iterator p = d.begin(); p != d.end(); ++p) - { - string s = p->first.substr(dhPrefix.size()); - int keyLength = atoi(s.c_str()); - if(keyLength > 0) - { - string file = p->second; - if(!checkPath(file, defaultDir, false)) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: DH parameter file not found:\n" + file; - throw ex; - } - if(!_dhParams->add(keyLength, file)) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: unable to read DH parameter file " + file; - throw ex; - } - } - } -#endif - } - } - } - - // - // Store a pointer to ourself for use in OpenSSL callbacks. - // - SSL_CTX_set_ex_data(_ctx, 0, this); - - // - // This is necessary for successful interop with Java. Without it, a Java - // client would fail to reestablish a connection: the server gets the - // error "session id context uninitialized" and the client receives - // "SSLHandshakeException: Remote host closed connection during handshake". - // - SSL_CTX_set_session_cache_mode(_ctx, SSL_SESS_CACHE_OFF); - - // - // Although we disable session caching, we still need to set a session ID - // context (ICE-5103). The value can be anything; here we just use the - // pointer to this SharedInstance object. - // - SSL_CTX_set_session_id_context(_ctx, reinterpret_cast<unsigned char*>(this), - static_cast<unsigned int>(sizeof(this))); - - // - // Select protocols. - // - if(protocols != 0) - { - setOptions(protocols); - } - - // - // Establish the cipher list. - // - string ciphers = properties->getProperty(propPrefix + "Ciphers"); - if(!ciphers.empty()) - { - if(!SSL_CTX_set_cipher_list(_ctx, ciphers.c_str())) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: unable to set ciphers using `" + ciphers + "':\n" + sslErrors(); - throw ex; - } - } - - // - // Determine whether a certificate is required from the peer. - // - { - int sslVerifyMode; - switch(_verifyPeer) - { - case 0: - sslVerifyMode = SSL_VERIFY_NONE; - break; - case 1: - sslVerifyMode = SSL_VERIFY_PEER; - break; - case 2: - sslVerifyMode = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT; - break; - default: - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: invalid value for " + propPrefix + "VerifyPeer"; - throw ex; - } - } - SSL_CTX_set_verify(_ctx, sslVerifyMode, IceSSL_opensslVerifyCallback); - } - } - catch(...) - { - // - // We free the SSL context regardless of whether the plugin created it - // or the application supplied it. - // - SSL_CTX_free(_ctx); - _ctx = 0; - throw; - } - - _initialized = true; -} - -void -IceSSL::SharedInstance::context(SSL_CTX* context) -{ - if(_initialized) - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: plug-in is already initialized"; - throw ex; - } - - assert(!_ctx); - _ctx = context; -} - -SSL_CTX* -IceSSL::SharedInstance::context() const -{ - return _ctx; -} - -void -IceSSL::SharedInstance::setCertificateVerifier(const CertificateVerifierPtr& verifier) -{ - _verifier = verifier; -} - -void -IceSSL::SharedInstance::setPasswordPrompt(const PasswordPromptPtr& prompt) -{ - _prompt = prompt; -} - -CommunicatorPtr -IceSSL::SharedInstance::communicator() const -{ - return _communicator; -} - -void -IceSSL::SharedInstance::verifyPeer(SSL* ssl, SOCKET fd, const string& address, const NativeConnectionInfoPtr& info) -{ - long result = SSL_get_verify_result(ssl); - if(result != X509_V_OK) - { - if(_verifyPeer == 0) - { - if(_securityTraceLevel >= 1) - { - ostringstream ostr; - ostr << "IceSSL: ignoring certificate verification failure:\n" << X509_verify_cert_error_string(result); - _logger->trace(_securityTraceCategory, ostr.str()); - } - } - else - { - ostringstream ostr; - ostr << "IceSSL: certificate verification failed:\n" << X509_verify_cert_error_string(result); - string msg = ostr.str(); - if(_securityTraceLevel >= 1) - { - _logger->trace(_securityTraceCategory, msg); - } - SecurityException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; - } - } - - X509* rawCert = SSL_get_peer_certificate(ssl); - CertificatePtr cert; - if(rawCert != 0) - { - cert = new Certificate(rawCert); - } - - // - // For an outgoing connection, we compare the proxy address (if any) against - // fields in the server's certificate (if any). - // - if(cert && !address.empty()) - { - // - // Extract the IP addresses and the DNS names from the subject - // alternative names. - // - vector<pair<int, string> > subjectAltNames = cert->getSubjectAlternativeNames(); - vector<string> ipAddresses; - vector<string> dnsNames; - for(vector<pair<int, string> >::const_iterator p = subjectAltNames.begin(); p != subjectAltNames.end(); ++p) - { - if(p->first == 7) - { - ipAddresses.push_back(IceUtilInternal::toLower(p->second)); - } - else if(p->first == 2) - { - dnsNames.push_back(IceUtilInternal::toLower(p->second)); - } - } - - // - // Compare the peer's address against the common name. - // - bool certNameOK = false; - string dn; - string addrLower = IceUtilInternal::toLower(address); - { - DistinguishedName d = cert->getSubjectDN(); - dn = IceUtilInternal::toLower(string(d)); - string cn = "cn=" + addrLower; - string::size_type pos = dn.find(cn); - if(pos != string::npos) - { - // - // Ensure we match the entire common name. - // - certNameOK = (pos + cn.size() == dn.size()) || (dn[pos + cn.size()] == ','); - } - } - - // - // Compare the peer's address against the dnsName and ipAddress - // values in the subject alternative name. - // - if(!certNameOK) - { - certNameOK = find(ipAddresses.begin(), ipAddresses.end(), addrLower) != ipAddresses.end(); - } - if(!certNameOK) - { - certNameOK = find(dnsNames.begin(), dnsNames.end(), addrLower) != dnsNames.end(); - } - - // - // 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))) - { - ostringstream ostr; - ostr << "IceSSL: "; - if(!_checkCertName) - { - ostr << "ignoring "; - } - ostr << "certificate validation failure:\npeer certificate does not have `" << address - << "' as its commonName or in its subjectAltName extension"; - if(!dn.empty()) - { - ostr << "\nSubject DN: " << dn; - } - if(!dnsNames.empty()) - { - ostr << "\nDNS names found in certificate: "; - for(vector<string>::const_iterator p = dnsNames.begin(); p != dnsNames.end(); ++p) - { - if(p != dnsNames.begin()) - { - ostr << ", "; - } - ostr << *p; - } - } - if(!ipAddresses.empty()) - { - ostr << "\nIP addresses found in certificate: "; - for(vector<string>::const_iterator p = ipAddresses.begin(); p != ipAddresses.end(); ++p) - { - if(p != ipAddresses.begin()) - { - ostr << ", "; - } - ostr << *p; - } - } - string msg = ostr.str(); - if(_securityTraceLevel >= 1) - { - Trace out(_logger, _securityTraceCategory); - out << msg; - } - if(_checkCertName) - { - SecurityException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; - } - } - } - - if(_verifyDepthMax > 0 && static_cast<int>(info->certs.size()) > _verifyDepthMax) - { - ostringstream ostr; - ostr << (info->incoming ? "incoming" : "outgoing") << " connection rejected:\n" - << "length of peer's certificate chain (" << info->certs.size() << ") exceeds maximum of " - << _verifyDepthMax; - string msg = ostr.str(); - if(_securityTraceLevel >= 1) - { - _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd)); - } - SecurityException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; - } - - if(!_trustManager->verify(info)) - { - string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by trust manager"; - if(_securityTraceLevel >= 1) - { - _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd)); - } - SecurityException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; - } - - if(_verifier && !_verifier->verify(info)) - { - string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier"; - if(_securityTraceLevel >= 1) - { - _logger->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd)); - } - SecurityException ex(__FILE__, __LINE__); - ex.reason = msg; - throw ex; - } -} - -string -IceSSL::SharedInstance::sslErrors() const -{ - return getSslErrors(_securityTraceLevel >= 1); -} - -void -IceSSL::SharedInstance::destroy() -{ - if(_ctx) - { - SSL_CTX_free(_ctx); - } -} - -string -IceSSL::SharedInstance::password(bool /*encrypting*/) -{ - if(_prompt) - { - try - { - return _prompt->getPassword(); - } - catch(...) - { - // - // Don't allow exceptions to cross an OpenSSL boundary. - // - return string(); - } - } - else - { - return _password; - } -} - -int -IceSSL::SharedInstance::verifyCallback(int ok, SSL* ssl, X509_STORE_CTX* c) -{ - if(!ok && _securityTraceLevel >= 1) - { - X509* cert = X509_STORE_CTX_get_current_cert(c); - int err = X509_STORE_CTX_get_error(c); - char buf[256]; - - Trace out(_logger, _securityTraceCategory); - out << "certificate verification failure\n"; - - X509_NAME_oneline(X509_get_issuer_name(cert), buf, static_cast<int>(sizeof(buf))); - out << "issuer = " << buf << '\n'; - X509_NAME_oneline(X509_get_subject_name(cert), buf, static_cast<int>(sizeof(buf))); - out << "subject = " << buf << '\n'; - out << "depth = " << X509_STORE_CTX_get_error_depth(c) << '\n'; - out << "error = " << X509_verify_cert_error_string(err) << '\n'; - out << IceInternal::fdToString(SSL_get_fd(ssl)); - } - return ok; -} - -#ifndef OPENSSL_NO_DH -DH* -IceSSL::SharedInstance::dhParams(int keyLength) -{ - return _dhParams->get(keyLength); -} -#endif - -int -IceSSL::SharedInstance::parseProtocols(const StringSeq& protocols) -{ - int v = 0; - - for(Ice::StringSeq::const_iterator p = protocols.begin(); p != protocols.end(); ++p) - { - string prot = *p; - - if(prot == "ssl3" || prot == "sslv3") - { - v |= SSLv3; - } - else if(prot == "tls" || prot == "tls1" || prot == "tlsv1" || prot == "tls1_0" || prot == "tlsv1_0") - { - v |= TLSv1_0; - } - else if(prot == "tls1_1" || prot == "tlsv1_1") - { - v |= TLSv1_1; - } - else if(prot == "tls1_2" || prot == "tlsv1_2") - { - v |= TLSv1_2; - } - else - { - PluginInitializationException ex(__FILE__, __LINE__); - ex.reason = "IceSSL: unrecognized protocol `" + prot + "'"; - throw ex; - } - } - - return v; -} - -SSL_METHOD* -IceSSL::SharedInstance::getMethod(int /*protocols*/) -{ - // - // Despite its name, the SSLv23 method can negotiate SSL3, TLS1.0, TLS1.1, and TLS1.2. - // We use the const_cast for backward compatibility with older OpenSSL releases. - // - SSL_METHOD* meth = const_cast<SSL_METHOD*>(SSLv23_method()); - - /* - * Early versions of OpenSSL 1.0.1 would not negotiate a TLS1.2 connection using - * the SSLv23 method. You can enable the code below to override the method. - if(protocols & TLSv1_2) - { - meth = const_cast<SSL_METHOD*>(TLSv1_2_method()); - } - */ - - return meth; -} - -void -IceSSL::SharedInstance::setOptions(int protocols) -{ - long opts = SSL_OP_NO_SSLv2; // SSLv2 is not supported. - if(!(protocols & SSLv3)) - { - opts |= SSL_OP_NO_SSLv3; - } - if(!(protocols & TLSv1_0)) - { - opts |= SSL_OP_NO_TLSv1; - } -#ifdef SSL_OP_NO_TLSv1_1 - if(!(protocols & TLSv1_1)) - { - opts |= SSL_OP_NO_TLSv1_1; - // - // The value of SSL_OP_NO_TLSv1_1 changed between 1.0.1a and 1.0.1b. - // - if(SSL_OP_NO_TLSv1_1 == 0x00000400L) - { - opts |= 0x10000000L; // New value of SSL_OP_NO_TLSv1_1. - } - } -#endif -#ifdef SSL_OP_NO_TLSv1_2 - if(!(protocols & TLSv1_2)) - { - opts |= SSL_OP_NO_TLSv1_2; - } -#endif - SSL_CTX_set_options(_ctx, opts); -} - -IceSSL::Instance::Instance(const SharedInstancePtr& sharedInstance, Ice::Short type, const std::string& protocol) : - ProtocolInstance(sharedInstance->communicator(), type, protocol), - _sharedInstance(sharedInstance) +IceSSL::Instance::Instance(const SSLEnginePtr& engine, Short type, const string& protocol) : + ProtocolInstance(engine->communicator(), type, protocol), + _engine(engine) { _securityTraceLevel = properties()->getPropertyAsInt("IceSSL.Trace.Security"); _securityTraceCategory = "Security"; @@ -1157,28 +32,10 @@ IceSSL::Instance::~Instance() { } -void -IceSSL::Instance::traceConnection(SSL* ssl, bool incoming) +bool +IceSSL::Instance::initialized() const { - Trace out(_logger, _securityTraceCategory); - out << "SSL summary for " << (incoming ? "incoming" : "outgoing") << " connection\n"; - - // - // The const_cast is necesary because Solaris still uses OpenSSL 0.9.7. - // - //const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); - SSL_CIPHER *cipher = const_cast<SSL_CIPHER*>(SSL_get_current_cipher(ssl)); - if(!cipher) - { - out << "unknown cipher\n"; - } - else - { - out << "cipher = " << SSL_CIPHER_get_name(cipher) << "\n"; - out << "bits = " << SSL_CIPHER_get_bits(cipher, 0) << "\n"; - out << "protocol = " << SSL_get_version(ssl) << "\n"; - } - out << IceInternal::fdToString(SSL_get_fd(ssl)); + return _engine->initialized(); } int @@ -1192,4 +49,3 @@ IceSSL::Instance::securityTraceCategory() const { return _securityTraceCategory; } - diff --git a/cpp/src/IceSSL/Instance.h b/cpp/src/IceSSL/Instance.h index d549b60f179..ee295a6d7d1 100644 --- a/cpp/src/IceSSL/Instance.h +++ b/cpp/src/IceSSL/Instance.h @@ -12,114 +12,37 @@ #include <IceSSL/InstanceF.h> #include <IceSSL/UtilF.h> -#include <Ice/CommunicatorF.h> -#include <Ice/Network.h> #include <Ice/ProtocolInstance.h> #include <Ice/ProtocolPluginFacadeF.h> #include <IceSSL/Plugin.h> +#include <IceSSL/SSLEngine.h> #include <IceSSL/TrustManagerF.h> #include <Ice/BuiltinSequences.h> namespace IceSSL { -class SharedInstance : public IceUtil::Shared -{ -public: - - SharedInstance(const Ice::CommunicatorPtr&); - ~SharedInstance(); - - void initialize(); - void context(SSL_CTX*); - SSL_CTX* context() const; - void setCertificateVerifier(const CertificateVerifierPtr&); - void setPasswordPrompt(const PasswordPromptPtr&); - - Ice::CommunicatorPtr communicator() const; - - void verifyPeer(SSL*, SOCKET, const std::string&, const NativeConnectionInfoPtr&); - - std::string sslErrors() const; - - void destroy(); - - // - // OpenSSL callbacks. - // - std::string password(bool); - int verifyCallback(int, SSL*, X509_STORE_CTX*); -#ifndef OPENSSL_NO_DH - DH* dhParams(int); -#endif - -private: - - enum Protocols { SSLv3 = 0x01, TLSv1_0 = 0x02, TLSv1_1 = 0x04, TLSv1_2 = 0x08 }; - static int parseProtocols(const Ice::StringSeq&); - - static SSL_METHOD* getMethod(int); - - void setOptions(int); - - bool _initOpenSSL; - const Ice::CommunicatorPtr _communicator; - const Ice::LoggerPtr _logger; - int _securityTraceLevel; - std::string _securityTraceCategory; - bool _initialized; - SSL_CTX* _ctx; - std::string _defaultDir; - bool _checkCertName; - int _verifyDepthMax; - int _verifyPeer; - std::string _password; -#ifndef OPENSSL_NO_DH - DHParamsPtr _dhParams; -#endif - CertificateVerifierPtr _verifier; - PasswordPromptPtr _prompt; - TrustManagerPtr _trustManager; -}; - class Instance : public IceInternal::ProtocolInstance { public: - Instance(const SharedInstancePtr&, Ice::Short, const std::string&); + Instance(const SSLEnginePtr&, Ice::Short, const std::string&); virtual ~Instance(); - SSL_CTX* - context() const - { - return _sharedInstance->context(); - } - - std::string - sslErrors() const - { - return _sharedInstance->sslErrors(); - } - - SharedInstancePtr - sharedInstance() const - { - return _sharedInstance; - } - - void - verifyPeer(SSL* ssl, SOCKET fd, const std::string& host, const NativeConnectionInfoPtr& info) + SSLEnginePtr + engine() const { - _sharedInstance->verifyPeer(ssl, fd, host, info); + return _engine; } + + bool initialized() const; - void traceConnection(SSL*, bool); int securityTraceLevel() const; std::string securityTraceCategory() const; private: - const SharedInstancePtr _sharedInstance; + const SSLEnginePtr _engine; int _securityTraceLevel; std::string _securityTraceCategory; }; diff --git a/cpp/src/IceSSL/InstanceF.h b/cpp/src/IceSSL/InstanceF.h index 3c2bcac856b..6c8f7f33d92 100644 --- a/cpp/src/IceSSL/InstanceF.h +++ b/cpp/src/IceSSL/InstanceF.h @@ -21,10 +21,6 @@ class Instance; IceUtil::Shared* upCast(IceSSL::Instance*); typedef IceInternal::Handle<Instance> InstancePtr; -class SharedInstance; -IceUtil::Shared* upCast(IceSSL::SharedInstance*); -typedef IceInternal::Handle<SharedInstance> SharedInstancePtr; - } #endif diff --git a/cpp/src/IceSSL/Makefile b/cpp/src/IceSSL/Makefile index adfe54a84db..0b77453d20e 100644 --- a/cpp/src/IceSSL/Makefile +++ b/cpp/src/IceSSL/Makefile @@ -21,13 +21,17 @@ OBJS = AcceptorI.o \ ConnectorI.o \ EndpointInfo.o \ ConnectionInfo.o \ - EndpointI.o \ - Instance.o \ - PluginI.o \ - TransceiverI.o \ - Util.o \ + EndpointI.o \ + Instance.o \ + PluginI.o \ + TransceiverI.o \ + SecureTransportTransceiverI.o \ + Util.o \ RFC2253.o \ - TrustManager.o + TrustManager.o \ + SSLEngine.o \ + OpenSSLEngine.o \ + SecureTransportEngine.o SRCS = $(OBJS:.o=.cpp) @@ -42,7 +46,7 @@ include $(top_srcdir)/config/Make.rules CPPFLAGS := -I.. $(CPPFLAGS) -DICE_SSL_API_EXPORTS $(OPENSSL_FLAGS) SLICE2CPPFLAGS := --ice --include-dir IceSSL --dll-export ICE_SSL_API $(SLICE2CPPFLAGS) -LINKWITH := $(BZIP2_RPATH_LINK) -lIce -lIceUtil $(OPENSSL_LIBS) $(CXXLIBS) +LINKWITH := $(BZIP2_RPATH_LINK) -lIce -lIceUtil $(SSL_OS_LIBS) $(CXXLIBS) ifeq ($(STATICLIBS),yes) $(libdir)/$(LIBNAME): $(OBJS) diff --git a/cpp/src/IceSSL/Makefile.mak b/cpp/src/IceSSL/Makefile.mak index 5dc4328322a..33ea5d194b5 100644 --- a/cpp/src/IceSSL/Makefile.mak +++ b/cpp/src/IceSSL/Makefile.mak @@ -25,7 +25,9 @@ OBJS = AcceptorI.obj \ TransceiverI.obj \ Util.obj \ RFC2253.obj \ - TrustManager.obj + TrustManager.obj \ + SSLEngine.obj \ + OpenSSLEngine.obj SRCS = $(OBJS:.obj=.cpp) diff --git a/cpp/src/IceSSL/OpenSSLEngine.cpp b/cpp/src/IceSSL/OpenSSLEngine.cpp new file mode 100644 index 00000000000..c73e27da33a --- /dev/null +++ b/cpp/src/IceSSL/OpenSSLEngine.cpp @@ -0,0 +1,920 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +#include <IceSSL/Config.h> + +#include <IceSSL/SSLEngine.h> +#include <IceSSL/Util.h> +#include <IceSSL/TrustManager.h> + +#include <Ice/Communicator.h> +#include <Ice/LocalException.h> +#include <Ice/Logger.h> +#include <Ice/LoggerUtil.h> +#include <Ice/Properties.h> + +#include <IceUtil/StringConverter.h> +#include <IceUtil/StringUtil.h> +#include <IceUtil/Mutex.h> +#include <IceUtil/MutexPtrLock.h> +#include <IceUtil/FileUtil.h> + +#ifdef ICE_USE_OPENSSL + +#include <openssl/rand.h> +#include <openssl/err.h> +#include <openssl/ssl.h> + +using namespace std; +using namespace Ice; +using namespace IceSSL; + +namespace +{ + +IceUtil::Mutex* staticMutex = 0; +int instanceCount = 0; +IceUtil::Mutex* locks = 0; + +class Init +{ +public: + + Init() + { + staticMutex = new IceUtil::Mutex; + } + + ~Init() + { + delete staticMutex; + staticMutex = 0; + + if(locks) + { + delete[] locks; + locks = 0; + } + } +}; + +Init init; +} + +extern "C" +{ + +// +// OpenSSL mutex callback. +// +void +IceSSL_opensslLockCallback(int mode, int n, const char* /*file*/, int /*line*/) +{ + assert(locks); + if(mode & CRYPTO_LOCK) + { + locks[n].lock(); + } + else + { + locks[n].unlock(); + } +} + +// +// OpenSSL thread id callback. +// +unsigned long +IceSSL_opensslThreadIdCallback() +{ +# if defined(_WIN32) + return static_cast<unsigned long>(GetCurrentThreadId()); +# elif defined(__FreeBSD__) || defined(__APPLE__) || defined(__osf1__) + // + // On some platforms, pthread_t is a pointer to a per-thread structure. + // + return reinterpret_cast<unsigned long>(pthread_self()); +# elif (defined(__linux) || defined(__sun) || defined(__hpux)) || defined(_AIX) + // + // On Linux, Solaris, HP-UX and AIX, pthread_t is an integer. + // + return static_cast<unsigned long>(pthread_self()); +# else +# error "Unknown platform" +# endif +} + +int +IceSSL_opensslPasswordCallback(char* buf, int size, int flag, void* userData) +{ + OpenSSLEngine* p = reinterpret_cast<OpenSSLEngine*>(userData); + string passwd = p->password(flag == 1); + int sz = static_cast<int>(passwd.size()); + if(sz > size) + { + sz = size - 1; + } +# if defined(_WIN32) + strncpy_s(buf, size, passwd.c_str(), sz); +# else + strncpy(buf, passwd.c_str(), sz); +# endif + buf[sz] = '\0'; + + for(string::iterator i = passwd.begin(); i != passwd.end(); ++i) + { + *i = '\0'; + } + + return sz; +} + +# ifndef OPENSSL_NO_DH +DH* +IceSSL_opensslDHCallback(SSL* ssl, int /*isExport*/, int keyLength) +{ + OpenSSLEngine* p = reinterpret_cast<OpenSSLEngine*>(SSL_CTX_get_ex_data(ssl->ctx, 0)); + return p->dhParams(keyLength); +} +# endif + +int +IceSSL_opensslVerifyCallback(int ok, X509_STORE_CTX* ctx) +{ + SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); + OpenSSLEngine* p = reinterpret_cast<OpenSSLEngine*>(SSL_CTX_get_ex_data(ssl->ctx, 0)); + return p->verifyCallback(ok, ssl, ctx); +} + +} + +namespace +{ +bool +passwordError() +{ + int reason = ERR_GET_REASON(ERR_peek_error()); + return (reason == PEM_R_BAD_BASE64_DECODE || + reason == PEM_R_BAD_DECRYPT || + reason == PEM_R_BAD_PASSWORD_READ || + reason == PEM_R_PROBLEMS_GETTING_PASSWORD); +} + +} + +IceUtil::Shared* IceSSL::upCast(IceSSL::OpenSSLEngine* p) { return p; } + +OpenSSLEngine::OpenSSLEngine(const CommunicatorPtr& communicator) : + SSLEngine(communicator), + _initialized(false), + _ctx(0) +{ + __setNoDelete(true); + + // + // Initialize OpenSSL if necessary. + // + IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(staticMutex); + instanceCount++; + + if(instanceCount == 1) + { + PropertiesPtr properties = communicator->getProperties(); + + // + // The IceSSL.InitOpenSSL property specifies whether we should perform the global + // startup (and shutdown) tasks for the OpenSSL library. + // + // If an application uses multiple components that each depend on OpenSSL, the + // application should disable OpenSSL initialization in those components and + // perform the initialization itself. + // + _initOpenSSL = properties->getPropertyAsIntWithDefault("IceSSL.InitOpenSSL", 1) > 0; + if(_initOpenSSL) + { + // + // Create the mutexes and set the callbacks. + // + if(!locks) + { + locks = new IceUtil::Mutex[CRYPTO_num_locks()]; + CRYPTO_set_locking_callback(IceSSL_opensslLockCallback); + CRYPTO_set_id_callback(IceSSL_opensslThreadIdCallback); + } + + // + // Load human-readable error messages. + // + SSL_load_error_strings(); + + // + // Initialize the SSL library. + // + SSL_library_init(); + + // + // This is necessary to allow programs that use OpenSSL 0.9.x to + // load private key files generated by OpenSSL 1.x. + // + OpenSSL_add_all_algorithms(); + + // + // Initialize the PRNG. + // +# ifdef WINDOWS + RAND_screen(); // Uses data from the screen if possible. +# endif + char randFile[1024]; + if(RAND_file_name(randFile, sizeof(randFile))) // Gets the name of a default seed file. + { + RAND_load_file(randFile, 1024); + } + + string randFiles = properties->getProperty("IceSSL.Random"); + + if(!randFiles.empty()) + { + vector<string> files; + string defaultDir = properties->getProperty("IceSSL.DefaultDir"); + + if(!IceUtilInternal::splitString(randFiles, IceUtilInternal::pathsep, files)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: invalid value for IceSSL.Random:\n" + randFiles; + throw ex; + } + for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) + { + string file = *p; + if(!checkPath(file, defaultDir, false)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: entropy data file not found:\n" + file; + throw ex; + } + if(!RAND_load_file(file.c_str(), 1024)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: unable to load entropy data from " + file; + throw ex; + } + } + } +# ifndef _WIN32 + // + // The Entropy Gathering Daemon (EGD) is not available on Windows. + // The file should be a Unix domain socket for the daemon. + // + string entropyDaemon = properties->getProperty("IceSSL.EntropyDaemon"); + if(!entropyDaemon.empty()) + { + if(RAND_egd(entropyDaemon.c_str()) <= 0) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: EGD failure using file " + entropyDaemon; + throw ex; + } + } +# endif + if(!RAND_status()) + { + getLogger()->warning("IceSSL: insufficient data to initialize PRNG"); + } + } + else + { + if(!properties->getProperty("IceSSL.Random").empty()) + { + getLogger()->warning("IceSSL: ignoring IceSSL.Random because OpenSSL initialization is disabled"); + } +# ifndef _WIN32 + else if(!properties->getProperty("IceSSL.EntropyDaemon").empty()) + { + getLogger()->warning("IceSSL: ignoring IceSSL.EntropyDaemon because OpenSSL initialization is disabled"); + } +# endif + } + } + __setNoDelete(false); +} + +OpenSSLEngine::~OpenSSLEngine() +{ + // + // Clean up OpenSSL resources. + // + IceUtilInternal::MutexPtrLock<IceUtil::Mutex> sync(staticMutex); + + if(--instanceCount == 0 && _initOpenSSL) + { + // + // NOTE: We can't destroy the locks here: threads which might have called openssl methods + // might access openssl locks upon termination (from DllMain/THREAD_DETACHED). Instead, + // we release the locks in the ~Init() static destructor. See bug #4156. + // + //CRYPTO_set_locking_callback(0); + //CRYPTO_set_id_callback(0); + //delete[] locks; + //locks = 0; + + CRYPTO_cleanup_all_ex_data(); + RAND_cleanup(); + ERR_free_strings(); + EVP_cleanup(); + } +} + +bool +OpenSSLEngine::initialized() const +{ + IceUtil::Mutex::Lock lock(_mutex); + return _initialized; +} + +void +OpenSSLEngine::initialize() +{ + IceUtil::Mutex::Lock lock(_mutex); + if(_initialized) + { + return; + } + + try + { + SSLEngine::initialize(); + + const string propPrefix = "IceSSL."; + PropertiesPtr properties = communicator()->getProperties(); + + // + // Protocols selects which protocols to enable. + // + const int protocols = parseProtocols(properties->getPropertyAsList(propPrefix + "Protocols")); + + // + // Create an SSL context if the application hasn't supplied one. + // + if(!_ctx) + { + _ctx = SSL_CTX_new(getMethod(protocols)); + if(!_ctx) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: unable to create SSL context:\n" + sslErrors(); + throw ex; + } + + // + // Check for a default directory. We look in this directory for + // files mentioned in the configuration. + // + string defaultDir = properties->getProperty(propPrefix + "DefaultDir"); + + // + // If the configuration defines a password, or the application has supplied + // a password prompt object, then register a password callback. Otherwise, + // let OpenSSL use its default behavior. + // + { + // TODO: Support quoted value? + string password = properties->getProperty(propPrefix + "Password"); + if(!password.empty() || getPasswordPrompt()) + { + SSL_CTX_set_default_passwd_cb(_ctx, IceSSL_opensslPasswordCallback); + SSL_CTX_set_default_passwd_cb_userdata(_ctx, this); + setPassword(password); + } + } + + int passwordRetryMax = properties->getPropertyAsIntWithDefault(propPrefix + "PasswordRetryMax", 3); + + // + // Establish the location of CA certificates. + // + { + string caFile = properties->getProperty(propPrefix + "CertAuthFile"); + string caDir = properties->getPropertyWithDefault(propPrefix + "CertAuthDir", defaultDir); + const char* file = 0; + const char* dir = 0; + if(!caFile.empty()) + { + if(!checkPath(caFile, defaultDir, false)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: CA certificate file not found:\n" + caFile; + throw ex; + } + file = caFile.c_str(); + } + if(!caDir.empty()) + { + if(!checkPath(caDir, defaultDir, true)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: CA certificate directory not found:\n" + caDir; + throw ex; + } + dir = caDir.c_str(); + } + if(file || dir) + { + // + // The certificate may be stored in an encrypted file, so handle + // password retries. + // + int count = 0; + int err = 0; + while(count < passwordRetryMax) + { + ERR_clear_error(); + err = SSL_CTX_load_verify_locations(_ctx, file, dir); + if(err) + { + break; + } + ++count; + } + if(err == 0) + { + string msg = "IceSSL: unable to establish CA certificates"; + if(passwordError()) + { + msg += ":\ninvalid password"; + } + else + { + string err = sslErrors(); + if(!err.empty()) + { + msg += ":\n" + err; + } + } + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = msg; + throw ex; + } + } + } + + // + // Establish the certificate chains and private keys. One RSA certificate and + // one DSA certificate are allowed. + // + { + string certFile = properties->getProperty(propPrefix + "CertFile"); + string keyFile = properties->getProperty(propPrefix + "KeyFile"); + vector<string>::size_type numCerts = 0; + if(!certFile.empty()) + { + vector<string> files; + if(!IceUtilInternal::splitString(certFile, IceUtilInternal::pathsep, files) || files.size() > 2) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: invalid value for " + propPrefix + "CertFile:\n" + certFile; + throw ex; + } + numCerts = files.size(); + for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) + { + string file = *p; + if(!checkPath(file, defaultDir, false)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: certificate file not found:\n" + file; + throw ex; + } + // + // The certificate may be stored in an encrypted file, so handle + // password retries. + // + int count = 0; + int err = 0; + while(count < passwordRetryMax) + { + ERR_clear_error(); + err = SSL_CTX_use_certificate_chain_file(_ctx, file.c_str()); + if(err) + { + break; + } + ++count; + } + if(err == 0) + { + string msg = "IceSSL: unable to load certificate chain from file " + file; + if(passwordError()) + { + msg += ":\ninvalid password"; + } + else + { + string err = sslErrors(); + if(!err.empty()) + { + msg += ":\n" + err; + } + } + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = msg; + throw ex; + } + } + } + if(keyFile.empty()) + { + keyFile = certFile; // Assume the certificate file also contains the private key. + } + if(!keyFile.empty()) + { + vector<string> files; + if(!IceUtilInternal::splitString(keyFile, IceUtilInternal::pathsep, files) || files.size() > 2) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: invalid value for " + propPrefix + "KeyFile:\n" + keyFile; + throw ex; + } + if(files.size() != numCerts) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: " + propPrefix + "KeyFile does not agree with " + propPrefix + "CertFile"; + throw ex; + } + for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) + { + string file = *p; + if(!checkPath(file, defaultDir, false)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: key file not found:\n" + file; + throw ex; + } + // + // The private key may be stored in an encrypted file, so handle + // password retries. + // + int count = 0; + int err = 0; + while(count < passwordRetryMax) + { + ERR_clear_error(); + err = SSL_CTX_use_PrivateKey_file(_ctx, file.c_str(), SSL_FILETYPE_PEM); + if(err) + { + break; + } + ++count; + } + if(err == 0) + { + string msg = "IceSSL: unable to load private key from file " + file; + if(passwordError()) + { + msg += ":\ninvalid password"; + } + else + { + string err = sslErrors(); + if(!err.empty()) + { + msg += ":\n" + err; + } + } + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = msg; + throw ex; + } + } + if(!SSL_CTX_check_private_key(_ctx)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: unable to validate private key(s):\n" + sslErrors(); + throw ex; + } + } + } + + // + // Diffie Hellman configuration. + // + { +# ifndef OPENSSL_NO_DH + _dhParams = new DHParams; + SSL_CTX_set_options(_ctx, SSL_OP_SINGLE_DH_USE); + SSL_CTX_set_tmp_dh_callback(_ctx, IceSSL_opensslDHCallback); +# endif + // + // Properties have the following form: + // + // ...DH.<keyLength>=file + // + const string dhPrefix = propPrefix + "DH."; + PropertyDict d = properties->getPropertiesForPrefix(dhPrefix); + if(!d.empty()) + { +# ifdef OPENSSL_NO_DH + getLogger()->warning("IceSSL: OpenSSL is not configured for Diffie Hellman"); +# else + for(PropertyDict::iterator p = d.begin(); p != d.end(); ++p) + { + string s = p->first.substr(dhPrefix.size()); + int keyLength = atoi(s.c_str()); + if(keyLength > 0) + { + string file = p->second; + if(!checkPath(file, defaultDir, false)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: DH parameter file not found:\n" + file; + throw ex; + } + if(!_dhParams->add(keyLength, file)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: unable to read DH parameter file " + file; + throw ex; + } + } + } +# endif + } + } + } + + // + // Store a pointer to ourself for use in OpenSSL callbacks. + // + SSL_CTX_set_ex_data(_ctx, 0, this); + + // + // This is necessary for successful interop with Java. Without it, a Java + // client would fail to reestablish a connection: the server gets the + // error "session id context uninitialized" and the client receives + // "SSLHandshakeException: Remote host closed connection during handshake". + // + SSL_CTX_set_session_cache_mode(_ctx, SSL_SESS_CACHE_OFF); + + // + // Although we disable session caching, we still need to set a session ID + // context (ICE-5103). The value can be anything; here we just use the + // pointer to this SharedInstance object. + // + SSL_CTX_set_session_id_context(_ctx, reinterpret_cast<unsigned char*>(this), + static_cast<unsigned int>(sizeof(this))); + + // + // Select protocols. + // + if(protocols != 0) + { + setOptions(protocols); + } + + // + // Establish the cipher list. + // + string ciphers = properties->getProperty(propPrefix + "Ciphers"); + if(!ciphers.empty()) + { + if(!SSL_CTX_set_cipher_list(_ctx, ciphers.c_str())) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: unable to set ciphers using `" + ciphers + "':\n" + sslErrors(); + throw ex; + } + } + + // + // Determine whether a certificate is required from the peer. + // + { + int sslVerifyMode; + switch(getVerifyPeer()) + { + case 0: + sslVerifyMode = SSL_VERIFY_NONE; + break; + case 1: + sslVerifyMode = SSL_VERIFY_PEER; + break; + case 2: + sslVerifyMode = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + break; + default: + { + assert(false); + } + } + SSL_CTX_set_verify(_ctx, sslVerifyMode, IceSSL_opensslVerifyCallback); + } + } + catch(...) + { + // + // We free the SSL context regardless of whether the plugin created it + // or the application supplied it. + // + SSL_CTX_free(_ctx); + _ctx = 0; + throw; + } + + _initialized = true; +} + +void +OpenSSLEngine::context(ContextRef context) +{ + if(initialized()) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: plug-in is already initialized"; + throw ex; + } + + assert(!_ctx); + _ctx = context; +} + +ContextRef +OpenSSLEngine::context() const +{ + return _ctx; +} + +void +OpenSSLEngine::verifyPeer(SSL* ssl, SOCKET fd, const string& address, const NativeConnectionInfoPtr& info) +{ + long result = SSL_get_verify_result(ssl); + if(result != X509_V_OK) + { + if(getVerifyPeer() == 0) + { + if(securityTraceLevel() >= 1) + { + ostringstream ostr; + ostr << "IceSSL: ignoring certificate verification failure:\n" << X509_verify_cert_error_string(result); + getLogger()->trace(securityTraceCategory(), ostr.str()); + } + } + else + { + ostringstream ostr; + ostr << "IceSSL: certificate verification failed:\n" << X509_verify_cert_error_string(result); + string msg = ostr.str(); + if(securityTraceLevel() >= 1) + { + getLogger()->trace(securityTraceCategory(), msg); + } + SecurityException ex(__FILE__, __LINE__); + ex.reason = msg; + throw ex; + } + } + SSLEngine::verifyPeer(fd, address, info); +} + +string +OpenSSLEngine::sslErrors() const +{ + return getSslErrors(securityTraceLevel() >= 1); +} + +void +OpenSSLEngine::destroy() +{ + if(_ctx) + { + SSL_CTX_free(_ctx); + } +} + +int +OpenSSLEngine::verifyCallback(int ok, SSL* ssl, X509_STORE_CTX* c) +{ + if(!ok && securityTraceLevel() >= 1) + { + X509* cert = X509_STORE_CTX_get_current_cert(c); + int err = X509_STORE_CTX_get_error(c); + char buf[256]; + + Trace out(getLogger(), securityTraceCategory()); + out << "certificate verification failure\n"; + + X509_NAME_oneline(X509_get_issuer_name(cert), buf, static_cast<int>(sizeof(buf))); + out << "issuer = " << buf << '\n'; + X509_NAME_oneline(X509_get_subject_name(cert), buf, static_cast<int>(sizeof(buf))); + out << "subject = " << buf << '\n'; + out << "depth = " << X509_STORE_CTX_get_error_depth(c) << '\n'; + out << "error = " << X509_verify_cert_error_string(err) << '\n'; + out << IceInternal::fdToString(SSL_get_fd(ssl)); + } + return ok; +} + +# ifndef OPENSSL_NO_DH +DH* +OpenSSLEngine::dhParams(int keyLength) +{ + return _dhParams->get(keyLength); +} +# endif + +int +OpenSSLEngine::parseProtocols(const StringSeq& protocols) const +{ + int v = 0; + + for(Ice::StringSeq::const_iterator p = protocols.begin(); p != protocols.end(); ++p) + { + string prot = *p; + + if(prot == "ssl3" || prot == "sslv3") + { + v |= SSLv3; + } + else if(prot == "tls" || prot == "tls1" || prot == "tlsv1" || prot == "tls1_0" || prot == "tlsv1_0") + { + v |= TLSv1_0; + } + else if(prot == "tls1_1" || prot == "tlsv1_1") + { + v |= TLSv1_1; + } + else if(prot == "tls1_2" || prot == "tlsv1_2") + { + v |= TLSv1_2; + } + else + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: unrecognized protocol `" + prot + "'"; + throw ex; + } + } + + return v; +} + +SSL_METHOD* +OpenSSLEngine::getMethod(int /*protocols*/) +{ + // + // Despite its name, the SSLv23 method can negotiate SSL3, TLS1.0, TLS1.1, and TLS1.2. + // We use the const_cast for backward compatibility with older OpenSSL releases. + // + SSL_METHOD* meth = const_cast<SSL_METHOD*>(SSLv23_method()); + + /* + * Early versions of OpenSSL 1.0.1 would not negotiate a TLS1.2 connection using + * the SSLv23 method. You can enable the code below to override the method. + if(protocols & TLSv1_2) + { + meth = const_cast<SSL_METHOD*>(TLSv1_2_method()); + } + */ + + return meth; +} + +void +OpenSSLEngine::setOptions(int protocols) +{ + long opts = SSL_OP_NO_SSLv2; // SSLv2 is not supported. + if(!(protocols & SSLv3)) + { + opts |= SSL_OP_NO_SSLv3; + } + if(!(protocols & TLSv1_0)) + { + opts |= SSL_OP_NO_TLSv1; + } +# ifdef SSL_OP_NO_TLSv1_1 + if(!(protocols & TLSv1_1)) + { + opts |= SSL_OP_NO_TLSv1_1; + // + // The value of SSL_OP_NO_TLSv1_1 changed between 1.0.1a and 1.0.1b. + // + if(SSL_OP_NO_TLSv1_1 == 0x00000400L) + { + opts |= 0x10000000L; // New value of SSL_OP_NO_TLSv1_1. + } + } +# endif +# ifdef SSL_OP_NO_TLSv1_2 + if(!(protocols & TLSv1_2)) + { + opts |= SSL_OP_NO_TLSv1_2; + } +# endif + SSL_CTX_set_options(_ctx, opts); +} + +#endif // ICESSL_USE_OPENSSL diff --git a/cpp/src/IceSSL/PluginI.cpp b/cpp/src/IceSSL/PluginI.cpp index 91e07455ff8..02f9cf1a6e1 100644 --- a/cpp/src/IceSSL/PluginI.cpp +++ b/cpp/src/IceSSL/PluginI.cpp @@ -30,8 +30,7 @@ extern "C" ICE_DECLSPEC_EXPORT Ice::Plugin* createIceSSL(const CommunicatorPtr& communicator, const string& /*name*/, const StringSeq& /*args*/) { - PluginI* plugin = new PluginI(communicator); - return plugin; + return new PluginI(communicator); } } @@ -41,8 +40,12 @@ createIceSSL(const CommunicatorPtr& communicator, const string& /*name*/, const // IceSSL::PluginI::PluginI(const Ice::CommunicatorPtr& communicator) { - _sharedInstance = new SharedInstance(communicator); - +#ifdef ICE_USE_OPENSSL + _engine = new OpenSSLEngine(communicator); +#else + _engine = new SecureTransportEngine(communicator); +#endif + IceInternal::ProtocolPluginFacadePtr facade = IceInternal::getProtocolPluginFacade(communicator); // @@ -50,42 +53,44 @@ IceSSL::PluginI::PluginI(const Ice::CommunicatorPtr& communicator) // in initialize, because the communicator may need to interpret // proxies before the plug-in is fully initialized. // - facade->addEndpointFactory(new EndpointFactoryI(new Instance(_sharedInstance, EndpointType, "ssl"))); + facade->addEndpointFactory(new EndpointFactoryI(new Instance(_engine, EndpointType, "ssl"))); } void IceSSL::PluginI::initialize() { - _sharedInstance->initialize(); + _engine->initialize(); } void IceSSL::PluginI::destroy() { - _sharedInstance->destroy(); - _sharedInstance = 0; + _engine->destroy(); + _engine = 0; } void -IceSSL::PluginI::setContext(SSL_CTX* context) +IceSSL::PluginI::setCertificateVerifier(const CertificateVerifierPtr& verifier) { - _sharedInstance->context(context); + _engine->setCertificateVerifier(verifier); } -SSL_CTX* -IceSSL::PluginI::getContext() +void +IceSSL::PluginI::setPasswordPrompt(const PasswordPromptPtr& prompt) { - return _sharedInstance->context(); + _engine->setPasswordPrompt(prompt); } +#ifdef ICE_USE_OPENSSL void -IceSSL::PluginI::setCertificateVerifier(const CertificateVerifierPtr& verifier) +IceSSL::PluginI::setContext(ContextRef context) { - _sharedInstance->setCertificateVerifier(verifier); + _engine->context(context); } -void -IceSSL::PluginI::setPasswordPrompt(const PasswordPromptPtr& prompt) +ContextRef +IceSSL::PluginI::getContext() { - _sharedInstance->setPasswordPrompt(prompt); + return _engine->context(); } +#endif diff --git a/cpp/src/IceSSL/PluginI.h b/cpp/src/IceSSL/PluginI.h index 4a0a030987b..7d1903aa7bd 100644 --- a/cpp/src/IceSSL/PluginI.h +++ b/cpp/src/IceSSL/PluginI.h @@ -11,13 +11,18 @@ #define ICE_SSL_PLUGIN_I_H #include <IceSSL/Plugin.h> -#include <IceSSL/InstanceF.h> +#include <IceSSL/SSLEngineF.h> #include <Ice/CommunicatorF.h> namespace IceSSL { -class PluginI : public IceSSL::Plugin +class PluginI : +#ifdef ICE_USE_OPENSSL + public OpenSSLPlugin +#else + public IceSSL::Plugin +#endif { public: @@ -32,14 +37,21 @@ public: // // From IceSSL::Plugin. // - virtual void setContext(SSL_CTX*); - virtual SSL_CTX* getContext(); virtual void setCertificateVerifier(const CertificateVerifierPtr&); virtual void setPasswordPrompt(const PasswordPromptPtr&); +#ifdef ICE_USE_OPENSSL + virtual void setContext(ContextRef); + virtual ContextRef getContext(); +#endif + private: - SharedInstancePtr _sharedInstance; +#ifdef ICE_USE_OPENSSL + OpenSSLEnginePtr _engine; +#else + SecureTransportEnginePtr _engine; +#endif }; } diff --git a/cpp/src/IceSSL/RFC2253.cpp b/cpp/src/IceSSL/RFC2253.cpp index 331838bf928..eaa86e979c5 100644 --- a/cpp/src/IceSSL/RFC2253.cpp +++ b/cpp/src/IceSSL/RFC2253.cpp @@ -231,6 +231,7 @@ parseAttributeTypeAndValue(const string& data, size_t& pos) pair<string, string> p; p.first = parseAttributeType(data, pos); eatWhite(data, pos); + if(pos >= data.size()) { throw ParseException(__FILE__, __LINE__, "invalid attribute type/value pair (unexpected end of data)"); diff --git a/cpp/src/IceSSL/SSLEngine.cpp b/cpp/src/IceSSL/SSLEngine.cpp new file mode 100644 index 00000000000..adbfb439bc0 --- /dev/null +++ b/cpp/src/IceSSL/SSLEngine.cpp @@ -0,0 +1,291 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +#include <IceSSL/SSLEngine.h> +#include <IceSSL/TrustManager.h> + +#include <IceUtil/StringUtil.h> + +#include <Ice/Communicator.h> +#include <Ice/Properties.h> +#include <Ice/Logger.h> +#include <Ice/LoggerUtil.h> +#include <Ice/LocalException.h> + +#include <string> + +using namespace std; +using namespace Ice; +using namespace IceSSL; + +IceUtil::Shared* IceSSL::upCast(IceSSL::SSLEngine* p) { return p; } + +IceSSL::SSLEngine::SSLEngine(const Ice::CommunicatorPtr& communicator) : + _communicator(communicator), + _logger(communicator->getLogger()), + _trustManager(new TrustManager(communicator)) +{ +} + +IceSSL::CertificateVerifierPtr +IceSSL::SSLEngine::getCertificateVerifier() const +{ + return _verifier; +} + +void +IceSSL::SSLEngine::setCertificateVerifier(const IceSSL::CertificateVerifierPtr& verifier) +{ + _verifier = verifier; +} + +IceSSL::PasswordPromptPtr +IceSSL::SSLEngine::getPasswordPrompt() const +{ + return _prompt; +} + +void +IceSSL::SSLEngine::setPasswordPrompt(const IceSSL::PasswordPromptPtr& prompt) +{ + _prompt = prompt; +} + +string +IceSSL::SSLEngine::password(bool /*encrypting*/) +{ + if(_prompt) + { + try + { + return _prompt->getPassword(); + } + catch(...) + { + // + // Don't allow exceptions to cross an OpenSSL boundary. + // + return string(); + } + } + else + { + return _password; + } +} + +string +IceSSL::SSLEngine::getPassword() const +{ + return _password; +} + +void +IceSSL::SSLEngine::setPassword(const std::string& password) +{ + _password = password; +} + +void +IceSSL::SSLEngine::initialize() +{ + const string propPrefix = "IceSSL."; + const PropertiesPtr properties = communicator()->getProperties(); + + // + // CheckCertName determines whether we compare the name in a peer's + // certificate against its hostname. + // + _checkCertName = properties->getPropertyAsIntWithDefault(propPrefix + "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(propPrefix + "VerifyDepthMax", 2); + + // + // VerifyPeer determines whether certificate validation failures abort a connection. + // + _verifyPeer = properties->getPropertyAsIntWithDefault(propPrefix + "VerifyPeer", 2); + + if(_verifyPeer < 0 || _verifyPeer > 2) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: invalid value for " + propPrefix + "VerifyPeer"; + throw ex; + } + + _securityTraceLevel = properties->getPropertyAsInt("IceSSL.Trace.Security"); + _securityTraceCategory = "Security"; +} + +void +IceSSL::SSLEngine::verifyPeer(SOCKET fd, const string& address, const NativeConnectionInfoPtr& info) +{ + const CertificateVerifierPtr verifier = getCertificateVerifier(); + + // + // For an outgoing connection, we compare the proxy address (if any) against + // fields in the server's certificate (if any). + // + if(!info->nativeCerts.empty() && !address.empty()) + { + const CertificatePtr cert = info->nativeCerts[0]; + // + // Extract the IP addresses and the DNS names from the subject + // alternative names. + // + vector<pair<int, string> > subjectAltNames = cert->getSubjectAlternativeNames(); + vector<string> ipAddresses; + vector<string> dnsNames; + for(vector<pair<int, string> >::const_iterator p = subjectAltNames.begin(); p != subjectAltNames.end(); ++p) + { + if(p->first == 7) + { + ipAddresses.push_back(IceUtilInternal::toLower(p->second)); + } + else if(p->first == 2) + { + dnsNames.push_back(IceUtilInternal::toLower(p->second)); + } + } + + // + // Compare the peer's address against the common name. + // + bool certNameOK = false; + string dn; + string addrLower = IceUtilInternal::toLower(address); + { + DistinguishedName d = cert->getSubjectDN(); + dn = IceUtilInternal::toLower(string(d)); + string cn = "cn=" + addrLower; + string::size_type pos = dn.find(cn); + if(pos != string::npos) + { + // + // Ensure we match the entire common name. + // + certNameOK = (pos + cn.size() == dn.size()) || (dn[pos + cn.size()] == ','); + } + } + + // + // Compare the peer's address against the dnsName and ipAddress + // values in the subject alternative name. + // + if(!certNameOK) + { + certNameOK = find(ipAddresses.begin(), ipAddresses.end(), addrLower) != ipAddresses.end(); + } + if(!certNameOK) + { + certNameOK = find(dnsNames.begin(), dnsNames.end(), addrLower) != dnsNames.end(); + } + + // + // 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))) + { + ostringstream ostr; + ostr << "IceSSL: "; + if(!_checkCertName) + { + ostr << "ignoring "; + } + ostr << "certificate validation failure:\npeer certificate does not have `" << address + << "' as its commonName or in its subjectAltName extension"; + if(!dn.empty()) + { + ostr << "\nSubject DN: " << dn; + } + if(!dnsNames.empty()) + { + ostr << "\nDNS names found in certificate: "; + for(vector<string>::const_iterator p = dnsNames.begin(); p != dnsNames.end(); ++p) + { + if(p != dnsNames.begin()) + { + ostr << ", "; + } + ostr << *p; + } + } + if(!ipAddresses.empty()) + { + ostr << "\nIP addresses found in certificate: "; + for(vector<string>::const_iterator p = ipAddresses.begin(); p != ipAddresses.end(); ++p) + { + if(p != ipAddresses.begin()) + { + ostr << ", "; + } + ostr << *p; + } + } + string msg = ostr.str(); + if(_securityTraceLevel >= 1) + { + Trace out(getLogger(), _securityTraceCategory); + out << msg; + } + if(_checkCertName) + { + SecurityException ex(__FILE__, __LINE__); + ex.reason = msg; + throw ex; + } + } + } + + if(_verifyDepthMax > 0 && static_cast<int>(info->certs.size()) > _verifyDepthMax) + { + ostringstream ostr; + ostr << (info->incoming ? "incoming" : "outgoing") << " connection rejected:\n" + << "length of peer's certificate chain (" << info->certs.size() << ") exceeds maximum of " + << _verifyDepthMax; + string msg = ostr.str(); + if(_securityTraceLevel >= 1) + { + getLogger()->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd)); + } + SecurityException ex(__FILE__, __LINE__); + ex.reason = msg; + throw ex; + } + + if(!_trustManager->verify(info)) + { + string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by trust manager"; + if(_securityTraceLevel >= 1) + { + getLogger()->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd)); + } + SecurityException ex(__FILE__, __LINE__); + ex.reason = msg; + throw ex; + } + + if(verifier && !verifier->verify(info)) + { + string msg = string(info->incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier"; + if(_securityTraceLevel >= 1) + { + getLogger()->trace(_securityTraceCategory, msg + "\n" + IceInternal::fdToString(fd)); + } + SecurityException ex(__FILE__, __LINE__); + ex.reason = msg; + throw ex; + } +} diff --git a/cpp/src/IceSSL/SSLEngine.h b/cpp/src/IceSSL/SSLEngine.h new file mode 100644 index 00000000000..e4f1891222a --- /dev/null +++ b/cpp/src/IceSSL/SSLEngine.h @@ -0,0 +1,179 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +#ifndef ICE_SSL_ENGINE_H +#define ICE_SSL_ENGINE_H + +#include <IceSSL/Plugin.h> +#include <IceSSL/Util.h> +#include <IceSSL/SSLEngineF.h> +#include <IceSSL/TrustManagerF.h> + +#include <IceUtil/ScopedArray.h> +#include <IceUtil/UniquePtr.h> +#include <IceUtil/Shared.h> +#include <IceUtil/Mutex.h> +#include <Ice/CommunicatorF.h> +#include <Ice/Network.h> + +#ifdef ICE_USE_SECURE_TRANSPORT +# include <Security/Security.h> +#endif + +namespace IceSSL +{ + +class SSLEngine : public IceUtil::Shared +{ +public: + + SSLEngine(const Ice::CommunicatorPtr&); + + Ice::CommunicatorPtr communicator() const { return _communicator; } + Ice::LoggerPtr getLogger() const { return _logger; }; + + void setCertificateVerifier(const CertificateVerifierPtr&); + void setPasswordPrompt(const PasswordPromptPtr&); + std::string password(bool); + + // + // Setup the engine. + // + virtual void initialize() = 0; + + virtual bool initialized() const = 0; + + // + // Destroy the engine. + // + virtual void destroy() = 0; + + // + // Verify peer certificate + // + virtual void verifyPeer(SOCKET, const std::string&, const NativeConnectionInfoPtr&); + + + CertificateVerifierPtr getCertificateVerifier() const; + PasswordPromptPtr getPasswordPrompt() const; + + std::string getPassword() const; + void setPassword(const std::string& password); + + int getVerifyPeer() const { return _verifyPeer; } + int securityTraceLevel() const { return _securityTraceLevel; } + std::string securityTraceCategory() const { return _securityTraceCategory; } + +private: + + const Ice::CommunicatorPtr _communicator; + const Ice::LoggerPtr _logger; + const TrustManagerPtr _trustManager; + + std::string _password; + CertificateVerifierPtr _verifier; + PasswordPromptPtr _prompt; + + bool _checkCertName; + int _verifyDepthMax; + int _verifyPeer; + int _securityTraceLevel; + std::string _securityTraceCategory; +}; + +#ifdef ICE_USE_SECURE_TRANSPORT + +class SecureTransportEngine : public SSLEngine +{ +public: + + SecureTransportEngine(const Ice::CommunicatorPtr&); + + virtual void initialize(); + virtual bool initialized() const; + virtual void destroy(); + + ContextRef newContext(bool); + CFArrayRef getCertificateAuthorities() const; + std::string getCipherName(SSLCipherSuite) const; + SecCertificateRef getCertificate() const; + SecKeychainRef getKeychain() const; + +private: + + void parseCiphers(const std::string& ciphers); + + bool _initialized; + ContextRef _ctx; + CFArrayRef _certificateAuthorities; + SecCertificateRef _cert; + SecKeyRef _key; + SecIdentityRef _identity; + SecKeychainRef _keychain; + + SSLProtocol _protocolVersionMax; + SSLProtocol _protocolVersionMin; + + std::string _defaultDir; + + + IceUtil::UniquePtr< IceUtil::ScopedArray<char> > _dhParams; + size_t _dhParamsLength; + + IceUtil::UniquePtr< IceUtil::ScopedArray<SSLCipherSuite> > _ciphers; + bool _allCiphers; + size_t _numCiphers; + IceUtil::Mutex _mutex; +}; + +#else +class OpenSSLEngine : public SSLEngine +{ +public: + + OpenSSLEngine(const Ice::CommunicatorPtr&); + ~OpenSSLEngine(); + + virtual void initialize(); + virtual bool initialized() const; + virtual void destroy(); + virtual void verifyPeer(SSL*, SOCKET, const std::string&, const NativeConnectionInfoPtr&); + virtual void traceConnection(); + + int verifyCallback(int , SSL*, X509_STORE_CTX*); +# ifndef OPENSSL_NO_DH + DH* dhParams(int); +# endif + ContextRef context() const; + void context(ContextRef); + std::string sslErrors() const; + +private: + + SSL_METHOD* getMethod(int); + void setOptions(int); + enum Protocols { SSLv3 = 0x01, TLSv1_0 = 0x02, TLSv1_1 = 0x04, TLSv1_2 = 0x08 }; + int parseProtocols(const Ice::StringSeq&) const; + + + bool _initOpenSSL; + bool _initialized; + ContextRef _ctx; + std::string _defaultDir; + +# ifndef OPENSSL_NO_DH + DHParamsPtr _dhParams; +# endif + IceUtil::Mutex _mutex; +}; +#endif + +} + +#endif diff --git a/cpp/src/IceSSL/SSLEngineF.h b/cpp/src/IceSSL/SSLEngineF.h new file mode 100644 index 00000000000..c064c2f4250 --- /dev/null +++ b/cpp/src/IceSSL/SSLEngineF.h @@ -0,0 +1,37 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +#ifndef ICE_SSL_ENGINE_F_H +#define ICE_SSL_ENGINE_F_H + +#include <IceUtil/Shared.h> +#include <Ice/Handle.h> + +#include <IceSSL/Plugin.h> + +namespace IceSSL +{ + +class SSLEngine; +ICE_SSL_API IceUtil::Shared* upCast(IceSSL::SSLEngine*); +typedef IceInternal::Handle<SSLEngine> SSLEnginePtr; + +# ifdef ICE_USE_SECURE_TRANSPORT +class SecureTransportEngine; +ICE_SSL_API IceUtil::Shared* upCast(IceSSL::SecureTransportEngine*); +typedef IceInternal::Handle<SecureTransportEngine> SecureTransportEnginePtr; +# else +class OpenSSLEngine; +ICE_SSL_API IceUtil::Shared* upCast(IceSSL::OpenSSLEngine*); +typedef IceInternal::Handle<OpenSSLEngine> OpenSSLEnginePtr; +# endif + +} + +#endif diff --git a/cpp/src/IceSSL/SecureTransportEngine.cpp b/cpp/src/IceSSL/SecureTransportEngine.cpp new file mode 100644 index 00000000000..5bf401947bd --- /dev/null +++ b/cpp/src/IceSSL/SecureTransportEngine.cpp @@ -0,0 +1,1515 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +#include <IceSSL/Config.h> + +#include <IceUtil/FileUtil.h> +#include <IceUtil/StringUtil.h> + +#include <Ice/LocalException.h> +#include <Ice/Properties.h> +#include <Ice/Communicator.h> +#include <Ice/Logger.h> +#include <Ice/LoggerUtil.h> + +#include <IceSSL/SecureTransportTransceiverI.h> +#include <IceSSL/Plugin.h> +#include <IceSSL/SSLEngine.h> +#include <IceSSL/Util.h> + +#ifdef ICE_USE_SECURE_TRANSPORT + +#include <regex.h> +#include <dirent.h> + +using namespace std; +using namespace IceUtil; +using namespace Ice; +using namespace IceSSL; + +namespace +{ + +vector<string> +dir(const string& path) +{ + vector<string> result; + + DIR* d = opendir(path.c_str()); + if(!d) + { + ostringstream os; + os << "failed to open dir `" << path << "'"; + throw IceUtil::IllegalArgumentException(__FILE__, __LINE__, os.str()); + } + + struct dirent* dp = 0; + while((dp = readdir(d))) + { + string name(dp->d_name); + if(IceUtilInternal::fileExists(path + "/" + name)) + { + result.push_back(name); + } + } + closedir(d); + return result; +} + +class RegExp : public IceUtil::Shared +{ +public: + + RegExp(const std::string&); + ~RegExp(); + bool match(const std::string&); + +private: + + regex_t _preg; +}; +typedef IceUtil::Handle<RegExp> RegExpPtr; + +RegExp::RegExp(const std::string& regexp) +{ + int err = regcomp(&_preg, regexp.c_str(), REG_EXTENDED | REG_NOSUB); + if(err != 0) + { + ostringstream os; + os << "failed to compiler regular expression `" << regexp << "' (error = " << err << ")"; + throw IllegalArgumentException(__FILE__, __LINE__, os.str()); + } +} + +RegExp::~RegExp() +{ + regfree(&_preg); +} + +bool +RegExp::match(const std::string& value) +{ + return regexec(&_preg, value.c_str(), 0, 0, 0) == 0; +} + +struct CipherExpression +{ + bool negation; + string cipher; + RegExpPtr re; +}; + +class CiphersHelper +{ +public: + + static void initialize(); + static SSLCipherSuite cipherForName(const string& name); + static string cipherName(SSLCipherSuite cipher); + +private: + + static map<string, SSLCipherSuite> _ciphers; +}; + +map<string, SSLCipherSuite> CiphersHelper::_ciphers; + +// +// Initialize a dictionary with the names of ciphers +// +void +CiphersHelper::initialize() +{ + _ciphers["NULL_WITH_NULL_NULL"] = SSL_NULL_WITH_NULL_NULL; + _ciphers["RSA_WITH_NULL_MD5"] = SSL_RSA_WITH_NULL_MD5; + _ciphers["RSA_WITH_NULL_SHA"] = SSL_RSA_WITH_NULL_SHA; + _ciphers["RSA_EXPORT_WITH_RC4_40_MD5"] = SSL_RSA_EXPORT_WITH_RC4_40_MD5; + _ciphers["RSA_WITH_RC4_128_MD5"] = SSL_RSA_WITH_RC4_128_MD5; + _ciphers["RSA_WITH_RC4_128_SHA"] = SSL_RSA_WITH_RC4_128_SHA; + _ciphers["RSA_EXPORT_WITH_RC2_CBC_40_MD5"] = SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5; + _ciphers["RSA_WITH_IDEA_CBC_SHA"] = SSL_RSA_WITH_IDEA_CBC_SHA; + _ciphers["RSA_EXPORT_WITH_DES40_CBC_SHA"] = SSL_RSA_EXPORT_WITH_DES40_CBC_SHA; + _ciphers["RSA_WITH_DES_CBC_SHA"] = SSL_RSA_WITH_DES_CBC_SHA; + _ciphers["RSA_WITH_3DES_EDE_CBC_SHA"] = SSL_RSA_WITH_3DES_EDE_CBC_SHA; + _ciphers["DH_DSS_EXPORT_WITH_DES40_CBC_SHA"] = SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA; + _ciphers["DH_DSS_WITH_DES_CBC_SHA"] = SSL_DH_DSS_WITH_DES_CBC_SHA; + _ciphers["DH_DSS_WITH_3DES_EDE_CBC_SHA"] = SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA; + _ciphers["DH_RSA_EXPORT_WITH_DES40_CBC_SHA"] = SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA; + _ciphers["DH_RSA_WITH_DES_CBC_SHA"] = SSL_DH_RSA_WITH_DES_CBC_SHA; + _ciphers["DH_RSA_WITH_3DES_EDE_CBC_SHA"] = SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA; + _ciphers["DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"] = SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA; + _ciphers["DHE_DSS_WITH_DES_CBC_SHA"] = SSL_DHE_DSS_WITH_DES_CBC_SHA; + _ciphers["DHE_DSS_WITH_3DES_EDE_CBC_SHA"] = SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA; + _ciphers["DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"] = SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA; + _ciphers["DHE_RSA_WITH_DES_CBC_SHA"] = SSL_DHE_RSA_WITH_DES_CBC_SHA; + _ciphers["DHE_RSA_WITH_3DES_EDE_CBC_SHA"] = SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA; + _ciphers["DH_anon_EXPORT_WITH_RC4_40_MD5"] = SSL_DH_anon_EXPORT_WITH_RC4_40_MD5; + _ciphers["DH_anon_WITH_RC4_128_MD5"] = SSL_DH_anon_WITH_RC4_128_MD5; + _ciphers["DH_anon_EXPORT_WITH_DES40_CBC_SHA"] = SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA; + _ciphers["DH_anon_WITH_DES_CBC_SHA"] = SSL_DH_anon_WITH_DES_CBC_SHA; + _ciphers["DH_anon_WITH_3DES_EDE_CBC_SHA"] = SSL_DH_anon_WITH_3DES_EDE_CBC_SHA; + _ciphers["FORTEZZA_DMS_WITH_NULL_SHA"] = SSL_FORTEZZA_DMS_WITH_NULL_SHA; + _ciphers["FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA"] = SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA; + + // + // TLS addenda using AES, per RFC 3268 + // + _ciphers["RSA_WITH_AES_128_CBC_SHA"] = TLS_RSA_WITH_AES_128_CBC_SHA; + _ciphers["DH_DSS_WITH_AES_128_CBC_SHA"] = TLS_DH_DSS_WITH_AES_128_CBC_SHA; + _ciphers["DH_RSA_WITH_AES_128_CBC_SHA"] = TLS_DH_RSA_WITH_AES_128_CBC_SHA; + _ciphers["DHE_DSS_WITH_AES_128_CBC_SHA"] = TLS_DHE_DSS_WITH_AES_128_CBC_SHA; + _ciphers["DHE_RSA_WITH_AES_128_CBC_SHA"] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; + _ciphers["DH_anon_WITH_AES_128_CBC_SHA"] = TLS_DH_anon_WITH_AES_128_CBC_SHA; + _ciphers["RSA_WITH_AES_256_CBC_SHA"] = TLS_RSA_WITH_AES_256_CBC_SHA; + _ciphers["DH_DSS_WITH_AES_256_CBC_SHA"] = TLS_DH_DSS_WITH_AES_256_CBC_SHA; + _ciphers["DH_RSA_WITH_AES_256_CBC_SHA"] = TLS_DH_RSA_WITH_AES_256_CBC_SHA; + _ciphers["DHE_DSS_WITH_AES_256_CBC_SHA"] = TLS_DHE_DSS_WITH_AES_256_CBC_SHA; + _ciphers["DHE_RSA_WITH_AES_256_CBC_SHA"] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA; + _ciphers["DH_anon_WITH_AES_256_CBC_SHA"] = TLS_DH_anon_WITH_AES_256_CBC_SHA; + + // + // ECDSA addenda, RFC 4492 + // + _ciphers["ECDH_ECDSA_WITH_NULL_SHA"] = TLS_ECDH_ECDSA_WITH_NULL_SHA; + _ciphers["ECDH_ECDSA_WITH_RC4_128_SHA"] = TLS_ECDH_ECDSA_WITH_RC4_128_SHA; + _ciphers["ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"] = TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA; + _ciphers["ECDH_ECDSA_WITH_AES_128_CBC_SHA"] = TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA; + _ciphers["ECDH_ECDSA_WITH_AES_256_CBC_SHA"] = TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA; + _ciphers["ECDHE_ECDSA_WITH_NULL_SHA"] = TLS_ECDHE_ECDSA_WITH_NULL_SHA; + _ciphers["ECDHE_ECDSA_WITH_RC4_128_SHA"] = TLS_ECDHE_ECDSA_WITH_RC4_128_SHA; + _ciphers["ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"] = TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA; + _ciphers["ECDHE_ECDSA_WITH_AES_128_CBC_SHA"] = TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA; + _ciphers["ECDHE_ECDSA_WITH_AES_256_CBC_SHA"] = TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; + _ciphers["ECDH_RSA_WITH_NULL_SHA"] = TLS_ECDH_RSA_WITH_NULL_SHA; + _ciphers["ECDH_RSA_WITH_RC4_128_SHA"] = TLS_ECDH_RSA_WITH_RC4_128_SHA; + _ciphers["ECDH_RSA_WITH_3DES_EDE_CBC_SHA"] = TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA; + _ciphers["ECDH_RSA_WITH_AES_128_CBC_SHA"] = TLS_ECDH_RSA_WITH_AES_128_CBC_SHA; + _ciphers["ECDH_RSA_WITH_AES_256_CBC_SHA"] = TLS_ECDH_RSA_WITH_AES_256_CBC_SHA; + _ciphers["ECDHE_RSA_WITH_NULL_SHA"] = TLS_ECDHE_RSA_WITH_NULL_SHA; + _ciphers["ECDHE_RSA_WITH_RC4_128_SHA"] = TLS_ECDHE_RSA_WITH_RC4_128_SHA; + _ciphers["ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"] = TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA; + _ciphers["ECDHE_RSA_WITH_AES_128_CBC_SHA"] = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA; + _ciphers["ECDHE_RSA_WITH_AES_256_CBC_SHA"] = TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; + _ciphers["ECDH_anon_WITH_NULL_SHA"] = TLS_ECDH_anon_WITH_NULL_SHA; + _ciphers["ECDH_anon_WITH_RC4_128_SHA"] = TLS_ECDH_anon_WITH_RC4_128_SHA; + _ciphers["ECDH_anon_WITH_3DES_EDE_CBC_SHA"] = TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA; + _ciphers["ECDH_anon_WITH_AES_128_CBC_SHA"] = TLS_ECDH_anon_WITH_AES_128_CBC_SHA; + _ciphers["ECDH_anon_WITH_AES_256_CBC_SHA"] = TLS_ECDH_anon_WITH_AES_256_CBC_SHA; + + // + // TLS 1.2 addenda, RFC 5246 + // + //_ciphers["NULL_WITH_NULL_NULL"] = TLS_NULL_WITH_NULL_NULL; + + // + // Server provided RSA certificate for key exchange. + // + //_ciphers["RSA_WITH_NULL_MD5"] = TLS_RSA_WITH_NULL_MD5; + //_ciphers["RSA_WITH_NULL_SHA"] = TLS_RSA_WITH_NULL_SHA; + //_ciphers["RSA_WITH_RC4_128_MD5"] = TLS_RSA_WITH_RC4_128_MD5; + //_ciphers["RSA_WITH_RC4_128_SHA"] = TLS_RSA_WITH_RC4_128_SHA; + //_ciphers["RSA_WITH_3DES_EDE_CBC_SHA"] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; + _ciphers["RSA_WITH_NULL_SHA256"] = TLS_RSA_WITH_NULL_SHA256; + _ciphers["RSA_WITH_AES_128_CBC_SHA256"] = TLS_RSA_WITH_AES_128_CBC_SHA256; + _ciphers["RSA_WITH_AES_256_CBC_SHA256"] = TLS_RSA_WITH_AES_256_CBC_SHA256; + + // + // Server-authenticated (and optionally client-authenticated) Diffie-Hellman. + // + //_ciphers["DH_DSS_WITH_3DES_EDE_CBC_SHA"] = TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA; + //_ciphers["DH_RSA_WITH_3DES_EDE_CBC_SHA"] = TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA; + //_ciphers["DHE_DSS_WITH_3DES_EDE_CBC_SHA"] = TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA; + //_ciphers["DHE_RSA_WITH_3DES_EDE_CBC_SHA"] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; + _ciphers["DH_DSS_WITH_AES_128_CBC_SHA256"] = TLS_DH_DSS_WITH_AES_128_CBC_SHA256; + _ciphers["DH_RSA_WITH_AES_128_CBC_SHA256"] = TLS_DH_RSA_WITH_AES_128_CBC_SHA256; + _ciphers["DHE_DSS_WITH_AES_128_CBC_SHA256"] = TLS_DHE_DSS_WITH_AES_128_CBC_SHA256; + _ciphers["DHE_RSA_WITH_AES_128_CBC_SHA256"] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; + _ciphers["DH_DSS_WITH_AES_256_CBC_SHA256"] = TLS_DH_DSS_WITH_AES_256_CBC_SHA256; + _ciphers["DH_RSA_WITH_AES_256_CBC_SHA256"] = TLS_DH_RSA_WITH_AES_256_CBC_SHA256; + _ciphers["DHE_DSS_WITH_AES_256_CBC_SHA256"] = TLS_DHE_DSS_WITH_AES_256_CBC_SHA256; + _ciphers["DHE_RSA_WITH_AES_256_CBC_SHA256"] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; + + // + // Completely anonymous Diffie-Hellman + // + //_ciphers["DH_anon_WITH_RC4_128_MD5"] = TLS_DH_anon_WITH_RC4_128_MD5; + //_ciphers["DH_anon_WITH_3DES_EDE_CBC_SHA"] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; + _ciphers["DH_anon_WITH_AES_128_CBC_SHA256"] = TLS_DH_anon_WITH_AES_128_CBC_SHA256; + _ciphers["DH_anon_WITH_AES_256_CBC_SHA256"] = TLS_DH_anon_WITH_AES_256_CBC_SHA256; + + // + // Addendum from RFC 4279, TLS PSK + // + _ciphers["PSK_WITH_RC4_128_SHA"] = TLS_PSK_WITH_RC4_128_SHA; + _ciphers["PSK_WITH_3DES_EDE_CBC_SHA"] = TLS_PSK_WITH_3DES_EDE_CBC_SHA; + _ciphers["PSK_WITH_AES_128_CBC_SHA"] = TLS_PSK_WITH_AES_128_CBC_SHA; + _ciphers["PSK_WITH_AES_256_CBC_SHA"] = TLS_PSK_WITH_AES_256_CBC_SHA; + _ciphers["DHE_PSK_WITH_RC4_128_SHA"] = TLS_DHE_PSK_WITH_RC4_128_SHA; + _ciphers["DHE_PSK_WITH_3DES_EDE_CBC_SHA"] = TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA; + _ciphers["DHE_PSK_WITH_AES_128_CBC_SHA"] = TLS_DHE_PSK_WITH_AES_128_CBC_SHA; + _ciphers["DHE_PSK_WITH_AES_256_CBC_SHA"] = TLS_DHE_PSK_WITH_AES_256_CBC_SHA; + _ciphers["RSA_PSK_WITH_RC4_128_SHA"] = TLS_RSA_PSK_WITH_RC4_128_SHA; + _ciphers["RSA_PSK_WITH_3DES_EDE_CBC_SHA"] = TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA; + _ciphers["RSA_PSK_WITH_AES_128_CBC_SHA"] = TLS_RSA_PSK_WITH_AES_128_CBC_SHA; + _ciphers["RSA_PSK_WITH_AES_256_CBC_SHA"] = TLS_RSA_PSK_WITH_AES_256_CBC_SHA; + + // + // RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption + // + _ciphers["PSK_WITH_NULL_SHA"] = TLS_PSK_WITH_NULL_SHA; + _ciphers["DHE_PSK_WITH_NULL_SHA"] = TLS_DHE_PSK_WITH_NULL_SHA; + _ciphers["RSA_PSK_WITH_NULL_SHA"] = TLS_RSA_PSK_WITH_NULL_SHA; + + // + // Addenda from rfc 5288 AES Galois Counter Mode (GCM) Cipher Suites for TLS. + // + _ciphers["RSA_WITH_AES_128_GCM_SHA256"] = TLS_RSA_WITH_AES_128_GCM_SHA256; + _ciphers["RSA_WITH_AES_256_GCM_SHA384"] = TLS_RSA_WITH_AES_256_GCM_SHA384; + _ciphers["DHE_RSA_WITH_AES_128_GCM_SHA256"] = TLS_DHE_RSA_WITH_AES_128_GCM_SHA256; + _ciphers["DHE_RSA_WITH_AES_256_GCM_SHA384"] = TLS_DHE_RSA_WITH_AES_256_GCM_SHA384; + _ciphers["DH_RSA_WITH_AES_128_GCM_SHA256"] = TLS_DH_RSA_WITH_AES_128_GCM_SHA256; + _ciphers["DH_RSA_WITH_AES_256_GCM_SHA384"] = TLS_DH_RSA_WITH_AES_256_GCM_SHA384; + _ciphers["DHE_DSS_WITH_AES_128_GCM_SHA256"] = TLS_DHE_DSS_WITH_AES_128_GCM_SHA256; + _ciphers["DHE_DSS_WITH_AES_256_GCM_SHA384"] = TLS_DHE_DSS_WITH_AES_256_GCM_SHA384; + _ciphers["DH_DSS_WITH_AES_128_GCM_SHA256"] = TLS_DH_DSS_WITH_AES_128_GCM_SHA256; + _ciphers["DH_DSS_WITH_AES_256_GCM_SHA384"] = TLS_DH_DSS_WITH_AES_256_GCM_SHA384; + _ciphers["DH_anon_WITH_AES_128_GCM_SHA256"] = TLS_DH_anon_WITH_AES_128_GCM_SHA256; + _ciphers["DH_anon_WITH_AES_256_GCM_SHA384"] = TLS_DH_anon_WITH_AES_256_GCM_SHA384; + + // + // RFC 5487 - PSK with SHA-256/384 and AES GCM + // + _ciphers["PSK_WITH_AES_128_GCM_SHA256"] = TLS_PSK_WITH_AES_128_GCM_SHA256; + _ciphers["PSK_WITH_AES_256_GCM_SHA384"] = TLS_PSK_WITH_AES_256_GCM_SHA384; + _ciphers["DHE_PSK_WITH_AES_128_GCM_SHA256"] = TLS_DHE_PSK_WITH_AES_128_GCM_SHA256; + _ciphers["DHE_PSK_WITH_AES_256_GCM_SHA384"] = TLS_DHE_PSK_WITH_AES_256_GCM_SHA384; + _ciphers["RSA_PSK_WITH_AES_128_GCM_SHA256"] = TLS_RSA_PSK_WITH_AES_128_GCM_SHA256; + _ciphers["RSA_PSK_WITH_AES_256_GCM_SHA384"] = TLS_RSA_PSK_WITH_AES_256_GCM_SHA384; + + _ciphers["PSK_WITH_AES_128_CBC_SHA256"] = TLS_PSK_WITH_AES_128_CBC_SHA256; + _ciphers["PSK_WITH_AES_256_CBC_SHA384"] = TLS_PSK_WITH_AES_256_CBC_SHA384; + _ciphers["PSK_WITH_NULL_SHA256"] = TLS_PSK_WITH_NULL_SHA256; + _ciphers["PSK_WITH_NULL_SHA384"] = TLS_PSK_WITH_NULL_SHA384; + + _ciphers["DHE_PSK_WITH_AES_128_CBC_SHA256"] = TLS_DHE_PSK_WITH_AES_128_CBC_SHA256; + _ciphers["DHE_PSK_WITH_AES_256_CBC_SHA384"] = TLS_DHE_PSK_WITH_AES_256_CBC_SHA384; + _ciphers["DHE_PSK_WITH_NULL_SHA256"] = TLS_DHE_PSK_WITH_NULL_SHA256; + _ciphers["DHE_PSK_WITH_NULL_SHA384"] = TLS_DHE_PSK_WITH_NULL_SHA384; + + _ciphers["RSA_PSK_WITH_AES_128_CBC_SHA256"] = TLS_RSA_PSK_WITH_AES_128_CBC_SHA256; + _ciphers["RSA_PSK_WITH_AES_256_CBC_SHA384"] = TLS_RSA_PSK_WITH_AES_256_CBC_SHA384; + _ciphers["RSA_PSK_WITH_NULL_SHA256"] = TLS_RSA_PSK_WITH_NULL_SHA256; + _ciphers["RSA_PSK_WITH_NULL_SHA384"] = TLS_RSA_PSK_WITH_NULL_SHA384; + + // + // Addenda from rfc 5289 Elliptic Curve Cipher Suites with HMAC SHA-256/384. + // + _ciphers["ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"] = TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; + _ciphers["ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"] = TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; + _ciphers["ECDH_ECDSA_WITH_AES_128_CBC_SHA256"] = TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256; + _ciphers["ECDH_ECDSA_WITH_AES_256_CBC_SHA384"] = TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384; + _ciphers["ECDHE_RSA_WITH_AES_128_CBC_SHA256"] = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256; + _ciphers["ECDHE_RSA_WITH_AES_256_CBC_SHA384"] = TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; + _ciphers["ECDH_RSA_WITH_AES_128_CBC_SHA256"] = TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256; + _ciphers["ECDH_RSA_WITH_AES_256_CBC_SHA384"] = TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384; + + // + // Addenda from rfc 5289 Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode (GCM) + // + _ciphers["ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"] = TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; + _ciphers["ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"] = TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; + _ciphers["ECDH_ECDSA_WITH_AES_128_GCM_SHA256"] = TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256; + _ciphers["ECDH_ECDSA_WITH_AES_256_GCM_SHA384"] = TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384; + _ciphers["ECDHE_RSA_WITH_AES_128_GCM_SHA256"] = TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; + _ciphers["ECDHE_RSA_WITH_AES_256_GCM_SHA384"] = TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; + _ciphers["ECDH_RSA_WITH_AES_128_GCM_SHA256"] = TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256; + _ciphers["ECDH_RSA_WITH_AES_256_GCM_SHA384"] = TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384; + + // + // RFC 5746 - Secure Renegotiation + // + _ciphers["EMPTY_RENEGOTIATION_INFO_SCSV"] = TLS_EMPTY_RENEGOTIATION_INFO_SCSV; + + // + // Tags for SSL 2 cipher kinds that are not specified for SSL 3. + // + _ciphers["RSA_WITH_RC2_CBC_MD5"] = SSL_RSA_WITH_RC2_CBC_MD5; + _ciphers["RSA_WITH_IDEA_CBC_MD5"] = SSL_RSA_WITH_IDEA_CBC_MD5; + _ciphers["RSA_WITH_DES_CBC_MD5"] = SSL_RSA_WITH_DES_CBC_MD5; + _ciphers["RSA_WITH_3DES_EDE_CBC_MD5"] = SSL_RSA_WITH_3DES_EDE_CBC_MD5; + _ciphers["NO_SUCH_CIPHERSUITE"] = SSL_NO_SUCH_CIPHERSUITE; +} + +SSLCipherSuite +CiphersHelper::cipherForName(const string& name) +{ + map<string, SSLCipherSuite>::const_iterator i = _ciphers.find(name); + if(i == _ciphers.end() || i->second == SSL_NO_SUCH_CIPHERSUITE) + { + PluginInitializationException ex(__FILE__, __LINE__, "IceSSL: no such cipher " + name); + throw ex; + } + return i->second; +} + +// +// Retrive the name of a cipher, SSLCipherSuite inlude duplicated values for TLS/SSL +// protocol ciphers, for example SSL_RSA_WITH_RC4_128_MD5/TLS_RSA_WITH_RC4_128_MD5 +// are represeted by the same SSLCipherSuite value, the names return by this method +// doesn't include a protocol prefix. +// +string +CiphersHelper::cipherName(SSLCipherSuite cipher) +{ + switch(cipher) + { + case SSL_NULL_WITH_NULL_NULL: + return "NULL_WITH_NULL_NULL"; + case SSL_RSA_WITH_NULL_MD5: + return "RSA_WITH_NULL_MD5"; + case SSL_RSA_WITH_NULL_SHA: + return "RSA_WITH_NULL_SHA"; + case SSL_RSA_EXPORT_WITH_RC4_40_MD5: + return "RSA_EXPORT_WITH_RC4_40_MD5"; + case SSL_RSA_WITH_RC4_128_MD5: + return "RSA_WITH_RC4_128_MD5"; + case SSL_RSA_WITH_RC4_128_SHA: + return "RSA_WITH_RC4_128_SHA"; + case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5: + return "RSA_EXPORT_WITH_RC2_CBC_40_MD5"; + case SSL_RSA_WITH_IDEA_CBC_SHA: + return "RSA_WITH_IDEA_CBC_SHA"; + case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: + return "RSA_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_RSA_WITH_DES_CBC_SHA: + return "RSA_WITH_DES_CBC_SHA"; + case SSL_RSA_WITH_3DES_EDE_CBC_SHA: + return "RSA_WITH_3DES_EDE_CBC_SHA"; + case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: + return "DH_DSS_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_DH_DSS_WITH_DES_CBC_SHA: + return "DH_DSS_WITH_DES_CBC_SHA"; + case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA: + return "DH_DSS_WITH_3DES_EDE_CBC_SHA"; + case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: + return "DH_RSA_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_DH_RSA_WITH_DES_CBC_SHA: + return "DH_RSA_WITH_DES_CBC_SHA"; + case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA: + return "DH_RSA_WITH_3DES_EDE_CBC_SHA"; + case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: + return "DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_DHE_DSS_WITH_DES_CBC_SHA: + return "DHE_DSS_WITH_DES_CBC_SHA"; + case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + return "DHE_DSS_WITH_3DES_EDE_CBC_SHA"; + case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: + return "DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_DHE_RSA_WITH_DES_CBC_SHA: + return "DHE_RSA_WITH_DES_CBC_SHA"; + case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + return "DHE_RSA_WITH_3DES_EDE_CBC_SHA"; + case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5: + return "DH_anon_EXPORT_WITH_RC4_40_MD5"; + case SSL_DH_anon_WITH_RC4_128_MD5: + return "DH_anon_WITH_RC4_128_MD5"; + case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA: + return "DH_anon_EXPORT_WITH_DES40_CBC_SHA"; + case SSL_DH_anon_WITH_DES_CBC_SHA: + return "DH_anon_WITH_DES_CBC_SHA"; + case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: + return "DH_anon_WITH_3DES_EDE_CBC_SHA"; + case SSL_FORTEZZA_DMS_WITH_NULL_SHA: + return "FORTEZZA_DMS_WITH_NULL_SHA"; + case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA: + return "FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA"; + + // + // TLS addenda using AES, per RFC 3268 + // + case TLS_RSA_WITH_AES_128_CBC_SHA: + return "RSA_WITH_AES_128_CBC_SHA"; + case TLS_DH_DSS_WITH_AES_128_CBC_SHA: + return "DH_DSS_WITH_AES_128_CBC_SHA"; + case TLS_DH_RSA_WITH_AES_128_CBC_SHA: + return "DH_RSA_WITH_AES_128_CBC_SHA"; + case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: + return "DHE_DSS_WITH_AES_128_CBC_SHA"; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + return "DHE_RSA_WITH_AES_128_CBC_SHA"; + case TLS_DH_anon_WITH_AES_128_CBC_SHA: + return "DH_anon_WITH_AES_128_CBC_SHA"; + case TLS_RSA_WITH_AES_256_CBC_SHA: + return "RSA_WITH_AES_256_CBC_SHA"; + case TLS_DH_DSS_WITH_AES_256_CBC_SHA: + return "DH_DSS_WITH_AES_256_CBC_SHA"; + case TLS_DH_RSA_WITH_AES_256_CBC_SHA: + return "DH_RSA_WITH_AES_256_CBC_SHA"; + case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: + return "DHE_DSS_WITH_AES_256_CBC_SHA"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + return "DHE_RSA_WITH_AES_256_CBC_SHA"; + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + return "DH_anon_WITH_AES_256_CBC_SHA"; + + // + // ECDSA addenda, RFC 4492 + // + case TLS_ECDH_ECDSA_WITH_NULL_SHA: + return "ECDH_ECDSA_WITH_NULL_SHA"; + case TLS_ECDH_ECDSA_WITH_RC4_128_SHA: + return "ECDH_ECDSA_WITH_RC4_128_SHA"; + case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: + return "ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"; + case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: + return "ECDH_ECDSA_WITH_AES_128_CBC_SHA"; + case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: + return "ECDH_ECDSA_WITH_AES_256_CBC_SHA"; + case TLS_ECDHE_ECDSA_WITH_NULL_SHA: + return "ECDHE_ECDSA_WITH_NULL_SHA"; + case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: + return "ECDHE_ECDSA_WITH_RC4_128_SHA"; + case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: + return "ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"; + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: + return "ECDHE_ECDSA_WITH_AES_128_CBC_SHA"; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + return "ECDHE_ECDSA_WITH_AES_256_CBC_SHA"; + case TLS_ECDH_RSA_WITH_NULL_SHA: + return "ECDH_RSA_WITH_NULL_SHA"; + case TLS_ECDH_RSA_WITH_RC4_128_SHA: + return "ECDH_RSA_WITH_RC4_128_SHA"; + case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: + return "ECDH_RSA_WITH_3DES_EDE_CBC_SHA"; + case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: + return "ECDH_RSA_WITH_AES_128_CBC_SHA"; + case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: + return "ECDH_RSA_WITH_AES_256_CBC_SHA"; + case TLS_ECDHE_RSA_WITH_NULL_SHA: + return "ECDHE_RSA_WITH_NULL_SHA"; + case TLS_ECDHE_RSA_WITH_RC4_128_SHA: + return "ECDHE_RSA_WITH_RC4_128_SHA"; + case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: + return "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: + return "ECDHE_RSA_WITH_AES_128_CBC_SHA"; + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: + return "ECDHE_RSA_WITH_AES_256_CBC_SHA"; + case TLS_ECDH_anon_WITH_NULL_SHA: + return "ECDH_anon_WITH_NULL_SHA"; + case TLS_ECDH_anon_WITH_RC4_128_SHA: + return "ECDH_anon_WITH_RC4_128_SHA"; + case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA: + return "ECDH_anon_WITH_3DES_EDE_CBC_SHA"; + case TLS_ECDH_anon_WITH_AES_128_CBC_SHA: + return "ECDH_anon_WITH_AES_128_CBC_SHA"; + case TLS_ECDH_anon_WITH_AES_256_CBC_SHA: + return "ECDH_anon_WITH_AES_256_CBC_SHA"; + + // + // TLS 1.2 addenda, RFC 5246 + // + //case TLS_NULL_WITH_NULL_NULL: + // return "NULL_WITH_NULL_NULL"; + + // + // Server provided RSA certificate for key exchange. + // + //case TLS_RSA_WITH_NULL_MD5: + // return "RSA_WITH_NULL_MD5"; + //case TLS_RSA_WITH_NULL_SHA: + // return "RSA_WITH_NULL_SHA"; + //case TLS_RSA_WITH_RC4_128_MD5: + // return "RSA_WITH_RC4_128_MD5"; + //case TLS_RSA_WITH_RC4_128_SHA: + // return "RSA_WITH_RC4_128_SHA"; + //case TLS_RSA_WITH_3DES_EDE_CBC_SHA: + // return "RSA_WITH_3DES_EDE_CBC_SHA"; + case TLS_RSA_WITH_NULL_SHA256: + return "RSA_WITH_NULL_SHA256"; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + return "RSA_WITH_AES_128_CBC_SHA256"; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + return "RSA_WITH_AES_256_CBC_SHA256"; + + // + // Server-authenticated (and optionally client-authenticated) Diffie-Hellman. + // + //case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: + // return "DH_DSS_WITH_3DES_EDE_CBC_SHA"; + //case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA: + // return "DH_RSA_WITH_3DES_EDE_CBC_SHA"; + //case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: + // return "DHE_DSS_WITH_3DES_EDE_CBC_SHA"; + //case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + // return "DHE_RSA_WITH_3DES_EDE_CBC_SHA"; + case TLS_DH_DSS_WITH_AES_128_CBC_SHA256: + return "DH_DSS_WITH_AES_128_CBC_SHA256"; + case TLS_DH_RSA_WITH_AES_128_CBC_SHA256: + return "DH_RSA_WITH_AES_128_CBC_SHA256"; + case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256: + return "DHE_DSS_WITH_AES_128_CBC_SHA256"; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + return "DHE_RSA_WITH_AES_128_CBC_SHA256"; + case TLS_DH_DSS_WITH_AES_256_CBC_SHA256: + return "DH_DSS_WITH_AES_256_CBC_SHA256"; + case TLS_DH_RSA_WITH_AES_256_CBC_SHA256: + return "DH_RSA_WITH_AES_256_CBC_SHA256"; + case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256: + return "DHE_DSS_WITH_AES_256_CBC_SHA256"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + return "DHE_RSA_WITH_AES_256_CBC_SHA256"; + + // + // Completely anonymous Diffie-Hellman + // + //case TLS_DH_anon_WITH_RC4_128_MD5: + // return "DH_anon_WITH_RC4_128_MD5"; + //case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA: + // return "DH_anon_WITH_3DES_EDE_CBC_SHA"; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + return "DH_anon_WITH_AES_128_CBC_SHA256"; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: + return "DH_anon_WITH_AES_256_CBC_SHA256"; + + // + // Addendum from RFC 4279, TLS PSK + // + case TLS_PSK_WITH_RC4_128_SHA: + return "PSK_WITH_RC4_128_SHA"; + case TLS_PSK_WITH_3DES_EDE_CBC_SHA: + return "PSK_WITH_3DES_EDE_CBC_SHA"; + case TLS_PSK_WITH_AES_128_CBC_SHA: + return "PSK_WITH_AES_128_CBC_SHA"; + case TLS_PSK_WITH_AES_256_CBC_SHA: + return "PSK_WITH_AES_256_CBC_SHA"; + case TLS_DHE_PSK_WITH_RC4_128_SHA: + return "DHE_PSK_WITH_RC4_128_SHA"; + case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA: + return "DHE_PSK_WITH_3DES_EDE_CBC_SHA"; + case TLS_DHE_PSK_WITH_AES_128_CBC_SHA: + return "DHE_PSK_WITH_AES_128_CBC_SHA"; + case TLS_DHE_PSK_WITH_AES_256_CBC_SHA: + return "DHE_PSK_WITH_AES_256_CBC_SHA"; + case TLS_RSA_PSK_WITH_RC4_128_SHA: + return "RSA_PSK_WITH_RC4_128_SHA"; + case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA: + return "RSA_PSK_WITH_3DES_EDE_CBC_SHA"; + case TLS_RSA_PSK_WITH_AES_128_CBC_SHA: + return "RSA_PSK_WITH_AES_128_CBC_SHA"; + case TLS_RSA_PSK_WITH_AES_256_CBC_SHA: + return "RSA_PSK_WITH_AES_256_CBC_SHA"; + + // + // RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption + // + case TLS_PSK_WITH_NULL_SHA: + return "PSK_WITH_NULL_SHA"; + case TLS_DHE_PSK_WITH_NULL_SHA: + return "DHE_PSK_WITH_NULL_SHA"; + case TLS_RSA_PSK_WITH_NULL_SHA: + return "RSA_PSK_WITH_NULL_SHA"; + + // + // Addenda from rfc 5288 AES Galois Counter Mode (GCM) Cipher Suites for TLS. + // + case TLS_RSA_WITH_AES_128_GCM_SHA256: + return "RSA_WITH_AES_128_GCM_SHA256"; + case TLS_RSA_WITH_AES_256_GCM_SHA384: + return "RSA_WITH_AES_256_GCM_SHA384"; + case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: + return "DHE_RSA_WITH_AES_128_GCM_SHA256"; + case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: + return "DHE_RSA_WITH_AES_256_GCM_SHA384"; + case TLS_DH_RSA_WITH_AES_128_GCM_SHA256: + return "DH_RSA_WITH_AES_128_GCM_SHA256"; + case TLS_DH_RSA_WITH_AES_256_GCM_SHA384: + return "DH_RSA_WITH_AES_256_GCM_SHA384"; + case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256: + return "DHE_DSS_WITH_AES_128_GCM_SHA256"; + case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384: + return "DHE_DSS_WITH_AES_256_GCM_SHA384"; + case TLS_DH_DSS_WITH_AES_128_GCM_SHA256: + return "DH_DSS_WITH_AES_128_GCM_SHA256"; + case TLS_DH_DSS_WITH_AES_256_GCM_SHA384: + return "DH_DSS_WITH_AES_256_GCM_SHA384"; + case TLS_DH_anon_WITH_AES_128_GCM_SHA256: + return "DH_anon_WITH_AES_128_GCM_SHA256"; + case TLS_DH_anon_WITH_AES_256_GCM_SHA384: + return "DH_anon_WITH_AES_256_GCM_SHA384"; + + // + // RFC 5487 - PSK with SHA-256/384 and AES GCM + // + case TLS_PSK_WITH_AES_128_GCM_SHA256: + return "PSK_WITH_AES_128_GCM_SHA256"; + case TLS_PSK_WITH_AES_256_GCM_SHA384: + return "PSK_WITH_AES_256_GCM_SHA384"; + case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256: + return "DHE_PSK_WITH_AES_128_GCM_SHA256"; + case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384: + return "DHE_PSK_WITH_AES_256_GCM_SHA384"; + case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256: + return "RSA_PSK_WITH_AES_128_GCM_SHA256"; + case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384: + return "RSA_PSK_WITH_AES_256_GCM_SHA384"; + + case TLS_PSK_WITH_AES_128_CBC_SHA256: + return "PSK_WITH_AES_128_CBC_SHA256"; + case TLS_PSK_WITH_AES_256_CBC_SHA384: + return "PSK_WITH_AES_256_CBC_SHA384"; + case TLS_PSK_WITH_NULL_SHA256: + return "WITH_NULL_SHA256"; + case TLS_PSK_WITH_NULL_SHA384: + return "PSK_WITH_NULL_SHA384"; + + case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256: + return "DHE_PSK_WITH_AES_128_CBC_SHA256"; + case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384: + return "DHE_PSK_WITH_AES_256_CBC_SHA384"; + case TLS_DHE_PSK_WITH_NULL_SHA256: + return "DHE_PSK_WITH_NULL_SHA256"; + case TLS_DHE_PSK_WITH_NULL_SHA384: + return "DHE_PSK_WITH_NULL_SHA384"; + + case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256: + return "RSA_PSK_WITH_AES_128_CBC_SHA256"; + case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384: + return "RSA_PSK_WITH_AES_256_CBC_SHA384"; + case TLS_RSA_PSK_WITH_NULL_SHA256: + return "RSA_PSK_WITH_NULL_SHA256"; + case TLS_RSA_PSK_WITH_NULL_SHA384: + return "RSA_PSK_WITH_NULL_SHA384"; + + // + // Addenda from rfc 5289 Elliptic Curve Cipher Suites with HMAC SHA-256/384. + // + case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: + return "ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"; + case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: + return "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"; + case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256: + return "ECDH_ECDSA_WITH_AES_128_CBC_SHA256"; + case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384: + return "ECDH_ECDSA_WITH_AES_256_CBC_SHA384"; + case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + return "ECDHE_RSA_WITH_AES_128_CBC_SHA256"; + case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384: + return "ECDHE_RSA_WITH_AES_256_CBC_SHA384"; + case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256: + return "ECDH_RSA_WITH_AES_128_CBC_SHA256"; + case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384: + return "ECDH_RSA_WITH_AES_256_CBC_SHA384"; + + // + // Addenda from rfc 5289 Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois Counter Mode (GCM) + // + case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + return "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"; + case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: + return "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"; + case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256: + return "ECDH_ECDSA_WITH_AES_128_GCM_SHA256"; + case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384: + return "ECDH_ECDSA_WITH_AES_256_GCM_SHA384"; + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: + return "ECDHE_RSA_WITH_AES_128_GCM_SHA256"; + case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: + return "ECDHE_RSA_WITH_AES_256_GCM_SHA384"; + case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256: + return "ECDH_RSA_WITH_AES_128_GCM_SHA256"; + case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384: + return "ECDH_RSA_WITH_AES_256_GCM_SHA384"; + + // + // RFC 5746 - Secure Renegotiation + // + case TLS_EMPTY_RENEGOTIATION_INFO_SCSV: + return "EMPTY_RENEGOTIATION_INFO_SCSV"; + + // + // Tags for SSL 2 cipher kinds that are not specified for SSL 3. + // + case SSL_RSA_WITH_RC2_CBC_MD5: + return "RSA_WITH_RC2_CBC_MD5"; + case SSL_RSA_WITH_IDEA_CBC_MD5: + return "RSA_WITH_IDEA_CBC_MD5"; + case SSL_RSA_WITH_DES_CBC_MD5: + return "RSA_WITH_DES_CBC_MD5"; + case SSL_RSA_WITH_3DES_EDE_CBC_MD5: + return "RSA_WITH_3DES_EDE_CBC_MD5"; + default: + return ""; + } +} + +SSLProtocol +parseProtocol(const std::string& prot) +{ + if(prot == "ssl2" || prot == "sslv2") + { + return kSSLProtocol2; + } + else if(prot == "ssl3" || prot == "sslv3") + { + return kSSLProtocol3; + } + else if(prot == "tls" || prot == "tls1" || prot == "tlsv1" || prot == "tls1_0" || prot == "tlsv1_0") + { + return kTLSProtocol1; + } + else if(prot == "tls1_1" || prot == "tlsv1_1") + { + return kTLSProtocol11; + } + else if(prot == "tls1_2" || prot == "tlsv1_2") + { + return kTLSProtocol12; + } + else + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: unrecognized protocol `" + prot + "'"; + throw ex; + } +} + +} + +IceUtil::Shared* IceSSL::upCast(IceSSL::SecureTransportEngine* p) { return p; } + +IceSSL::SecureTransportEngine::SecureTransportEngine(const Ice::CommunicatorPtr& communicator) : + SSLEngine(communicator), + _initialized(false), + _ctx(0), + _certificateAuthorities(0), + _cert(0), + _key(0), + _identity(0), + _keychain(0), + _protocolVersionMax(kSSLProtocolUnknown), + _protocolVersionMin(kSSLProtocolUnknown), + _dhParams(0), + _dhParamsLength(0), + _ciphers(new ScopedArray<SSLCipherSuite>()), + _allCiphers(false), + _numCiphers(-1) +{ +} + +bool +IceSSL::SecureTransportEngine::initialized() const +{ + IceUtil::Mutex::Lock lock(_mutex); + return _initialized; +} +// +// Setup the engine. +// +void +IceSSL::SecureTransportEngine::initialize() +{ + IceUtil::Mutex::Lock lock(_mutex); + if(_initialized) + { + return; + } + + SSLEngine::initialize(); + + const string propPrefix = "IceSSL."; + const PropertiesPtr properties = communicator()->getProperties(); + + // + // Check for a default directory. We look in this directory for + // files mentioned in the configuration. + // + string defaultDir = properties->getProperty(propPrefix + "DefaultDir"); + + // + // Open the application KeyChain or create it if the keychain doesn't exists + // + string keychainPath = properties->getProperty("IceSSL.Keychain"); + string keychainPassword = properties->getProperty("IceSSL.KeychainPassword"); + + // + // KeyChain path is relative to the current working directory. + // + if(keychainPath.empty()) + { + keychainPath = "login.keychain"; + } + else + { + if(!IceUtilInternal::isAbsolutePath(keychainPath)) + { + string cwd; + if(IceUtilInternal::getcwd(cwd) == 0) + { + keychainPath = string(cwd) + '/' + keychainPath; + } + } + } + + bool usePassword = !keychainPassword.empty(); + size_t size = keychainPassword.size(); + const char* password = usePassword ? keychainPassword.c_str() : 0; + + OSStatus err = SecKeychainOpen(keychainPath.c_str(), &_keychain); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: unable to open keychain: `" << keychainPath << "'\n" << errorToString(err); + throw PluginInitializationException(__FILE__, __LINE__, os.str()); + } + + SecKeychainStatus status; + err = SecKeychainGetStatus(_keychain, &status); + + if(err == noErr) + { + err = SecKeychainUnlock(_keychain, size, password, usePassword); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: unable to unlock keychain: `" << keychainPath << "'\n" << errorToString(err); + throw PluginInitializationException(__FILE__, __LINE__, os.str()); + } + } + else if(err == errSecNoSuchKeychain) + { + err = SecKeychainCreate(keychainPath.c_str(), size, password, keychainPassword.empty(), 0, &_keychain); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: unable to create keychain: `" << keychainPath << "'\n" << errorToString(err); + throw PluginInitializationException(__FILE__, __LINE__, os.str()); + } + } + else + { + ostringstream os; + os << "IceSSL: unable to open keychain: `" << keychainPath << "'\n" << errorToString(err); + throw PluginInitializationException(__FILE__, __LINE__, os.str()); + } + + int passwordRetryMax = properties->getPropertyAsIntWithDefault(propPrefix + "PasswordRetryMax", 3); + PasswordPromptPtr passwordPrompt = getPasswordPrompt(); + // + // Load the CA certificates used to authenticate peers into + // _certificateAuthorities array. + // + { + try + { + string caFile = properties->getProperty(propPrefix + "CertAuthFile"); + if(!caFile.empty()) + { + if(!checkPath(caFile, defaultDir, false)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: CA certificate file not found:\n" + caFile; + throw ex; + } + _certificateAuthorities = loadCACertificates(caFile); + } + } + catch(const CertificateReadException& ce) + { + PluginInitializationException ex(__FILE__, __LINE__, ce.reason); + throw ex; + } + + string caDir = properties->getPropertyWithDefault(propPrefix + "CertAuthDir", defaultDir); + if(!caDir.empty()) + { + CFMutableArrayRef certificateAuthorities; + if(_certificateAuthorities) + { + certificateAuthorities = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, _certificateAuthorities); + CFRelease(_certificateAuthorities); + } + else + { + certificateAuthorities = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + } + + vector<string> files = dir(caDir); + for(vector<string>::const_iterator i = files.begin(); i != files.end(); ++i) + { + try + { + CFArrayRef tmp = loadCACertificates(caDir + "/" + *i); + + CFArrayAppendArray(certificateAuthorities, tmp, CFRangeMake(0, CFArrayGetCount(tmp))); + CFRelease(tmp); + } + catch(const CertificateReadException&) + { + // + // Some files in CertAuthDir might not be certificates, we just ignore those files. + // + } + } + _certificateAuthorities = certificateAuthorities; + } + } + + // + // Import the application certificate and private keys into the application + // keychain. + // + { + string certFile = properties->getProperty(propPrefix + "CertFile"); + string keyFile = properties->getProperty(propPrefix + "KeyFile"); + vector<string>::size_type numCerts = 0; + + CFDataRef hash = 0; + + if(!certFile.empty()) + { + try + { + vector<string> files; + if(!IceUtilInternal::splitString(certFile, IceUtilInternal::pathsep, files) || files.size() > 2) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: invalid value for " + propPrefix + "CertFile:\n" + certFile; + throw ex; + } + numCerts = files.size(); + for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) + { + string file = *p; + if(!checkPath(file, defaultDir, false)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: certificate file not found:\n" + file; + throw ex; + } + + loadCertificate(&_cert, &hash, keyFile.empty() ? &_key : 0, _keychain, file, + properties->getProperty(propPrefix + "Password"), passwordPrompt, + passwordRetryMax); + break; + } + } + catch(const CertificateReadException& ce) + { + PluginInitializationException ex(__FILE__, __LINE__, ce.reason); + throw ex; + } + } + + if(!keyFile.empty()) + { + try + { + vector<string> files; + if(!IceUtilInternal::splitString(keyFile, IceUtilInternal::pathsep, files) || files.size() > 2) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: invalid value for " + propPrefix + "KeyFile:\n" + keyFile; + throw ex; + } + if(files.size() != numCerts) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: " + propPrefix + "KeyFile does not agree with " + propPrefix + "CertFile"; + throw ex; + } + for(vector<string>::iterator p = files.begin(); p != files.end(); ++p) + { + string file = *p; + if(!checkPath(file, defaultDir, false)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: key file not found:\n" + file; + throw ex; + } + // + // The private key may be stored in an encrypted file, so handle + // password retries. + // + loadPrivateKey(&_key, keyLabel(_cert), hash, _keychain, file, + properties->getProperty(propPrefix + "Password"), + passwordPrompt, passwordRetryMax); + break; + } + } + catch(const CertificateReadException& ce) + { + PluginInitializationException ex(__FILE__, __LINE__, ce.reason); + throw ex; + } + } + + if(_cert) + { + err = SecIdentityCreateWithCertificate(_keychain, _cert, &_identity); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: unable to create the certificate identity:\n" << errorToString(err); + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + } + + if(hash) + { + CFRelease(hash); + } + } + + // + // DiffieHellmanParams in DER format. + // + { + string dhParams = properties->getProperty(propPrefix + "DHParams"); + if(!dhParams.empty()) + { + if(!checkPath(dhParams, defaultDir, false)) + { + PluginInitializationException ex(__FILE__, __LINE__); + ex.reason = "IceSSL: DH params file not found:\n" + dhParams; + throw ex; + } + + ScopedArray<char> buffer; + _dhParamsLength = readFile(dhParams, buffer); + _dhParams.reset(new ScopedArray<char>(buffer)); + } + } + + // + // Establish the cipher list. + // + string ciphers = properties->getProperty(propPrefix + "Ciphers"); + if(!ciphers.empty()) + { + // + // Context used to get the cipher list + // + _ctx = SSLCreateContext(kCFAllocatorDefault, kSSLServerSide, kSSLStreamType); + CiphersHelper::initialize(); + parseCiphers(ciphers); + } + + // + // Parse protocols + // + const string protocolVersionMax = properties->getProperty(propPrefix + "ProtocolVersionMax"); + if(!protocolVersionMax.empty()) + { + _protocolVersionMax = parseProtocol(protocolVersionMax); + } + + const string protocolVersionMin = properties->getProperty(propPrefix + "ProtocolVersionMin"); + if(!protocolVersionMin.empty()) + { + _protocolVersionMin = parseProtocol(protocolVersionMin); + } + _initialized = true; +} + +// +// Destroy the engine. +// +void +IceSSL::SecureTransportEngine::destroy() +{ + if(_certificateAuthorities) + { + CFRelease(_certificateAuthorities); + _certificateAuthorities = 0; + } + + if(_identity) + { + CFRelease(_identity); + _identity = 0; + } + + if(_cert) + { + CFRelease(_cert); + _cert = 0; + } + + if(_key) + { + CFRelease(_key); + _key = 0; + } + + if(_keychain) + { + CFRelease(_keychain); + _keychain = 0; + } + + if(_ctx) + { + CFRelease(_ctx); + _ctx = 0; + } +} + +ContextRef +IceSSL::SecureTransportEngine::newContext(bool incoming) +{ + ContextRef ssl = SSLCreateContext(kCFAllocatorDefault, incoming ? kSSLServerSide : kSSLClientSide, kSSLStreamType); + if(!ssl) + { + PluginInitializationException ex(__FILE__, __LINE__, "IceSSL: unable to create SSL context"); + throw ex; + } + + OSStatus err = noErr; + if(incoming) + { + switch(getVerifyPeer()) + { + case 0: + { + SSLSetClientSideAuthenticate(ssl, kNeverAuthenticate); + break; + } + case 1: + { + SSLSetClientSideAuthenticate(ssl, kTryAuthenticate); + break; + } + case 2: + { + SSLSetClientSideAuthenticate(ssl, kAlwaysAuthenticate); + break; + } + default: + { + assert(false); + break; + } + } + + if(_dhParamsLength > 0) + { + err = SSLSetDiffieHellmanParams(ssl, _dhParams->get(), _dhParamsLength); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: unable to create the trust object:\n" << errorToString(err); + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + } + } + + if(_cert) + { + // + // Retrieve the certificate chain + // + SecPolicyRef policy = SecPolicyCreateSSL(true, 0); + SecTrustRef trust; + err = SecTrustCreateWithCertificates((CFArrayRef)_cert, policy, &trust); + if(err != noErr || !trust) + { + ostringstream os; + os << "IceSSL: unable to create the trust object"; + if(err != noErr) + { + os << '\n' << errorToString(err); + } + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + err = SecTrustSetAnchorCertificates(trust, _certificateAuthorities); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: unable to establish the anchor certificates\n" << errorToString(err); + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + SecTrustResultType trustResult; + err = SecTrustEvaluate(trust, &trustResult); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: unable to evaluate trust:\n" << errorToString(err); + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + int chainLength = SecTrustGetCertificateCount(trust); + CFMutableArrayRef chain = CFArrayCreateMutable(kCFAllocatorDefault, chainLength, &kCFTypeArrayCallBacks); + CFArrayAppendValue(chain, _identity); + for(int i = 1; i < chainLength; ++i) + { + CFArrayAppendValue(chain, SecTrustGetCertificateAtIndex(trust, i)); + } + CFRelease(trust); + + err = SSLSetCertificate(ssl, chain); + + CFRelease(chain); + + if(err != noErr) + { + ostringstream os; + os << "IceSSL: unable to set the SSL context certificate identity:\n" << errorToString(err); + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + } + + + if(_numCiphers != -1) + { + err = SSLSetEnabledCiphers(ssl, _ciphers->get(), _numCiphers); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: failed to set enabled ciphers:\n" << errorToString(err); + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + } + err = SSLSetSessionOption(ssl, incoming ? kSSLSessionOptionBreakOnClientAuth : kSSLSessionOptionBreakOnServerAuth, + true); + + if(err != noErr) + { + ostringstream os; + os << "IceSSL: failed to set SSL option:\n" << errorToString(err); + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + if(_protocolVersionMax != kSSLProtocolUnknown) + { + err = SSLSetProtocolVersionMax(ssl, _protocolVersionMax); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: failed to set SSL protocol version max:\n" << errorToString(err); + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + } + + if(_protocolVersionMin != kSSLProtocolUnknown) + { + err = SSLSetProtocolVersionMin(ssl, _protocolVersionMin); + if(err != noErr) + { + ostringstream os; + os << "IceSSL: failed to set SSL protocol version min:\n" << errorToString(err); + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + } + + return ssl; +} + +CFArrayRef +IceSSL::SecureTransportEngine::getCertificateAuthorities() const +{ + return _certificateAuthorities; +} + +string +IceSSL::SecureTransportEngine::getCipherName(SSLCipherSuite cipher) const +{ + return CiphersHelper::cipherName(cipher); +} + +void +IceSSL::SecureTransportEngine::parseCiphers(const string& ciphers) +{ + vector<string> tokens; + vector<CipherExpression> cipherExpressions; + + IceUtilInternal::splitString(ciphers, " \t", tokens); + for(vector<string>::const_iterator i = tokens.begin(); i != tokens.end(); ++i) + { + string token(*i); + if(token == "ALL") + { + if(i != tokens.begin()) + { + ostringstream os; + os << "IceSSL: `ALL' must be first in cipher list `" << ciphers << "'"; + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + _allCiphers = true; + } + else + { + CipherExpression ce; + if(token.find('!') == 0) + { + ce.negation = true; + if(token.size() > 1) + { + token = token.substr(1); + } + else + { + ostringstream os; + os << "IceSSL: invalid cipher expression `" << token << "'"; + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + } + + if(token.find('(') == 0) + { + if(token.rfind(')') != token.size() - 1) + { + ostringstream os; + os << "IceSSL: invalid cipher expression `" << token << "'"; + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + try + { + ce.re = new RegExp(token.substr(1, token.size() - 2)); + } + catch(const Ice::SyscallException&) + { + ostringstream os; + os << "IceSSL: invalid cipher expression `" << token << "'"; + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + } + else + { + ce.cipher = token; + } + + cipherExpressions.push_back(ce); + } + } + size_t numSupportedCiphers = 0; + SSLGetNumberSupportedCiphers(_ctx, &numSupportedCiphers); + + ScopedArray<SSLCipherSuite> buffer(new SSLCipherSuite[numSupportedCiphers]); + + OSStatus err; + if((err = SSLGetSupportedCiphers(_ctx, buffer.get(), &numSupportedCiphers)) != noErr) + { + ostringstream os; + os << "IceSSL: unable to get supported ciphers list (error = " << err << ")"; + PluginInitializationException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + SSLCipherSuite* supported = buffer.get(); + vector<SSLCipherSuite> allCiphers; + if(_allCiphers) + { + for(int i = 0; i < numSupportedCiphers; ++i) + { + allCiphers.push_back(supported[i]); + } + } + + for(vector<CipherExpression>::const_iterator i = cipherExpressions.begin(); i != cipherExpressions.end(); ++i) + { + CipherExpression ce = *i; + if(ce.negation) + { + for(vector<SSLCipherSuite>::iterator j = allCiphers.begin(); j != allCiphers.end();) + { + SSLCipherSuite cipher = *j; + string name = CiphersHelper::cipherName(cipher); + + if(ce.cipher.empty()) + { + if(ce.re->match(name)) + { + j = allCiphers.erase(j); + continue; + } + } + else + { + if(ce.cipher == name) + { + j = allCiphers.erase(j); + continue; + } + } + j++; + } + } + else + { + if(ce.cipher.empty()) + { + for(int i = 0; i < numSupportedCiphers; ++i) + { + SSLCipherSuite cipher = supported[i]; + string name = CiphersHelper::cipherName(cipher); + if(ce.re->match(name)) + { + vector<SSLCipherSuite>::const_iterator k = find(allCiphers.begin(), allCiphers.end(), cipher); + if(k == allCiphers.end()) + { + allCiphers.push_back(cipher); + } + } + } + } + else + { + SSLCipherSuite cipher = CiphersHelper::cipherForName(ce.cipher); + vector<SSLCipherSuite>::const_iterator k = find(allCiphers.begin(), allCiphers.end(), cipher); + if(k == allCiphers.end()) + { + allCiphers.push_back(cipher); + } + } + } + } + + if(!allCiphers.empty()) + { + _ciphers.reset(new ScopedArray<SSLCipherSuite>(new SSLCipherSuite[allCiphers.size()])); + SSLCipherSuite* enabled = _ciphers->get(); + for(vector<SSLCipherSuite>::const_iterator i = allCiphers.begin(); i != allCiphers.end(); ++i) + { + *(enabled++) = *i; + } + } + _numCiphers = allCiphers.size(); +} + +SecCertificateRef +IceSSL::SecureTransportEngine::getCertificate() const +{ + return _cert; +} + +SecKeychainRef +IceSSL::SecureTransportEngine::getKeychain() const +{ + return _keychain; +} + +#endif diff --git a/cpp/src/IceSSL/SecureTransportTransceiverI.cpp b/cpp/src/IceSSL/SecureTransportTransceiverI.cpp new file mode 100644 index 00000000000..38572c5ba65 --- /dev/null +++ b/cpp/src/IceSSL/SecureTransportTransceiverI.cpp @@ -0,0 +1,1055 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +#include <IceSSL/SecureTransportTransceiverI.h> +#include <IceUtil/FileUtil.h> +#include <IceUtil/Mutex.h> +#include <IceUtil/MutexPtrLock.h> + +#include <IceSSL/ConnectionInfo.h> +#include <IceSSL/Instance.h> +#include <IceSSL/Util.h> +#include <Ice/Communicator.h> +#include <Ice/LoggerUtil.h> +#include <Ice/Buffer.h> +#include <Ice/LocalException.h> + +#ifdef ICE_USE_SECURE_TRANSPORT + +using namespace std; +using namespace Ice; +using namespace IceSSL; + +namespace +{ + +string +trustResultDescription(SecTrustResultType result) +{ + switch(result) + { + case kSecTrustResultInvalid: + { + return "Invalid setting or result."; + } + case kSecTrustResultDeny: + { + return "The user specified that the certificate should not be trusted."; + } + case kSecTrustResultRecoverableTrustFailure: + { + return "Trust denied; retry after changing settings."; + } + case kSecTrustResultFatalTrustFailure: + { + return "Trust denied; no simple fix is available."; + } + case kSecTrustResultOtherError: + { + return "A failure other than that of trust evaluation."; + } + default: + { + assert(false); + return ""; + } + } +} + +string +protocolName(SSLProtocol protocol) +{ + switch(protocol) + { + case kSSLProtocol2: + return "SSL 2.0"; + case kSSLProtocol3: + return "SSL 3.0"; + case kTLSProtocol1: + return "TLS 1.0"; + case kTLSProtocol11: + return "TLS 1.1"; + case kTLSProtocol12: + return "TLS 1.2"; + default: + return "Unknown"; + } +} + +// +// Socket write callback +// +OSStatus +socketWrite(SSLConnectionRef connection, const void* data, size_t* length) +{ + const TransceiverI* transceiver = static_cast<const TransceiverI*>(connection); + assert(transceiver); + return transceiver->writeRaw(reinterpret_cast<const char*>(data), length); +} + +// +// Socket read callback +// +OSStatus +socketRead(SSLConnectionRef connection, void* data, size_t* length) +{ + const TransceiverI* transceiver = static_cast<const TransceiverI*>(connection); + assert(transceiver); + return transceiver->readRaw(reinterpret_cast<char*>(data), length); +} + +} + +IceInternal::NativeInfoPtr +IceSSL::TransceiverI::getNativeInfo() +{ + return this; +} + +IceInternal::SocketOperation +IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::Buffer& writeBuffer, bool&) +{ + try + { + if(_state == StateNeedConnect) + { + _state = StateConnectPending; + return IceInternal::SocketOperationConnect; + } + else if(_state <= StateConnectPending) + { + IceInternal::doFinishConnect(_fd); + _desc = IceInternal::fdToString(_fd, _proxy, _addr, true); + + if(_proxy) + { + // + // Prepare the read & write buffers in advance. + // + _proxy->beginWriteConnectRequest(_addr, writeBuffer); + _proxy->beginReadConnectRequestResponse(readBuffer); + + // + // Write the proxy connection message using TCP. + // + if(writeRaw(writeBuffer)) + { + // + // Write completed without blocking. + // + _proxy->endWriteConnectRequest(writeBuffer); + + // + // Try to read the response using TCP. + // + if(readRaw(readBuffer)) + { + // + // Read completed without blocking - fall through. + // + _proxy->endReadConnectRequestResponse(readBuffer); + } + else + { + // + // Return SocketOperationRead to indicate we need to complete the read. + // + _state = StateProxyConnectRequestPending; // Wait for proxy response + return IceInternal::SocketOperationRead; + } + } + else + { + // + // Return SocketOperationWrite to indicate we need to complete the write. + // + _state = StateProxyConnectRequest; // Send proxy connect request + return IceInternal::SocketOperationWrite; + } + } + + _state = StateConnected; + } + else if(_state == StateProxyConnectRequest) + { + // + // Write completed. + // + _proxy->endWriteConnectRequest(writeBuffer); + _state = StateProxyConnectRequestPending; // Wait for proxy response + return IceInternal::SocketOperationRead; + } + else if(_state == StateProxyConnectRequestPending) + { + // + // Read completed. + // + _proxy->endReadConnectRequestResponse(readBuffer); + _state = StateConnected; + } + + assert(_state == StateConnected); + + OSStatus err = noErr; + + if(!_ssl) + { + // + // Initialize SSL context + // + _ssl = _engine->newContext(_incoming); + if((err = SSLSetIOFuncs(_ssl, socketRead, socketWrite)) != noErr) + { + ostringstream os; + os << "IceSSL: cannot set SSL IO functions\n" << errorToString(err); + throw PluginInitializationException(__FILE__, __LINE__, os.str()); + } + + if((err = SSLSetConnection(_ssl, reinterpret_cast<SSLConnectionRef>(this))) != noErr) + { + ostringstream os; + os << "IceSSL: cannot set SSL connection\n" << errorToString(err); + throw PluginInitializationException(__FILE__, __LINE__, os.str()); + } + } + + SSLSessionState state; + SSLGetSessionState(_ssl, &state); + + // + // SSL Handshake + // + while(state == kSSLHandshake || state == kSSLIdle) + { + err = SSLHandshake(_ssl); + if(err != noErr) + { + switch(err) + { + case errSSLWouldBlock: + { + assert(_flags & SSLWantRead || _flags & SSLWantWrite); + return _flags & SSLWantRead ? IceInternal::SocketOperationRead : IceInternal::SocketOperationWrite; + } + case errSSLPeerAuthCompleted: + { + assert(!_trust); + err = SSLCopyPeerTrust(_ssl, &_trust); + if(err != noErr) + { + break; + } + + SecTrustResultType trustResult = kSecTrustResultOtherError; + + if(_trust) + { + err = SecTrustSetAnchorCertificates(_trust, _engine->getCertificateAuthorities()); + + if(err != noErr) + { + ostringstream os; + os << "SSL handsake failure:\n" << errorToString(err); + throw SecurityException(__FILE__, __LINE__, os.str()); + } + + // + // Disable network fetch, we don't want this to block. + // + err = SecTrustSetNetworkFetchAllowed(_trust, false); + if(err != noErr) + { + ostringstream os; + os << "SSL handsake failure:\n" << errorToString(err); + throw SecurityException(__FILE__, __LINE__, os.str()); + } + + // + // Evaluate the trust + // + err = SecTrustEvaluate(_trust, &trustResult); + if(err != noErr) + { + ostringstream os; + os << "SSL handsake failure:\n" << errorToString(err); + throw SecurityException(__FILE__, __LINE__, os.str());; + } + } + + switch(trustResult) + { + case kSecTrustResultUnspecified: + case kSecTrustResultProceed: + { + // + // Trust verify success. + // + break; + } + case kSecTrustResultInvalid: + //case kSecTrustResultConfirm: // Used in old OS X versions + case kSecTrustResultDeny: + case kSecTrustResultRecoverableTrustFailure: + case kSecTrustResultFatalTrustFailure: + case kSecTrustResultOtherError: + { + if(_engine->getVerifyPeer() == 0) + { + if(_instance->traceLevel() >= 1) + { + ostringstream os; + os << "IceSSL: ignoring certificate verification failure\n" + << trustResultDescription(trustResult); + _instance->logger()->trace(_instance->traceCategory(), os.str()); + } + break; + } + else + { + ostringstream os; + os << "IceSSL: certificate verification failure\n" + << trustResultDescription(trustResult); + string msg = os.str(); + if(_instance->traceLevel() >= 1) + { + _instance->logger()->trace(_instance->traceCategory(), msg); + } + throw ProtocolException(__FILE__, __LINE__, msg); + } + } + } + // + // Call SSLHandshake to resume the handsake. + // + continue; + } + default: + { + break; + } + } + + if(err == errSSLClosedGraceful || err == errSSLClosedAbort) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = 0; + throw ex; + } + else + { + IceInternal::Address remoteAddr; + string desc = "<not available>"; + if(IceInternal::fdToRemoteAddress(_fd, remoteAddr)) + { + desc = IceInternal::addrToString(remoteAddr); + } + ostringstream os; + os << "SSL error occurred for new " << (_incoming ? "incoming" : "outgoing") + << " connection:\nremote address = " << desc << "\n" + << errorToString(err); + ProtocolException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + } + break; + } + _engine->verifyPeer(_fd, _host, getNativeConnectionInfo()); + _state = StateHandshakeComplete; + } + catch(const Ice::LocalException& ex) + { + if(_instance->traceLevel() >= 2) + { + Trace out(_instance->logger(), _instance->traceCategory()); + out << "failed to establish " << _instance->protocol() << " connection\n"; + if(_incoming) + { + out << IceInternal::fdToString(_fd) << "\n" << ex; + } + else + { + out << IceInternal::fdToString(_fd, _proxy, _addr, false) << "\n" << ex; + } + } + throw; + } + + if(_instance->traceLevel() >= 1) + { + Trace out(_instance->logger(), _instance->traceCategory()); + if(_incoming) + { + out << "accepted " << _instance->protocol() << " connection\n" << _desc; + } + else + { + out << _instance->protocol() << " connection established\n" << _desc; + } + } + + if(_instance->securityTraceLevel() >= 1) + { + traceConnection(); + } + + return IceInternal::SocketOperationNone; +} + +IceInternal::SocketOperation +IceSSL::TransceiverI::closing(bool initiator, const Ice::LocalException&) +{ + // If we are initiating the connection closure, wait for the peer + // to close the TCP/IP connection. Otherwise, close immediately. + return initiator ? IceInternal::SocketOperationRead : IceInternal::SocketOperationNone; +} + +void +IceSSL::TransceiverI::close() +{ + if(_state == StateHandshakeComplete && _instance->traceLevel() >= 1) + { + Trace out(_instance->logger(), _instance->traceCategory()); + out << "closing " << _instance->protocol() << " connection\n" << toString(); + } + + if(_trust) + { + CFRelease(_trust); + _trust = 0; + } + + if(_ssl) + { + SSLClose(_ssl); + CFRelease(_ssl); + _ssl = 0; + } + + assert(_fd != INVALID_SOCKET); + try + { + IceInternal::closeSocket(_fd); + _fd = INVALID_SOCKET; + } + catch(const SocketException&) + { + _fd = INVALID_SOCKET; + throw; + } +} + +IceInternal::SocketOperation +IceSSL::TransceiverI::write(IceInternal::Buffer& buf) +{ + if(_state == StateProxyConnectRequest) + { + // + // We need to write the proxy message, but we have to use TCP and not SSL. + // + return writeRaw(buf) ? IceInternal::SocketOperationNone : IceInternal::SocketOperationWrite; + } + + size_t processed = 0; + + if(_buffered > 0) + { + // + // Required to flush SSL buffers + // + if(SSLWrite(_ssl, 0, 0, &processed) == errSSLWouldBlock) + { + return IceInternal::SocketOperationWrite; + } + + buf.i += _buffered; + _buffered = 0; + } + + if(buf.i == buf.b.end()) + { + return IceInternal::SocketOperationNone; + } + + // + // It's impossible for packetSize to be more than an Int. + // + size_t packetSize = static_cast<size_t>(buf.b.end() - buf.i); + packetSize = std::min(packetSize, _maxSendPacketSize); + while(buf.i != buf.b.end()) + { + assert(_fd != INVALID_SOCKET); + OSStatus ret = SSLWrite(_ssl, reinterpret_cast<const void*>(buf.i), packetSize, &processed); + if(ret != noErr) + { + if(ret == errSSLWouldBlock) + { + _buffered = processed; + assert(_flags & SSLWantWrite); + return IceInternal::SocketOperationWrite; + } + + if(ret == errSSLClosedGraceful) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = 0; + throw ex; + } + + // + // SSL protocol errors are defined in SecureTransport.h are in the range + // -9800 to -9849 + // + if(ret <= -9800 && ret >= -9849) + { + ProtocolException ex(__FILE__, __LINE__); + ostringstream os; + os << "SSL protocol error during read:\n" << errorToString(ret); + ex.reason = os.str(); + throw ex; + } + + errno = ret; + if(IceInternal::connectionLost()) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + else + { + SocketException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + } + + if(_instance->traceLevel() >= 3) + { + Trace out(_instance->logger(), _instance->traceCategory()); + out << "sent " << processed << " of " << packetSize << " bytes via " << protocol() << "\n" << toString(); + } + + buf.i += processed; + + if(packetSize > buf.b.end() - buf.i) + { + packetSize = static_cast<int>(buf.b.end() - buf.i); + } + } + + return IceInternal::SocketOperationNone; +} + +IceInternal::SocketOperation +IceSSL::TransceiverI::read(IceInternal::Buffer& buf, bool&) +{ + if(_state == StateProxyConnectRequestPending) + { + // + // We need to read the proxy reply, but we have to use TCP and not SSL. + // + return readRaw(buf) ? IceInternal::SocketOperationNone : IceInternal::SocketOperationRead; + } + + if(buf.i == buf.b.end()) + { + return IceInternal::SocketOperationNone; + } + // + // It's impossible for packetSize to be more than an Int. + // + size_t packetSize = static_cast<int>(buf.b.end() - buf.i); + size_t processed = 0; + + packetSize = std::min(packetSize, _maxReceivePacketSize); + + while(buf.i != buf.b.end()) + { + assert(_fd != INVALID_SOCKET); + OSStatus ret = SSLRead(_ssl, reinterpret_cast<void*>(buf.i), packetSize, &processed); + if(ret != noErr) + { + if(ret == errSSLWouldBlock) + { + buf.i += processed; + assert(_flags & SSLWantRead); + return IceInternal::SocketOperationRead; + } + + if(ret == errSSLClosedGraceful || ret == errSSLPeerBadRecordMac || ret == errSSLPeerDecryptionFail) + { + // + // Forcefully closing a connection can result in SSLRead reporting + // "decryption failed or bad record mac". We trap that error and + // treat it as the loss of a connection. + // + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = 0; + throw ex; + } + + // + // SSL protocol errors are defined in SecureTransport.h are in the range + // -9800 to -9849 + // + if(ret <= -9800 && ret >= -9849) + { + ProtocolException ex(__FILE__, __LINE__); + ostringstream os; + os << "SSL protocol error during read:\n" << errorToString(ret); + ex.reason = os.str(); + throw ex; + } + + errno = ret; + if(IceInternal::connectionLost()) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + else + { + SocketException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + } + + if(_instance->traceLevel() >= 3) + { + Trace out(_instance->logger(), _instance->traceCategory()); + out << "received " << processed << " of " << packetSize << " bytes via " << protocol() << "\n" + << toString(); + } + buf.i += processed; + + if(packetSize > buf.b.end() - buf.i) + { + packetSize = static_cast<int>(buf.b.end() - buf.i); + } + } + return IceInternal::SocketOperationNone; +} + +string +IceSSL::TransceiverI::protocol() const +{ + return _instance->protocol(); +} + +string +IceSSL::TransceiverI::toString() const +{ + return _desc; +} + +Ice::ConnectionInfoPtr +IceSSL::TransceiverI::getInfo() const +{ + return getNativeConnectionInfo(); +} + +void +IceSSL::TransceiverI::checkSendSize(const IceInternal::Buffer& buf, size_t messageSizeMax) +{ + if(buf.b.size() > messageSizeMax) + { + IceInternal::Ex::throwMemoryLimitException(__FILE__, __LINE__, buf.b.size(), messageSizeMax); + } +} + +SecTrustRef +IceSSL::TransceiverI::trust() const +{ + return _trust; +} + +ContextRef +IceSSL::TransceiverI::context() const +{ + return _ssl; +} + +IceSSL::TransceiverI::TransceiverI(const InstancePtr& instance, SOCKET fd, const IceInternal::NetworkProxyPtr& proxy, + const string& host, const IceInternal::Address& addr) : + IceInternal::NativeInfo(fd), + _instance(instance), + _engine(SecureTransportEnginePtr::dynamicCast(instance->engine())), + _proxy(proxy), + _host(host), + _addr(addr), + _incoming(false), + _ssl(0), + _trust(0), + _buffered(0), + _state(StateNeedConnect) +{ + assert(_engine); + IceInternal::setBlock(fd, false); + IceInternal::setTcpBufSize(fd, _instance->properties(), _instance->logger()); + + IceInternal::Address connectAddr = proxy ? proxy->getAddress() : addr; + if(IceInternal::doConnect(_fd, connectAddr)) + { + _state = StateConnected; + _desc = IceInternal::fdToString(_fd, _proxy, _addr, true); + if(_instance->traceLevel() >= 1) + { + Trace out(_instance->logger(), _instance->traceCategory()); + out << _instance->protocol() << " connection established\n" << _desc; + } + } + else + { + _desc = IceInternal::fdToString(_fd, _proxy, _addr, true); + } + + // + // Limit the size of packet pass to SSLWrite/SSLRead to avoid blocking and + // holding too much memory. + // + _maxSendPacketSize = std::min(512, IceInternal::getSendBufferSize(fd)); + _maxReceivePacketSize = std::min(512, IceInternal::getRecvBufferSize(fd)); +} + +IceSSL::TransceiverI::TransceiverI(const InstancePtr& instance, SOCKET fd, const string& adapterName) : + IceInternal::NativeInfo(fd), + _instance(instance), + _engine(SecureTransportEnginePtr::dynamicCast(instance->engine())), + _addr(IceInternal::Address()), + _adapterName(adapterName), + _incoming(true), + _ssl(0), + _trust(0), + _buffered(0), + _state(StateConnected), + _desc(IceInternal::fdToString(fd)) +{ + assert(_engine); + IceInternal::setBlock(fd, false); + IceInternal::setTcpBufSize(fd, _instance->properties(), _instance->logger()); + + // + // Limit the size of packet pass to SSLWrite/SSLRead to avoid blocking and + // holding too much memory. + // + _maxSendPacketSize = std::min(512, IceInternal::getSendBufferSize(fd)); + _maxReceivePacketSize = std::min(512, IceInternal::getRecvBufferSize(fd)); +} + +IceSSL::TransceiverI::~TransceiverI() +{ + assert(_fd == INVALID_SOCKET); +} + +NativeConnectionInfoPtr +IceSSL::TransceiverI::getNativeConnectionInfo() const +{ + NativeConnectionInfoPtr info = new NativeConnectionInfo(); + IceInternal::fdToAddressAndPort(_fd, info->localAddress, info->localPort, info->remoteAddress, info->remotePort); + + if(_ssl) + { + for(int i = 0, count = SecTrustGetCertificateCount(_trust); i < count; ++i) + { + SecCertificateRef cert = SecTrustGetCertificateAtIndex(_trust, i); + CFRetain(cert); + + CertificatePtr certificate = new Certificate(cert); + info->nativeCerts.push_back(certificate); + info->certs.push_back(certificate->encode()); + } + + SSLCipherSuite cipher; + SSLGetNegotiatedCipher(_ssl, &cipher); + info->cipher = _engine->getCipherName(cipher); + } + + info->adapterName = _adapterName; + info->incoming = _incoming; + return info; +} + +bool +IceSSL::TransceiverI::writeRaw(IceInternal::Buffer& buf) +{ + // + // It's impossible for packetSize to be more than an Int. + // + int packetSize = static_cast<int>(buf.b.end() - buf.i); + while(buf.i != buf.b.end()) + { + assert(_fd != INVALID_SOCKET); + + ssize_t ret = ::send(_fd, reinterpret_cast<const char*>(&*buf.i), packetSize, 0); + if(ret == 0) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = 0; + throw ex; + } + + if(ret == SOCKET_ERROR) + { + if(IceInternal::interrupted()) + { + continue; + } + + if(IceInternal::noBuffers() && packetSize > 1024) + { + packetSize /= 2; + continue; + } + + if(IceInternal::wouldBlock()) + { + return false; + } + + if(IceInternal::connectionLost()) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + else + { + SocketException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + } + + if(_instance->traceLevel() >= 3) + { + Trace out(_instance->logger(), _instance->traceCategory()); + out << "sent " << ret << " of " << packetSize << " bytes via " << protocol() << "\n" << toString(); + } + + buf.i += ret; + + if(packetSize > buf.b.end() - buf.i) + { + packetSize = static_cast<int>(buf.b.end() - buf.i); + } + } + + return true; +} + +bool +IceSSL::TransceiverI::readRaw(IceInternal::Buffer& buf) +{ + // + // It's impossible for packetSize to be more than an Int. + // + int packetSize = static_cast<int>(buf.b.end() - buf.i); + while(buf.i != buf.b.end()) + { + assert(_fd != INVALID_SOCKET); + ssize_t ret = ::recv(_fd, reinterpret_cast<char*>(&*buf.i), packetSize, 0); + + if(ret == 0) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = 0; + throw ex; + } + + if(ret == SOCKET_ERROR) + { + if(IceInternal::interrupted()) + { + continue; + } + + if(IceInternal::noBuffers() && packetSize > 1024) + { + packetSize /= 2; + continue; + } + + if(IceInternal::wouldBlock()) + { + return false; + } + + if(IceInternal::connectionLost()) + { + ConnectionLostException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + else + { + SocketException ex(__FILE__, __LINE__); + ex.error = IceInternal::getSocketErrno(); + throw ex; + } + } + + if(_instance->traceLevel() >= 3) + { + Trace out(_instance->logger(), _instance->traceCategory()); + out << "received " << ret << " of " << packetSize << " bytes via " << protocol() << "\n" << toString(); + } + + buf.i += ret; + + packetSize = static_cast<int>(buf.b.end() - buf.i); + } + + return true; +} + +// +// Trace connection +// +void +IceSSL::TransceiverI::traceConnection() +{ + assert(_ssl); + Trace out(_instance->logger(), _instance->traceCategory()); + out << "SSL summary for " << (_incoming ? "incoming" : "outgoing") << " connection\n"; + + SSLProtocol protocol; + SSLGetNegotiatedProtocolVersion(_ssl, &protocol); + const string sslProtocolName = protocolName(protocol); + + SSLCipherSuite cipher; + SSLGetNegotiatedCipher(_ssl, &cipher); + const string sslCipherName = _engine->getCipherName(cipher); + + if(sslCipherName.empty()) + { + out << "unknown cipher\n"; + } + else + { + out << "cipher = " << sslCipherName << "\n"; + out << "protocol = " << sslProtocolName << "\n"; + } + out << IceInternal::fdToString(_fd); +} + +OSStatus +IceSSL::TransceiverI::writeRaw(const char* data, size_t* length) const +{ + _flags &= ~SSLWantWrite; + + assert(_fd != INVALID_SOCKET); + + char* i = const_cast<char*>(data); + int packetSize = *length; + char* end = i + packetSize; + + while(i != end) + { + ssize_t ret = ::send(_fd, const_cast<const char*>(i), packetSize, 0); + if(ret == 0) + { + return errSSLClosedGraceful; + } + + if(ret == SOCKET_ERROR) + { + if(IceInternal::interrupted()) + { + continue; + } + + if(IceInternal::noBuffers() && packetSize > 1024) + { + packetSize /= 2; + continue; + } + + if(IceInternal::wouldBlock()) + { + *length = static_cast<int>(i - data); + _flags |= SSLWantWrite; + return errSSLWouldBlock; + } + return errno; + } + + i += ret; + if(_instance->traceLevel() >= 3) + { + Trace out(_instance->logger(), _instance->traceCategory()); + out << "sent " << ret << " of " << packetSize << " bytes via " << protocol() << "\n" << toString(); + } + + if(packetSize > end - i) + { + packetSize = static_cast<int>(end - i); + } + } + *length = static_cast<int>(i - data); + return noErr; +} + +OSStatus +IceSSL::TransceiverI::readRaw(char* data, size_t* length) const +{ + _flags &= ~SSLWantRead; + + assert(_fd != INVALID_SOCKET); + + char* i = data; + int packetSize = *length; + char* end = i + packetSize; + while(i != end) + { + ssize_t ret = ::recv(_fd, i, packetSize, 0); + if(ret == 0) + { + return errSSLClosedGraceful; + } + + if(ret == SOCKET_ERROR) + { + if(IceInternal::interrupted()) + { + continue; + } + + if(IceInternal::noBuffers() && packetSize > 1024) + { + packetSize /= 2; + continue; + } + + if(IceInternal::wouldBlock()) + { + *length = static_cast<int>(i - data); + _flags |= SSLWantRead; + return errSSLWouldBlock; + } + return errno; + } + + i += ret; + + if(_instance->traceLevel() >= 3) + { + Trace out(_instance->logger(), _instance->traceCategory()); + out << "received " << ret << " of " << packetSize << " bytes via " << protocol() << "\n" << toString(); + } + + packetSize = static_cast<int>(end - i); + } + + *length = static_cast<int>(i - data); + return noErr; +} + +#endif diff --git a/cpp/src/IceSSL/SecureTransportTransceiverI.h b/cpp/src/IceSSL/SecureTransportTransceiverI.h new file mode 100644 index 00000000000..c38c8d26b4c --- /dev/null +++ b/cpp/src/IceSSL/SecureTransportTransceiverI.h @@ -0,0 +1,114 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 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. +// +// ********************************************************************** + +#ifndef ICE_SSL_SECURE_TRANSPORT_TRANSCEIVER_I_H +#define ICE_SSL_SECURE_TRANSPORT_TRANSCEIVER_I_H + +#include <IceSSL/Config.h> +#include <IceSSL/InstanceF.h> +#include <IceSSL/SSLEngineF.h> +#include <IceSSL/Plugin.h> + +#include <Ice/Transceiver.h> +#include <Ice/Network.h> + +#ifdef ICE_USE_SECURE_TRANSPORT + +#include <Security/Security.h> +#include <CoreFoundation/CoreFoundation.h> + +namespace IceSSL +{ + +class ConnectorI; +class AcceptorI; + +class TransceiverI : public IceInternal::Transceiver, public IceInternal::NativeInfo +{ + enum State + { + StateNeedConnect, + StateConnectPending, + StateProxyConnectRequest, + StateProxyConnectRequestPending, + StateConnected, + StateHandshakeComplete + }; + +public: + + virtual IceInternal::NativeInfoPtr getNativeInfo(); + + virtual IceInternal::SocketOperation initialize(IceInternal::Buffer&, IceInternal::Buffer&, bool&); + virtual IceInternal::SocketOperation closing(bool, const Ice::LocalException&); + virtual void close(); + virtual IceInternal::SocketOperation write(IceInternal::Buffer&); + virtual IceInternal::SocketOperation read(IceInternal::Buffer&, bool&); + + virtual std::string protocol() const; + virtual std::string toString() const; + virtual Ice::ConnectionInfoPtr getInfo() const; + virtual void checkSendSize(const IceInternal::Buffer&, size_t); + + ContextRef context() const; + SecTrustRef trust() const; + OSStatus writeRaw(const char*, size_t*) const; + OSStatus readRaw(char*, size_t*) const; + +private: + + TransceiverI(const InstancePtr&, SOCKET, const IceInternal::NetworkProxyPtr&, const std::string&, + const IceInternal::Address&); + TransceiverI(const InstancePtr&, SOCKET, const std::string&); + virtual ~TransceiverI(); + + virtual NativeConnectionInfoPtr getNativeConnectionInfo() const; + + void traceConnection(); + + bool writeRaw(IceInternal::Buffer&); + bool readRaw(IceInternal::Buffer&); + + friend class ConnectorI; + friend class AcceptorI; + + const InstancePtr _instance; + const SecureTransportEnginePtr _engine; + + const IceInternal::NetworkProxyPtr _proxy; + const std::string _host; + const IceInternal::Address _addr; + + const std::string _adapterName; + const bool _incoming; + + ContextRef _ssl; + SecTrustRef _trust; + + size_t _buffered; + enum SSLWantFlags + { + SSLWantRead = 0x1, + SSLWantWrite = 0x2 + }; + + mutable Ice::Byte _flags; + + State _state; + std::string _desc; + size_t _maxSendPacketSize; + size_t _maxReceivePacketSize; +}; +typedef IceUtil::Handle<TransceiverI> TransceiverIPtr; + +} + +#endif + +#endif diff --git a/cpp/src/IceSSL/TransceiverI.cpp b/cpp/src/IceSSL/TransceiverI.cpp index 1e07c42e9c3..d749cdf9651 100644 --- a/cpp/src/IceSSL/TransceiverI.cpp +++ b/cpp/src/IceSSL/TransceiverI.cpp @@ -7,10 +7,11 @@ // // ********************************************************************** +#include <IceSSL/TransceiverI.h> + #include <IceUtil/Mutex.h> #include <IceUtil/MutexPtrLock.h> -#include <IceSSL/TransceiverI.h> #include <IceSSL/ConnectionInfo.h> #include <IceSSL/Instance.h> #include <IceSSL/Util.h> @@ -21,14 +22,11 @@ #include <IceUtil/DisableWarnings.h> +#ifdef ICE_USE_OPENSSL + #include <openssl/err.h> #include <openssl/bio.h> -// Ignore OS X OpenSSL deprecation warnings -#ifdef __APPLE__ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - using namespace std; using namespace Ice; using namespace IceSSL; @@ -231,7 +229,7 @@ IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::B throw ex; } - _ssl = SSL_new(_instance->context()); + _ssl = SSL_new(_engine->context()); if(!_ssl) { BIO_free(bio); @@ -364,7 +362,7 @@ IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::B } ostringstream ostr; ostr << "SSL error occurred for new " << (_incoming ? "incoming" : "outgoing") - << " connection:\nremote address = " << desc << "\n" << _instance->sslErrors(); + << " connection:\nremote address = " << desc << "\n" << _engine->sslErrors(); ProtocolException ex(__FILE__, __LINE__); ex.reason = ostr.str(); throw ex; @@ -373,7 +371,7 @@ IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::B } } - _instance->verifyPeer(_ssl, _fd, _host, getNativeConnectionInfo()); + _engine->verifyPeer(_ssl, _fd, _host, getNativeConnectionInfo()); _state = StateHandshakeComplete; } catch(const Ice::LocalException& ex) @@ -409,7 +407,7 @@ IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::B if(_instance->securityTraceLevel() >= 1) { - _instance->traceConnection(_ssl, _incoming); + traceConnection(); } return IceInternal::SocketOperationNone; @@ -597,7 +595,7 @@ IceSSL::TransceiverI::write(IceInternal::Buffer& buf) case SSL_ERROR_SSL: { ProtocolException ex(__FILE__, __LINE__); - ex.reason = "SSL protocol error during write:\n" + _instance->sslErrors(); + ex.reason = "SSL protocol error during write:\n" + _engine->sslErrors(); throw ex; } } @@ -770,7 +768,7 @@ IceSSL::TransceiverI::read(IceInternal::Buffer& buf, bool&) else { ProtocolException ex(__FILE__, __LINE__); - ex.reason = "SSL protocol error during read:\n" + _instance->sslErrors(); + ex.reason = "SSL protocol error during read:\n" + _engine->sslErrors(); throw ex; } } @@ -1041,6 +1039,7 @@ IceSSL::TransceiverI::TransceiverI(const InstancePtr& instance, SOCKET fd, const const string& host, const IceInternal::Address& addr) : IceInternal::NativeInfo(fd), _instance(instance), + _engine(OpenSSLEnginePtr::dynamicCast(instance->engine())), _proxy(proxy), _host(host), _addr(addr), @@ -1078,6 +1077,7 @@ IceSSL::TransceiverI::TransceiverI(const InstancePtr& instance, SOCKET fd, const IceSSL::TransceiverI::TransceiverI(const InstancePtr& instance, SOCKET fd, const string& adapterName) : IceInternal::NativeInfo(fd), _instance(instance), + _engine(OpenSSLEnginePtr::dynamicCast(instance->engine())), _addr(IceInternal::Address()), _adapterName(adapterName), _incoming(true), @@ -1366,6 +1366,30 @@ IceSSL::TransceiverI::readAsync(char* buf, int packetSize) #endif +void +IceSSL::TransceiverI::traceConnection() +{ + Trace out(_instance->logger(), _instance->traceCategory()); + out << "SSL summary for " << (_incoming ? "incoming" : "outgoing") << " connection\n"; + + // + // The const_cast is necesary because Solaris still uses OpenSSL 0.9.7. + // + //const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl); + SSL_CIPHER *cipher = const_cast<SSL_CIPHER*>(SSL_get_current_cipher(_ssl)); + if(!cipher) + { + out << "unknown cipher\n"; + } + else + { + out << "cipher = " << SSL_CIPHER_get_name(cipher) << "\n"; + out << "bits = " << SSL_CIPHER_get_bits(cipher, 0) << "\n"; + out << "protocol = " << SSL_get_version(_ssl) << "\n"; + } + out << IceInternal::fdToString(SSL_get_fd(_ssl)); +} + bool IceSSL::TransceiverI::writeRaw(IceInternal::Buffer& buf) { @@ -1507,3 +1531,4 @@ IceSSL::TransceiverI::readRaw(IceInternal::Buffer& buf) return true; } +#endif diff --git a/cpp/src/IceSSL/TransceiverI.h b/cpp/src/IceSSL/TransceiverI.h index 4025da7f108..b74da103485 100644 --- a/cpp/src/IceSSL/TransceiverI.h +++ b/cpp/src/IceSSL/TransceiverI.h @@ -10,12 +10,16 @@ #ifndef ICE_SSL_TRANSCEIVER_I_H #define ICE_SSL_TRANSCEIVER_I_H +#include <IceSSL/Config.h> #include <IceSSL/InstanceF.h> #include <IceSSL/Plugin.h> +#include <IceSSL/SSLEngineF.h> #include <Ice/Transceiver.h> #include <Ice/Network.h> +#ifdef ICE_USE_OPENSSL + typedef struct ssl_st SSL; typedef struct bio_st BIO; @@ -76,6 +80,7 @@ private: int readAsync(char*, int); #endif + void traceConnection(); bool writeRaw(IceInternal::Buffer&); bool readRaw(IceInternal::Buffer&); @@ -83,7 +88,8 @@ private: friend class AcceptorI; const InstancePtr _instance; - + const OpenSSLEnginePtr _engine; + const IceInternal::NetworkProxyPtr _proxy; const std::string _host; const IceInternal::Address _addr; @@ -114,3 +120,5 @@ typedef IceUtil::Handle<TransceiverI> TransceiverIPtr; } #endif + +#endif diff --git a/cpp/src/IceSSL/Util.cpp b/cpp/src/IceSSL/Util.cpp index a46db385398..518fd9f5d67 100644 --- a/cpp/src/IceSSL/Util.cpp +++ b/cpp/src/IceSSL/Util.cpp @@ -13,33 +13,31 @@ #endif #include <IceSSL/Util.h> +#include <IceUtil/ScopedArray.h> #include <IceUtil/FileUtil.h> + #include <Ice/LocalException.h> #include <Ice/Network.h> #include <Ice/Object.h> -#ifdef _WIN32 -# include <direct.h> -# include <sys/types.h> -#endif - -#include <openssl/err.h> - -#include <IceUtil/DisableWarnings.h> - -// Ignore OS X OpenSSL deprecation warnings -#ifdef __APPLE__ -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#ifdef ICE_USE_OPENSSL +# ifdef _WIN32 +# include <direct.h> +# include <sys/types.h> +# endif +# include <openssl/err.h> #endif using namespace std; using namespace Ice; +using namespace IceUtil; using namespace IceSSL; +#ifdef ICE_USE_OPENSSL namespace { -#ifndef OPENSSL_NO_DH +# ifndef OPENSSL_NO_DH // The following arrays are predefined Diffie Hellman group parameters. // These are known strong primes, distributed with the OpenSSL library @@ -278,41 +276,7 @@ IceSSL::DHParams::get(int keyLength) } } -#endif - -bool -IceSSL::checkPath(string& path, const string& defaultDir, bool dir) -{ - // - // Check if file exists. If not, try prepending the default - // directory and check again. If the path exists, the string - // argument is modified and true is returned. Otherwise - // false is returned. - // - IceUtilInternal::structstat st; - int err = IceUtilInternal::stat(path, &st); - if(err == 0) - { - return dir ? S_ISDIR(st.st_mode) != 0 : S_ISREG(st.st_mode) != 0; - } - - if(!defaultDir.empty()) - { -#ifdef _WIN32 - string s = defaultDir + "\\" + path; -#else - string s = defaultDir + "/" + path; -#endif - err = ::IceUtilInternal::stat(s.c_str(), &st); - if(err == 0 && ((!dir && S_ISREG(st.st_mode)) || (dir && S_ISDIR(st.st_mode)))) - { - path = s; - return true; - } - } - - return false; -} +# endif string IceSSL::getSslErrors(bool verbose) @@ -368,3 +332,583 @@ IceSSL::getSslErrors(bool verbose) return ostr.str(); } + +#elif defined(ICE_USE_SECURE_TRANSPORT) + +string +IceSSL::errorToString(CFErrorRef err) +{ + ostringstream os; + if(err) + { + CFStringRef s = CFErrorCopyDescription(err); + os << "(error: " << CFErrorGetCode(err) << " description: " << fromCFString(s) << ")"; + CFRelease(s); + } + return os.str(); +} + +string +IceSSL::errorToString(OSStatus status) +{ + ostringstream os; + os << "(error: " << status; + CFStringRef s = SecCopyErrorMessageString(status, 0); + if(s) + { + os << " description: " << fromCFString(s); + CFRelease(s); + } + os << ")"; + return os.str(); +} + +std::string +IceSSL::fromCFString(CFStringRef v) +{ + string s; + if(v) + { + CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(v), kCFStringEncodingUTF8); + IceUtil::ScopedArray<char> buffer(new char[size + 1]); + CFStringGetCString(v, buffer.get(), size + 1, kCFStringEncodingUTF8); + s.assign(buffer.get()); + } + return s; +} + +int +IceSSL::readFile(const string& file, ScopedArray<char>& buffer) +{ + IceUtilInternal::ifstream is(file); + + if(!is.good()) + { + throw CertificateReadException(__FILE__, __LINE__, "error opening file " + file); + } + + is.seekg (0, is.end); + streampos length = is.tellg(); + is.seekg (0, is.beg); + + buffer.reset(new char[length]); + is.read(buffer.get(), length); + + if(!is.good()) + { + throw CertificateReadException(__FILE__, __LINE__, "error reading file " + file); + } + return length; +} + +namespace +{ + +// +// Retrive the certificate subject key identifier, the caller must release the returned CFData +// object. +// +CFDataRef +getSubjectKeyIdentifier(SecCertificateRef cert) +{ + CFDataRef data = 0; + CFErrorRef err = 0; + CFArrayRef keys = CFArrayCreate(NULL, &kSecOIDSubjectKeyIdentifier , 1, &kCFTypeArrayCallBacks); + CFDictionaryRef values = SecCertificateCopyValues(cert, keys, &err); + CFRelease(keys); + + if(err) + { + ostringstream os; + os << "Failed to copy certificate subject key identifier\n" << errorToString(err); + CFRelease(err); + CertificateReadException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + if(values) + { + CFDictionaryRef ski = (CFDictionaryRef)CFDictionaryGetValue(values, kSecOIDSubjectKeyIdentifier); + if(ski) + { + CFArrayRef propertyValues = (CFArrayRef)CFDictionaryGetValue(ski, kSecPropertyKeyValue); + for(int i = 0, length = CFArrayGetCount(propertyValues); i < length; ++i) + { + CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(propertyValues, i); + CFStringRef label = (CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyLabel); + if(CFEqual(label, CFSTR("Key Identifier"))) + { + data = (CFDataRef)CFDictionaryGetValue(dict, kSecPropertyKeyValue); + CFRetain(data); + break; + } + } + } + CFRelease(values); + } + return data; +} + +// +// Check the certificate basic constraints to check if the certificate is marked as a CA. +// +bool +isCA(SecCertificateRef cert) +{ + bool ca = false; + CFErrorRef err = 0; + CFArrayRef keys = CFArrayCreate(NULL, &kSecOIDBasicConstraints, 1, &kCFTypeArrayCallBacks); + CFDictionaryRef values = SecCertificateCopyValues(cert, keys, &err); + CFRelease(keys); + + if(err) + { + ostringstream os; + os << "Failed to copy certificate basic constraints\n" << errorToString(err); + CFRelease(err); + CertificateReadException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + if(values) + { + CFDictionaryRef basicConstraints = (CFDictionaryRef)CFDictionaryGetValue(values, kSecOIDBasicConstraints); + if(basicConstraints) + { + CFArrayRef propertyValues = (CFArrayRef)CFDictionaryGetValue(basicConstraints, kSecPropertyKeyValue); + int size = CFArrayGetCount(propertyValues); + for(int i = 0; i < size; ++i) + { + CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex(propertyValues, i); + CFStringRef label = (CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyLabel); + if(CFEqual(label, CFSTR("Certificate Authority"))) + { + CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, kSecPropertyKeyValue); + if(CFEqual(value, CFSTR("Yes"))) + { + ca = true; + } + break; + } + } + CFRelease(values); + } + } + return ca; +} + +// +// Search the keychain for an existing item with the same hash and type, +// the hash is the certificate subject key identifier. For private key +// items the hash should match kSecAttrApplicationLabel attribute, for +// certificate items it should match the kSecAttrSubjectKeyID attribute. +// +SecKeychainItemRef +copyMatching(SecKeychainRef keychain, CFDataRef hash, CFTypeRef type) +{ + assert(keychain); + assert(hash); + assert(type == kSecClassKey || type == kSecClassCertificate); + + const void* values[] = {keychain}; + CFArrayRef searchList = CFArrayCreate(kCFAllocatorDefault, values, 1, &kCFTypeArrayCallBacks); + + CFMutableDictionaryRef query = + CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFDictionarySetValue(query, kSecClass, type); + CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitOne); + CFDictionarySetValue(query, kSecMatchSearchList, searchList); + CFDictionarySetValue(query, type == kSecClassKey ? kSecAttrApplicationLabel : kSecAttrSubjectKeyID, hash); + CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue); + + SecKeychainItemRef item = 0; + OSStatus err = SecItemCopyMatching(query, (CFTypeRef*)&item); + + CFRelease(searchList); + CFRelease(query); + + if(err != noErr && err != errSecItemNotFound) + { + ostringstream os; + os << "Error searching for keychain items\n" << errorToString(err); + CertificateReadException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + + return item; +} + +// +// Add an item to the keychain, if the keychain already has this item return the existing item, +// otherwise return the new added item. +// +SecKeychainItemRef +addToKeychain(SecKeychainRef keychain, SecKeychainItemRef item, CFDataRef hash, CFTypeRef type) +{ + assert(keychain); + assert(item); + assert(hash); + + SecKeychainItemRef newItem = copyMatching(keychain, hash, type); + if(!newItem) + { + CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + CFDictionarySetValue(query, kSecUseKeychain, keychain); + CFDictionarySetValue(query, kSecClass, type); + CFDictionarySetValue(query, kSecValueRef, item); + CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue); + + CFArrayRef added = 0; + OSStatus err = SecItemAdd(query, (CFTypeRef*)&added); + CFRelease(query); + + if(err != noErr) + { + ostringstream os; + os << "Failure adding " << (type == kSecClassKey ? "key" : "certificate") + << " to keychain\n" << errorToString(err); + CertificateReadException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + newItem = (SecKeychainItemRef)CFArrayGetValueAtIndex(added, 0); + CFRetain(newItem); + CFRelease(added); + } + + assert(newItem); + + return newItem; +} + +// +// Load keychain items (Certificates or Private Keys) from a file. On return items param contain +// the list of items, the caller must release it. +// +void +loadKeychainItems(CFArrayRef* items, CFTypeRef type, const string& file, const string& passphrase, + const PasswordPromptPtr& prompt, int passwordRetryMax) +{ + assert(type == kSecClassCertificate || type == kSecClassKey); + ScopedArray<char> buffer; + int length = readFile(file, buffer); + CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, + (const UInt8*)buffer.get(), + length, + kCFAllocatorNull); + + + SecExternalFormat format = kSecFormatUnknown; + SecExternalItemType itemType = type == kSecClassKey ? kSecItemTypePrivateKey : kSecItemTypeCertificate; + + SecItemImportExportKeyParameters params; + memset(¶ms, 0, sizeof(params)); + params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; + if(type == kSecClassKey) + { + params.flags |= kSecKeyNoAccessControl; + + // + // If the application doesn't provide an password prompt configure + // the default OS X password prompt. + // + if(!prompt) + { + params.flags |= kSecKeySecurePassphrase; + ostringstream os; + os << "Enter the password for\n" << file; + + params.alertPrompt = toCFString(os.str()); + } + } + + if(!passphrase.empty()) + { + params.passphrase = toCFString(passphrase); + } + OSStatus err = SecItemImport(data, 0, &format, &itemType, 0, ¶ms, 0, items); + if(params.passphrase) + { + CFRelease(params.passphrase); + } + + if(prompt && err == errSecPassphraseRequired) + { + for(int i = 0; i < passwordRetryMax; ++i) + { + params.passphrase = toCFString(prompt->getPassword()); + err = SecItemImport(data, 0, &format, &itemType, 0, ¶ms, 0, items); + if(params.passphrase) + { + CFRelease(params.passphrase); + } + + if(err != errSecPassphraseRequired && err != errSecInvalidData) + { + break; + } + } + } + if(params.alertPrompt) + { + CFRelease(params.alertPrompt); + } + CFRelease(data); + + if(err != noErr) + { + ostringstream os; + os << "Error reading " << (type == kSecClassCertificate ? "certificate " : "private key ") + << "from file: `" << file << "'\n" << errorToString(err); + CertificateReadException ex(__FILE__, __LINE__, os.str()); + throw ex; + } +} + +} + +// +// Helper function to generate the private key label (display name) used +// in the keychain. +// +string +IceSSL::keyLabel(SecCertificateRef cert) +{ + CFStringRef commonName; + OSStatus err = SecCertificateCopyCommonName(cert, &commonName); + if(err != noErr) + { + ostringstream os; + os << "certificate error:\n" << errorToString(err); + CertificateReadException ex(__FILE__, __LINE__, os.str()); + throw ex; + } + string label = fromCFString(commonName); + CFRelease(commonName); + return label.empty() ? "Imported Private Key" : (label + " - Private Key"); +} + +// +// Imports a certificate private key and optionally add it to a keychain. +// +void +IceSSL::loadPrivateKey(SecKeyRef* key, const string& label, CFDataRef hash, SecKeychainRef keychain, + const string& file, const string& passphrase, const PasswordPromptPtr& prompt, + int passwordRetryMax) +{ + assert(key); + CFArrayRef items = 0; + try + { + loadKeychainItems(&items, kSecClassKey, file, passphrase, prompt, passwordRetryMax); + if(items && CFArrayGetCount(items) > 0) + { + SecKeychainItemRef item = (SecKeychainItemRef)CFArrayGetValueAtIndex(items, 0); + assert(SecKeyGetTypeID() == CFGetTypeID(item)); + CFRetain(item); + *key = (SecKeyRef)item; + + CFRelease(items); + items = 0; + + if(keychain) + { + SecKeychainItemRef newItem = addToKeychain(keychain, item, hash, kSecClassKey); + assert(newItem); + CFRelease(*key); + *key = (SecKeyRef)newItem; + if(hash) + { + // + // Create the association between the private key and the certificate, + // kSecKeyLabel attribute should match the subject key identifier. + // + SecKeychainAttribute attr; + attr.tag = kSecKeyLabel; + attr.data = (void*)CFDataGetBytePtr(hash); + attr.length = CFDataGetLength(hash); + + SecKeychainAttributeList attrs; + attrs.attr = &attr; + attrs.count = 1; + + SecKeychainItemModifyAttributesAndData(newItem, &attrs, 0, 0); + } + + if(!label.empty()) + { + // + // kSecKeyPrintName attribute correspond to the keychain display + // name. + // + SecKeychainAttribute att; + att.tag = kSecKeyPrintName; + att.data = (void*)label.c_str(); + att.length = label.size(); + + SecKeychainAttributeList attrs; + attrs.attr = &att; + attrs.count = 1; + + SecKeychainItemModifyAttributesAndData(newItem, &attrs, 0, 0); + } + } + } + } + catch(...) + { + if(hash) + { + CFRelease(hash); + hash = 0; + } + + if(items) + { + CFRelease(items); + items = 0; + } + + if(*key) + { + CFRelease(*key); + *key = 0; + } + + throw; + } +} + +// +void +IceSSL::loadCertificate(SecCertificateRef* cert, CFDataRef* hash, SecKeyRef* key, SecKeychainRef keychain, + const string& file, const string& passphrase, const PasswordPromptPtr& prompt, + int passwordRetryMax) +{ + assert(cert); + CFArrayRef items = 0; + try + { + loadKeychainItems(&items, kSecClassCertificate, file, passphrase, prompt, passwordRetryMax); + if(items && CFArrayGetCount(items) > 0) + { + SecKeychainItemRef item = (SecKeychainItemRef)CFArrayGetValueAtIndex(items, 0); + assert(SecCertificateGetTypeID() == CFGetTypeID(item)); + CFRetain(item); + *cert = (SecCertificateRef)item; + + CFRelease(items); + items = 0; + + // + // Copy the public key hash, that is used when added the private key + // to create an association between the certificate and the corresponding + // private key. + // + if(hash) + { + *hash = getSubjectKeyIdentifier(*cert); + + if(keychain) + { + SecKeychainItemRef newItem = addToKeychain(keychain, item, *hash, kSecClassCertificate); + assert(newItem); + CFRelease(*cert); + *cert = (SecCertificateRef)newItem; + + } + } + + if(key) + { + loadPrivateKey(key, keyLabel(*cert), hash ? *hash : 0, keychain, file, passphrase, prompt, passwordRetryMax); + } + } + } + catch(...) + { + if(*cert) + { + CFRelease(*cert); + *cert = 0; + } + + if(*hash) + { + CFRelease(*hash); + *hash = 0; + } + + if(items) + { + CFRelease(items); + items = 0; + } + + if(key && *key) + { + CFRelease(*key); + *key = 0; + } + + throw; + } +} + +CFArrayRef +IceSSL::loadCACertificates(const string& file, const string& passphrase, const PasswordPromptPtr& prompt, + int passwordRetryMax) +{ + CFArrayRef items = 0; + loadKeychainItems(&items, kSecClassCertificate, file, passphrase, prompt, passwordRetryMax); + CFMutableArrayRef certificateAuthorities = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + if(items) + { + for(CFIndex i = 0, size = CFArrayGetCount(items); i < size; ++i) + { + SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(items, i); + if(isCA(cert)) + { + CFArrayAppendValue(certificateAuthorities, cert); + } + } + CFRelease(items); + } + return certificateAuthorities; +} + +#endif // End ICE_USE_OPENSSL + +bool +IceSSL::checkPath(string& path, const string& defaultDir, bool dir) +{ + // + // Check if file exists. If not, try prepending the default + // directory and check again. If the path exists, the string + // argument is modified and true is returned. Otherwise + // false is returned. + // + IceUtilInternal::structstat st; + int err = IceUtilInternal::stat(path, &st); + if(err == 0) + { + return dir ? S_ISDIR(st.st_mode) != 0 : S_ISREG(st.st_mode) != 0; + } + + if(!defaultDir.empty()) + { + string s = defaultDir + IceUtilInternal::separator + path; + err = ::IceUtilInternal::stat(s.c_str(), &st); + if(err == 0 && ((!dir && S_ISREG(st.st_mode)) || (dir && S_ISDIR(st.st_mode)))) + { + path = s; + return true; + } + } + + return false; +} diff --git a/cpp/src/IceSSL/Util.h b/cpp/src/IceSSL/Util.h index a49aaf60927..0224f978a86 100644 --- a/cpp/src/IceSSL/Util.h +++ b/cpp/src/IceSSL/Util.h @@ -10,20 +10,29 @@ #ifndef ICE_SSL_UTIL_H #define ICE_SSL_UTIL_H +#include <IceSSL/Config.h> #include <IceSSL/UtilF.h> #include <Ice/Network.h> #include <IceUtil/Mutex.h> #include <IceUtil/Shared.h> +#include <IceUtil/ScopedArray.h> #include <IceSSL/Plugin.h> #include <list> -#include <openssl/ssl.h> +#ifdef ICE_USE_OPENSSL +# include <openssl/ssl.h> +#else +# include <Security/Security.h> +# include <CoreFoundation/CoreFoundation.h> +#endif + +#ifdef ICE_USE_OPENSSL namespace IceSSL { -#ifndef OPENSSL_NO_DH +# ifndef OPENSSL_NO_DH class DHParams : public IceUtil::Shared, public IceUtil::Mutex { public: @@ -45,17 +54,72 @@ private: DH* _dh2048; DH* _dh4096; }; -#endif +# endif // -// Determine if a file or directory exists, with an optional default directory. +// Accumulate the OpenSSL error stack into a string. // -bool checkPath(std::string&, const std::string&, bool); +std::string getSslErrors(bool); + +} +#elif defined(ICE_USE_SECURE_TRANSPORT) + +namespace IceSSL +{ // -// Accumulate the OpenSSL error stack into a string. +// Helper functions to use by Secure Transport. // -std::string getSslErrors(bool); + +std::string fromCFString(CFStringRef); + +inline CFStringRef +toCFString(const std::string& s) +{ + return CFStringCreateWithCString(NULL, s.c_str(), kCFStringEncodingUTF8); +} + +std::string errorToString(CFErrorRef); + +std::string errorToString(OSStatus); + +// +// Read a while file into memory buffer and return the number of bytes read. +// +int readFile(const std::string&, IceUtil::ScopedArray<char>&); + + +std::string keyLabel(SecCertificateRef); + +// +// Read a private key from an file and optionaly import into a keychain. +// +void loadPrivateKey(SecKeyRef*, const std::string&, CFDataRef, SecKeychainRef, + const std::string&, const std::string&, const PasswordPromptPtr&, + int); + +// +// Read a certificate and key from an file and optionaly import then into a +// keychain. +// +void loadCertificate(SecCertificateRef*, CFDataRef*, SecKeyRef*, SecKeychainRef, + const std::string&, const std::string& = "", + const PasswordPromptPtr& = 0, int = 0); + +CFArrayRef loadCACertificates(const std::string&, const std::string& = "", const PasswordPromptPtr& = 0, + int = 0); + +} + +#endif + +namespace IceSSL +{ + +// +// Determine if a file or directory exists, with an optional default directory. +// +bool checkPath(std::string&, const std::string&, bool); } diff --git a/cpp/src/IceUtil/FileUtil.cpp b/cpp/src/IceUtil/FileUtil.cpp index 89379f5f3b6..250abf105ea 100644 --- a/cpp/src/IceUtil/FileUtil.cpp +++ b/cpp/src/IceUtil/FileUtil.cpp @@ -23,6 +23,17 @@ using namespace std; +namespace IceUtilInternal +{ +#ifdef _WIN32 +const string pathsep = ";"; +const string separator = "\\"; +#else +const string pathsep = ":"; +const string separator = "/"; +#endif +} + // // Determine if path is an absolute path // diff --git a/cpp/src/ca/iceca b/cpp/src/ca/iceca index afb9b1d77a6..0d551890688 100755 --- a/cpp/src/ca/iceca +++ b/cpp/src/ca/iceca @@ -394,13 +394,13 @@ default_ca = ice\n\ \n\ [ ice ]\n\ default_days = 1825 # How long certs are valid.\n\ -default_md = md5 # The Message Digest type.\n\ +default_md = sha256 # The Message Digest type.\n\ preserve = no # Keep passed DN ordering?\n\ \n\ [ req ]\n\ default_bits = 2048\n\ default_keyfile = $ENV::ICE_CA_HOME/ca/db/ca_key.pem\n\ -default_md = md5\n\ +default_md = sha256\n\ prompt = no\n\ distinguished_name = dn\n\ x509_extensions = extensions\n\ @@ -440,7 +440,7 @@ serial = $dir/serial # The current serial number.\n\ certs = $dir # Where issued certs are kept.\n\ RANDFILE = $dir/.rand # Private random number file.\n\ default_days = 1825 # How long certs are valid.\n\ -default_md = md5 # The Message Digest type.\n\ +default_md = sha256 # The Message Digest type.\n\ preserve = yes # Keep passed DN ordering?\n\ \n\ policy = ca_policy\n\ @@ -471,7 +471,7 @@ authorityKeyIdentifier = keyid:always,issuer:always\n\ \n\ [ req ]\n\ default_bits = 1024\n\ -default_md = md5\n\ +default_md = sha256\n\ prompt = no\n\ distinguished_name = dn\n\ x509_extensions = extensions\n\ diff --git a/cpp/test/Ice/hash/Client.cpp b/cpp/test/Ice/hash/Client.cpp index e9fc9f4b2c6..4dd5c152973 100644 --- a/cpp/test/Ice/hash/Client.cpp +++ b/cpp/test/Ice/hash/Client.cpp @@ -44,6 +44,8 @@ int main(int argc, char** argv) // In Ice for WinRT IceSSL is part of Ice core. // id.properties->setProperty("Ice.Plugin.IceSSL", "IceSSL:createIceSSL"); + id.properties->setProperty("IceSSL.Keychain", "client.keychain"); + id.properties->setProperty("IceSSL.KeychainPassword", "password"); #endif Ice::CommunicatorPtr communicator = Ice::initialize(id); for(i = 0; proxyCollisions < maxCollisions && diff --git a/cpp/test/Ice/timeout/AllTests.cpp b/cpp/test/Ice/timeout/AllTests.cpp index d53632860bd..f05e7697abe 100644 --- a/cpp/test/Ice/timeout/AllTests.cpp +++ b/cpp/test/Ice/timeout/AllTests.cpp @@ -247,7 +247,7 @@ allTests(const Ice::CommunicatorPtr& communicator) initData.properties->setProperty("Ice.Override.Timeout", "250"); Ice::CommunicatorPtr comm = Ice::initialize(initData); TimeoutPrx to = TimeoutPrx::checkedCast(comm->stringToProxy(sref)); - to->holdAdapter(500); + to->holdAdapter(1000); try { ByteSeq seq(10000000); diff --git a/cpp/test/Ice/timeout/Client.cpp b/cpp/test/Ice/timeout/Client.cpp index d0df0c07416..130ad1f3280 100644 --- a/cpp/test/Ice/timeout/Client.cpp +++ b/cpp/test/Ice/timeout/Client.cpp @@ -50,7 +50,7 @@ main(int argc, char* argv[]) // We need to send messages large enough to cause the transport // buffers to fill up. // - initData.properties->setProperty("Ice.MessageSizeMax", "10000"); + initData.properties->setProperty("Ice.MessageSizeMax", "10000000"); communicator = Ice::initialize(argc, argv, initData); status = run(argc, argv, communicator); diff --git a/cpp/test/Ice/timeout/Server.cpp b/cpp/test/Ice/timeout/Server.cpp index 3244621f13b..f9e7a363b41 100644 --- a/cpp/test/Ice/timeout/Server.cpp +++ b/cpp/test/Ice/timeout/Server.cpp @@ -43,6 +43,12 @@ main(int argc, char* argv[]) // This test kills connections, so we don't want warnings. // initData.properties->setProperty("Ice.Warn.Connections", "0"); + + // + // We need to send messages large enough to cause the transport + // buffers to fill up. + // + initData.properties->setProperty("Ice.MessageSizeMax", "10000000"); communicator = Ice::initialize(argc, argv, initData); status = run(argc, argv, communicator); diff --git a/cpp/test/IceGrid/session/Server.cpp b/cpp/test/IceGrid/session/Server.cpp index d69e9de79b3..6d585f41762 100644 --- a/cpp/test/IceGrid/session/Server.cpp +++ b/cpp/test/IceGrid/session/Server.cpp @@ -44,10 +44,10 @@ public: IceSSL::CertificatePtr cert = IceSSL::Certificate::decode(info.certs[0]); test(cert->getIssuerDN() == IceSSL::DistinguishedName( - "emailAddress=info@zeroc.com,CN=ZeroC Test CA,OU=Ice,O=ZeroC\\, Inc.,L=Palm Beach Gardens," - "ST=Florida,C=US")); + "emailAddress=info@zeroc.com,CN=ZeroC Test CA,OU=Ice,O=ZeroC\\, Inc.,L=Palm Beach Gardens," + "ST=Florida,C=US")); test(cert->getSubjectDN() == IceSSL::DistinguishedName( - "CN=Client,emailAddress=info@zeroc.com,OU=Ice,O=ZeroC\\, Inc.,ST=Florida,C=US")); + "CN=Client,emailAddress=info@zeroc.com,OU=Ice,O=ZeroC\\, Inc.,ST=Florida,C=US")); test(cert->checkValidity()); return true; diff --git a/cpp/test/IceSSL/certs/dh_params1024.der b/cpp/test/IceSSL/certs/dh_params1024.der Binary files differnew file mode 100644 index 00000000000..c389d364e4b --- /dev/null +++ b/cpp/test/IceSSL/certs/dh_params1024.der diff --git a/cpp/test/IceSSL/certs/dh_params512.der b/cpp/test/IceSSL/certs/dh_params512.der Binary files differnew file mode 100644 index 00000000000..fce094731f9 --- /dev/null +++ b/cpp/test/IceSSL/certs/dh_params512.der diff --git a/cpp/test/IceSSL/certs/makecerts b/cpp/test/IceSSL/certs/makecerts index 496780f00bd..f7d188bf9c5 100755 --- a/cpp/test/IceSSL/certs/makecerts +++ b/cpp/test/IceSSL/certs/makecerts @@ -224,3 +224,14 @@ if ! [ -f dsaparam1024.pem ]; then openssl ca -config client.cnf -batch -in $TMP/req.pem -out c_dsa_nopass_ca1_pub.pem \ -cert cacert1.pem -keyfile cakey1.pem fi + +# +# Generate DH parameters to use with OS X Secure Transport. +# +if ! [ -f dh_params512.der ]; then + openssl dhparam -outform DER -out dh_params512.der 512 +fi + +if ! [ -f dh_params1024.der ]; then + openssl dhparam -outform DER -out dh_params1024.der 1024 +fi diff --git a/cpp/test/IceSSL/configuration/AllTests.cpp b/cpp/test/IceSSL/configuration/AllTests.cpp index 97a84c62093..8b7dfa8af01 100644 --- a/cpp/test/IceSSL/configuration/AllTests.cpp +++ b/cpp/test/IceSSL/configuration/AllTests.cpp @@ -8,9 +8,11 @@ // ********************************************************************** #include <Ice/Ice.h> +#include <IceUtil/FileUtil.h> #include <IceSSL/Plugin.h> #include <TestCommon.h> #include <Test.h> +#include <Util.h> using namespace std; using namespace Ice; @@ -151,6 +153,13 @@ createClientProps(const Ice::PropertiesPtr& defaultProperties, const string& def { result->setProperty("Ice.Default.Host", defaultHost); } +#ifdef ICE_USE_SECURE_TRANSPORT + const string keychainName = "client.keychain"; + const string keychainPassword = "password"; + removeKeychain(keychainName, keychainPassword); + result->setProperty("IceSSL.Keychain", keychainName); + result->setProperty("IceSSL.KeychainPassword", keychainPassword); +#endif return result; } @@ -168,6 +177,10 @@ createServerProps(const Ice::PropertiesPtr& defaultProperties, const string& def { result["Ice.Default.Host"] = defaultHost; } +#ifdef ICE_USE_SECURE_TRANSPORT + result["IceSSL.Keychain"] = "server.keychain"; + result["IceSSL.KeychainPassword"] = "password"; +#endif return result; } @@ -214,7 +227,11 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) InitializationData initData; initData.properties = createClientProps(defaultProperties, defaultDir, defaultHost); initData.properties->setProperty("Ice.InitPlugins", "0"); +#ifdef ICE_USE_OPENSSL initData.properties->setProperty("IceSSL.Ciphers", "ADH"); +#else + initData.properties->setProperty("IceSSL.Ciphers", "DH_anon_WITH_AES_256_CBC_SHA"); +#endif initData.properties->setProperty("IceSSL.VerifyPeer", "0"); CommunicatorPtr comm = initialize(initData); PluginManagerPtr pm = comm->getPluginManager(); @@ -223,7 +240,11 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) test(obj); Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(obj); Test::Properties d = createServerProps(defaultProperties, defaultDir, defaultHost); +#ifdef ICE_USE_OPENSSL d["IceSSL.Ciphers"] = "ADH"; +#else + d["IceSSL.Ciphers"] = "DH_anon_WITH_AES_256_CBC_SHA"; +#endif d["IceSSL.VerifyPeer"] = "0"; Test::ServerPrx server = fact->createServer(d); try @@ -256,6 +277,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) CommunicatorPtr comm = initialize(initData); Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); test(fact); + Test::Properties d = createServerProps(defaultProperties, defaultDir, defaultHost); d["IceSSL.CertAuthFile"] = "cacert1.pem"; d["IceSSL.CertFile"] = "s_rsa_nopass_ca1_pub.pem"; @@ -323,7 +345,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) { // Expected. } -#ifdef _WIN32 +#if defined(_WIN32) || defined(ICE_USE_SECURE_TRANSPORT) catch(const ConnectionLostException&) { // Expected. @@ -378,9 +400,9 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) test(caCert->checkValidity()); test(!caCert->checkValidity(IceUtil::Time::seconds(0))); - test(!serverCert->verify(serverCert->getPublicKey())); - test(serverCert->verify(caCert->getPublicKey())); - test(caCert->verify(caCert->getPublicKey())); + test(!serverCert->verify(serverCert)); + test(serverCert->verify(caCert)); + test(caCert->verify(caCert)); IceSSL::NativeConnectionInfoPtr info = IceSSL::NativeConnectionInfoPtr::dynamicCast(server->ice_getConnection()->getInfo()); @@ -396,7 +418,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) test(info->nativeCerts[0]->checkValidity() && info->nativeCerts[1]->checkValidity()); test(!info->nativeCerts[0]->checkValidity(IceUtil::Time::seconds(0)) && !info->nativeCerts[1]->checkValidity(IceUtil::Time::seconds(0))); - test(info->nativeCerts[0]->verify(info->nativeCerts[1]->getPublicKey())); + test(info->nativeCerts[0]->verify(info->nativeCerts[1])); test(info->nativeCerts.size() == 2 && info->nativeCerts[0]->getSubjectDN() == serverCert->getSubjectDN() && info->nativeCerts[0]->getIssuerDN() == serverCert->getIssuerDN()); @@ -456,7 +478,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) { // Expected. } -#ifdef _WIN32 +#if defined(_WIN32) || defined(ICE_USE_SECURE_TRANSPORT) catch(const ConnectionLostException&) { // Expected. @@ -501,7 +523,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) { // Expected. } -#ifdef _WIN32 +#if defined(_WIN32) || defined(ICE_USE_SECURE_TRANSPORT) catch(const ConnectionLostException&) { // Expected. @@ -509,7 +531,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) #endif catch(const LocalException&) { - test(false); + test(false); } fact->destroyServer(server); @@ -650,7 +672,11 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) // InitializationData initData; initData.properties = createClientProps(defaultProperties, defaultDir, defaultHost); +#ifdef ICE_USE_OPENSSL initData.properties->setProperty("IceSSL.Ciphers", "ADH"); +#else + initData.properties->setProperty("IceSSL.Ciphers", "(DH_anon*)"); +#endif initData.properties->setProperty("IceSSL.VerifyPeer", "0"); CommunicatorPtr comm = initialize(initData); IceSSL::PluginPtr plugin = IceSSL::PluginPtr::dynamicCast(comm->getPluginManager()->getPlugin("IceSSL")); @@ -661,12 +687,17 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); test(fact); Test::Properties d = createServerProps(defaultProperties, defaultDir, defaultHost); +#ifdef ICE_USE_OPENSSL + string cipherSub = "ADH-"; d["IceSSL.Ciphers"] = "ADH"; +#else + string cipherSub = "DH_anon"; + d["IceSSL.Ciphers"] = "(DH_anon*)"; +#endif d["IceSSL.VerifyPeer"] = "0"; Test::ServerPrx server = fact->createServer(d); try { - string cipherSub = "ADH-"; server->checkCipher(cipherSub); IceSSL::NativeConnectionInfoPtr info = IceSSL::NativeConnectionInfoPtr::dynamicCast(server->ice_getConnection()->getInfo()); @@ -744,8 +775,10 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) } cout << "ok" << endl; + cout << "testing protocols... " << flush; { +#ifndef ICE_USE_SECURE_TRANSPORT // // This should fail because the client and server have no protocol // in common. @@ -804,9 +837,72 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) } fact->destroyServer(server); comm->destroy(); +#else + // + // This should fail because the client and server have no protocol + // in common. + // + InitializationData initData; + initData.properties = createClientProps(defaultProperties, defaultDir, defaultHost); + initData.properties->setProperty("IceSSL.Ciphers", "(DH_anon*)"); + initData.properties->setProperty("IceSSL.VerifyPeer", "0"); + initData.properties->setProperty("IceSSL.ProtocolVersionMax", "ssl3"); + initData.properties->setProperty("IceSSL.ProtocolVersionMin", "ssl3"); + CommunicatorPtr comm = initialize(initData); + Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); + test(fact); + Test::Properties d = createServerProps(defaultProperties, defaultDir, defaultHost); + d["IceSSL.Ciphers"] = "(DH_anon*)"; + d["IceSSL.VerifyPeer"] = "0"; + d["IceSSL.ProtocolVersionMax"] = "tls1_2"; + d["IceSSL.ProtocolVersionMin"] = "tls1_2"; + Test::ServerPrx server = fact->createServer(d); + try + { + server->ice_ping(); + test(false); + } + catch(const ProtocolException&) + { + // Expected on some platforms. + } + catch(const ConnectionLostException&) + { + // Expected on some platforms. + } + catch(const LocalException&) + { + test(false); + } + fact->destroyServer(server); + comm->destroy(); + + // + // This should succeed. + // + comm = initialize(initData); + fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); + test(fact); + d = createServerProps(defaultProperties, defaultDir, defaultHost); + d["IceSSL.Ciphers"] = "(DH_anon*)"; + d["IceSSL.VerifyPeer"] = "0"; + d["IceSSL.ProtocolVersionMax"] = "tls1"; + d["IceSSL.ProtocolVersionMin"] = "ssl3"; + server = fact->createServer(d); + try + { + server->ice_ping(); + } + catch(const LocalException&) + { + test(false); + } + fact->destroyServer(server); + comm->destroy(); +#endif } cout << "ok" << endl; - + cout << "testing expired certificates... " << flush; { // @@ -840,7 +936,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) { // Expected. } -#ifdef _WIN32 +#if defined(_WIN32) || defined(ICE_USE_SECURE_TRANSPORT) catch(const ConnectionLostException&) { // Expected. @@ -862,6 +958,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) IceSSL::Certificate::load(defaultDir + "/c_rsa_nopass_ca1_exp_pub.pem"); test(!cert->checkValidity()); } + initData.properties = createClientProps(defaultProperties, defaultDir, defaultHost); initData.properties->setProperty("IceSSL.CertAuthFile", "cacert1.pem"); initData.properties->setProperty("IceSSL.CertFile", "c_rsa_nopass_ca1_exp_pub.pem"); initData.properties->setProperty("IceSSL.KeyFile", "c_rsa_nopass_ca1_exp_priv.pem"); @@ -882,7 +979,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) { // Expected. } -#ifdef _WIN32 +#if defined(_WIN32) || defined(ICE_USE_SECURE_TRANSPORT) catch(const ConnectionLostException&) { // Expected. @@ -985,6 +1082,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) try { pm->initializePlugins(); + test(false); } catch(const PluginInitializationException&) { @@ -1008,7 +1106,11 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) // InitializationData initData; initData.properties = createClientProps(defaultProperties, defaultDir, defaultHost); +#ifdef ICE_USE_OPENSSL initData.properties->setProperty("IceSSL.Ciphers", "ADH"); +#else + initData.properties->setProperty("IceSSL.Ciphers", "(DH_anon*)"); +#endif CommunicatorPtr comm = initialize(initData); Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); test(fact); @@ -1016,12 +1118,17 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) d["IceSSL.CertAuthFile"] = "cacert1.pem"; d["IceSSL.CertFile"] = "s_rsa_nopass_ca1_pub.pem"; d["IceSSL.KeyFile"] = "s_rsa_nopass_ca1_priv.pem"; +#ifdef ICE_USE_OPENSSL + string cipherSub = "ADH-"; d["IceSSL.Ciphers"] = "RSA:ADH"; +#else + string cipherSub = "DH_"; + d["IceSSL.Ciphers"] = "(RSA_*) (DH_anon*)"; +#endif d["IceSSL.VerifyPeer"] = "1"; Test::ServerPrx server = fact->createServer(d); try { - string cipherSub = "ADH-"; server->checkCipher(cipherSub); IceSSL::NativeConnectionInfoPtr info = IceSSL::NativeConnectionInfoPtr::dynamicCast(server->ice_getConnection()->getInfo()); @@ -1035,6 +1142,68 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) comm->destroy(); } +#ifdef ICE_USE_SECURE_TRANSPORT + { + // + // Test IceSSL.DHParams + // + InitializationData initData; + initData.properties = createClientProps(defaultProperties, defaultDir, defaultHost); + initData.properties->setProperty("IceSSL.Ciphers", "(DH_anon*)"); + CommunicatorPtr comm = initialize(initData); + Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); + test(fact); + Test::Properties d = createServerProps(defaultProperties, defaultDir, defaultHost); + d["IceSSL.Ciphers"] = "(DH_anon*)"; + d["IceSSL.DHParams"] = "dh_params512.der"; + d["IceSSL.VerifyPeer"] = "0"; + Test::ServerPrx server = fact->createServer(d); + try + { + server->checkCipher("DH_anon"); + } + catch(const LocalException& ex) + { + cerr << ex << endl; + test(false); + } + fact->destroyServer(server); + comm->destroy(); + } + + { + // + // Test IceSSL.DHParams + // + InitializationData initData; + initData.properties = createClientProps(defaultProperties, defaultDir, defaultHost); + initData.properties->setProperty("IceSSL.Ciphers", "(DH_anon*)"); + CommunicatorPtr comm = initialize(initData); + Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); + test(fact); + Test::Properties d = createServerProps(defaultProperties, defaultDir, defaultHost); + d["IceSSL.Ciphers"] = "(DH_anon*)"; + d["IceSSL.DHParams"] = "dh_params1024.der"; + d["IceSSL.VerifyPeer"] = "0"; + Test::ServerPrx server = fact->createServer(d); + try + { + server->checkCipher("DH_anon"); + } + catch(const LocalException& ex) + { + cerr << ex << endl; + test(false); + } + fact->destroyServer(server); + comm->destroy(); + } +#endif + +// +// No DSA support in Secure Transport. +// +#ifndef ICE_USE_SECURE_TRANSPORT { // // Configure a server with RSA and DSA certificates. @@ -1154,12 +1323,12 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) { // Expected. } -#ifdef _WIN32 +# ifdef _WIN32 catch(const ConnectionLostException&) { // Expected. } -#endif +# endif catch(const LocalException& ex) { cerr << ex << endl; @@ -1168,6 +1337,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) fact->destroyServer(server); comm->destroy(); } +#endif cout << "ok" << endl; cout << "testing IceSSL.TrustOnly... " << flush; @@ -1643,14 +1813,22 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) InitializationData initData; initData.properties = createClientProps(defaultProperties, defaultDir, defaultHost); CommunicatorPtr comm = initialize(initData); +#ifdef ICE_USE_OPENSSL initData.properties->setProperty("IceSSL.Ciphers", "ADH"); +#else + initData.properties->setProperty("IceSSL.Ciphers", "(DH_anon*)"); +#endif initData.properties->setProperty("IceSSL.VerifyPeer", "0"); Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); test(fact); Test::Properties d = createServerProps(defaultProperties, defaultDir, defaultHost); d["IceSSL.TrustOnly"] = "C=US, ST=Florida, O=ZeroC\\, Inc.,OU=Ice, emailAddress=info@zeroc.com, CN=Client"; +#ifdef ICE_USE_OPENSSL d["IceSSL.Ciphers"] = "ADH"; +#else + d["IceSSL.Ciphers"] = "(DH_anon*)"; +#endif d["IceSSL.VerifyPeer"] = "0"; Test::ServerPrx server = fact->createServer(d); try @@ -1671,14 +1849,22 @@ allTests(const CommunicatorPtr& communicator, const string& testDir) InitializationData initData; initData.properties = createClientProps(defaultProperties, defaultDir, defaultHost); CommunicatorPtr comm = initialize(initData); +#ifdef ICE_USE_OPENSSL initData.properties->setProperty("IceSSL.Ciphers", "ADH"); +#else + initData.properties->setProperty("IceSSL.Ciphers", "(DH_anon*)"); +#endif initData.properties->setProperty("IceSSL.VerifyPeer", "0"); Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); test(fact); Test::Properties d = createServerProps(defaultProperties, defaultDir, defaultHost); d["IceSSL.TrustOnly"] = "!C=US, ST=Florida, O=ZeroC\\, Inc.,OU=Ice, emailAddress=info@zeroc.com, CN=Client"; +#ifdef ICE_USE_OPENSSL d["IceSSL.Ciphers"] = "ADH"; +#else + d["IceSSL.Ciphers"] = "(DH_anon*)"; +#endif d["IceSSL.VerifyPeer"] = "0"; Test::ServerPrx server = fact->createServer(d); try diff --git a/cpp/test/IceSSL/configuration/Makefile b/cpp/test/IceSSL/configuration/Makefile index ff8a2c9091d..b0c096e4731 100644 --- a/cpp/test/IceSSL/configuration/Makefile +++ b/cpp/test/IceSSL/configuration/Makefile @@ -33,12 +33,16 @@ include $(top_srcdir)/config/Make.rules CPPFLAGS := -I. -I../../include $(CPPFLAGS) +ifeq ($(UNAME),Darwin) +LINK_WITH = -framework Security -framework CoreFoundation +endif + $(CLIENT): $(COBJS) rm -f $@ - $(call mktest,$@,$(COBJS),$(ICESSL_LIBS) $(LIBS)) + $(call mktest,$@,$(COBJS),$(ICESSL_LIBS) $(LINK_WITH) $(LIBS)) $(SERVER): $(SOBJS) rm -f $@ - $(call mktest,$@,$(SOBJS),$(ICESSL_LIBS) $(LIBS)) + $(call mktest,$@,$(SOBJS),$(ICESSL_LIBS) $(LINK_WITH) $(LIBS)) include .depend diff --git a/cpp/test/IceSSL/configuration/TestI.cpp b/cpp/test/IceSSL/configuration/TestI.cpp index f24f506c159..151f141ad1e 100644 --- a/cpp/test/IceSSL/configuration/TestI.cpp +++ b/cpp/test/IceSSL/configuration/TestI.cpp @@ -12,6 +12,7 @@ #include <TestI.h> #include <TestCommon.h> #include <IceSSL/Plugin.h> +#include <Util.h> using namespace std; using namespace Ice; @@ -68,7 +69,11 @@ ServerI::checkCipher(const string& cipher, const Ice::Current& c) void ServerI::destroy() { + string defaultDir = _communicator->getProperties()->getProperty("IceSSL.DefaultDir"); _communicator->destroy(); +#ifdef ICE_USE_SECURE_TRANSPORT + removeKeychain("server.keychain", "password"); +#endif } Test::ServerPrx diff --git a/cpp/test/IceSSL/configuration/Util.h b/cpp/test/IceSSL/configuration/Util.h new file mode 100644 index 00000000000..a34d17cc87c --- /dev/null +++ b/cpp/test/IceSSL/configuration/Util.h @@ -0,0 +1,37 @@ + +#include <IceSSL/IceSSL.h> + +#ifdef ICE_USE_SECURE_TRANSPORT + +#include <IceUtil/FileUtil.h> +#include <Security/Security.h> + +void +removeKeychain(const std::string& keychainPath, const std::string& password) +{ + // + // KeyChain path is relative to the current working directory. + // + std::string path = keychainPath; + if(!IceUtilInternal::isAbsolutePath(keychainPath)) + { + std::string cwd; + if(IceUtilInternal::getcwd(cwd) == 0) + { + path = std::string(cwd) + '/' + path; + } + } + + SecKeychainRef keychain; + OSStatus err = SecKeychainOpen(path.c_str(), &keychain); + if(err == noErr) + { + err = SecKeychainUnlock(keychain, password.size(), password.c_str(), true); + if(err == noErr) + { + err = SecKeychainDelete(keychain); + } + CFRelease(keychain); + } +} +#endif
\ No newline at end of file diff --git a/cs/src/Ice/PropertyNames.cs b/cs/src/Ice/PropertyNames.cs index 9d5f5b7d85b..10c2ccdd18a 100644 --- a/cs/src/Ice/PropertyNames.cs +++ b/cs/src/Ice/PropertyNames.cs @@ -6,7 +6,7 @@ // ICE_LICENSE file included in this distribution. // // ********************************************************************** -// Generated by makeprops.py from file ./config/PropertyNames.xml, Wed Jun 4 19:01:29 2014 +// Generated by makeprops.py from file ../config/PropertyNames.xml, Thu Jun 5 21:19:31 2014 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -111,8 +111,7 @@ namespace IceInternal new Property(@"^Ice\.IPv6$", false, null), new Property(@"^Ice\.EventLog\.Source$", false, null), new Property(@"^Ice\.FactoryAssemblies$", false, null), - new Property(@"^Ice\.GC$", false, null), - new Property(@"^Ice\.GC\.Interval$", true, @"Ice.GC"), + new Property(@"^Ice\.CollectObjects$", false, null), new Property(@"^Ice\.ImplicitContext$", false, null), new Property(@"^Ice\.InitPlugins$", false, null), new Property(@"^Ice\.LogFile$", false, null), @@ -157,7 +156,6 @@ namespace IceInternal new Property(@"^Ice\.ThreadPool\.Server\.ThreadPriority$", false, null), new Property(@"^Ice\.ThreadPriority$", false, null), new Property(@"^Ice\.Trace\.Admin\.Properties$", false, null), - new Property(@"^Ice\.Trace\.GC$", true, null), new Property(@"^Ice\.Trace\.Locator$", false, null), new Property(@"^Ice\.Trace\.Network$", false, null), new Property(@"^Ice\.Trace\.Protocol$", false, null), @@ -816,12 +814,15 @@ namespace IceInternal new Property(@"^IceSSL\.Ciphers$", false, null), new Property(@"^IceSSL\.DefaultDir$", false, null), new Property(@"^IceSSL\.DH\.[^\s]+$", false, null), + new Property(@"^IceSSL\.DHParams$", false, null), new Property(@"^IceSSL\.EntropyDaemon$", false, null), new Property(@"^IceSSL\.FindCert\.[^\s]+$", false, null), new Property(@"^IceSSL\.ImportCert\.[^\s]+$", false, null), new Property(@"^IceSSL\.InitOpenSSL$", false, null), new Property(@"^IceSSL\.KeyFile$", false, null), new Property(@"^IceSSL\.KeySet$", false, null), + new Property(@"^IceSSL\.Keychain$", false, null), + new Property(@"^IceSSL\.KeychainPassword$", false, null), new Property(@"^IceSSL\.Keystore$", false, null), new Property(@"^IceSSL\.KeystorePassword$", false, null), new Property(@"^IceSSL\.KeystoreType$", false, null), @@ -830,6 +831,8 @@ namespace IceInternal new Property(@"^IceSSL\.PasswordRetryMax$", false, null), new Property(@"^IceSSL\.PersistKeySet$", false, null), new Property(@"^IceSSL\.Protocols$", false, null), + new Property(@"^IceSSL\.ProtocolVersionMax$", false, null), + new Property(@"^IceSSL\.ProtocolVersionMin$", false, null), new Property(@"^IceSSL\.Random$", false, null), new Property(@"^IceSSL\.Trace\.Security$", false, null), new Property(@"^IceSSL\.TrustOnly$", false, null), diff --git a/java/src/IceInternal/PropertyNames.java b/java/src/IceInternal/PropertyNames.java index 9859bfcac97..51aaf7ee700 100644 --- a/java/src/IceInternal/PropertyNames.java +++ b/java/src/IceInternal/PropertyNames.java @@ -6,7 +6,7 @@ // ICE_LICENSE file included in this distribution. // // ********************************************************************** -// Generated by makeprops.py from file ./config/PropertyNames.xml, Wed Jun 4 19:01:29 2014 +// Generated by makeprops.py from file ../config/PropertyNames.xml, Thu Jun 5 21:19:31 2014 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -111,8 +111,7 @@ public final class PropertyNames new Property("Ice\\.IPv6", false, null), new Property("Ice\\.EventLog\\.Source", false, null), new Property("Ice\\.FactoryAssemblies", false, null), - new Property("Ice\\.GC", false, null), - new Property("Ice\\.GC\\.Interval", true, "Ice.GC"), + new Property("Ice\\.CollectObjects", false, null), new Property("Ice\\.ImplicitContext", false, null), new Property("Ice\\.InitPlugins", false, null), new Property("Ice\\.LogFile", false, null), @@ -157,7 +156,6 @@ public final class PropertyNames new Property("Ice\\.ThreadPool\\.Server\\.ThreadPriority", false, null), new Property("Ice\\.ThreadPriority", false, null), new Property("Ice\\.Trace\\.Admin\\.Properties", false, null), - new Property("Ice\\.Trace\\.GC", true, null), new Property("Ice\\.Trace\\.Locator", false, null), new Property("Ice\\.Trace\\.Network", false, null), new Property("Ice\\.Trace\\.Protocol", false, null), @@ -816,12 +814,15 @@ public final class PropertyNames new Property("IceSSL\\.Ciphers", false, null), new Property("IceSSL\\.DefaultDir", false, null), new Property("IceSSL\\.DH\\.[^\\s]+", false, null), + new Property("IceSSL\\.DHParams", false, null), new Property("IceSSL\\.EntropyDaemon", false, null), new Property("IceSSL\\.FindCert\\.[^\\s]+", false, null), new Property("IceSSL\\.ImportCert\\.[^\\s]+", false, null), new Property("IceSSL\\.InitOpenSSL", false, null), new Property("IceSSL\\.KeyFile", false, null), new Property("IceSSL\\.KeySet", false, null), + new Property("IceSSL\\.Keychain", false, null), + new Property("IceSSL\\.KeychainPassword", false, null), new Property("IceSSL\\.Keystore", false, null), new Property("IceSSL\\.KeystorePassword", false, null), new Property("IceSSL\\.KeystoreType", false, null), @@ -830,6 +831,8 @@ public final class PropertyNames new Property("IceSSL\\.PasswordRetryMax", false, null), new Property("IceSSL\\.PersistKeySet", false, null), new Property("IceSSL\\.Protocols", false, null), + new Property("IceSSL\\.ProtocolVersionMax", false, null), + new Property("IceSSL\\.ProtocolVersionMin", false, null), new Property("IceSSL\\.Random", false, null), new Property("IceSSL\\.Trace\\.Security", false, null), new Property("IceSSL\\.TrustOnly", false, null), diff --git a/js/src/Ice/PropertyNames.js b/js/src/Ice/PropertyNames.js index 6dd789a3b42..2584cb90ea1 100644 --- a/js/src/Ice/PropertyNames.js +++ b/js/src/Ice/PropertyNames.js @@ -6,7 +6,7 @@ // ICE_LICENSE file included in this distribution. // // ********************************************************************** -// Generated by makeprops.py from file ./config/PropertyNames.xml, Wed Jun 4 19:01:29 2014 +// Generated by makeprops.py from file ../config/PropertyNames.xml, Thu Jun 5 21:19:31 2014 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -114,8 +114,7 @@ new Property("/^Ice\.IPv6/", false, null), new Property("/^Ice\.EventLog\.Source/", false, null), new Property("/^Ice\.FactoryAssemblies/", false, null), - new Property("/^Ice\.GC/", false, null), - new Property("/^Ice\.GC\.Interval/", true, "Ice.GC"), + new Property("/^Ice\.CollectObjects/", false, null), new Property("/^Ice\.ImplicitContext/", false, null), new Property("/^Ice\.InitPlugins/", false, null), new Property("/^Ice\.LogFile/", false, null), @@ -160,7 +159,6 @@ new Property("/^Ice\.ThreadPool\.Server\.ThreadPriority/", false, null), new Property("/^Ice\.ThreadPriority/", false, null), new Property("/^Ice\.Trace\.Admin\.Properties/", false, null), - new Property("/^Ice\.Trace\.GC/", true, null), new Property("/^Ice\.Trace\.Locator/", false, null), new Property("/^Ice\.Trace\.Network/", false, null), new Property("/^Ice\.Trace\.Protocol/", false, null), diff --git a/py/demo/Glacier2/callback/config.client b/py/demo/Glacier2/callback/config.client index 58e513140d6..57e516d4668 100644 --- a/py/demo/Glacier2/callback/config.client +++ b/py/demo/Glacier2/callback/config.client @@ -56,3 +56,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password diff --git a/py/demo/Glacier2/callback/config.glacier2 b/py/demo/Glacier2/callback/config.glacier2 index cd9075b4e8a..aa737517f5c 100644 --- a/py/demo/Glacier2/callback/config.glacier2 +++ b/py/demo/Glacier2/callback/config.glacier2 @@ -126,3 +126,6 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=glacier2.keychain +IceSSL.KeychainPassword=password + diff --git a/py/demo/Ice/callback/config.client b/py/demo/Ice/callback/config.client index 5adf6dba22d..585386d09c1 100644 --- a/py/demo/Ice/callback/config.client +++ b/py/demo/Ice/callback/config.client @@ -50,3 +50,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password
\ No newline at end of file diff --git a/py/demo/Ice/callback/config.server b/py/demo/Ice/callback/config.server index ad65f43d4d8..98b8830645a 100644 --- a/py/demo/Ice/callback/config.server +++ b/py/demo/Ice/callback/config.server @@ -44,3 +44,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password
\ No newline at end of file diff --git a/py/demo/Ice/hello/config.client b/py/demo/Ice/hello/config.client index 45f24989be0..5c412eddd05 100644 --- a/py/demo/Ice/hello/config.client +++ b/py/demo/Ice/hello/config.client @@ -53,6 +53,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/py/demo/Ice/hello/config.server b/py/demo/Ice/hello/config.server index 01ed3ded598..1607ee2367c 100644 --- a/py/demo/Ice/hello/config.server +++ b/py/demo/Ice/hello/config.server @@ -49,6 +49,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password # # For secure web sockets we need to disable VerifyPeer. diff --git a/py/demo/Ice/latency/config.client b/py/demo/Ice/latency/config.client index f88fdf8f932..9d45b40b231 100644 --- a/py/demo/Ice/latency/config.client +++ b/py/demo/Ice/latency/config.client @@ -29,6 +29,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/py/demo/Ice/latency/config.server b/py/demo/Ice/latency/config.server index 745da939068..b5b63fa5fa0 100644 --- a/py/demo/Ice/latency/config.server +++ b/py/demo/Ice/latency/config.server @@ -22,6 +22,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password # # For secure web sockets we need to disable VerifyPeer. diff --git a/py/demo/Ice/properties/config.client b/py/demo/Ice/properties/config.client index 8b079dfc267..db41ebb2459 100644 --- a/py/demo/Ice/properties/config.client +++ b/py/demo/Ice/properties/config.client @@ -49,3 +49,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password
\ No newline at end of file diff --git a/py/demo/Ice/properties/config.server b/py/demo/Ice/properties/config.server index 3fae2c52d4d..2969290fd56 100644 --- a/py/demo/Ice/properties/config.server +++ b/py/demo/Ice/properties/config.server @@ -59,3 +59,5 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password diff --git a/py/demo/Ice/throughput/config.client b/py/demo/Ice/throughput/config.client index 0ec65d10bbe..121898e30eb 100644 --- a/py/demo/Ice/throughput/config.client +++ b/py/demo/Ice/throughput/config.client @@ -29,6 +29,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/py/demo/Ice/throughput/config.server b/py/demo/Ice/throughput/config.server index 8a57efbe27b..1e1182633ac 100644 --- a/py/demo/Ice/throughput/config.server +++ b/py/demo/Ice/throughput/config.server @@ -23,6 +23,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=s_rsa1024_pub.pem IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.Keychain=server.keychain +IceSSL.KeychainPassword=password # # For secure web sockets we need to disable VerifyPeer. diff --git a/py/test/Ice/timeout/AllTests.py b/py/test/Ice/timeout/AllTests.py index 74fde1c929b..b5df795245a 100644 --- a/py/test/Ice/timeout/AllTests.py +++ b/py/test/Ice/timeout/AllTests.py @@ -92,7 +92,7 @@ def allTests(communicator, collocated): else: seq = bytes([0 for x in range(0, 10000000)]) to = Test.TimeoutPrx.uncheckedCast(obj.ice_timeout(250)) - to.holdAdapter(500) + to.holdAdapter(1000) try: to.sendData(seq) test(False) @@ -208,9 +208,9 @@ def allTests(communicator, collocated): # initData = Ice.InitializationData() initData.properties = communicator.getProperties().clone() - initData.properties.setProperty("Ice.Override.ConnectTimeout", "250") + initData.properties.setProperty("Ice.Override.ConnectTimeout", "500") comm = Ice.initialize(initData) - timeout.holdAdapter(500) + timeout.holdAdapter(750) to = Test.TimeoutPrx.uncheckedCast(comm.stringToProxy(sref)) try: to.op() @@ -221,8 +221,8 @@ def allTests(communicator, collocated): # Calling ice_timeout() should have no effect on the connect timeout. # timeout.op() # Ensure adapter is active. - timeout.holdAdapter(500) - to = Test.TimeoutPrx.uncheckedCast(to.ice_timeout(750)) + timeout.holdAdapter(750) + to = Test.TimeoutPrx.uncheckedCast(to.ice_timeout(1000)) try: to.op() test(False) @@ -232,8 +232,8 @@ def allTests(communicator, collocated): # Verify that timeout set via ice_timeout() is still used for requests. # to.op() # Force connection. - timeout.holdAdapter(500); - to = Test.TimeoutPrx.uncheckedCast(to.ice_timeout(250)); + timeout.holdAdapter(750); + to = Test.TimeoutPrx.uncheckedCast(to.ice_timeout(500)); try: to.sendData(seq) test(False) diff --git a/py/test/Ice/timeout/Client.py b/py/test/Ice/timeout/Client.py index 71113828f45..1114f12c75c 100755 --- a/py/test/Ice/timeout/Client.py +++ b/py/test/Ice/timeout/Client.py @@ -40,7 +40,7 @@ try: # We need to send messages large enough to cause the transport # buffers to fill up. # - initData.properties.setProperty("Ice.MessageSizeMax", "10000"); + initData.properties.setProperty("Ice.MessageSizeMax", "10000000"); # # For this test, we want to disable retries. diff --git a/rb/demo/Ice/hello/config.client b/rb/demo/Ice/hello/config.client index c71a06aa5a0..b573693537b 100644 --- a/rb/demo/Ice/hello/config.client +++ b/rb/demo/Ice/hello/config.client @@ -43,6 +43,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/rb/demo/Ice/latency/config.client b/rb/demo/Ice/latency/config.client index bbe9ec81b88..c140ea09eab 100644 --- a/rb/demo/Ice/latency/config.client +++ b/rb/demo/Ice/latency/config.client @@ -17,6 +17,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/rb/demo/Ice/throughput/config.client b/rb/demo/Ice/throughput/config.client index b32876fb71f..6e969c7cf54 100644 --- a/rb/demo/Ice/throughput/config.client +++ b/rb/demo/Ice/throughput/config.client @@ -17,6 +17,8 @@ IceSSL.DefaultDir=../../../../certs IceSSL.CertAuthFile=cacert.pem IceSSL.CertFile=c_rsa1024_pub.pem IceSSL.KeyFile=c_rsa1024_priv.pem +IceSSL.Keychain=client.keychain +IceSSL.KeychainPassword=password # # IceMX configuration. diff --git a/rb/test/Ice/timeout/AllTests.rb b/rb/test/Ice/timeout/AllTests.rb index 401e7362bb8..db9288c7769 100644 --- a/rb/test/Ice/timeout/AllTests.rb +++ b/rb/test/Ice/timeout/AllTests.rb @@ -152,9 +152,9 @@ def allTests(communicator) # initData = Ice::InitializationData.new initData.properties = communicator.getProperties().clone() - initData.properties.setProperty("Ice.Override.ConnectTimeout", "250") + initData.properties.setProperty("Ice.Override.ConnectTimeout", "500") comm = Ice.initialize(initData) - timeout.holdAdapter(500) + timeout.holdAdapter(1000) to = Test::TimeoutPrx::uncheckedCast(comm.stringToProxy(sref)) begin to.op() @@ -167,8 +167,8 @@ def allTests(communicator) # Calling ice_timeout() should have no effect on the connect timeout. # timeout.op() # Ensure adapter is active. - timeout.holdAdapter(500) - to = Test::TimeoutPrx::uncheckedCast(to.ice_timeout(750)) + timeout.holdAdapter(750) + to = Test::TimeoutPrx::uncheckedCast(to.ice_timeout(1000)) begin to.op() test(false) @@ -180,8 +180,8 @@ def allTests(communicator) # Verify that timeout set via ice_timeout() is still used for requests. # to.op() # Force connection. - timeout.holdAdapter(500); - to = Test::TimeoutPrx::uncheckedCast(to.ice_timeout(250)) + timeout.holdAdapter(750) + to = Test::TimeoutPrx::uncheckedCast(to.ice_timeout(500)) begin to.sendData(seq) test(false) diff --git a/scripts/TestUtil.py b/scripts/TestUtil.py index 04323018352..860d78cf23d 100755 --- a/scripts/TestUtil.py +++ b/scripts/TestUtil.py @@ -707,6 +707,12 @@ sslConfigTree = { "--IceSSL.CheckCertName=0" }, } + +if isDarwin(): + sslConfigTree["cpp"]["client"] += " --IceSSL.Keychain=client.keychain --IceSSL.KeychainPassword=password" + sslConfigTree["cpp"]["server"] += " --IceSSL.Keychain=server.keychain --IceSSL.KeychainPassword=password" + sslConfigTree["cpp"]["colloc"] += " --IceSSL.Keychain=colloc.keychain --IceSSL.KeychainPassword=password" + sslConfigTree["py"] = sslConfigTree["cpp"] sslConfigTree["rb"] = sslConfigTree["cpp"] sslConfigTree["php"] = sslConfigTree["cpp"] |