summaryrefslogtreecommitdiff
path: root/cpp/src
diff options
context:
space:
mode:
authorJose <jose@zeroc.com>2014-06-06 16:33:11 +0200
committerJose <jose@zeroc.com>2014-06-06 16:33:11 +0200
commit7ba5b1fa9d9849182b19aebe5bad1570fb82452b (patch)
tree2b6a4f6407b4cc860d01f6e737959122a719ca3f /cpp/src
parentFixed ICE-5499, new garbage collection support (diff)
downloadice-7ba5b1fa9d9849182b19aebe5bad1570fb82452b.tar.bz2
ice-7ba5b1fa9d9849182b19aebe5bad1570fb82452b.tar.xz
ice-7ba5b1fa9d9849182b19aebe5bad1570fb82452b.zip
Fixed (ICE-4894) - Native SSL implementation for OS X
Diffstat (limited to 'cpp/src')
-rw-r--r--cpp/src/Ice/Network.h2
-rw-r--r--cpp/src/Ice/PropertyNames.cpp11
-rw-r--r--cpp/src/Ice/PropertyNames.h2
-rw-r--r--cpp/src/IceSSL/AcceptorI.cpp4
-rw-r--r--cpp/src/IceSSL/Certificate.cpp531
-rw-r--r--cpp/src/IceSSL/ConnectorI.cpp3
-rw-r--r--cpp/src/IceSSL/EndpointI.cpp2
-rw-r--r--cpp/src/IceSSL/Instance.cpp1156
-rw-r--r--cpp/src/IceSSL/Instance.h93
-rw-r--r--cpp/src/IceSSL/InstanceF.h4
-rw-r--r--cpp/src/IceSSL/Makefile18
-rw-r--r--cpp/src/IceSSL/Makefile.mak4
-rw-r--r--cpp/src/IceSSL/OpenSSLEngine.cpp920
-rw-r--r--cpp/src/IceSSL/PluginI.cpp41
-rw-r--r--cpp/src/IceSSL/PluginI.h22
-rw-r--r--cpp/src/IceSSL/RFC2253.cpp1
-rw-r--r--cpp/src/IceSSL/SSLEngine.cpp291
-rw-r--r--cpp/src/IceSSL/SSLEngine.h179
-rw-r--r--cpp/src/IceSSL/SSLEngineF.h37
-rw-r--r--cpp/src/IceSSL/SecureTransportEngine.cpp1515
-rw-r--r--cpp/src/IceSSL/SecureTransportTransceiverI.cpp1055
-rw-r--r--cpp/src/IceSSL/SecureTransportTransceiverI.h114
-rw-r--r--cpp/src/IceSSL/TransceiverI.cpp49
-rw-r--r--cpp/src/IceSSL/TransceiverI.h10
-rw-r--r--cpp/src/IceSSL/Util.cpp640
-rw-r--r--cpp/src/IceSSL/Util.h78
-rw-r--r--cpp/src/IceUtil/FileUtil.cpp11
-rwxr-xr-xcpp/src/ca/iceca8
28 files changed, 5426 insertions, 1375 deletions
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(&params, 0, sizeof(params));
+ params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+
+ CFArrayRef items = 0;
+
+ OSStatus err = SecItemImport(data, 0, &format, &type, 0, &params, 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(&params, 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, &params, 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, &params, 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\