diff options
author | Benoit Foucher <benoit@zeroc.com> | 2015-04-28 19:27:04 +0200 |
---|---|---|
committer | Benoit Foucher <benoit@zeroc.com> | 2015-04-28 19:27:04 +0200 |
commit | e6e102cc642e78cf9da55645c82f5bfe6eacb76d (patch) | |
tree | ab5861ee9ad2a909fa0dc8f25b1b12e0dd1d6527 | |
parent | Fixed ICE-6443 and other SSL fixes (diff) | |
download | ice-e6e102cc642e78cf9da55645c82f5bfe6eacb76d.tar.bz2 ice-e6e102cc642e78cf9da55645c82f5bfe6eacb76d.tar.xz ice-e6e102cc642e78cf9da55645c82f5bfe6eacb76d.zip |
Fixed previous commit which was incomplete
73 files changed, 1715 insertions, 934 deletions
diff --git a/CHANGELOG-3.6.md b/CHANGELOG-3.6.md index c908d8de793..a2f8c5d272b 100644 --- a/CHANGELOG-3.6.md +++ b/CHANGELOG-3.6.md @@ -19,11 +19,23 @@ These are the changes since Ice 3.5.1. ## General Changes -- The default value of the IceSSL.VerifyDepthMax property is now 3 (it was previously 2). This allows certificate chains of 3 certificates (e.g: a Peer, CA and Root certificate chain). +- Added `IceSSL::WSSConnectionInfo` local Slice class to provide information on a WSS connection. This class extends the `IceSSL::ConnectionInfo` class. The `IceSSL::WSSNativeConnectionInfo` was also added to provide the language mapping specific certificates associated with the SSL connection. + +- Added `IceSSL::WSSEndpointInfo` local Slice class to provide WSS endpoint information. This class extends the `IceSSL::EndpointInfo` class. + +- Updated `Ice::WSEndpointInfo` to extend `Ice::TCPEndpointInfo` and `Ice::WSConnectionInfo` to extend `Ice::TCPConnectionInfo` + +- IceSSL now supports using the platform Root Certificate Authorities to validate remote certificates. The use of the platform Root CAs can be enabled with the `IceSSL.UsePlatformCAs` property. The platform Root CAs are only checked if `Ice.CAs` is not set. + +- The `IceSSL.CertAuthFile` and `IceSSL.CertAuthDir` properties have been deprecated. You should now use the `Ice.CAs` property to configure the path of the PEM file containing the Root Certificate Authorities or the path of a directory containing the certificates (OpenSSL only). + +- The `IceSSL.KeyFile` property has been deprecated. You should instead use `Ice.CertFile` and PKCS12 files to configure the IceSSL identity. + +- The default value of the `IceSSL.VerifyDepthMax` property is now 3 (it was previously 2). This allows certificate chains of 3 certificates (e.g: a Peer, CA and Root certificate chain). - The certificate chain provided in the `IceSSL::ConnectionInfo` should now always include the root certificate if the chain could successfully be verified. -- Added `verified` member to the `IceSSL::ConnectionInfo` class. This member indicates whether or not the peer certificate was successfully verified. This member is useful for clients which set IceSSL.VerifyPeer=0 to check if the server certificate could be verified or not. For server connections, the member should always be `true` since servers always reject invalid client certificates. +- Added `verified` member to the `IceSSL::ConnectionInfo` class. This member indicates whether or not the peer certificate was successfully verified. This member is useful for clients which set `IceSSL.VerifyPeer=0` to check if the server certificate could be verified or not. For server connections, the member should always be `true` since servers always reject invalid client certificates. - The Ice distribution now supports the Objective-C mapping on OS X. diff --git a/config/PropertyNames.xml b/config/PropertyNames.xml index c7992e322e6..2432f3bc19f 100644 --- a/config/PropertyNames.xml +++ b/config/PropertyNames.xml @@ -560,8 +560,9 @@ generated from the section label. <section name="IceSSL"> <property name="Alias" /> - <property name="CertAuthDir" /> - <property name="CertAuthFile" /> + <property name="CAs"/> + <property name="CertAuthDir" deprecated="true"/> + <property name="CertAuthFile" deprecated="true"/> <property name="CertStore" /> <property name="CertFile" /> <property name="CertVerifier" /> @@ -573,10 +574,10 @@ generated from the section label. <property name="DHParams" /> <property name="EntropyDaemon" /> <property name="FindCert" /> - <property name="FindCert.[any]" /> + <property name="FindCert.[any]" deprecated="true"/> <property name="ImportCert.[any]" deprecated="true"/> <property name="InitOpenSSL" /> - <property name="KeyFile" /> + <property name="KeyFile" deprecated="true"/> <property name="KeySet"/> <property name="Keychain"/> <property name="KeychainPassword"/> @@ -599,6 +600,7 @@ generated from the section label. <property name="Truststore" /> <property name="TruststorePassword" /> <property name="TruststoreType" /> + <property name="UsePlatformCAs" /> <property name="VerifyDepthMax" /> <property name="VerifyPeer" /> </section> diff --git a/cpp/include/IceSSL/Plugin.h b/cpp/include/IceSSL/Plugin.h index 5a7e7ea86cc..367fd73c5dd 100644 --- a/cpp/include/IceSSL/Plugin.h +++ b/cpp/include/IceSSL/Plugin.h @@ -55,14 +55,14 @@ #ifdef ICE_USE_OPENSSL // -// Pointer to an opaque SSL session context object. ssl_ctx_st is the +// Pointer to an opaque SSL session context object. ssl_ctx_st is the // OpenSSL type that holds configuration settings for all SSL // connections. // typedef struct ssl_ctx_st SSL_CTX; // -// Pointer to an opaque certificate object. X509_st is the OpenSSL +// Pointer to an opaque certificate object. X509_st is the OpenSSL // type that represents a certificate. // typedef struct x509_st* X509CertificateRef; @@ -185,7 +185,7 @@ class ICE_SSL_API PublicKey : public IceUtil::Shared public: ~PublicKey(); - + // // Retrieve the native public key value wrapped by this object. // @@ -202,7 +202,7 @@ private: CertificatePtr _cert; KeyRef _key; - + }; typedef IceUtil::Handle<PublicKey> PublicKeyPtr; @@ -282,7 +282,7 @@ class ICE_SSL_API Certificate : public IceUtil::Shared public: // - // Construct a certificate using a native certificate. + // Construct a certificate using a native certificate. // // The Certificate class assumes ownership of the given native // certificate. @@ -321,7 +321,7 @@ public: // public key. Returns true if signed, false otherwise. // bool verify(const CertificatePtr&) const; - + #ifdef ICE_USE_OPENSSL // // Verify that this certificate was signed by the given public @@ -331,10 +331,10 @@ public: // engines that require a certificate and not just a public key to // verify the certificate signature. // - ICE_DEPRECATED_API("verify(const PublicKeyPtr&) is deprecated, use verify(const CertificatePtr&) instead") + ICE_DEPRECATED_API("verify(const PublicKeyPtr&) is deprecated, use verify(const CertificatePtr&) instead") bool verify(const PublicKeyPtr&) const; #endif - + // // Return a string encoding of the certificate in PEM format. // Raises CertificateEncodingException if an error occurs. @@ -433,7 +433,7 @@ public: // // Retrieve the native X509 certificate value wrapped by this - // object. + // object. // // The returned reference is only valid for the lifetime of this // object. With SecureTransport you can increment the reference @@ -470,6 +470,23 @@ public: typedef IceUtil::Handle<NativeConnectionInfo> NativeConnectionInfoPtr; // +// WSSNativeConnectionInfo is an extension of IceSSL::WSSConnectionInfo +// that provides access to native certificates. +// +class ICE_SSL_API WSSNativeConnectionInfo : public WSSConnectionInfo +{ +public: + + // + // The certificate chain. This may be empty if the peer did not + // supply a certificate. The peer's certificate (if any) is the + // first one in the chain. + // + std::vector<CertificatePtr> nativeCerts; +}; +typedef IceUtil::Handle<WSSNativeConnectionInfo> WSSNativeConnectionInfoPtr; + +// // An application can customize the certificate verification process // by implementing the CertificateVerifier interface. // @@ -528,7 +545,7 @@ public: // the plug-in is initialized. // virtual void setPasswordPrompt(const PasswordPromptPtr&) = 0; - + #ifdef ICE_USE_OPENSSL // // Establish the OpenSSL context. This must be done before the @@ -539,7 +556,7 @@ public: // When the application supplies its own OpenSSL context, the // plug-in ignores configuration properties related to certificates, // keys, and passwords. - // + // // Note that the plugin assumes ownership of the given context. // virtual void setContext(SSL_CTX*) = 0; @@ -548,7 +565,7 @@ public: // Obtain the SSL context. Use caution when modifying this value. // Changes made to this value have no effect on existing connections. // - virtual SSL_CTX* getContext() = 0; + virtual SSL_CTX* getContext() = 0; #endif }; typedef IceUtil::Handle<Plugin> PluginPtr; diff --git a/cpp/src/Ice/EndpointI.h b/cpp/src/Ice/EndpointI.h index 8c9eb5fa055..df41372bd82 100644 --- a/cpp/src/Ice/EndpointI.h +++ b/cpp/src/Ice/EndpointI.h @@ -160,6 +160,38 @@ inline bool operator<(const EndpointI& l, const EndpointI& r) return static_cast<const ::Ice::LocalObject&>(l) < static_cast<const ::Ice::LocalObject&>(r); } +template<typename T> class InfoI : public T +{ +public: + + InfoI(const EndpointIPtr& endpoint) : _endpoint(endpoint) + { + } + + virtual Ice::Short + type() const + { + return _endpoint->type(); + } + + virtual bool + datagram() const + { + return _endpoint->datagram(); + } + + virtual bool + secure() const + { + return _endpoint->secure(); + } + +private: + + const EndpointIPtr _endpoint; +}; + + } #endif diff --git a/cpp/src/Ice/OpaqueEndpointI.cpp b/cpp/src/Ice/OpaqueEndpointI.cpp index e2f478c7231..c061eb0acfa 100644 --- a/cpp/src/Ice/OpaqueEndpointI.cpp +++ b/cpp/src/Ice/OpaqueEndpointI.cpp @@ -56,11 +56,11 @@ IceInternal::OpaqueEndpointI::OpaqueEndpointI(Short type, BasicStream* s) : _typ namespace { -class InfoI : public Ice::OpaqueEndpointInfo +class OpaqueEndpointInfoI : public Ice::OpaqueEndpointInfo { public: - InfoI(Ice::Short type, const Ice::EncodingVersion& rawEncoding, const Ice::ByteSeq& rawByes); + OpaqueEndpointInfoI(Ice::Short type, const Ice::EncodingVersion& rawEncoding, const Ice::ByteSeq& rawByes); virtual Ice::Short type() const @@ -89,7 +89,8 @@ private: // // COMPILERFIX: inlining this constructor causes crashes with gcc 4.0.1. // -InfoI::InfoI(Ice::Short type, const Ice::EncodingVersion& rawEncoding, const Ice::ByteSeq& rawBytes) : +OpaqueEndpointInfoI::OpaqueEndpointInfoI(Ice::Short type, const Ice::EncodingVersion& rawEncoding, + const Ice::ByteSeq& rawBytes) : Ice::OpaqueEndpointInfo(-1, false, rawEncoding, rawBytes), _type(type) { @@ -106,7 +107,7 @@ IceInternal::OpaqueEndpointI::streamWrite(BasicStream* s) const Ice::EndpointInfoPtr IceInternal::OpaqueEndpointI::getInfo() const { - return new InfoI(_type, _rawEncoding, _rawBytes); + return new OpaqueEndpointInfoI(_type, _rawEncoding, _rawBytes); } Short diff --git a/cpp/src/Ice/PropertyNames.cpp b/cpp/src/Ice/PropertyNames.cpp index 95cf61169a8..11b035d47df 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, Thu Mar 19 17:54:51 2015 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Tue Apr 28 17:34:50 2015 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -998,8 +998,9 @@ const IceInternal::PropertyArray const IceInternal::Property IceSSLPropsData[] = { IceInternal::Property("IceSSL.Alias", false, 0), - IceInternal::Property("IceSSL.CertAuthDir", false, 0), - IceInternal::Property("IceSSL.CertAuthFile", false, 0), + IceInternal::Property("IceSSL.CAs", false, 0), + IceInternal::Property("IceSSL.CertAuthDir", true, 0), + IceInternal::Property("IceSSL.CertAuthFile", true, 0), IceInternal::Property("IceSSL.CertStore", false, 0), IceInternal::Property("IceSSL.CertFile", false, 0), IceInternal::Property("IceSSL.CertVerifier", false, 0), @@ -1011,10 +1012,10 @@ const IceInternal::Property IceSSLPropsData[] = IceInternal::Property("IceSSL.DHParams", false, 0), IceInternal::Property("IceSSL.EntropyDaemon", false, 0), IceInternal::Property("IceSSL.FindCert", false, 0), - IceInternal::Property("IceSSL.FindCert.*", false, 0), + IceInternal::Property("IceSSL.FindCert.*", true, 0), IceInternal::Property("IceSSL.ImportCert.*", true, 0), IceInternal::Property("IceSSL.InitOpenSSL", false, 0), - IceInternal::Property("IceSSL.KeyFile", false, 0), + IceInternal::Property("IceSSL.KeyFile", true, 0), IceInternal::Property("IceSSL.KeySet", false, 0), IceInternal::Property("IceSSL.Keychain", false, 0), IceInternal::Property("IceSSL.KeychainPassword", false, 0), @@ -1037,6 +1038,7 @@ const IceInternal::Property IceSSLPropsData[] = IceInternal::Property("IceSSL.Truststore", false, 0), IceInternal::Property("IceSSL.TruststorePassword", false, 0), IceInternal::Property("IceSSL.TruststoreType", false, 0), + IceInternal::Property("IceSSL.UsePlatformCAs", false, 0), IceInternal::Property("IceSSL.VerifyDepthMax", false, 0), IceInternal::Property("IceSSL.VerifyPeer", false, 0), }; diff --git a/cpp/src/Ice/PropertyNames.h b/cpp/src/Ice/PropertyNames.h index 28b47265af6..cf92a38d43f 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, Thu Mar 19 17:54:51 2015 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Tue Apr 28 17:34:50 2015 // IMPORTANT: Do not edit this file -- any edits made here will be lost! diff --git a/cpp/src/Ice/TcpEndpointI.cpp b/cpp/src/Ice/TcpEndpointI.cpp index 606a442e94a..5e744c394bb 100644 --- a/cpp/src/Ice/TcpEndpointI.cpp +++ b/cpp/src/Ice/TcpEndpointI.cpp @@ -62,39 +62,17 @@ IceInternal::TcpEndpointI::TcpEndpointI(const ProtocolInstancePtr& instance, Bas EndpointInfoPtr IceInternal::TcpEndpointI::getInfo() const { - class InfoI : public Ice::TCPEndpointInfo - { - public: - - InfoI(const EndpointIPtr& endpoint) : _endpoint(endpoint) - { - } - - virtual Ice::Short - type() const - { - return _endpoint->type(); - } - - virtual bool - datagram() const - { - return _endpoint->datagram(); - } - - virtual bool - secure() const - { - return _endpoint->secure(); - } - - private: - - const EndpointIPtr _endpoint; - }; + TCPEndpointInfoPtr info = new InfoI<Ice::TCPEndpointInfo>(const_cast<TcpEndpointI*>(this)); + fillEndpointInfo(info.get()); + return info; +} - TCPEndpointInfoPtr info = new InfoI(const_cast<TcpEndpointI*>(this)); +EndpointInfoPtr +IceInternal::TcpEndpointI::getWSInfo(const string& resource) const +{ + WSEndpointInfoPtr info = new InfoI<Ice::WSEndpointInfo>(const_cast<TcpEndpointI*>(this)); fillEndpointInfo(info.get()); + info->resource = resource; return info; } diff --git a/cpp/src/Ice/TcpEndpointI.h b/cpp/src/Ice/TcpEndpointI.h index 2525d940168..d29498fc181 100644 --- a/cpp/src/Ice/TcpEndpointI.h +++ b/cpp/src/Ice/TcpEndpointI.h @@ -14,11 +14,12 @@ #include <Ice/IPEndpointI.h> #include <Ice/EndpointFactory.h> #include <Ice/Network.h> // for IceIternal::Address +#include <Ice/WSEndpoint.h> namespace IceInternal { -class TcpEndpointI : public IPEndpointI +class TcpEndpointI : public IPEndpointI, public WSEndpointDelegate { public: @@ -28,6 +29,7 @@ public: TcpEndpointI(const ProtocolInstancePtr&, BasicStream*); virtual Ice::EndpointInfoPtr getInfo() const; + virtual Ice::EndpointInfoPtr getWSInfo(const std::string&) const; virtual Ice::Int timeout() const; virtual EndpointIPtr timeout(Ice::Int) const; diff --git a/cpp/src/Ice/TcpTransceiver.cpp b/cpp/src/Ice/TcpTransceiver.cpp index ee022c639e9..4fa6d0bce2d 100644 --- a/cpp/src/Ice/TcpTransceiver.cpp +++ b/cpp/src/Ice/TcpTransceiver.cpp @@ -105,13 +105,17 @@ IceInternal::TcpTransceiver::toDetailedString() const Ice::ConnectionInfoPtr IceInternal::TcpTransceiver::getInfo() const { - Ice::TCPConnectionInfoPtr info = new Ice::TCPConnectionInfo(); - fdToAddressAndPort(_stream->fd(), info->localAddress, info->localPort, info->remoteAddress, info->remotePort); - if(_stream->fd() != INVALID_SOCKET) - { - info->rcvSize = getRecvBufferSize(_stream->fd()); - info->sndSize = getSendBufferSize(_stream->fd()); - } + TCPConnectionInfoPtr info = new TCPConnectionInfo(); + fillConnectionInfo(info); + return info; +} + +Ice::ConnectionInfoPtr +IceInternal::TcpTransceiver::getWSInfo(const Ice::HeaderDict& headers) const +{ + WSConnectionInfoPtr info = new WSConnectionInfo(); + fillConnectionInfo(info); + info->headers = headers; return info; } @@ -136,3 +140,13 @@ IceInternal::TcpTransceiver::~TcpTransceiver() { } +void +IceInternal::TcpTransceiver::fillConnectionInfo(const TCPConnectionInfoPtr& info) const +{ + fdToAddressAndPort(_stream->fd(), info->localAddress, info->localPort, info->remoteAddress, info->remotePort); + if(_stream->fd() != INVALID_SOCKET) + { + info->rcvSize = getRecvBufferSize(_stream->fd()); + info->sndSize = getSendBufferSize(_stream->fd()); + } +} diff --git a/cpp/src/Ice/TcpTransceiver.h b/cpp/src/Ice/TcpTransceiver.h index ee6c57214d9..16845238ae4 100644 --- a/cpp/src/Ice/TcpTransceiver.h +++ b/cpp/src/Ice/TcpTransceiver.h @@ -14,6 +14,7 @@ #include <Ice/Transceiver.h> #include <Ice/Network.h> #include <Ice/StreamSocket.h> +#include <Ice/WSTransceiver.h> namespace IceInternal { @@ -21,7 +22,7 @@ namespace IceInternal class TcpConnector; class TcpAcceptor; -class TcpTransceiver : public Transceiver +class TcpTransceiver : public Transceiver, public WSTransceiverDelegate { public: @@ -42,6 +43,7 @@ public: virtual std::string toString() const; virtual std::string toDetailedString() const; virtual Ice::ConnectionInfoPtr getInfo() const; + virtual Ice::ConnectionInfoPtr getWSInfo(const Ice::HeaderDict&) const; virtual void checkSendSize(const Buffer&); virtual void setBufferSize(int rcvSize, int sndSize); @@ -50,6 +52,8 @@ private: TcpTransceiver(const ProtocolInstancePtr&, const StreamSocketPtr&); virtual ~TcpTransceiver(); + void fillConnectionInfo(const Ice::TCPConnectionInfoPtr&) const; + friend class TcpConnector; friend class TcpAcceptor; diff --git a/cpp/src/Ice/UdpEndpointI.cpp b/cpp/src/Ice/UdpEndpointI.cpp index 6b0584fb2ba..019ff02a368 100644 --- a/cpp/src/Ice/UdpEndpointI.cpp +++ b/cpp/src/Ice/UdpEndpointI.cpp @@ -76,38 +76,7 @@ IceInternal::UdpEndpointI::UdpEndpointI(const ProtocolInstancePtr& instance, Bas EndpointInfoPtr IceInternal::UdpEndpointI::getInfo() const { - class InfoI : public Ice::UDPEndpointInfo - { - public: - - InfoI(const EndpointIPtr& endpoint) : _endpoint(endpoint) - { - } - - virtual Ice::Short - type() const - { - return _endpoint->type(); - } - - virtual bool - datagram() const - { - return _endpoint->datagram(); - } - - virtual bool - secure() const - { - return _endpoint->secure(); - } - - private: - - const EndpointIPtr _endpoint; - }; - - Ice::UDPEndpointInfoPtr info = new InfoI(const_cast<UdpEndpointI*>(this)); + Ice::UDPEndpointInfoPtr info = new InfoI<Ice::UDPEndpointInfo>(const_cast<UdpEndpointI*>(this)); fillEndpointInfo(info.get()); return info; } diff --git a/cpp/src/Ice/WSEndpoint.cpp b/cpp/src/Ice/WSEndpoint.cpp index 54acdef77c4..d23b952b6b5 100644 --- a/cpp/src/Ice/WSEndpoint.cpp +++ b/cpp/src/Ice/WSEndpoint.cpp @@ -47,41 +47,8 @@ IceInternal::WSEndpoint::WSEndpoint(const ProtocolInstancePtr& instance, const E Ice::EndpointInfoPtr IceInternal::WSEndpoint::getInfo() const { - class InfoI : public WSEndpointInfo - { - public: - - InfoI(const EndpointIPtr& e) : _endpoint(e) - { - } - - virtual Short - type() const - { - return _endpoint->type(); - } - - virtual bool - datagram() const - { - return _endpoint->datagram(); - } - - virtual bool - secure() const - { - return _endpoint->secure(); - } - - private: - - const EndpointIPtr _endpoint; - }; - - WSEndpointInfoPtr info = new InfoI(const_cast<WSEndpoint*>(this)); - _delegate->fillEndpointInfo(info.get()); - info->resource = _resource; - return info; + assert(dynamic_cast<WSEndpointDelegate*>(_delegate.get())); + return dynamic_cast<WSEndpointDelegate*>(_delegate.get())->getWSInfo(_resource); } Ice::Short diff --git a/cpp/src/Ice/WSEndpoint.h b/cpp/src/Ice/WSEndpoint.h index 255bcf6cc1b..6fb08211a84 100644 --- a/cpp/src/Ice/WSEndpoint.h +++ b/cpp/src/Ice/WSEndpoint.h @@ -20,6 +20,17 @@ namespace IceInternal { +// +// Delegate interface implemented by TcpEndpoint or IceSSL::Endpoint or any endpoint that WS can +// delegate to. +// +class ICE_API WSEndpointDelegate : virtual public IceUtil::Shared +{ +public: + + virtual Ice::EndpointInfoPtr getWSInfo(const std::string&) const = 0; +}; + class WSEndpoint : public EndpointI { public: diff --git a/cpp/src/Ice/WSTransceiver.cpp b/cpp/src/Ice/WSTransceiver.cpp index d476b52a67e..08764fbc267 100644 --- a/cpp/src/Ice/WSTransceiver.cpp +++ b/cpp/src/Ice/WSTransceiver.cpp @@ -803,17 +803,8 @@ IceInternal::WSTransceiver::toDetailedString() const Ice::ConnectionInfoPtr IceInternal::WSTransceiver::getInfo() const { - IPConnectionInfoPtr di = IPConnectionInfoPtr::dynamicCast(_delegate->getInfo()); - assert(di); - WSConnectionInfoPtr info = new WSConnectionInfo(); - info->localAddress = di->localAddress; - info->localPort = di->localPort; - info->remoteAddress = di->remoteAddress; - info->remotePort = di->remotePort; - info->rcvSize = di->rcvSize; - info->sndSize = di->sndSize; - info->headers = _parser->getHeaders(); - return info; + assert(dynamic_cast<WSTransceiverDelegate*>(_delegate.get())); + return dynamic_cast<WSTransceiverDelegate*>(_delegate.get())->getWSInfo(_parser->getHeaders()); } void diff --git a/cpp/src/Ice/WSTransceiver.h b/cpp/src/Ice/WSTransceiver.h index 2273f51a683..c3d8d760e22 100644 --- a/cpp/src/Ice/WSTransceiver.h +++ b/cpp/src/Ice/WSTransceiver.h @@ -24,6 +24,17 @@ namespace IceInternal class ConnectorI; class AcceptorI; +// +// Delegate interface implemented by TcpTransceiver or IceSSL::Transceiver or any transport that WS can +// delegate to. +// +class ICE_API WSTransceiverDelegate : virtual public IceUtil::Shared +{ +public: + + virtual Ice::ConnectionInfoPtr getWSInfo(const Ice::HeaderDict&) const = 0; +}; + class WSTransceiver : public Transceiver { public: diff --git a/cpp/src/Ice/winrt/StreamEndpointI.cpp b/cpp/src/Ice/winrt/StreamEndpointI.cpp index 60aca535bdd..bdd3dc0295d 100644 --- a/cpp/src/Ice/winrt/StreamEndpointI.cpp +++ b/cpp/src/Ice/winrt/StreamEndpointI.cpp @@ -44,43 +44,6 @@ createIceSSL(const CommunicatorPtr& com, const string&, const StringSeq&) } -namespace -{ - -template<class T> class InfoI : public T -{ -public: - - InfoI(const ProtocolInstancePtr& instance, Ice::Int to, bool comp, const string& host, Ice::Int port) : - T(to, comp, host, port, ""), _instance(instance) - { - } - - virtual Ice::Short - type() const - { - return _instance->type(); - } - - virtual bool - datagram() const - { - return false; - } - - virtual bool - secure() const - { - return _instance->secure(); - } - -private: - - ProtocolInstancePtr _instance; -}; - -} - IceUtil::Shared* IceInternal::upCast(StreamEndpointI* p) { return p; } IceInternal::StreamEndpointI::StreamEndpointI(const ProtocolInstancePtr& instance, const string& ho, Int po, Int ti, @@ -110,14 +73,37 @@ IceInternal::StreamEndpointI::StreamEndpointI(const ProtocolInstancePtr& instanc EndpointInfoPtr IceInternal::StreamEndpointI::getInfo() const { + IPEndpointInfoPtr info; + if(_instance->secure()) + { + info = new InfoI<IceSSL::EndpointInfo>(const_cast<StreamEndpointI*>(this)); + } + else + { + info = new InfoI<Ice::TCPEndpointInfo>(const_cast<StreamEndpointI*>(this)); + } + fillEndpointInfo(info.get()); + return info; +} + +EndpointInfoPtr +IceInternal::StreamEndpointI::getWSInfo(const string& resource) const +{ + IPEndpointInfoPtr info; if(_instance->secure()) { - return new InfoI<IceSSL::EndpointInfo>(_instance, _timeout, _compress, _host, _port); + IceSSL::WSSEndpointInfoPtr i = new InfoI<IceSSL::WSSEndpointInfo>(const_cast<StreamEndpointI*>(this)); + i->resource = resource; + info = i; } else { - return new InfoI<Ice::TCPEndpointInfo>(_instance, _timeout, _compress, _host, _port); + Ice::WSEndpointInfoPtr i = new InfoI<Ice::WSEndpointInfo>(const_cast<StreamEndpointI*>(this)); + i->resource = resource; + info = i; } + fillEndpointInfo(info.get()); + return info; } Int @@ -180,8 +166,7 @@ IceInternal::StreamEndpointI::datagram() const bool IceInternal::StreamEndpointI::secure() const { - return _instance->type() == IceSSL::EndpointType || - _instance->type() == WSSEndpointType; + return _instance->type() == IceSSL::EndpointType || _instance->type() == WSSEndpointType; } TransceiverPtr diff --git a/cpp/src/Ice/winrt/StreamEndpointI.h b/cpp/src/Ice/winrt/StreamEndpointI.h index 8e77b2d1397..d3f9f1bbe66 100644 --- a/cpp/src/Ice/winrt/StreamEndpointI.h +++ b/cpp/src/Ice/winrt/StreamEndpointI.h @@ -13,13 +13,14 @@ #include <IceUtil/Config.h> #include <Ice/IPEndpointI.h> #include <Ice/EndpointFactory.h> +#include <Ice/WSEndpoint.h> #include <Ice/Network.h> // for IceIternal::Address #include <Ice/winrt/StreamF.h> namespace IceInternal { -class StreamEndpointI : public IPEndpointI +class StreamEndpointI : public IPEndpointI, WSEndpointDelegate { public: @@ -28,6 +29,7 @@ public: StreamEndpointI(const ProtocolInstancePtr&, BasicStream*); virtual Ice::EndpointInfoPtr getInfo() const; + virtual Ice::EndpointInfoPtr getWSInfo(const std::string&) const; virtual Ice::Int timeout() const; virtual EndpointIPtr timeout(Ice::Int) const; diff --git a/cpp/src/Ice/winrt/StreamTransceiver.cpp b/cpp/src/Ice/winrt/StreamTransceiver.cpp index 3c4a30f8cc6..7cf9946ed9c 100644 --- a/cpp/src/Ice/winrt/StreamTransceiver.cpp +++ b/cpp/src/Ice/winrt/StreamTransceiver.cpp @@ -302,12 +302,29 @@ IceInternal::StreamTransceiver::getInfo() const { info = new Ice::TCPConnectionInfo(); } - fdToAddressAndPort(_fd, info->localAddress, info->localPort, info->remoteAddress, info->remotePort); - info->rcvSize = getRecvBufferSize(_fd); - info->sndSize = getSendBufferSize(_fd); + fillConnectionInfo(info); return info; } +Ice::ConnectionInfoPtr +IceInternal::StreamTransceiver::getWSInfo(const Ice::HeaderDict& headers) const +{ + if(_instance->secure()) + { + IceSSL::WSSConnectionInfoPtr info = new IceSSL::WSSConnectionInfo(); + fillConnectionInfo(info); + info->headers = headers; + return info; + } + else + { + Ice::WSConnectionInfoPtr info = new Ice::WSConnectionInfo(); + fillConnectionInfo(info); + info->headers = headers; + return info; + } +} + void IceInternal::StreamTransceiver::checkSendSize(const Buffer&) { @@ -387,3 +404,12 @@ IceInternal::StreamTransceiver::checkIfErrorOrCompleted(SocketOperation op, IAsy return true; // Prevent compiler warning. } } + +void +IceInternal::StreamTransceiver::fillConnectionInfo(const Ice::IPConnectionInfoPtr& info) const +{ + fdToAddressAndPort(_fd, info->localAddress, info->localPort, info->remoteAddress, info->remotePort); + info->rcvSize = getRecvBufferSize(_fd); + info->sndSize = getSendBufferSize(_fd); +} + diff --git a/cpp/src/Ice/winrt/StreamTransceiver.h b/cpp/src/Ice/winrt/StreamTransceiver.h index 4a32cfd10e8..6d7effd8e49 100644 --- a/cpp/src/Ice/winrt/StreamTransceiver.h +++ b/cpp/src/Ice/winrt/StreamTransceiver.h @@ -13,6 +13,7 @@ #include <Ice/ProtocolInstanceF.h> #include <Ice/Transceiver.h> #include <Ice/Network.h> +#include <Ice/WSTransceiver.h> namespace IceInternal { @@ -20,7 +21,7 @@ namespace IceInternal class StreamConnector; class StreamAcceptor; -class StreamTransceiver : public Transceiver, public NativeInfo +class StreamTransceiver : public Transceiver, public NativeInfo, public WSTransceiverDelegate { enum State { @@ -49,6 +50,7 @@ public: virtual std::string toString() const; virtual std::string toDetailedString() const; virtual Ice::ConnectionInfoPtr getInfo() const; + virtual Ice::ConnectionInfoPtr getWSInfo(const Ice::HeaderDict&) const; virtual void checkSendSize(const Buffer&); virtual void setBufferSize(int rcvSize, int sndSize); @@ -58,8 +60,8 @@ private: virtual ~StreamTransceiver(); void connect(const Address&); - bool checkIfErrorOrCompleted(SocketOperation, Windows::Foundation::IAsyncInfo^, int = 0); + void fillConnectionInfo(const Ice::IPConnectionInfoPtr&) const; friend class StreamConnector; friend class StreamAcceptor; diff --git a/cpp/src/IceSSL/EndpointI.cpp b/cpp/src/IceSSL/EndpointI.cpp index 124c63dc645..6893db0e117 100644 --- a/cpp/src/IceSSL/EndpointI.cpp +++ b/cpp/src/IceSSL/EndpointI.cpp @@ -53,39 +53,17 @@ IceSSL::EndpointI::EndpointI(const InstancePtr& instance, IceInternal::BasicStre Ice::EndpointInfoPtr IceSSL::EndpointI::getInfo() const { - class InfoI : public EndpointInfo - { - public: - - InfoI(const IceInternal::EndpointIPtr& endpoint) : _endpoint(endpoint) - { - } - - virtual Ice::Short - type() const - { - return _endpoint->type(); - } - - virtual bool - datagram() const - { - return _endpoint->datagram(); - } - - virtual bool - secure() const - { - return _endpoint->secure(); - } - - private: - - const IceInternal::EndpointIPtr _endpoint; - }; + EndpointInfoPtr info = new IceInternal::InfoI<EndpointInfo>(const_cast<EndpointI*>(this)); + fillEndpointInfo(info.get()); + return info; +} - IPEndpointInfoPtr info = new InfoI(const_cast<EndpointI*>(this)); +Ice::EndpointInfoPtr +IceSSL::EndpointI::getWSInfo(const string& resource) const +{ + WSSEndpointInfoPtr info = new IceInternal::InfoI<WSSEndpointInfo>(const_cast<EndpointI*>(this)); fillEndpointInfo(info.get()); + info->resource = resource; return info; } diff --git a/cpp/src/IceSSL/EndpointI.h b/cpp/src/IceSSL/EndpointI.h index b3a154a6ad4..74f04d1665a 100644 --- a/cpp/src/IceSSL/EndpointI.h +++ b/cpp/src/IceSSL/EndpointI.h @@ -12,6 +12,7 @@ #include <Ice/IPEndpointI.h> #include <Ice/EndpointFactory.h> +#include <Ice/WSEndpoint.h> #include <IceSSL/InstanceF.h> #include <IceSSL/EndpointInfo.h> #include <Ice/Network.h> @@ -19,7 +20,7 @@ namespace IceSSL { -class EndpointI : public IceInternal::IPEndpointI +class EndpointI : public IceInternal::IPEndpointI, public IceInternal::WSEndpointDelegate { public: @@ -29,6 +30,7 @@ public: EndpointI(const InstancePtr&, IceInternal::BasicStream*); virtual Ice::EndpointInfoPtr getInfo() const; + virtual Ice::EndpointInfoPtr getWSInfo(const std::string&) const; virtual Ice::Int timeout() const; virtual IceInternal::EndpointIPtr timeout(Ice::Int) const; diff --git a/cpp/src/IceSSL/OpenSSLEngine.cpp b/cpp/src/IceSSL/OpenSSLEngine.cpp index c47e19eb99f..cc617008547 100644 --- a/cpp/src/IceSSL/OpenSSLEngine.cpp +++ b/cpp/src/IceSSL/OpenSSLEngine.cpp @@ -395,8 +395,22 @@ OpenSSLEngine::initialize() // Establish the location of CA certificates. // { - string caFile = properties->getProperty(propPrefix + "CertAuthFile"); - string caDir = properties->getPropertyWithDefault(propPrefix + "CertAuthDir", defaultDir); + string caFile = properties->getProperty(propPrefix + "CAs"); + string caDir; + if(!caFile.empty()) + { + if(!checkPath(caFile, defaultDir, false) && checkPath(caFile, defaultDir, true)) + { + caDir = caFile; + caFile = ""; + } + } + else + { + // Deprecated properties + caFile = properties->getProperty(propPrefix + "CertAuthFile"); + caDir = properties->getProperty(propPrefix + "CertAuthDir"); + } const char* file = 0; const char* dir = 0; if(!caFile.empty()) @@ -452,6 +466,10 @@ OpenSSLEngine::initialize() throw PluginInitializationException(__FILE__, __LINE__, msg); } } + else if(properties->getPropertyAsInt("IceSSL.UsePlatformCAs") > 0) + { + SSL_CTX_set_default_verify_paths(_ctx); + } } // diff --git a/cpp/src/IceSSL/OpenSSLTransceiverI.cpp b/cpp/src/IceSSL/OpenSSLTransceiverI.cpp index bed46b3b8b4..2b40c62a4b6 100644 --- a/cpp/src/IceSSL/OpenSSLTransceiverI.cpp +++ b/cpp/src/IceSSL/OpenSSLTransceiverI.cpp @@ -276,11 +276,12 @@ IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::B throw ex; } } - else if(_info) + else { - _info->verified = true; + _verified = true; } - _engine->verifyPeer(_stream->fd(), _host, getNativeConnectionInfo()); + + _engine->verifyPeer(_stream->fd(), _host, NativeConnectionInfoPtr::dynamicCast(getInfo())); if(_engine->securityTraceLevel() >= 1) { @@ -573,7 +574,18 @@ IceSSL::TransceiverI::toDetailedString() const Ice::ConnectionInfoPtr IceSSL::TransceiverI::getInfo() const { - return getNativeConnectionInfo(); + NativeConnectionInfoPtr info = new NativeConnectionInfo(); + fillConnectionInfo(info, info->nativeCerts); + return info; +} + +Ice::ConnectionInfoPtr +IceSSL::TransceiverI::getWSInfo(const Ice::HeaderDict& headers) const +{ + WSSNativeConnectionInfoPtr info = new WSSNativeConnectionInfo(); + fillConnectionInfo(info, info->nativeCerts); + info->headers = headers; + return info; } void @@ -609,17 +621,18 @@ IceSSL::TransceiverI::verifyCallback(int ok, X509_STORE_CTX* c) } // - // Initialize the native connection info with the verified - // certificate chain. SSL_get_peer_cert_chain doesn't return the - // verified chain, it returns the chain sent by the peer. + // Initialize the native certs with the verified certificate chain. SSL_get_peer_cert_chain + // doesn't return the verified chain, it returns the chain sent by the peer. // - try - { - _info = initNativeConnectionInfo(c); - } - catch(const Ice::Exception&) + STACK_OF(X509)* chain = X509_STORE_CTX_get1_chain(c); + if(chain != 0) { - // Ignore + _nativeCerts.clear(); + for(int i = 0; i < sk_X509_num(chain); ++i) + { + _nativeCerts.push_back(new Certificate(X509_dup(sk_X509_value(chain, i)))); + } + sk_X509_pop_free(chain, X509_free); } // @@ -639,6 +652,7 @@ IceSSL::TransceiverI::TransceiverI(const InstancePtr& instance, const IceInterna _adapterName(incoming ? hostOrAdapterName : ""), _incoming(incoming), _stream(stream), + _verified(false), _ssl(0) { } @@ -647,20 +661,9 @@ IceSSL::TransceiverI::~TransceiverI() { } -NativeConnectionInfoPtr -IceSSL::TransceiverI::getNativeConnectionInfo() const -{ - if(!_info) - { - return initNativeConnectionInfo(0); - } - return _info; -} - -NativeConnectionInfoPtr -IceSSL::TransceiverI::initNativeConnectionInfo(X509_STORE_CTX* ctx) const +void +IceSSL::TransceiverI::fillConnectionInfo(const ConnectionInfoPtr& info, std::vector<CertificatePtr>& nativeCerts) const { - NativeConnectionInfoPtr info = new NativeConnectionInfo(); IceInternal::fdToAddressAndPort(_stream->fd(), info->localAddress, info->localPort, info->remoteAddress, info->remotePort); if(_stream->fd() != INVALID_SOCKET) @@ -670,57 +673,11 @@ IceSSL::TransceiverI::initNativeConnectionInfo(X509_STORE_CTX* ctx) const } info->adapterName = _adapterName; info->incoming = _incoming; - info->verified = false; - - STACK_OF(X509)* chain = 0; - if(ctx) - { - // - // This is called from the verify callback where OpenSSL provides the verified - // certificate chain. - // - chain = X509_STORE_CTX_get1_chain(ctx); - } - - if(chain == 0 && _ssl != 0) - { - // - // On the client side, SSL_get_peer_cert_chain returns the entire chain of certs. - // On the server side, the peer certificate must be obtained separately. - // - // Since we have no clear idea whether the connection is server or client side, - // the peer certificate is obtained separately and compared against the first - // certificate in the chain. If they are not the same, it is added to the chain. - // - X509* cert = SSL_get_peer_certificate(_ssl); - chain = SSL_get_peer_cert_chain(_ssl); - if(cert != 0 && (chain == 0 || sk_X509_num(chain) == 0 || cert != sk_X509_value(chain, 0))) - { - CertificatePtr certificate = new Certificate(cert); - info->nativeCerts.push_back(certificate); - info->certs.push_back(certificate->encode()); - } - else - { - X509_free(cert); - } - } - - if(chain != 0) + info->verified = _verified; + nativeCerts = _nativeCerts; + for(vector<CertificatePtr>::const_iterator p = _nativeCerts.begin(); p != _nativeCerts.end(); ++p) { - for(int i = 0; i < sk_X509_num(chain); ++i) - { - // - // Duplicate the certificate since the stack comes straight from the SSL connection. - // - CertificatePtr certificate = new Certificate(X509_dup(sk_X509_value(chain, i))); - info->nativeCerts.push_back(certificate); - info->certs.push_back(certificate->encode()); - } - if(ctx) - { - sk_X509_pop_free(chain, X509_free); - } + info->certs.push_back((*p)->encode()); } if(_ssl != 0) { @@ -728,7 +685,6 @@ IceSSL::TransceiverI::initNativeConnectionInfo(X509_STORE_CTX* ctx) const } info->adapterName = _adapterName; info->incoming = _incoming; - return info; } #endif diff --git a/cpp/src/IceSSL/OpenSSLTransceiverI.h b/cpp/src/IceSSL/OpenSSLTransceiverI.h index 85fa55d4548..a64e932a0f5 100644 --- a/cpp/src/IceSSL/OpenSSLTransceiverI.h +++ b/cpp/src/IceSSL/OpenSSLTransceiverI.h @@ -19,6 +19,7 @@ #include <Ice/Transceiver.h> #include <Ice/Network.h> #include <Ice/StreamSocket.h> +#include <Ice/WSTransceiver.h> #ifdef ICE_USE_OPENSSL @@ -31,7 +32,7 @@ namespace IceSSL class ConnectorI; class AcceptorI; -class TransceiverI : public IceInternal::Transceiver +class TransceiverI : public IceInternal::Transceiver, public IceInternal::WSTransceiverDelegate { public: @@ -46,6 +47,7 @@ public: virtual std::string toString() const; virtual std::string toDetailedString() const; virtual Ice::ConnectionInfoPtr getInfo() const; + virtual Ice::ConnectionInfoPtr getWSInfo(const Ice::HeaderDict&) const; virtual void checkSendSize(const IceInternal::Buffer&); virtual void setBufferSize(int rcvSize, int sndSize); @@ -56,8 +58,7 @@ private: TransceiverI(const InstancePtr&, const IceInternal::StreamSocketPtr&, const std::string&, bool); virtual ~TransceiverI(); - virtual NativeConnectionInfoPtr getNativeConnectionInfo() const; - NativeConnectionInfoPtr initNativeConnectionInfo(X509_STORE_CTX*) const; + void fillConnectionInfo(const ConnectionInfoPtr&, std::vector<CertificatePtr>&) const; friend class ConnectorI; friend class AcceptorI; @@ -68,7 +69,8 @@ private: const std::string _adapterName; const bool _incoming; const IceInternal::StreamSocketPtr _stream; - NativeConnectionInfoPtr _info; + bool _verified; + std::vector<CertificatePtr> _nativeCerts; SSL* _ssl; }; diff --git a/cpp/src/IceSSL/SChannelEngine.cpp b/cpp/src/IceSSL/SChannelEngine.cpp index efb3b099ce3..ddc47619373 100644 --- a/cpp/src/IceSSL/SChannelEngine.cpp +++ b/cpp/src/IceSSL/SChannelEngine.cpp @@ -56,7 +56,7 @@ struct CertChainEngineConfig # endif void -addCertificateToStore(const string& file, HCERTSTORE store, PCCERT_CONTEXT* cert = 0) +addCertificatesToStore(const string& file, HCERTSTORE store, PCCERT_CONTEXT* cert = 0) { vector<char> buffer; readFile(file, buffer); @@ -65,29 +65,50 @@ addCertificateToStore(const string& file, HCERTSTORE store, PCCERT_CONTEXT* cert throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: certificate file is empty:\n" + file); } - vector<BYTE> outBuffer; - outBuffer.resize(buffer.size()); - DWORD outLength = static_cast<DWORD>(outBuffer.size()); - - if(!CryptStringToBinary(&buffer[0], static_cast<DWORD>(buffer.size()), CRYPT_STRING_BASE64HEADER, - &outBuffer[0], &outLength, 0, 0)) + string strbuf(buffer.begin(), buffer.end()); + string::size_type size, startpos, endpos = 0; + bool first = true; + while(true) { - // - // Base64 data should always be bigger than binary - // - assert(GetLastError() != ERROR_MORE_DATA); - throw PluginInitializationException(__FILE__, __LINE__, - "IceSSL: error decoding certificate:\n" + lastErrorToString()); - } + startpos = strbuf.find("-----BEGIN CERTIFICATE-----", endpos); + if(startpos != string::npos) + { + endpos = strbuf.find("-----END CERTIFICATE-----", startpos); + size = endpos - startpos + sizeof("-----END CERTIFICATE-----"); + } + else if(first) + { + startpos = 0; + endpos = string::npos; + size = strbuf.size(); + } + else + { + break; + } - if(!CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &outBuffer[0], - outLength, CERT_STORE_ADD_NEW, cert)) - { - if(GetLastError() != static_cast<DWORD>(CRYPT_E_EXISTS)) + vector<BYTE> outBuffer; + outBuffer.resize(size); + DWORD outLength = static_cast<DWORD>(outBuffer.size()); + if(!CryptStringToBinary(&buffer[startpos], static_cast<DWORD>(size), CRYPT_STRING_ANY, &outBuffer[0], + &outLength, 0, 0)) { + assert(GetLastError() != ERROR_MORE_DATA); // Base64 data should always be bigger than binary throw PluginInitializationException(__FILE__, __LINE__, - "IceSSL: error decoding certificate:\n" + lastErrorToString()); + "IceSSL: error decoding certificate:\n" + lastErrorToString()); + } + + if(!CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &outBuffer[0], + outLength, CERT_STORE_ADD_NEW, first ? cert : 0)) + { + if(GetLastError() != static_cast<DWORD>(CRYPT_E_EXISTS)) + { + throw PluginInitializationException(__FILE__, __LINE__, + "IceSSL: error decoding certificate:\n" + lastErrorToString()); + } } + + first = false; } } @@ -244,8 +265,12 @@ SChannelEngine::initialize() // // Create trusted CA store with contents of CertAuthFile // - string caFile = properties->getProperty(prefix + "CertAuthFile"); - if(!caFile.empty()) + string caFile = properties->getProperty(prefix + "CAs"); + if(caFile.empty()) + { + caFile = properties->getProperty(prefix + "CertAuthFile"); + } + if(!caFile.empty() || properties->getPropertyAsInt("IceSSL.UsePlatformCAs") <= 0) { _rootStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, 0); if(!_rootStore) @@ -253,15 +278,20 @@ SChannelEngine::initialize() throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: error creating in memory certificate store:\n" + lastErrorToString()); } - + } + if(!caFile.empty()) + { if(!checkPath(caFile, defaultDir, false)) { throw PluginInitializationException(__FILE__, __LINE__, "IceSSL: CA certificate file not found:\n" + caFile); } - addCertificateToStore(caFile, _rootStore); + addCertificatesToStore(caFile, _rootStore); + } + if(_rootStore) + { // // Create a chain engine that uses our Trusted Root Store // @@ -410,26 +440,8 @@ SChannelEngine::initialize() "IceSSL: certificate error:\n" + lastErrorToString()); } - // - // If we found a certificate, add it to a new memory store. We - // can't use directly the certificate context from the PFX - // store: while it works for certificates without - // intermediates, it doesn't if the certificate has - // intermediates, the intermediates certificates aren't being - // sent. - // - HCERTSTORE newStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, 0); - PCCERT_CONTEXT newCert; - if(!CertAddCertificateContextToStore(newStore, cert, CERT_STORE_ADD_ALWAYS, &newCert)) - { - CertCloseStore(newStore, 0); - throw PluginInitializationException(__FILE__, __LINE__, - "IceSSL: certificate error:\n" + lastErrorToString()); - } - _certs.push_back(newCert); - _stores.push_back(newStore); - CertFreeCertificateContext(cert); - CertCloseStore(store, 0); + _certs.push_back(cert); + _stores.push_back(store); continue; } @@ -560,7 +572,7 @@ SChannelEngine::initialize() "store:\n" + lastErrorToString()); } - addCertificateToStore(certFile, store, &cert); + addCertificatesToStore(certFile, store, &cert); // // Associate key & certificate @@ -705,6 +717,17 @@ SChannelEngine::newCredentialsHandle(bool incoming) // the root certificate either way. // cred.dwFlags = SCH_CRED_NO_SYSTEM_MAPPER; + + // + // There's no way to prevent SChannel from sending "CA names" to the + // client. Recent Windows versions don't CA names but older ones do + // send all the trusted root CA names. We provide the root store to + // ensure that for these older Windows versions, we also include the + // CA names of your trusted roots. IceSSL for Java will only send a + // client certificate if the client certificate CA matches one of the + // CA names sent by the server. + // + cred.hRootStore = _rootStore; } else { diff --git a/cpp/src/IceSSL/SChannelTransceiverI.cpp b/cpp/src/IceSSL/SChannelTransceiverI.cpp index 74d9d840ed2..126a966f6b3 100644 --- a/cpp/src/IceSSL/SChannelTransceiverI.cpp +++ b/cpp/src/IceSSL/SChannelTransceiverI.cpp @@ -715,7 +715,7 @@ IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::B } } - _engine->verifyPeer(_stream->fd(), _host, getNativeConnectionInfo()); + _engine->verifyPeer(_stream->fd(), _host, NativeConnectionInfoPtr::dynamicCast(getInfo())); _state = StateHandshakeComplete; if(_instance->engine()->securityTraceLevel() >= 1) @@ -948,7 +948,18 @@ IceSSL::TransceiverI::toDetailedString() const Ice::ConnectionInfoPtr IceSSL::TransceiverI::getInfo() const { - return getNativeConnectionInfo(); + NativeConnectionInfoPtr info = new NativeConnectionInfo(); + fillConnectionInfo(info, info->nativeCerts); + return info; +} + +Ice::ConnectionInfoPtr +IceSSL::TransceiverI::getWSInfo(const Ice::HeaderDict& headers) const +{ + WSSNativeConnectionInfoPtr info = new WSSNativeConnectionInfo(); + fillConnectionInfo(info, info->nativeCerts); + info->headers = headers; + return info; } void @@ -984,10 +995,9 @@ IceSSL::TransceiverI::~TransceiverI() { } -NativeConnectionInfoPtr -IceSSL::TransceiverI::getNativeConnectionInfo() const +void +IceSSL::TransceiverI::fillConnectionInfo(const ConnectionInfoPtr& info, vector<CertificatePtr>& nativeCerts) const { - NativeConnectionInfoPtr info = new NativeConnectionInfo(); IceInternal::fdToAddressAndPort(_stream->fd(), info->localAddress, info->localPort, info->remoteAddress, info->remotePort); if(_stream->fd() != INVALID_SOCKET) @@ -1032,7 +1042,7 @@ IceSSL::TransceiverI::getNativeConnectionInfo() const } CertificatePtr certificate = new Certificate(cc); - info->nativeCerts.push_back(certificate); + nativeCerts.push_back(certificate); info->certs.push_back(certificate->encode()); } CertFreeCertificateChain(certChain); @@ -1059,7 +1069,6 @@ IceSSL::TransceiverI::getNativeConnectionInfo() const info->adapterName = _adapterName; info->incoming = _incoming; - return info; } bool diff --git a/cpp/src/IceSSL/SChannelTransceiverI.h b/cpp/src/IceSSL/SChannelTransceiverI.h index a029cf596bf..f60d54ff6d6 100644 --- a/cpp/src/IceSSL/SChannelTransceiverI.h +++ b/cpp/src/IceSSL/SChannelTransceiverI.h @@ -19,6 +19,7 @@ #include <Ice/Network.h> #include <Ice/Buffer.h> #include <Ice/StreamSocket.h> +#include <Ice/WSTransceiver.h> #ifdef ICE_USE_SCHANNEL @@ -42,7 +43,7 @@ namespace IceSSL class ConnectorI; class AcceptorI; -class TransceiverI : public IceInternal::Transceiver +class TransceiverI : public IceInternal::Transceiver, public IceInternal::WSTransceiverDelegate { public: @@ -63,6 +64,7 @@ public: virtual std::string toString() const; virtual std::string toDetailedString() const; virtual Ice::ConnectionInfoPtr getInfo() const; + virtual Ice::ConnectionInfoPtr getWSInfo(const Ice::HeaderDict&) const; virtual void checkSendSize(const IceInternal::Buffer&); virtual void setBufferSize(int rcvSize, int sndSize); @@ -71,7 +73,7 @@ private: TransceiverI(const InstancePtr&, const IceInternal::StreamSocketPtr&, const std::string&, bool); virtual ~TransceiverI(); - virtual NativeConnectionInfoPtr getNativeConnectionInfo() const; + void fillConnectionInfo(const ConnectionInfoPtr&, std::vector<CertificatePtr>&) const; IceInternal::SocketOperation sslHandshake(); diff --git a/cpp/src/IceSSL/SecureTransportEngine.cpp b/cpp/src/IceSSL/SecureTransportEngine.cpp index 9264bf252aa..a7102f64279 100644 --- a/cpp/src/IceSSL/SecureTransportEngine.cpp +++ b/cpp/src/IceSSL/SecureTransportEngine.cpp @@ -905,7 +905,11 @@ IceSSL::SecureTransportEngine::initialize() // try { - string caFile = properties->getProperty("IceSSL.CertAuthFile"); + string caFile = properties->getProperty("IceSSL.CAs"); + if(caFile.empty()) + { + caFile = properties->getProperty("IceSSL.CertAuthFile"); + } if(!caFile.empty()) { if(!checkPath(caFile, defaultDir, false)) @@ -915,6 +919,11 @@ IceSSL::SecureTransportEngine::initialize() } _certificateAuthorities = loadCACertificates(caFile); } + else if(properties->getPropertyAsInt("IceSSL.UsePlatformCAs") <= 0) + { + // Setup an empty list of Root CAs to not use the system root CAs. + _certificateAuthorities = CFArrayCreate(0, 0, 0, 0); + } } catch(const CertificateReadException& ce) { diff --git a/cpp/src/IceSSL/SecureTransportTransceiverI.cpp b/cpp/src/IceSSL/SecureTransportTransceiverI.cpp index 3ff588cd9e2..211a4ca3fe8 100644 --- a/cpp/src/IceSSL/SecureTransportTransceiverI.cpp +++ b/cpp/src/IceSSL/SecureTransportTransceiverI.cpp @@ -258,7 +258,7 @@ IceSSL::TransceiverI::initialize(IceInternal::Buffer& readBuffer, IceInternal::B << "remote address = " << desc << "\n" << errorToString(err); throw ProtocolException(__FILE__, __LINE__, os.str()); } - _engine->verifyPeer(_stream->fd(), _host, getNativeConnectionInfo()); + _engine->verifyPeer(_stream->fd(), _host, NativeConnectionInfoPtr::dynamicCast(getInfo())); if(_instance->engine()->securityTraceLevel() >= 1) { @@ -485,7 +485,18 @@ IceSSL::TransceiverI::toDetailedString() const Ice::ConnectionInfoPtr IceSSL::TransceiverI::getInfo() const { - return getNativeConnectionInfo(); + NativeConnectionInfoPtr info = new NativeConnectionInfo(); + fillConnectionInfo(info, info->nativeCerts); + return info; +} + +Ice::ConnectionInfoPtr +IceSSL::TransceiverI::getWSInfo(const Ice::HeaderDict& headers) const +{ + WSSNativeConnectionInfoPtr info = new WSSNativeConnectionInfo(); + fillConnectionInfo(info, info->nativeCerts); + info->headers = headers; + return info; } void @@ -526,10 +537,9 @@ IceSSL::TransceiverI::~TransceiverI() { } -NativeConnectionInfoPtr -IceSSL::TransceiverI::getNativeConnectionInfo() const +void +IceSSL::TransceiverI::fillConnectionInfo(const ConnectionInfoPtr& info, std::vector<CertificatePtr>& nativeCerts) const { - NativeConnectionInfoPtr info = new NativeConnectionInfo(); IceInternal::fdToAddressAndPort(_stream->fd(), info->localAddress, info->localPort, info->remoteAddress, info->remotePort); if(_stream->fd() != INVALID_SOCKET) @@ -546,7 +556,7 @@ IceSSL::TransceiverI::getNativeConnectionInfo() const CFRetain(cert); CertificatePtr certificate = new Certificate(cert); - info->nativeCerts.push_back(certificate); + nativeCerts.push_back(certificate); info->certs.push_back(certificate->encode()); } @@ -562,7 +572,6 @@ IceSSL::TransceiverI::getNativeConnectionInfo() const info->adapterName = _adapterName; info->incoming = _incoming; - return info; } OSStatus diff --git a/cpp/src/IceSSL/SecureTransportTransceiverI.h b/cpp/src/IceSSL/SecureTransportTransceiverI.h index aaf232c4032..c81ee7aaef1 100644 --- a/cpp/src/IceSSL/SecureTransportTransceiverI.h +++ b/cpp/src/IceSSL/SecureTransportTransceiverI.h @@ -18,6 +18,7 @@ #include <Ice/Transceiver.h> #include <Ice/Network.h> #include <Ice/StreamSocket.h> +#include <Ice/WSTransceiver.h> #ifdef ICE_USE_SECURE_TRANSPORT @@ -30,7 +31,7 @@ namespace IceSSL class ConnectorI; class AcceptorI; -class TransceiverI : public IceInternal::Transceiver +class TransceiverI : public IceInternal::Transceiver, public IceInternal::WSTransceiverDelegate { public: @@ -46,6 +47,7 @@ public: virtual std::string toString() const; virtual std::string toDetailedString() const; virtual Ice::ConnectionInfoPtr getInfo() const; + virtual Ice::ConnectionInfoPtr getWSInfo(const Ice::HeaderDict&) const; virtual void checkSendSize(const IceInternal::Buffer&); virtual void setBufferSize(int rcvSize, int sndSize); @@ -57,7 +59,7 @@ private: TransceiverI(const InstancePtr&, const IceInternal::StreamSocketPtr&, const std::string&, bool); virtual ~TransceiverI(); - virtual NativeConnectionInfoPtr getNativeConnectionInfo() const; + void fillConnectionInfo(const ConnectionInfoPtr&, std::vector<CertificatePtr>&) const; friend class ConnectorI; friend class AcceptorI; diff --git a/cpp/test/Ice/info/AllTests.cpp b/cpp/test/Ice/info/AllTests.cpp index 80a3bd258de..f8094effbae 100644 --- a/cpp/test/Ice/info/AllTests.cpp +++ b/cpp/test/Ice/info/AllTests.cpp @@ -44,7 +44,7 @@ allTests(const Ice::CommunicatorPtr& communicator) test((ipEndpoint->type() == Ice::TCPEndpointType && Ice::TCPEndpointInfoPtr::dynamicCast(ipEndpoint)) || (ipEndpoint->type() == IceSSL::EndpointType && IceSSL::EndpointInfoPtr::dynamicCast(ipEndpoint)) || (ipEndpoint->type() == Ice::WSEndpointType && Ice::WSEndpointInfoPtr::dynamicCast(ipEndpoint)) || - (ipEndpoint->type() == Ice::WSSEndpointType && Ice::WSEndpointInfoPtr::dynamicCast(ipEndpoint))); + (ipEndpoint->type() == Ice::WSSEndpointType && IceSSL::WSSEndpointInfoPtr::dynamicCast(ipEndpoint))); Ice::UDPEndpointInfoPtr udpEndpoint = Ice::UDPEndpointInfoPtr::dynamicCast(endps[1]->getInfo()); test(udpEndpoint); @@ -184,13 +184,26 @@ allTests(const Ice::CommunicatorPtr& communicator) if(base->ice_getConnection()->type() == "ws" || base->ice_getConnection()->type() == "wss") { - Ice::WSConnectionInfoPtr wsinfo = Ice::WSConnectionInfoPtr::dynamicCast(info); - test(wsinfo); + Ice::HeaderDict headers; - test(wsinfo->headers["Upgrade"] == "websocket"); - test(wsinfo->headers["Connection"] == "Upgrade"); - test(wsinfo->headers["Sec-WebSocket-Protocol"] == "ice.zeroc.com"); - test(wsinfo->headers.find("Sec-WebSocket-Accept") != wsinfo->headers.end()); + Ice::WSConnectionInfoPtr wsinfo = Ice::WSConnectionInfoPtr::dynamicCast(info); + if(wsinfo) + { + headers = wsinfo->headers; + } + + IceSSL::WSSConnectionInfoPtr wssinfo = IceSSL::WSSConnectionInfoPtr::dynamicCast(info); + if(wssinfo) + { + headers = wssinfo->headers; + test(wssinfo->verified); + test(!wssinfo->certs.empty()); + } + + test(headers["Upgrade"] == "websocket"); + test(headers["Connection"] == "Upgrade"); + test(headers["Sec-WebSocket-Protocol"] == "ice.zeroc.com"); + test(headers.find("Sec-WebSocket-Accept") != headers.end()); test(ctx["ws.Upgrade"] == "websocket"); test(ctx["ws.Connection"] == "Upgrade"); diff --git a/cpp/test/Ice/info/TestI.cpp b/cpp/test/Ice/info/TestI.cpp index ba63bf96744..11588b29294 100644 --- a/cpp/test/Ice/info/TestI.cpp +++ b/cpp/test/Ice/info/TestI.cpp @@ -84,5 +84,14 @@ TestI::getConnectionInfoAsContext(const Ice::Current& c) } } + IceSSL::WSSConnectionInfoPtr wssinfo = IceSSL::WSSConnectionInfoPtr::dynamicCast(info); + if(wssinfo) + { + for(Ice::HeaderDict::const_iterator p = wssinfo->headers.begin(); p != wssinfo->headers.end(); ++p) + { + ctx["ws." + p->first] = p->second; + } + } + return ctx; } diff --git a/cpp/test/IceSSL/certs/makecerts.py b/cpp/test/IceSSL/certs/makecerts.py index 3c241767a1c..57bfdfcfeba 100755 --- a/cpp/test/IceSSL/certs/makecerts.py +++ b/cpp/test/IceSSL/certs/makecerts.py @@ -74,6 +74,12 @@ dsaca = IceCertUtils.OpenSSLCertificateFactory(home=ca1.home, keyalg="dsa", keys # if force or not os.path.exists("cacert1.pem"): ca1.getCA().save("cacert1.pem") if force or not os.path.exists("cacert2.pem"): ca2.getCA().save("cacert2.pem") +if force or not os.path.exists("cacert1.der"): ca1.getCA().save("cacert1.der") +if force or not os.path.exists("cacerts.pem"): + pem = "" + with open("cacert1.pem", "r") as f: pem += f.read() + with open("cacert2.pem", "r") as f: pem += f.read() + with open("cacerts.pem", "w") as f: f.write(pem); # Also export the ca2 self-signed certificate, it's used by the tests to test self-signed certificates if force or not os.path.exists("cacert2_pub.pem"): ca2.getCA().save("cacert2_pub.pem") diff --git a/cpp/test/IceSSL/configuration/AllTests.cpp b/cpp/test/IceSSL/configuration/AllTests.cpp index 448eb075a0f..4a51b3416f1 100644 --- a/cpp/test/IceSSL/configuration/AllTests.cpp +++ b/cpp/test/IceSSL/configuration/AllTests.cpp @@ -39,43 +39,113 @@ readFile(const string& file, vector<char>& buffer) } #ifdef ICE_USE_SCHANNEL -void -findCertsCleanup(HCERTSTORE store, const vector<HCERTSTORE>& stores, const vector<PCCERT_CONTEXT>& certs) +class ImportCerts { - for(vector<PCCERT_CONTEXT>::const_iterator i = certs.begin(); i != certs.end(); ++i) - { - PCCERT_CONTEXT cert = *i; - +public: - DWORD size = 0; + ImportCerts(const string& defaultDir, const char* certificates[]) + { // - // Retrieve the certificate CERT_KEY_PROV_INFO_PROP_ID property, we use the CRYPT_KEY_PROV_INFO - // data to then remove the key set associated with the certificate. + // First we need to import some certificates in the user store. // - if(CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, 0, &size)) + _store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, L"MY"); + test(_store); + + for(int i = 0; certificates[i] != 0; ++i) { - vector<char> buf(size); - if(CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, &buf[0], &size)) + vector<char> buffer; + readFile(defaultDir + certificates[i], buffer); + + CRYPT_DATA_BLOB p12Blob; + p12Blob.cbData = static_cast<DWORD>(buffer.size()); + p12Blob.pbData = reinterpret_cast<BYTE*>(&buffer[0]); + + HCERTSTORE p12 = PFXImportCertStore(&p12Blob, L"password", CRYPT_USER_KEYSET); + _stores.push_back(p12); + + PCCERT_CONTEXT next = 0; + PCCERT_CONTEXT newCert = 0; + do { - CRYPT_KEY_PROV_INFO* keyProvInfo = reinterpret_cast<CRYPT_KEY_PROV_INFO*>(&buf[0]); - HCRYPTPROV cryptProv = 0; - if(CryptAcquireContextW(&cryptProv, keyProvInfo->pwszContainerName, keyProvInfo->pwszProvName, - keyProvInfo->dwProvType, 0)) + if((next = CertFindCertificateInStore(p12, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, + CERT_FIND_ANY, 0, next))) { - CryptAcquireContextW(&cryptProv, keyProvInfo->pwszContainerName, keyProvInfo->pwszProvName, - keyProvInfo->dwProvType, CRYPT_DELETEKEYSET); + if(CertAddCertificateContextToStore(_store, next, CERT_STORE_ADD_ALWAYS, &newCert)) + { + _certs.push_back(newCert); + } + } + } + while(next); + } + } + + ~ImportCerts() + { + cleanup(); + } + + void cleanup() + { + for(vector<PCCERT_CONTEXT>::const_iterator i = _certs.begin(); i != _certs.end(); ++i) + { + PCCERT_CONTEXT cert = *i; + + // Retrieve the certificate CERT_KEY_PROV_INFO_PROP_ID property, we use the CRYPT_KEY_PROV_INFO + // data to then remove the key set associated with the certificate. + // + DWORD size = 0; + if(CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, 0, &size)) + { + vector<char> buf(size); + if(CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, &buf[0], &size)) + { + CRYPT_KEY_PROV_INFO* keyProvInfo = reinterpret_cast<CRYPT_KEY_PROV_INFO*>(&buf[0]); + HCRYPTPROV cryptProv = 0; + if(CryptAcquireContextW(&cryptProv, keyProvInfo->pwszContainerName, keyProvInfo->pwszProvName, + keyProvInfo->dwProvType, 0)) + { + CryptAcquireContextW(&cryptProv, keyProvInfo->pwszContainerName, keyProvInfo->pwszProvName, + keyProvInfo->dwProvType, CRYPT_DELETEKEYSET); + } } } CertDeleteCertificateFromStore(cert); } + _certs.clear(); + for(vector<HCERTSTORE>::const_iterator i = _stores.begin(); i != _stores.end(); ++i) + { + CertCloseStore(*i, 0); + } + _stores.clear(); + if(_store) + { + CertCloseStore(_store, 0); + _store = 0; + } + } + +private: + + HCERTSTORE _store; + vector<HCERTSTORE> _stores; + vector<PCCERT_CONTEXT> _certs; +}; +#else +class ImportCerts +{ +public: + ImportCerts(const string& defaultDir, const char* certificates[]) + { + // Nothing to do. } - for(vector<HCERTSTORE>::const_iterator i = stores.begin(); i != stores.end(); ++i) + + void cleanup() { - CertCloseStore(*i, 0); } -} +}; #endif class PasswordPromptI : public IceSSL::PasswordPrompt @@ -240,7 +310,10 @@ createServerProps(const Ice::PropertiesPtr& defaultProps, const string& defaultD { Test::Properties result; result["Ice.Plugin.IceSSL"] = "IceSSL:createIceSSL"; - result["IceSSL.DefaultDir"] = defaultDir; + if(!defaultDir.empty()) + { + result["IceSSL.DefaultDir"] = defaultDir; + } if(!defaultProps->getProperty("Ice.IPv6").empty()) { result["Ice.IPv6"] = defaultProps->getProperty("Ice.IPv6"); @@ -273,28 +346,22 @@ createServerProps(const Ice::PropertiesPtr& defaultProps, const string& defaultD // If no CA is specified, we don't set IceSSL.DefaultDir since // with OpenSSL the CAs might still be found. // - string pfx; - if(ca.empty()) - { - d = createServerProps(defaultProps, "", defaultHost, p12); - pfx = defaultDir + "/"; - } - else + d = createServerProps(defaultProps, defaultDir, defaultHost, p12); + if(!ca.empty()) { - d = createServerProps(defaultProps, defaultDir, defaultHost, p12); - d["IceSSL.CertAuthFile"] = ca + ".pem"; + d["IceSSL.CAs"] = ca + ".pem"; } if(!cert.empty()) { if(p12) { - d["IceSSL.CertFile"] = pfx + cert + ".p12"; + d["IceSSL.CertFile"] = cert + ".p12"; } else { - d["IceSSL.CertFile"] = pfx + cert + "_pub.pem"; - d["IceSSL.KeyFile"] = pfx + cert + "_priv.pem"; + d["IceSSL.CertFile"] = cert + "_pub.pem"; + d["IceSSL.KeyFile"] = cert + "_priv.pem"; } } return d; @@ -306,32 +373,22 @@ createClientProps(const Ice::PropertiesPtr& defaultProps, const string& defaultD { Ice::PropertiesPtr properties; - // - // If no CA is specified, we don't set IceSSL.DefaultDir since - // with OpenSSL the CAs might still be found. - // - string pfx; - if(ca.empty()) + properties = createClientProps(defaultProps, defaultDir, defaultHost, p12); + if(!ca.empty()) { - properties = createClientProps(defaultProps, "", defaultHost, p12); - pfx = defaultDir + "/"; - } - else - { - properties = createClientProps(defaultProps, defaultDir, defaultHost, p12); - properties->setProperty("IceSSL.CertAuthFile", ca + ".pem"); + properties->setProperty("IceSSL.CAs", ca + ".pem"); } if(!cert.empty()) { if(p12) { - properties->setProperty("IceSSL.CertFile", pfx + cert + ".p12"); + properties->setProperty("IceSSL.CertFile", cert + ".p12"); } else { - properties->setProperty("IceSSL.CertFile", pfx + cert + "_pub.pem"); - properties->setProperty("IceSSL.KeyFile", pfx + cert + "_priv.pem"); + properties->setProperty("IceSSL.CertFile", cert + "_pub.pem"); + properties->setProperty("IceSSL.KeyFile", cert + "_priv.pem"); } } return properties; @@ -456,8 +513,9 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b server->noCert(); test(!IceSSL::ConnectionInfoPtr::dynamicCast(server->ice_getConnection()->getInfo())->verified); } - catch(const LocalException&) + catch(const LocalException& ex) { + cerr << ex << endl; test(false); } fact->destroyServer(server); @@ -821,6 +879,9 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b cout << "testing certificate chains... " << flush; { + const char* certificates[] = {"/s_rsa_cai2.p12", 0}; + ImportCerts import(defaultDir, certificates); + InitializationData initData; initData.properties = createClientProps(defaultProps, defaultDir, defaultHost, p12, "", ""); initData.properties->setProperty("IceSSL.VerifyPeer", "0"); @@ -843,9 +904,9 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b test(info->nativeCerts.size() == 1); test(!info->verified); } - catch(const Ice::LocalException& ex) + catch(const Ice::LocalException&) { - cerr << ex << endl; + import.cleanup(); test(false); } fact->destroyServer(server); @@ -870,6 +931,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b catch(const Ice::LocalException& ex) { cerr << ex << endl; + import.cleanup(); test(false); } fact->destroyServer(server); @@ -897,6 +959,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b catch(const Ice::LocalException& ex) { cerr << ex << endl; + import.cleanup(); test(false); } fact->destroyServer(server); @@ -926,6 +989,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b catch(const Ice::LocalException& ex) { cerr << ex << endl; + import.cleanup(); test(false); } fact->destroyServer(server); @@ -950,6 +1014,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b try { IceSSL::NativeConnectionInfoPtr::dynamicCast(server->ice_getConnection()->getInfo()); + import.cleanup(); test(false); } catch(const Ice::SecurityException&) @@ -959,6 +1024,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b catch(const Ice::LocalException& ex) { cerr << ex << endl; + import.cleanup(); test(false); } fact->destroyServer(server); @@ -988,6 +1054,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b } catch(const Ice::LocalException&) { + import.cleanup(); test(false); } fact->destroyServer(server); @@ -1000,6 +1067,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b try { IceSSL::NativeConnectionInfoPtr::dynamicCast(server->ice_getConnection()->getInfo()); + import.cleanup(); test(false); } catch(const Ice::SecurityException&) @@ -1033,6 +1101,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b } catch(const Ice::LocalException&) { + import.cleanup(); test(false); } fact->destroyServer(server); @@ -1058,6 +1127,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b try { server->ice_getConnection(); + import.cleanup(); test(false); } catch(const Ice::ProtocolException&) @@ -1070,6 +1140,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b } catch(const Ice::LocalException&) { + import.cleanup(); test(false); } fact->destroyServer(server); @@ -1086,12 +1157,14 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b } catch(const Ice::LocalException&) { + import.cleanup(); test(false); } fact->destroyServer(server); } comm->destroy(); + import.cleanup(); } cout << "ok" << endl; @@ -1538,17 +1611,17 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b cout << "testing CA certificate directory... " << flush; { // - // Don't specify CertAuthFile explicitly; we let OpenSSL find the CA + // Don't specify CAs explicitly; we let OpenSSL find the CA // certificate in the default directory. // InitializationData initData; initData.properties = createClientProps(defaultProps, defaultDir, defaultHost, p12, "c_rsa_ca1", ""); - initData.properties->setProperty("IceSSL.DefaultDir", defaultDir); + initData.properties->setProperty("IceSSL.CAs", defaultDir); CommunicatorPtr comm = initialize(initData); Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); test(fact); Test::Properties d = createServerProps(defaultProps, defaultDir, defaultHost, p12, "s_rsa_ca1", ""); - d["IceSSL.DefaultDir"] = defaultDir; + d["IceSSL.CAs"] = defaultDir; Test::ServerPrx server = fact->createServer(d); try { @@ -1565,6 +1638,56 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b cout << "ok" << endl; #endif + cout << "testing multiple CA certificates... " << flush; + { + InitializationData initData; + initData.properties = createClientProps(defaultProps, defaultDir, defaultHost, p12, "c_rsa_ca1", "cacerts"); + CommunicatorPtr comm = initialize(initData); + Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); + test(fact); + Test::Properties d = createServerProps(defaultProps, defaultDir, defaultHost, p12, "s_rsa_ca2", "cacerts"); + d["IceSSL.VerifyPeer"] = "2"; + Test::ServerPrx server = fact->createServer(d); + try + { + server->ice_ping(); + } + catch(const Ice::LocalException&) + { + test(false); + } + fact->destroyServer(server); + comm->destroy(); + } + cout << "ok" << endl; + +#ifndef ICE_USE_OPENSSL + cout << "testing DER CA certificate... " << flush; + { + InitializationData initData; + initData.properties = createClientProps(defaultProps, defaultDir, defaultHost, p12, "c_rsa_ca1", ""); + initData.properties->setProperty("IceSSL.CAs", "cacert1.der"); + CommunicatorPtr comm = initialize(initData); + Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); + test(fact); + Test::Properties d = createServerProps(defaultProps, defaultDir, defaultHost, p12, "s_rsa_ca1", ""); + d["IceSSL.VerifyPeer"] = "2"; + d["IceSSL.CAs"] = "cacert1.der"; + Test::ServerPrx server = fact->createServer(d); + try + { + server->ice_ping(); + } + catch(const Ice::LocalException&) + { + test(false); + } + fact->destroyServer(server); + comm->destroy(); + } + cout << "ok" << endl; +#endif + // // SChannel doesn't support PEM Password protected certificates certificates // @@ -2806,7 +2929,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b { #if defined(ICE_USE_SCHANNEL) - cerr << "testing IceSSL.FindCert... " << flush; + cout << "testing IceSSL.FindCert... " << flush; const char* clientFindCertProperties[] = { "SUBJECTDN:'CN=Client, OU=Ice, O=\"ZeroC, Inc.\", L=Jupiter, S=Florida, C=US, E=info@zeroc.com'", @@ -2842,50 +2965,13 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b }; const char* certificates[] = {"/s_rsa_ca1.p12", "/c_rsa_ca1.p12", 0}; - - // - // First we need to import some certificates in the user store. - // - HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, L"MY"); - test(store); - - vector<HCERTSTORE> stores; - vector<PCCERT_CONTEXT> certs; - - for(int i = 0; certificates[i] != 0; ++i) - { - vector<char> buffer; - readFile(defaultDir + certificates[i], buffer); - - CRYPT_DATA_BLOB p12Blob; - p12Blob.cbData = static_cast<DWORD>(buffer.size()); - p12Blob.pbData = reinterpret_cast<BYTE*>(&buffer[0]); - - HCERTSTORE p12 = PFXImportCertStore(&p12Blob, L"password", CRYPT_USER_KEYSET); - - PCCERT_CONTEXT next = 0; - PCCERT_CONTEXT newCert = 0; - do - { - if((next = CertFindCertificateInStore(p12, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - CERT_FIND_ANY, 0, next))) - { - if(CertAddCertificateContextToStore(store, next, CERT_STORE_ADD_ALWAYS, &newCert)) - { - certs.push_back(newCert); - } - } - } - while(next); - - stores.push_back(p12); - } + ImportCerts import(defaultDir, certificates); for(int i = 0; clientFindCertProperties[i] != 0; i++) { InitializationData initData; initData.properties = createClientProps(defaultProps, defaultDir, defaultHost, p12); - initData.properties->setProperty("IceSSL.CertAuthFile", "cacert1.pem"); + initData.properties->setProperty("IceSSL.CAs", "cacert1.pem"); initData.properties->setProperty("IceSSL.FindCert.CurrentUser.My", clientFindCertProperties[i]); // // Use TrustOnly to ensure the peer has pick the expected certificate. @@ -2897,7 +2983,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); test(fact); Test::Properties d = createServerProps(defaultProps, defaultDir, defaultHost, p12, "s_rsa_ca1", "cacert1"); - d["IceSSL.CertAuthFile"] = "cacert1.pem"; + d["IceSSL.CAs"] = "cacert1.pem"; d["IceSSL.FindCert.CurrentUser.My"] = serverFindCertProperties[i]; // // Use TrustOnly to ensure the peer has pick the expected certificate. @@ -2912,7 +2998,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b catch(const LocalException& ex) { cerr << ex << endl; - findCertsCleanup(store, stores, certs); + import.cleanup(); test(false); } fact->destroyServer(server); @@ -2926,12 +3012,12 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b { InitializationData initData; initData.properties = createClientProps(defaultProps, defaultDir, defaultHost, p12); - initData.properties->setProperty("IceSSL.CertAuthFile", "cacert1.pem"); + initData.properties->setProperty("IceSSL.CAs", "cacert1.pem"); initData.properties->setProperty("IceSSL.FindCert.CurrentUser.My", failFindCertProperties[i]); try { CommunicatorPtr comm = initialize(initData); - findCertsCleanup(store, stores, certs); + import.cleanup(); test(false); } catch(const PluginInitializationException&) @@ -2941,12 +3027,12 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b catch(const Ice::LocalException& ex) { cerr << ex << endl; - findCertsCleanup(store, stores, certs); + import.cleanup(); test(false); } } - findCertsCleanup(store, stores, certs); + import.cleanup(); // // These must fail because we have already remove the certificates. @@ -2955,7 +3041,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b { InitializationData initData; initData.properties = createClientProps(defaultProps, defaultDir, defaultHost, p12); - initData.properties->setProperty("IceSSL.CertAuthFile", "cacert1.pem"); + initData.properties->setProperty("IceSSL.CAs", "cacert1.pem"); initData.properties->setProperty("IceSSL.FindCert.CurrentUser.My", clientFindCertProperties[i]); try { @@ -2972,9 +3058,9 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b test(false); } } - cerr << "ok" << endl; + cout << "ok" << endl; #elif defined(ICE_USE_SECURE_TRANSPORT) - cerr << "testing IceSSL.FindCert... " << flush; + cout << "testing IceSSL.FindCert... " << flush; const char* clientFindCertProperties[] = { "SUBJECT:Client", @@ -3012,7 +3098,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b { InitializationData initData; initData.properties = createClientProps(defaultProps, defaultDir, defaultHost, p12); - initData.properties->setProperty("IceSSL.CertAuthFile", "cacert1.pem"); + initData.properties->setProperty("IceSSL.CAs", "cacert1.pem"); initData.properties->setProperty("IceSSL.Keychain", "../certs/Find.keychain"); initData.properties->setProperty("IceSSL.KeychainPassword", "password"); initData.properties->setProperty("IceSSL.FindCert", clientFindCertProperties[i]); @@ -3026,7 +3112,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b Test::ServerFactoryPrx fact = Test::ServerFactoryPrx::checkedCast(comm->stringToProxy(factoryRef)); test(fact); Test::Properties d = createServerProps(defaultProps, defaultDir, defaultHost, p12); - d["IceSSL.CertAuthFile"] = "cacert1.pem"; + d["IceSSL.CAs"] = "cacert1.pem"; d["IceSSL.Keychain"] = "../certs/Find.keychain"; d["IceSSL.KeychainPassword"] = "password"; d["IceSSL.FindCert"] = serverFindCertProperties[i]; @@ -3071,10 +3157,55 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12, b test(false); } } - cerr << "ok" << endl; + cout << "ok" << endl; #endif } + cout << "testing system CAs... " << flush; + { + InitializationData initData; + initData.properties = createClientProps(defaultProps, "", defaultHost, false); + initData.properties->setProperty("IceSSL.VerifyDepthMax", "4"); + initData.properties->setProperty("Ice.Override.Timeout", "5000"); // 5s timeout + CommunicatorPtr comm = initialize(initData); + Ice::ObjectPrx p = comm->stringToProxy("dummy:wss -h demo.zeroc.com -p 5064"); + try + { + p->ice_ping(); + test(false); + } + catch(const Ice::SecurityException&) + { + // Expected, by default we don't check for system CAs. + } + catch(const Ice::LocalException& ex) + { + cerr << "warning: unable to connect to demo.zeroc.com to check system CA:\n" << ex << endl; + } + comm->destroy(); + + initData.properties = createClientProps(defaultProps, "", defaultHost, false); + initData.properties->setProperty("IceSSL.VerifyDepthMax", "4"); + initData.properties->setProperty("Ice.Override.Timeout", "5000"); // 5s timeout + initData.properties->setProperty("IceSSL.UsePlatformCAs", "1"); + comm = initialize(initData); + p = comm->stringToProxy("dummy:wss -h demo.zeroc.com -p 5064"); + + IceSSL::WSSConnectionInfoPtr info; + try + { + info = IceSSL::WSSConnectionInfoPtr::dynamicCast(p->ice_getConnection()->getInfo()); + test(info->verified); + } + catch(const Ice::LocalException& ex) + { + cerr << ex << endl; + test(false); + } + comm->destroy(); + } + cout << "ok" << endl; + if(shutdown) { factory->shutdown(); diff --git a/cpp/test/IceSSL/configuration/run.py b/cpp/test/IceSSL/configuration/run.py index 58c6b79a9e6..e877d3bf802 100755 --- a/cpp/test/IceSSL/configuration/run.py +++ b/cpp/test/IceSSL/configuration/run.py @@ -8,7 +8,7 @@ # # ********************************************************************** -import os, sys, atexit +import os, sys, atexit, re path = [ ".", "..", "../..", "../../..", "../../../.." ] head = os.path.dirname(sys.argv[0]) @@ -20,6 +20,10 @@ if len(path) == 0: sys.path.append(os.path.join(path[0], "scripts")) import TestUtil +# Filter-out the deprecated property warnings +TestUtil.clientTraceFilters = [ lambda x: re.sub("-! .* warning: deprecated property: IceSSL.KeyFile\n", "", x) ] +TestUtil.serverTraceFilters = [ lambda x: re.sub("-! .* warning: deprecated property: IceSSL.KeyFile\n", "", x) ] + certsPath = os.path.abspath(os.path.join(os.getcwd(), "..", "certs")) keychainPath = os.path.abspath(os.path.join(certsPath, "Find.keychain")) diff --git a/csharp/src/Ice/PropertyNames.cs b/csharp/src/Ice/PropertyNames.cs index 279d5cb0ff2..cd2190d1f47 100644 --- a/csharp/src/Ice/PropertyNames.cs +++ b/csharp/src/Ice/PropertyNames.cs @@ -6,7 +6,7 @@ // ICE_LICENSE file included in this distribution. // // ********************************************************************** -// Generated by makeprops.py from file ../config/PropertyNames.xml, Tue Dec 9 12:08:30 2014 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Tue Apr 28 17:34:50 2015 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -188,6 +188,7 @@ namespace IceInternal new Property(@"^Ice\.Warn\.UnusedProperties$", false, null), new Property(@"^Ice\.CacheMessageBuffers$", false, null), new Property(@"^Ice\.ThreadInterruptSafe$", false, null), + new Property(@"^Ice\.Voip$", false, null), null }; @@ -969,8 +970,9 @@ namespace IceInternal public static Property[] IceSSLProps = { new Property(@"^IceSSL\.Alias$", false, null), - new Property(@"^IceSSL\.CertAuthDir$", false, null), - new Property(@"^IceSSL\.CertAuthFile$", false, null), + new Property(@"^IceSSL\.CAs$", false, null), + new Property(@"^IceSSL\.CertAuthDir$", true, null), + new Property(@"^IceSSL\.CertAuthFile$", true, null), new Property(@"^IceSSL\.CertStore$", false, null), new Property(@"^IceSSL\.CertFile$", false, null), new Property(@"^IceSSL\.CertVerifier$", false, null), @@ -982,10 +984,10 @@ namespace IceInternal new Property(@"^IceSSL\.DHParams$", false, null), new Property(@"^IceSSL\.EntropyDaemon$", false, null), new Property(@"^IceSSL\.FindCert$", false, null), - new Property(@"^IceSSL\.FindCert\.[^\s]+$", false, null), + new Property(@"^IceSSL\.FindCert\.[^\s]+$", true, null), new Property(@"^IceSSL\.ImportCert\.[^\s]+$", true, null), new Property(@"^IceSSL\.InitOpenSSL$", false, null), - new Property(@"^IceSSL\.KeyFile$", false, null), + new Property(@"^IceSSL\.KeyFile$", true, null), new Property(@"^IceSSL\.KeySet$", false, null), new Property(@"^IceSSL\.Keychain$", false, null), new Property(@"^IceSSL\.KeychainPassword$", false, null), @@ -1008,6 +1010,7 @@ namespace IceInternal new Property(@"^IceSSL\.Truststore$", false, null), new Property(@"^IceSSL\.TruststorePassword$", false, null), new Property(@"^IceSSL\.TruststoreType$", false, null), + new Property(@"^IceSSL\.UsePlatformCAs$", false, null), new Property(@"^IceSSL\.VerifyDepthMax$", false, null), new Property(@"^IceSSL\.VerifyPeer$", false, null), null diff --git a/csharp/src/Ice/TcpEndpointI.cs b/csharp/src/Ice/TcpEndpointI.cs index fcfa84ab0c3..71b0e498230 100644 --- a/csharp/src/Ice/TcpEndpointI.cs +++ b/csharp/src/Ice/TcpEndpointI.cs @@ -15,7 +15,7 @@ namespace IceInternal using System; using System.Globalization; - sealed class TcpEndpointI : IPEndpointI + sealed class TcpEndpointI : IPEndpointI, WSEndpointDelegate { public TcpEndpointI(ProtocolInstance instance, string ho, int po, EndPoint sourceAddr, int ti, string conId, bool co) : @@ -71,6 +71,39 @@ namespace IceInternal return info; } + private sealed class WSInfoI : Ice.WSEndpointInfo + { + public WSInfoI(IPEndpointI e) + { + _endpoint = e; + } + + public override short type() + { + return _endpoint.type(); + } + + public override bool datagram() + { + return _endpoint.datagram(); + } + + public override bool secure() + { + return _endpoint.secure(); + } + + private IPEndpointI _endpoint; + } + + public Ice.EndpointInfo getWSInfo(string resource) + { + WSInfoI info = new WSInfoI(this); + fillEndpointInfo(info); + info.resource = resource; + return info; + } + public override int timeout() { return _timeout; diff --git a/csharp/src/Ice/TcpTransceiver.cs b/csharp/src/Ice/TcpTransceiver.cs index 11f7fd49578..c434f1f33db 100644 --- a/csharp/src/Ice/TcpTransceiver.cs +++ b/csharp/src/Ice/TcpTransceiver.cs @@ -12,10 +12,11 @@ namespace IceInternal using System; using System.ComponentModel; using System.Diagnostics; + using System.Collections.Generic; using System.Net; using System.Net.Sockets; - sealed class TcpTransceiver : Transceiver + sealed class TcpTransceiver : Transceiver, WSTransceiverDelegate { public Socket fd() { @@ -88,17 +89,15 @@ namespace IceInternal public Ice.ConnectionInfo getInfo() { Ice.TCPConnectionInfo info = new Ice.TCPConnectionInfo(); - if(_stream.fd() != null) - { - EndPoint localEndpoint = Network.getLocalAddress(_stream.fd()); - info.localAddress = Network.endpointAddressToString(localEndpoint); - info.localPort = Network.endpointPort(localEndpoint); - EndPoint remoteEndpoint = Network.getRemoteAddress(_stream.fd()); - info.remoteAddress = Network.endpointAddressToString(remoteEndpoint); - info.remotePort = Network.endpointPort(remoteEndpoint); - info.rcvSize = Network.getRecvBufferSize(_stream.fd()); - info.sndSize = Network.getSendBufferSize(_stream.fd()); - } + fillConnectionInfo(info); + return info; + } + + public Ice.ConnectionInfo getWSInfo(Dictionary<string, string> headers) + { + Ice.WSConnectionInfo info = new Ice.WSConnectionInfo(); + fillConnectionInfo(info); + info.headers = headers; return info; } @@ -130,6 +129,21 @@ namespace IceInternal _stream = stream; } + private void fillConnectionInfo(Ice.TCPConnectionInfo info) + { + if(_stream.fd() != null) + { + EndPoint localEndpoint = Network.getLocalAddress(_stream.fd()); + info.localAddress = Network.endpointAddressToString(localEndpoint); + info.localPort = Network.endpointPort(localEndpoint); + EndPoint remoteEndpoint = Network.getRemoteAddress(_stream.fd()); + info.remoteAddress = Network.endpointAddressToString(remoteEndpoint); + info.remotePort = Network.endpointPort(remoteEndpoint); + info.rcvSize = Network.getRecvBufferSize(_stream.fd()); + info.sndSize = Network.getSendBufferSize(_stream.fd()); + } + } + private readonly ProtocolInstance _instance; private readonly StreamSocket _stream; } diff --git a/csharp/src/Ice/WSEndpoint.cs b/csharp/src/Ice/WSEndpoint.cs index 27f7816d5e9..5d0660726c8 100644 --- a/csharp/src/Ice/WSEndpoint.cs +++ b/csharp/src/Ice/WSEndpoint.cs @@ -14,6 +14,15 @@ namespace IceInternal using System.Collections.Generic; using System.Globalization; + // + // Delegate interface implemented by TcpEndpoint or IceSSL.EndpointI or any endpoint that WS can + // delegate to. + // + public interface WSEndpointDelegate + { + Ice.EndpointInfo getWSInfo(string resource); + }; + sealed class WSEndpoint : EndpointI { internal WSEndpoint(ProtocolInstance instance, EndpointI del, string res) @@ -71,10 +80,8 @@ namespace IceInternal public override Ice.EndpointInfo getInfo() { - InfoI info = new InfoI(this); - _delegate.fillEndpointInfo(info); - info.resource = _resource; - return info; + Debug.Assert(_delegate is WSEndpointDelegate); + return ((WSEndpointDelegate)_delegate).getWSInfo(_resource); } public override short type() diff --git a/csharp/src/Ice/WSTransceiver.cs b/csharp/src/Ice/WSTransceiver.cs index 7218027bdc2..98dd6f8fdae 100644 --- a/csharp/src/Ice/WSTransceiver.cs +++ b/csharp/src/Ice/WSTransceiver.cs @@ -16,6 +16,15 @@ namespace IceInternal using System.Security.Cryptography; using System.Text; + // + // Delegate interface implemented by TcpTransceiver or IceSSL.TransceiverI or any endpoint that WS can + // delegate to. + // + public interface WSTransceiverDelegate + { + Ice.ConnectionInfo getWSInfo(Dictionary<string, string> headers); + }; + sealed class WSTransceiver : Transceiver { public Socket fd() @@ -629,16 +638,8 @@ namespace IceInternal public Ice.ConnectionInfo getInfo() { - Ice.IPConnectionInfo di = (Ice.IPConnectionInfo)_delegate.getInfo(); - Ice.WSConnectionInfo info = new Ice.WSConnectionInfo(); - info.localAddress = di.localAddress; - info.localPort = di.localPort; - info.remoteAddress = di.remoteAddress; - info.remotePort = di.remotePort; - info.rcvSize = di.rcvSize; - info.sndSize = di.sndSize; - info.headers = _parser.getHeaders(); - return info; + Debug.Assert(_delegate is WSTransceiverDelegate); + return ((WSTransceiverDelegate)_delegate).getWSInfo(_parser.getHeaders()); } public void checkSendSize(Buffer buf) diff --git a/csharp/src/IceSSL/EndpointI.cs b/csharp/src/IceSSL/EndpointI.cs index edc04dd09d9..8c02bad421a 100644 --- a/csharp/src/IceSSL/EndpointI.cs +++ b/csharp/src/IceSSL/EndpointI.cs @@ -15,7 +15,7 @@ namespace IceSSL using System.Net; using System.Globalization; - sealed class EndpointI : IceInternal.IPEndpointI + sealed class EndpointI : IceInternal.IPEndpointI, IceInternal.WSEndpointDelegate { internal EndpointI(Instance instance, string ho, int po, EndPoint sourceAddr, int ti, string conId, bool co) : base(instance, ho, po, sourceAddr, conId) @@ -76,6 +76,42 @@ namespace IceSSL return info; } + private sealed class WSSInfoI : IceSSL.WSSEndpointInfo + { + public WSSInfoI(EndpointI e) + { + _endpoint = e; + } + + override public short type() + { + return _endpoint.type(); + } + + override public bool datagram() + { + return _endpoint.datagram(); + } + + override public bool secure() + { + return _endpoint.secure(); + } + + private EndpointI _endpoint; + } + + // + // Return the endpoint information. + // + public Ice.EndpointInfo getWSInfo(string resource) + { + WSSInfoI info = new WSSInfoI(this); + fillEndpointInfo(info); + info.resource = resource; + return info; + } + // // Return the timeout for the endpoint in milliseconds. 0 means // non-blocking, -1 means no timeout. diff --git a/csharp/src/IceSSL/SSLEngine.cs b/csharp/src/IceSSL/SSLEngine.cs index c0ae3a79c04..7a0180d3fcf 100644 --- a/csharp/src/IceSSL/SSLEngine.cs +++ b/csharp/src/IceSSL/SSLEngine.cs @@ -305,7 +305,15 @@ namespace IceSSL if(_caCerts == null) { - string certAuthFile = properties.getProperty(prefix + "CertAuthFile"); + string certAuthFile = properties.getProperty(prefix + "CAs"); + if(certAuthFile.Length == 0) + { + certAuthFile = properties.getProperty(prefix + "CertAuthFile"); + } + if(certAuthFile.Length > 0 || properties.getPropertyAsInt(prefix + "UsePlatformCAs") <= 0) + { + _caCerts = new X509Certificate2Collection(); + } if(certAuthFile.Length > 0) { if(!checkPath(ref certAuthFile)) @@ -315,12 +323,59 @@ namespace IceSSL throw e; } - _caCerts = new X509Certificate2Collection(); try { - _caCerts.Add(new X509Certificate2(certAuthFile)); + using(System.IO.FileStream fs = System.IO.File.OpenRead(certAuthFile)) + { + byte[] data = new byte[fs.Length]; + fs.Read(data, 0, data.Length); + + string strbuf = ""; + try + { + strbuf = System.Text.Encoding.UTF8.GetString(data); + } + catch(Exception) + { + // Ignore + } + + if(strbuf.Length == data.Length) + { + int size, startpos, endpos = 0; + bool first = true; + while(true) + { + startpos = strbuf.IndexOf("-----BEGIN CERTIFICATE-----", endpos); + if(startpos != -1) + { + endpos = strbuf.IndexOf("-----END CERTIFICATE-----", startpos); + size = endpos - startpos + "-----END CERTIFICATE-----".Length; + } + else if(first) + { + startpos = 0; + endpos = strbuf.Length; + size = strbuf.Length; + } + else + { + break; + } + + byte[] cert = new byte[size]; + System.Buffer.BlockCopy(data, startpos, cert, 0, size); + _caCerts.Import(cert); + first = false; + } + } + else + { + _caCerts.Import(data); + } + } } - catch(CryptographicException ex) + catch(Exception ex) { Ice.PluginInitializationException e = new Ice.PluginInitializationException(ex); e.reason = "IceSSL: error while attempting to load CA certificate from " + certAuthFile; diff --git a/csharp/src/IceSSL/TransceiverI.cs b/csharp/src/IceSSL/TransceiverI.cs index 96eec4be71b..85ec68d0e4f 100644 --- a/csharp/src/IceSSL/TransceiverI.cs +++ b/csharp/src/IceSSL/TransceiverI.cs @@ -22,7 +22,7 @@ namespace IceSSL using System.Threading; using System.Text; - sealed class TransceiverI : IceInternal.Transceiver + sealed class TransceiverI : IceInternal.Transceiver, IceInternal.WSTransceiverDelegate { public Socket fd() { @@ -49,7 +49,7 @@ namespace IceSSL Debug.Assert(_sslStream.IsAuthenticated); _authenticated = true; - _instance.verifyPeer(getNativeConnectionInfo(), _stream.fd(), _host); + _instance.verifyPeer((NativeConnectionInfo)getInfo(), _stream.fd(), _host); if(_instance.securityTraceLevel() >= 1) { @@ -302,7 +302,17 @@ namespace IceSSL public Ice.ConnectionInfo getInfo() { - return getNativeConnectionInfo(); + NativeConnectionInfo info = new NativeConnectionInfo(); + info.nativeCerts = fillConnectionInfo(info); + return info; + } + + public Ice.ConnectionInfo getWSInfo(Dictionary<string, string> headers) + { + WSSNativeConnectionInfo info = new WSSNativeConnectionInfo(); + info.nativeCerts = fillConnectionInfo(info); + info.headers = headers; + return info; } public void checkSendSize(IceInternal.Buffer buf) @@ -367,9 +377,9 @@ namespace IceSSL } } - private NativeConnectionInfo getNativeConnectionInfo() + private X509Certificate2[] fillConnectionInfo(ConnectionInfo info) { - IceSSL.NativeConnectionInfo info = new IceSSL.NativeConnectionInfo(); + X509Certificate2[] nativeCerts = null; if(_stream.fd() != null) { IPEndPoint localEndpoint = (IPEndPoint)IceInternal.Network.getLocalAddress(_stream.fd()); @@ -392,19 +402,19 @@ namespace IceSSL info.cipher = _sslStream.CipherAlgorithm.ToString(); if(_chain.ChainElements != null && _chain.ChainElements.Count > 0) { - info.nativeCerts = new X509Certificate2[_chain.ChainElements.Count]; + nativeCerts = new X509Certificate2[_chain.ChainElements.Count]; for(int i = 0; i < _chain.ChainElements.Count; ++i) { - info.nativeCerts[i] = _chain.ChainElements[i].Certificate; + nativeCerts[i] = _chain.ChainElements[i].Certificate; } } #endif List<string> certs = new List<string>(); #if !UNITY - if(info.nativeCerts != null) + if(nativeCerts != null) { - foreach(X509Certificate2 cert in info.nativeCerts) + foreach(X509Certificate2 cert in nativeCerts) { StringBuilder s = new StringBuilder(); s.Append("-----BEGIN CERTIFICATE-----\n"); @@ -419,7 +429,7 @@ namespace IceSSL } info.adapterName = _adapterName; info.incoming = _incoming; - return info; + return nativeCerts; } private bool startAuthenticate(IceInternal.AsyncCallback callback, object state) @@ -571,19 +581,44 @@ namespace IceSSL SslPolicyErrors policyErrors) { #if !UNITY - SslPolicyErrors sslPolicyErrors = policyErrors; + string message = ""; + int errors = (int)policyErrors; if(certificate != null) { - sslPolicyErrors = SslPolicyErrors.None; - _verified = _chain.Build(new X509Certificate2(certificate)); - if(_chain.ChainStatus.Length > 0) + _chain.Build(new X509Certificate2(certificate)); + if(_chain.ChainStatus != null && _chain.ChainStatus.Length > 0) + { + errors = (int)SslPolicyErrors.RemoteCertificateChainErrors; + } + else if(_instance.engine().caCerts() != null) + { + X509ChainElement e = _chain.ChainElements[_chain.ChainElements.Count - 1]; + if(!_chain.ChainPolicy.ExtraStore.Contains(e.Certificate)) + { + if(_verifyPeer > 0) + { + message = message + "\npuntrusted root certificate"; + } + else + { + message = message + "\nuntrusted root certificate (ignored)"; + _verified = false; + } + errors = (int)SslPolicyErrors.RemoteCertificateChainErrors; + } + else + { + _verified = true; + return true; + } + } + else { - sslPolicyErrors = SslPolicyErrors.RemoteCertificateChainErrors; + _verified = true; + return true; } } - string message = ""; - int errors = (int)sslPolicyErrors; if((errors & (int)SslPolicyErrors.RemoteCertificateNotAvailable) > 0) { // @@ -618,81 +653,87 @@ namespace IceSSL } - if((errors & (int)SslPolicyErrors.RemoteCertificateChainErrors) > 0) + if((errors & (int)SslPolicyErrors.RemoteCertificateChainErrors) > 0 && + _chain.ChainStatus != null && _chain.ChainStatus.Length > 0) { - if(_chain.ChainStatus != null) + int errorCount = 0; + foreach(X509ChainStatus status in _chain.ChainStatus) { - int errorCount = _chain.ChainStatus.Length; - foreach(X509ChainStatus status in _chain.ChainStatus) + if(status.Status == X509ChainStatusFlags.UntrustedRoot && _instance.engine().caCerts() != null) { - if(status.Status == X509ChainStatusFlags.UntrustedRoot && - _instance.engine().caCerts() != null && _verified) + // + // Untrusted root is OK when using our custom chain engine if + // the CA certificate is present in the chain policy extra store. + // + X509ChainElement e = _chain.ChainElements[_chain.ChainElements.Count - 1]; + if(!_chain.ChainPolicy.ExtraStore.Contains(e.Certificate)) { - // - // Untrusted root is OK when using our custom chain engine if - // the CA certificate is present in the chain policy extra store. - // - X509ChainElement e = _chain.ChainElements[_chain.ChainElements.Count - 1]; - if(_chain.ChainPolicy.ExtraStore.Contains(e.Certificate)) - { - --errorCount; - } - } - else if(status.Status == X509ChainStatusFlags.Revoked) - { - if(_instance.checkCRL() > 0) + if(_verifyPeer > 0) { - message = message + "\ncertificate revoked"; + message = message + "\npuntrusted root certificate"; + ++errorCount; } else { - message = message + "\ncertificate revoked (ignored)"; - --errorCount; + message = message + "\nuntrusted root certificate (ignored)"; } } - else if(status.Status == X509ChainStatusFlags.RevocationStatusUnknown) + else { - // - // If a certificate's revocation status cannot be determined, the strictest - // policy is to reject the connection. - // - if(_instance.checkCRL() > 1) - { - message = message + "\ncertificate revocation status unknown"; - } - else - { - message = message + "\ncertificate revocation status unknown (ignored)"; - --errorCount; - } + _verified = true; } - else if(status.Status == X509ChainStatusFlags.PartialChain) + } + else if(status.Status == X509ChainStatusFlags.Revoked) + { + if(_instance.checkCRL() > 0) { - if(_verifyPeer > 0) - { - message = message + "\npartial certificate chain"; - } - else - { - message = message + "\npartial certificate chain (ignored)"; - --errorCount; - } + message = message + "\ncertificate revoked"; + ++errorCount; } - else if(status.Status == X509ChainStatusFlags.NoError) + else { - --errorCount; + message = message + "\ncertificate revoked (ignored)"; + } + } + else if(status.Status == X509ChainStatusFlags.RevocationStatusUnknown) + { + // + // If a certificate's revocation status cannot be determined, the strictest + // policy is to reject the connection. + // + if(_instance.checkCRL() > 1) + { + message = message + "\ncertificate revocation status unknown"; + ++errorCount; } else { - message = message + "\ncertificate chain error: " + status.Status.ToString(); + message = message + "\ncertificate revocation status unknown (ignored)"; } } - - if(errorCount == 0) + else if(status.Status == X509ChainStatusFlags.PartialChain) + { + if(_verifyPeer > 0) + { + message = message + "\npartial certificate chain"; + ++errorCount; + } + else + { + message = message + "\npartial certificate chain (ignored)"; + } + } + else if(status.Status != X509ChainStatusFlags.NoError) { - errors ^= (int)SslPolicyErrors.RemoteCertificateChainErrors; + message = message + "\ncertificate chain error: " + status.Status.ToString(); + ++errorCount; } } + + if(errorCount == 0) + { + errors ^= (int)SslPolicyErrors.RemoteCertificateChainErrors; + } } if(errors > 0) @@ -714,11 +755,10 @@ namespace IceSSL } else if(message.Length > 0 && _instance.securityTraceLevel() >= 1) { - _instance.logger().trace(_instance.securityTraceCategory(), "SSL certificate validation status:" + - message); + _instance.logger().trace(_instance.securityTraceCategory(), + "SSL certificate validation status:" + message); } #endif - return true; } diff --git a/csharp/src/IceSSL/Util.cs b/csharp/src/IceSSL/Util.cs index 9fa6b8d0142..0dd847fcb10 100644 --- a/csharp/src/IceSSL/Util.cs +++ b/csharp/src/IceSSL/Util.cs @@ -28,6 +28,20 @@ namespace IceSSL public System.Security.Cryptography.X509Certificates.X509Certificate2[] nativeCerts; } + /// <summary> + /// This class provides information about a connection to applications + /// that require information about a peer. + /// </summary> + public sealed class WSSNativeConnectionInfo : WSSConnectionInfo + { + /// <summary> + /// The certificate chain. This may be null if the peer did not + /// supply a certificate. The peer's certificate (if any) is the + /// first one in the chain. + /// </summary> + public System.Security.Cryptography.X509Certificates.X509Certificate2[] nativeCerts; + } + public sealed class Util { public static X509Certificate2 createCertificate(string certPEM) diff --git a/csharp/test/Ice/info/AllTests.cs b/csharp/test/Ice/info/AllTests.cs index 87019626c24..f2b3a21f714 100644 --- a/csharp/test/Ice/info/AllTests.cs +++ b/csharp/test/Ice/info/AllTests.cs @@ -69,7 +69,7 @@ public class AllTests : TestCommon.TestApp test(ipEndpoint.type() == Ice.TCPEndpointType.value && ipEndpoint is Ice.TCPEndpointInfo || ipEndpoint.type() == IceSSL.EndpointType.value && ipEndpoint is IceSSL.EndpointInfo || ipEndpoint.type() == Ice.WSEndpointType.value && ipEndpoint is Ice.WSEndpointInfo || - ipEndpoint.type() == Ice.WSSEndpointType.value && ipEndpoint is Ice.WSEndpointInfo); + ipEndpoint.type() == Ice.WSSEndpointType.value && ipEndpoint is IceSSL.WSSEndpointInfo); #endif Ice.UDPEndpointInfo udpEndpoint = (Ice.UDPEndpointInfo)endps[1].getInfo(); @@ -209,12 +209,19 @@ public class AllTests : TestCommon.TestApp if(@base.ice_getConnection().type().Equals("ws") || @base.ice_getConnection().type().Equals("wss")) { - test(info is Ice.WSConnectionInfo); - Ice.WSConnectionInfo wsinfo = (Ice.WSConnectionInfo)info; - test(wsinfo.headers["Upgrade"].Equals("websocket")); - test(wsinfo.headers["Connection"].Equals("Upgrade")); - test(wsinfo.headers["Sec-WebSocket-Protocol"].Equals("ice.zeroc.com")); - test(wsinfo.headers["Sec-WebSocket-Accept"] != null); + Dictionary<string, string> headers; + if(info is Ice.WSConnectionInfo) + { + headers = ((Ice.WSConnectionInfo)info).headers; + } + else + { + headers = ((IceSSL.WSSConnectionInfo)info).headers; + } + test(headers["Upgrade"].Equals("websocket")); + test(headers["Connection"].Equals("Upgrade")); + test(headers["Sec-WebSocket-Protocol"].Equals("ice.zeroc.com")); + test(headers["Sec-WebSocket-Accept"] != null); test(ctx["ws.Upgrade"].Equals("websocket")); test(ctx["ws.Connection"].Equals("Upgrade")); diff --git a/csharp/test/Ice/info/Makefile.mak b/csharp/test/Ice/info/Makefile.mak index b6dd92ff24f..49377776519 100644 --- a/csharp/test/Ice/info/Makefile.mak +++ b/csharp/test/Ice/info/Makefile.mak @@ -30,4 +30,4 @@ client.exe: $(C_SRCS) $(GEN_SRCS) $(MCS) $(MCSFLAGS) -out:$@ -r:"$(refdir)\Ice.dll" -r:"$(refdir)\IceSSL.dll" $(C_SRCS) $(GEN_SRCS) server.exe: $(S_SRCS) $(GEN_SRCS) - $(MCS) $(MCSFLAGS) -out:$@ -r:"$(refdir)\Ice.dll" $(S_SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) -out:$@ -r:"$(refdir)\Ice.dll" -r:"$(refdir)\IceSSL.dll" $(S_SRCS) $(GEN_SRCS) diff --git a/csharp/test/Ice/info/TestI.cs b/csharp/test/Ice/info/TestI.cs index 3e15e9fecc1..d84e4bd64e0 100644 --- a/csharp/test/Ice/info/TestI.cs +++ b/csharp/test/Ice/info/TestI.cs @@ -63,6 +63,15 @@ public class TestI : TestIntfDisp_ } } + if(info is IceSSL.WSSConnectionInfo) + { + IceSSL.WSSConnectionInfo wssinfo = (IceSSL.WSSConnectionInfo)info; + foreach(KeyValuePair<string, string> e in wssinfo.headers) + { + ctx["ws." + e.Key] = e.Value; + } + } + return ctx; } } diff --git a/csharp/test/IceSSL/certs/makecerts.py b/csharp/test/IceSSL/certs/makecerts.py index 4e413e7a926..73a18296d47 100755 --- a/csharp/test/IceSSL/certs/makecerts.py +++ b/csharp/test/IceSSL/certs/makecerts.py @@ -66,6 +66,12 @@ cai2 = cai1.getIntermediateFactory("intermediate1") if force or not os.path.exists("cacert1.pem"): ca1.getCA().save("cacert1.pem") if force or not os.path.exists("cacert2.pem"): ca2.getCA().save("cacert2.pem") +if force or not os.path.exists("cacert1.der"): ca1.getCA().save("cacert1.der") +if force or not os.path.exists("cacerts.pem"): + pem = "" + with open("cacert1.pem", "r") as f: pem += f.read() + with open("cacert2.pem", "r") as f: pem += f.read() + with open("cacerts.pem", "w") as f: f.write(pem); certs = [ (ca1, "s_rsa_ca1", None, {}), @@ -88,7 +94,7 @@ certs = [ for (ca, alias, path, args) in certs: if not path: path = alias cert = ca.get(alias) - if force or not os.path.exists(path + ".p12"): + if force or not os.path.exists(path + ".p12"): cert.save(path + ".p12", **args) # Also export the ca2 self-signed certificate, it's used by the tests to test self-signed certificates diff --git a/csharp/test/IceSSL/configuration/AllTests.cs b/csharp/test/IceSSL/configuration/AllTests.cs index cd890b61f86..d81617df54c 100644 --- a/csharp/test/IceSSL/configuration/AllTests.cs +++ b/csharp/test/IceSSL/configuration/AllTests.cs @@ -89,7 +89,7 @@ public class AllTests } if(ca.Length > 0) { - d["IceSSL.CertAuthFile"] = ca + ".pem"; + d["IceSSL.CAs"] = ca + ".pem"; } d["IceSSL.Password"] = "password"; return d; @@ -105,7 +105,7 @@ public class AllTests } if(ca.Length > 0) { - initData.properties.setProperty("IceSSL.CertAuthFile", ca + ".pem"); + initData.properties.setProperty("IceSSL.CAs", ca + ".pem"); } initData.properties.setProperty("IceSSL.Password", "password"); return initData; @@ -208,7 +208,7 @@ public class AllTests coll.Add(cert); initData = createClientProps(defaultProperties, defaultDir, defaultHost); initData.properties.setProperty("Ice.InitPlugins", "0"); - initData.properties.setProperty("IceSSL.CertAuthFile", caCert1File); + initData.properties.setProperty("IceSSL.CAs", caCert1File); Ice.Communicator comm = Ice.Util.initialize(ref args, initData); Ice.PluginManager pm = comm.getPluginManager(); IceSSL.Plugin plugin = (IceSSL.Plugin)pm.getPlugin("IceSSL"); @@ -289,7 +289,7 @@ public class AllTests test(!((IceSSL.ConnectionInfo)server.ice_getConnection().getInfo()).verified); } catch(Ice.LocalException ex) - { + { Console.WriteLine(ex.ToString()); test(false); } @@ -314,7 +314,7 @@ public class AllTests test(((IceSSL.ConnectionInfo)server.ice_getConnection().getInfo()).verified); } catch(Ice.LocalException ex) - { + { Console.WriteLine(ex.ToString()); test(false); } @@ -336,7 +336,7 @@ public class AllTests server.noCert(); } catch(Ice.LocalException) - { + { test(false); } fact.destroyServer(server); @@ -652,260 +652,278 @@ public class AllTests Console.Out.Write("testing certificate chains... "); Console.Out.Flush(); { - IceSSL.NativeConnectionInfo info; - - initData = createClientProps(defaultProperties, defaultDir, defaultHost, "", ""); - initData.properties.setProperty("IceSSL.VerifyPeer", "0"); - Ice.Communicator comm = Ice.Util.initialize(initData); - - Test.ServerFactoryPrx fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); - test(fact != null); - - // - // The client can't verify the server certificate but it should - // still provide it. "s_rsa_ca1" doesn't include the root so the - // cert size should be 1. - // - d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_ca1", ""); - d["IceSSL.VerifyPeer"] = "0"; - Test.ServerPrx server = fact.createServer(d); - try - { - info = (IceSSL.NativeConnectionInfo)server.ice_getConnection().getInfo(); - test(info.nativeCerts.Length == 1); - test(!info.verified); - } - catch(Ice.LocalException) - { - test(false); - } - fact.destroyServer(server); - - // - // Setting the CA for the server shouldn't change anything, it - // shouldn't modify the cert chain sent to the client. - // - d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_ca1", "cacert1"); - d["IceSSL.VerifyPeer"] = "0"; - server = fact.createServer(d); - try - { - info = (IceSSL.NativeConnectionInfo)server.ice_getConnection().getInfo(); - test(info.nativeCerts.Length == 1); - test(!info.verified); - } - catch(Ice.LocalException) + X509Store certStore = new X509Store("My", StoreLocation.CurrentUser); + certStore.Open(OpenFlags.ReadWrite); + X509Certificate2Collection certs = new X509Certificate2Collection(); + certs.Import(defaultDir + "/s_rsa_cai2.p12", "password", X509KeyStorageFlags.DefaultKeySet); + foreach(X509Certificate2 cert in certs) { - test(false); + certStore.Add(cert); } - fact.destroyServer(server); - - // - // The client can't verify the server certificate but should - // still provide it. "s_rsa_wroot_ca1" includes the root so - // the cert size should be 2. - // - d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_wroot_ca1", ""); - d["IceSSL.VerifyPeer"] = "0";; - server = fact.createServer(d); try { - info = (IceSSL.NativeConnectionInfo)server.ice_getConnection().getInfo(); - test(info.nativeCerts.Length == 1); // Like the SChannel transport, .NET never sends the root. - } - catch(Ice.LocalException) - { - test(false); - } - fact.destroyServer(server); - comm.destroy(); + IceSSL.NativeConnectionInfo info; - // - // Now the client verifies the server certificate - // - initData = createClientProps(defaultProperties, defaultDir, defaultHost, "", "cacert1"); - initData.properties.setProperty("IceSSL.VerifyPeer", "1"); - comm = Ice.Util.initialize(initData); + initData = createClientProps(defaultProperties, defaultDir, defaultHost, "", ""); + initData.properties.setProperty("IceSSL.VerifyPeer", "0"); + Ice.Communicator comm = Ice.Util.initialize(initData); - fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); - test(fact != null); + Test.ServerFactoryPrx fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); + test(fact != null); - { + // + // The client can't verify the server certificate but it should + // still provide it. "s_rsa_ca1" doesn't include the root so the + // cert size should be 1. + // d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_ca1", ""); - d["IceSSL.VerifyPeer"] = "0";; - server = fact.createServer(d); + d["IceSSL.VerifyPeer"] = "0"; + Test.ServerPrx server = fact.createServer(d); try { info = (IceSSL.NativeConnectionInfo)server.ice_getConnection().getInfo(); - test(info.nativeCerts.Length == 2); - test(info.verified); + test(info.nativeCerts.Length == 1); + test(!info.verified); } catch(Ice.LocalException) { test(false); } fact.destroyServer(server); - } - - // - // Try certificate with one intermediate and VerifyDepthMax=2 - // - initData = createClientProps(defaultProperties, defaultDir, defaultHost, "", "cacert1"); - initData.properties.setProperty("IceSSL.VerifyPeer", "1"); - initData.properties.setProperty("IceSSL.VerifyDepthMax", "2"); - comm = Ice.Util.initialize(initData); - fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); - test(fact != null); - - { - d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai1", ""); - d["IceSSL.VerifyPeer"] = "0";; + // + // Setting the CA for the server shouldn't change anything, it + // shouldn't modify the cert chain sent to the client. + // + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_ca1", "cacert1"); + d["IceSSL.VerifyPeer"] = "0"; server = fact.createServer(d); try { - server.ice_getConnection().getInfo(); - test(false); - } - catch(Ice.SecurityException) - { - // Chain length too long + info = (IceSSL.NativeConnectionInfo)server.ice_getConnection().getInfo(); + test(info.nativeCerts.Length == 1); + test(!info.verified); } catch(Ice.LocalException) { test(false); } fact.destroyServer(server); - } - comm.destroy(); - - // - // Set VerifyDepthMax to 3 (the default) - // - initData = createClientProps(defaultProperties, defaultDir, defaultHost, "", "cacert1"); - initData.properties.setProperty("IceSSL.VerifyPeer", "1"); - //initData.properties.setProperty("IceSSL.VerifyDepthMax", "3"); - comm = Ice.Util.initialize(initData); - fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); - test(fact != null); - - { - d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai1", ""); + // + // The client can't verify the server certificate but should + // still provide it. "s_rsa_wroot_ca1" includes the root so + // the cert size should be 2. + // + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_wroot_ca1", ""); d["IceSSL.VerifyPeer"] = "0";; server = fact.createServer(d); try { info = (IceSSL.NativeConnectionInfo)server.ice_getConnection().getInfo(); - test(info.nativeCerts.Length == 3); - test(info.verified); + test(info.nativeCerts.Length == 1); // Like the SChannel transport, .NET never sends the root. } catch(Ice.LocalException) { test(false); } fact.destroyServer(server); - } + comm.destroy(); + + // + // Now the client verifies the server certificate + // + initData = createClientProps(defaultProperties, defaultDir, defaultHost, "", "cacert1"); + initData.properties.setProperty("IceSSL.VerifyPeer", "1"); + comm = Ice.Util.initialize(initData); + + fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); + test(fact != null); - { - d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai2", ""); - d["IceSSL.VerifyPeer"] = "0";; - server = fact.createServer(d); - try { - server.ice_getConnection().getInfo(); - test(false); + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_ca1", ""); + d["IceSSL.VerifyPeer"] = "0";; + server = fact.createServer(d); + try + { + info = (IceSSL.NativeConnectionInfo)server.ice_getConnection().getInfo(); + test(info.nativeCerts.Length == 2); + test(info.verified); + } + catch(Ice.LocalException) + { + test(false); + } + fact.destroyServer(server); } - catch(Ice.SecurityException) + + // + // Try certificate with one intermediate and VerifyDepthMax=2 + // + initData = createClientProps(defaultProperties, defaultDir, defaultHost, "", "cacert1"); + initData.properties.setProperty("IceSSL.VerifyPeer", "1"); + initData.properties.setProperty("IceSSL.VerifyDepthMax", "2"); + comm = Ice.Util.initialize(initData); + + fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); + test(fact != null); + { - // Chain length too long + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai1", ""); + d["IceSSL.VerifyPeer"] = "0";; + server = fact.createServer(d); + try + { + server.ice_getConnection().getInfo(); + test(false); + } + catch(Ice.SecurityException) + { + // Chain length too long + } + catch(Ice.LocalException) + { + test(false); + } + fact.destroyServer(server); } - fact.destroyServer(server); - } - comm.destroy(); + comm.destroy(); - // - // Increase VerifyDepthMax to 4 - // - initData = createClientProps(defaultProperties, defaultDir, defaultHost, "", "cacert1"); - initData.properties.setProperty("IceSSL.VerifyPeer", "1"); - initData.properties.setProperty("IceSSL.VerifyDepthMax", "4"); - comm = Ice.Util.initialize(initData); + // + // Set VerifyDepthMax to 3 (the default) + // + initData = createClientProps(defaultProperties, defaultDir, defaultHost, "", "cacert1"); + initData.properties.setProperty("IceSSL.VerifyPeer", "1"); + //initData.properties.setProperty("IceSSL.VerifyDepthMax", "3"); + comm = Ice.Util.initialize(initData); - fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); - test(fact != null); + fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); + test(fact != null); - { - d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai2", ""); - d["IceSSL.VerifyPeer"] = "0";; - server = fact.createServer(d); - try { - info = (IceSSL.NativeConnectionInfo)server.ice_getConnection().getInfo(); - test(info.nativeCerts.Length == 4); - test(info.verified); + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai1", ""); + d["IceSSL.VerifyPeer"] = "0";; + server = fact.createServer(d); + try + { + info = (IceSSL.NativeConnectionInfo)server.ice_getConnection().getInfo(); + test(info.nativeCerts.Length == 3); + test(info.verified); + } + catch(Ice.LocalException) + { + test(false); + } + fact.destroyServer(server); } - catch(Ice.LocalException) + { - test(false); + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai2", ""); + d["IceSSL.VerifyPeer"] = "0";; + server = fact.createServer(d); + try + { + server.ice_getConnection().getInfo(); + test(false); + } + catch(Ice.SecurityException) + { + // Chain length too long + } + fact.destroyServer(server); } - fact.destroyServer(server); - } - - comm.destroy(); + comm.destroy(); - // - // Increase VerifyDepthMax to 4 - // - initData = createClientProps(defaultProperties, defaultDir, defaultHost, "c_rsa_cai2", "cacert1"); - initData.properties.setProperty("IceSSL.VerifyPeer", "1"); - initData.properties.setProperty("IceSSL.VerifyDepthMax", "4"); - comm = Ice.Util.initialize(initData); + // + // Increase VerifyDepthMax to 4 + // + initData = createClientProps(defaultProperties, defaultDir, defaultHost, "", "cacert1"); + initData.properties.setProperty("IceSSL.VerifyPeer", "1"); + initData.properties.setProperty("IceSSL.VerifyDepthMax", "4"); + comm = Ice.Util.initialize(initData); - fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); - test(fact != null); + fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); + test(fact != null); - { - d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai2", "cacert1"); - d["IceSSL.VerifyPeer"] = "2"; - server = fact.createServer(d); - try { - server.ice_getConnection(); - test(false); - } - catch(Ice.ProtocolException) - { - // Expected + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai2", ""); + d["IceSSL.VerifyPeer"] = "0";; + server = fact.createServer(d); + try + { + info = (IceSSL.NativeConnectionInfo)server.ice_getConnection().getInfo(); + test(info.nativeCerts.Length == 4); + test(info.verified); + } + catch(Ice.LocalException) + { + test(false); + } + fact.destroyServer(server); } - catch(Ice.ConnectionLostException) + + comm.destroy(); + + // + // Increase VerifyDepthMax to 4 + // + initData = createClientProps(defaultProperties, defaultDir, defaultHost, "c_rsa_cai2", "cacert1"); + initData.properties.setProperty("IceSSL.VerifyPeer", "1"); + initData.properties.setProperty("IceSSL.VerifyDepthMax", "4"); + comm = Ice.Util.initialize(initData); + + fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); + test(fact != null); + { - // Expected + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai2", "cacert1"); + d["IceSSL.VerifyPeer"] = "2"; + server = fact.createServer(d); + try + { + server.ice_getConnection(); + test(false); + } + catch(Ice.ProtocolException) + { + // Expected + } + catch(Ice.ConnectionLostException) + { + // Expected + } + catch(Ice.LocalException) + { + test(false); + } + fact.destroyServer(server); } - catch(Ice.LocalException) + { - test(false); + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai2", "cacert1"); + d["IceSSL.VerifyPeer"] = "2"; + d["IceSSL.VerifyDepthMax"] = "4"; + server = fact.createServer(d); + try + { + server.ice_getConnection(); + } + catch(Ice.LocalException) + { + test(false); + } + fact.destroyServer(server); } - fact.destroyServer(server); - } + comm.destroy(); + } + finally { - d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_cai2", "cacert1"); - d["IceSSL.VerifyPeer"] = "2"; - d["IceSSL.VerifyDepthMax"] = "4"; - server = fact.createServer(d); - try - { - server.ice_getConnection(); - } - catch(Ice.LocalException) + foreach(X509Certificate2 cert in certs) { - test(false); + certStore.Remove(cert); } - fact.destroyServer(server); } - - comm.destroy(); } Console.Out.WriteLine("ok"); @@ -1210,6 +1228,55 @@ public class AllTests } Console.Out.WriteLine("ok"); } + + Console.Out.Write("testing multiple CA certificates... "); + Console.Out.Flush(); + { + initData = createClientProps(defaultProperties, defaultDir, defaultHost, "c_rsa_ca1", "cacerts"); + Ice.Communicator comm = Ice.Util.initialize(initData); + Test.ServerFactoryPrx fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); + test(fact != null); + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_ca2", "cacerts"); + d["IceSSL.VerifyPeer"] = "2"; + Test.ServerPrx server = fact.createServer(d); + try + { + server.ice_ping(); + } + catch(Ice.LocalException) + { + test(false); + } + fact.destroyServer(server); + comm.destroy(); + } + Console.Out.WriteLine("ok"); + + Console.Out.Write("testing DER CA certificate... "); + Console.Out.Flush(); + { + initData = createClientProps(defaultProperties, defaultDir, defaultHost, "c_rsa_ca1", ""); + initData.properties.setProperty("IceSSL.CAs", "cacert1.der"); + Ice.Communicator comm = Ice.Util.initialize(initData); + Test.ServerFactoryPrx fact = Test.ServerFactoryPrxHelper.checkedCast(comm.stringToProxy(factoryRef)); + test(fact != null); + d = createServerProps(defaultProperties, defaultDir, defaultHost, "s_rsa_ca1", ""); + d["IceSSL.VerifyPeer"] = "2"; + d["IceSSL.CAs"] = "cacert1.der"; + Test.ServerPrx server = fact.createServer(d); + try + { + server.ice_ping(); + } + catch(Ice.LocalException) + { + test(false); + } + fact.destroyServer(server); + comm.destroy(); + } + Console.Out.WriteLine("ok"); + Console.Out.Write("testing passwords... "); Console.Out.Flush(); { @@ -2165,6 +2232,48 @@ public class AllTests } } Console.Out.WriteLine("ok"); + + Console.Out.Write("testing system CAs... "); + Console.Out.Flush(); + { + initData = createClientProps(defaultProperties, defaultDir, defaultHost); + initData.properties.setProperty("IceSSL.VerifyDepthMax", "4"); + initData.properties.setProperty("Ice.Override.Timeout", "5000"); // 5s timeout + Ice.Communicator comm = Ice.Util.initialize(initData); + Ice.ObjectPrx p = comm.stringToProxy("dummy:wss -h demo.zeroc.com -p 5064"); + try + { + p.ice_ping(); + test(false); + } + catch(Ice.SecurityException) + { + // Expected, by default we don't check for system CAs. + } + catch(Ice.LocalException) + { + test(false); + } + + initData = createClientProps(defaultProperties, defaultDir, defaultHost); + initData.properties.setProperty("IceSSL.VerifyDepthMax", "4"); + initData.properties.setProperty("Ice.Override.Timeout", "5000"); // 5s timeout + initData.properties.setProperty("IceSSL.UsePlatformCAs", "1"); + comm = Ice.Util.initialize(initData); + p = comm.stringToProxy("dummy:wss -h demo.zeroc.com -p 5064"); + IceSSL.WSSConnectionInfo info; + try + { + info = (IceSSL.WSSConnectionInfo)p.ice_getConnection().getInfo(); + test(info.verified); + } + catch(Ice.LocalException) + { + test(false); + } + comm.destroy(); + } + Console.Out.WriteLine("ok"); } finally { diff --git a/java/src/Ice/src/main/java/IceInternal/PropertyNames.java b/java/src/Ice/src/main/java/IceInternal/PropertyNames.java index d3660b21946..54868a2488f 100644 --- a/java/src/Ice/src/main/java/IceInternal/PropertyNames.java +++ b/java/src/Ice/src/main/java/IceInternal/PropertyNames.java @@ -6,7 +6,7 @@ // ICE_LICENSE file included in this distribution. // // ********************************************************************** -// Generated by makeprops.py from file ../config/PropertyNames.xml, Tue Dec 9 12:08:30 2014 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Tue Apr 28 17:34:50 2015 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -188,6 +188,7 @@ public final class PropertyNames new Property("Ice\\.Warn\\.UnusedProperties", false, null), new Property("Ice\\.CacheMessageBuffers", false, null), new Property("Ice\\.ThreadInterruptSafe", false, null), + new Property("Ice\\.Voip", false, null), null }; @@ -969,8 +970,9 @@ public final class PropertyNames public static final Property IceSSLProps[] = { new Property("IceSSL\\.Alias", false, null), - new Property("IceSSL\\.CertAuthDir", false, null), - new Property("IceSSL\\.CertAuthFile", false, null), + new Property("IceSSL\\.CAs", false, null), + new Property("IceSSL\\.CertAuthDir", true, null), + new Property("IceSSL\\.CertAuthFile", true, null), new Property("IceSSL\\.CertStore", false, null), new Property("IceSSL\\.CertFile", false, null), new Property("IceSSL\\.CertVerifier", false, null), @@ -982,10 +984,10 @@ public final class PropertyNames new Property("IceSSL\\.DHParams", false, null), new Property("IceSSL\\.EntropyDaemon", false, null), new Property("IceSSL\\.FindCert", false, null), - new Property("IceSSL\\.FindCert\\.[^\\s]+", false, null), + new Property("IceSSL\\.FindCert\\.[^\\s]+", true, null), new Property("IceSSL\\.ImportCert\\.[^\\s]+", true, null), new Property("IceSSL\\.InitOpenSSL", false, null), - new Property("IceSSL\\.KeyFile", false, null), + new Property("IceSSL\\.KeyFile", true, null), new Property("IceSSL\\.KeySet", false, null), new Property("IceSSL\\.Keychain", false, null), new Property("IceSSL\\.KeychainPassword", false, null), @@ -1008,6 +1010,7 @@ public final class PropertyNames new Property("IceSSL\\.Truststore", false, null), new Property("IceSSL\\.TruststorePassword", false, null), new Property("IceSSL\\.TruststoreType", false, null), + new Property("IceSSL\\.UsePlatformCAs", false, null), new Property("IceSSL\\.VerifyDepthMax", false, null), new Property("IceSSL\\.VerifyPeer", false, null), null diff --git a/java/src/Ice/src/main/java/IceInternal/TcpEndpointI.java b/java/src/Ice/src/main/java/IceInternal/TcpEndpointI.java index be274252438..a4547612335 100644 --- a/java/src/Ice/src/main/java/IceInternal/TcpEndpointI.java +++ b/java/src/Ice/src/main/java/IceInternal/TcpEndpointI.java @@ -9,7 +9,7 @@ package IceInternal; -final class TcpEndpointI extends IPEndpointI +final class TcpEndpointI extends IPEndpointI implements WSEndpointDelegate { public TcpEndpointI(ProtocolInstance instance, String ho, int po, java.net.InetSocketAddress sourceAddr, int ti, String conId, boolean co) @@ -40,27 +40,57 @@ final class TcpEndpointI extends IPEndpointI public Ice.EndpointInfo getInfo() { Ice.TCPEndpointInfo info = new Ice.TCPEndpointInfo() + { + @Override + public short type() { - @Override - public short type() - { - return TcpEndpointI.this.type(); - } + return TcpEndpointI.this.type(); + } - @Override - public boolean datagram() - { - return TcpEndpointI.this.datagram(); - } + @Override + public boolean datagram() + { + return TcpEndpointI.this.datagram(); + } - @Override - public boolean secure() - { - return TcpEndpointI.this.secure(); - } - }; + @Override + public boolean secure() + { + return TcpEndpointI.this.secure(); + } + }; + fillEndpointInfo(info); + return info; + } + + // + // Return the WebSocket endpoint information. + // + @Override + public Ice.EndpointInfo getWSInfo(String resource) + { + Ice.WSEndpointInfo info = new Ice.WSEndpointInfo() + { + @Override + public short type() + { + return TcpEndpointI.this.type(); + } + @Override + public boolean datagram() + { + return TcpEndpointI.this.datagram(); + } + + @Override + public boolean secure() + { + return TcpEndpointI.this.secure(); + } + }; fillEndpointInfo(info); + info.resource = resource; return info; } diff --git a/java/src/Ice/src/main/java/IceInternal/TcpTransceiver.java b/java/src/Ice/src/main/java/IceInternal/TcpTransceiver.java index 997ddc09d8c..94a5717b02d 100644 --- a/java/src/Ice/src/main/java/IceInternal/TcpTransceiver.java +++ b/java/src/Ice/src/main/java/IceInternal/TcpTransceiver.java @@ -9,7 +9,7 @@ package IceInternal; -final class TcpTransceiver implements Transceiver +final class TcpTransceiver implements Transceiver, WSTransceiverDelegate { @Override public java.nio.channels.SelectableChannel fd() @@ -79,22 +79,16 @@ final class TcpTransceiver implements Transceiver public Ice.ConnectionInfo getInfo() { Ice.TCPConnectionInfo info = new Ice.TCPConnectionInfo(); - if(_stream.fd() != null) - { - java.net.Socket socket = _stream.fd().socket(); - info.localAddress = socket.getLocalAddress().getHostAddress(); - info.localPort = socket.getLocalPort(); - if(socket.getInetAddress() != null) - { - info.remoteAddress = socket.getInetAddress().getHostAddress(); - info.remotePort = socket.getPort(); - } - if(!socket.isClosed()) - { - info.rcvSize = Network.getRecvBufferSize(_stream.fd()); - info.sndSize = Network.getSendBufferSize(_stream.fd()); - } - } + fillConnectionInfo(info); + return info; + } + + @Override + public Ice.ConnectionInfo getWSInfo(java.util.Map<String, String> headers) + { + Ice.WSConnectionInfo info = new Ice.WSConnectionInfo(); + fillConnectionInfo(info); + info.headers = headers; // Provided header is a copy so no need to clone here. return info; } @@ -115,6 +109,26 @@ final class TcpTransceiver implements Transceiver _stream = stream; } + private void fillConnectionInfo(Ice.IPConnectionInfo info) + { + if(_stream.fd() != null) + { + java.net.Socket socket = _stream.fd().socket(); + info.localAddress = socket.getLocalAddress().getHostAddress(); + info.localPort = socket.getLocalPort(); + if(socket.getInetAddress() != null) + { + info.remoteAddress = socket.getInetAddress().getHostAddress(); + info.remotePort = socket.getPort(); + } + if(!socket.isClosed()) + { + info.rcvSize = Network.getRecvBufferSize(_stream.fd()); + info.sndSize = Network.getSendBufferSize(_stream.fd()); + } + } + } + final private ProtocolInstance _instance; final private StreamSocket _stream; } diff --git a/java/src/Ice/src/main/java/IceInternal/WSEndpoint.java b/java/src/Ice/src/main/java/IceInternal/WSEndpoint.java index 7716771e919..dd64dbfb8af 100644 --- a/java/src/Ice/src/main/java/IceInternal/WSEndpoint.java +++ b/java/src/Ice/src/main/java/IceInternal/WSEndpoint.java @@ -41,29 +41,8 @@ final class WSEndpoint extends IceInternal.EndpointI @Override public Ice.EndpointInfo getInfo() { - Ice.WSEndpointInfo info = new Ice.WSEndpointInfo() - { - @Override - public short type() - { - return WSEndpoint.this.type(); - } - - @Override - public boolean datagram() - { - return WSEndpoint.this.datagram(); - } - - @Override - public boolean secure() - { - return WSEndpoint.this.secure(); - } - }; - _delegate.fillEndpointInfo(info); - info.resource = _resource; - return info; + assert(_delegate instanceof WSEndpointDelegate); + return ((WSEndpointDelegate)_delegate).getWSInfo(_resource); } @Override diff --git a/java/src/Ice/src/main/java/IceInternal/WSTransceiver.java b/java/src/Ice/src/main/java/IceInternal/WSTransceiver.java index fa8e1279fb2..d4ceb98f777 100644 --- a/java/src/Ice/src/main/java/IceInternal/WSTransceiver.java +++ b/java/src/Ice/src/main/java/IceInternal/WSTransceiver.java @@ -478,16 +478,8 @@ final class WSTransceiver implements Transceiver @Override public Ice.ConnectionInfo getInfo() { - Ice.IPConnectionInfo di = (Ice.IPConnectionInfo)_delegate.getInfo(); - Ice.WSConnectionInfo info = new Ice.WSConnectionInfo(); - info.localAddress = di.localAddress; - info.localPort = di.localPort; - info.remoteAddress = di.remoteAddress; - info.remotePort = di.remotePort; - info.rcvSize = di.rcvSize; - info.sndSize = di.sndSize; - info.headers = _parser.getHeaders(); - return info; + assert(_delegate instanceof WSTransceiverDelegate); + return ((WSTransceiverDelegate)_delegate).getWSInfo(_parser.getHeaders()); } @Override diff --git a/java/src/Ice/src/main/java/IceSSL/EndpointI.java b/java/src/Ice/src/main/java/IceSSL/EndpointI.java index 8a922bf4e0d..266d59ce306 100644 --- a/java/src/Ice/src/main/java/IceSSL/EndpointI.java +++ b/java/src/Ice/src/main/java/IceSSL/EndpointI.java @@ -9,7 +9,7 @@ package IceSSL; -final class EndpointI extends IceInternal.IPEndpointI +final class EndpointI extends IceInternal.IPEndpointI implements IceInternal.WSEndpointDelegate { public EndpointI(Instance instance, String ho, int po, java.net.InetSocketAddress sourceAddr, int ti, String conId, boolean co) @@ -43,27 +43,57 @@ final class EndpointI extends IceInternal.IPEndpointI public Ice.EndpointInfo getInfo() { Ice.IPEndpointInfo info = new IceSSL.EndpointInfo() + { + @Override + public short type() { - @Override - public short type() - { - return EndpointI.this.type(); - } + return EndpointI.this.type(); + } - @Override - public boolean datagram() - { - return EndpointI.this.datagram(); - } + @Override + public boolean datagram() + { + return EndpointI.this.datagram(); + } - @Override - public boolean secure() - { - return EndpointI.this.secure(); - } - }; + @Override + public boolean secure() + { + return EndpointI.this.secure(); + } + }; + fillEndpointInfo(info); + return info; + } + + // + // Return the secure WebSocket endpoint information. + // + @Override + public Ice.EndpointInfo getWSInfo(String resource) + { + IceSSL.WSSEndpointInfo info = new IceSSL.WSSEndpointInfo() + { + @Override + public short type() + { + return EndpointI.this.type(); + } + @Override + public boolean datagram() + { + return EndpointI.this.datagram(); + } + + @Override + public boolean secure() + { + return EndpointI.this.secure(); + } + }; fillEndpointInfo(info); + info.resource = resource; return info; } diff --git a/java/src/Ice/src/main/java/IceSSL/SSLEngine.java b/java/src/Ice/src/main/java/IceSSL/SSLEngine.java index bacb4a9cf26..daf4efdbc12 100644 --- a/java/src/Ice/src/main/java/IceSSL/SSLEngine.java +++ b/java/src/Ice/src/main/java/IceSSL/SSLEngine.java @@ -507,20 +507,70 @@ class SSLEngine } } } - else - { - ts = keys; - } // - // Collect the trust managers. + // Collect the trust managers. Use IceSSL.Truststore if + // specified, otherwise use the Java root CAs if + // Ice.Use.PlatformCAs is enabled. If none of these are enabled, + // use the keystore or a dummy trust manager which rejects any + // certificate. // javax.net.ssl.TrustManager[] trustManagers = null; { String algorithm = javax.net.ssl.TrustManagerFactory.getDefaultAlgorithm(); javax.net.ssl.TrustManagerFactory tmf = javax.net.ssl.TrustManagerFactory.getInstance(algorithm); - tmf.init(ts); - trustManagers = tmf.getTrustManagers(); + java.security.KeyStore trustStore = null; + if(ts != null) + { + trustStore = ts; + } + else if(properties.getPropertyAsInt("IceSSL.UsePlatformCAs") <= 0) + { + if(keys != null) + { + trustStore = keys; + } + else + { + trustManagers = new javax.net.ssl.TrustManager[] + { + new javax.net.ssl.X509TrustManager() + { + @Override + public void + checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + throw new CertificateException("no trust anchors"); + } + + @Override + public void + checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + throw new CertificateException("no trust anchors"); + } + + @Override + public X509Certificate[] + getAcceptedIssuers() + { + return new X509Certificate[0]; + } + } + }; + } + } + else + { + trustStore = null; + } + if(trustManagers == null) + { + tmf.init(trustStore); + trustManagers = tmf.getTrustManagers(); + } assert(trustManagers != null); } @@ -537,11 +587,17 @@ class SSLEngine X509Certificate[] certs = ((javax.net.ssl.X509TrustManager)tm).getAcceptedIssuers(); for(X509Certificate cert : certs) { - anchors.add(new TrustAnchor(cert, null)); + if(cert.getBasicConstraints() >= 0) // Only add CAs + { + anchors.add(new TrustAnchor(cert, null)); + } } } - _validatorParams = new PKIXParameters(anchors); - _validatorParams.setRevocationEnabled(false); + if(!anchors.isEmpty()) + { + _validatorParams = new PKIXParameters(anchors); + _validatorParams.setRevocationEnabled(false); + } // // Wrap each trust manager. @@ -575,6 +631,11 @@ class SSLEngine Certificate[] getVerifiedCertificateChain(Certificate[] chain) { + if(_validatorParams == null) + { + return null; // Couldn't validate the given certificate chain. + } + List<Certificate> certs = new ArrayList<Certificate>(java.util.Arrays.asList(chain)); try { diff --git a/java/src/Ice/src/main/java/IceSSL/TransceiverI.java b/java/src/Ice/src/main/java/IceSSL/TransceiverI.java index c9de921c8f4..89828ffa495 100644 --- a/java/src/Ice/src/main/java/IceSSL/TransceiverI.java +++ b/java/src/Ice/src/main/java/IceSSL/TransceiverI.java @@ -13,7 +13,7 @@ import java.nio.*; import javax.net.ssl.*; import javax.net.ssl.SSLEngineResult.*; -final class TransceiverI implements IceInternal.Transceiver +final class TransceiverI implements IceInternal.Transceiver, IceInternal.WSTransceiverDelegate { @Override public java.nio.channels.SelectableChannel fd() @@ -39,7 +39,7 @@ final class TransceiverI implements IceInternal.Transceiver // // Additional verification. // - _instance.verifyPeer(getNativeConnectionInfo(), _stream.fd(), _host); + _instance.verifyPeer((NativeConnectionInfo)getInfo(), _stream.fd(), _host); if(_instance.securityTraceLevel() >= 1) { @@ -233,7 +233,18 @@ final class TransceiverI implements IceInternal.Transceiver @Override public Ice.ConnectionInfo getInfo() { - return getNativeConnectionInfo(); + NativeConnectionInfo info = new NativeConnectionInfo(); + info.nativeCerts = fillConnectionInfo(info); + return info; + } + + @Override + public Ice.ConnectionInfo getWSInfo(java.util.Map<String, String> headers) + { + WSSNativeConnectionInfo info = new WSSNativeConnectionInfo(); + info.nativeCerts = fillConnectionInfo(info); + info.headers = headers; // Provided header is a copy so no need to clone here. + return info; } @Override @@ -267,12 +278,12 @@ final class TransceiverI implements IceInternal.Transceiver } } - private NativeConnectionInfo getNativeConnectionInfo() + private java.security.cert.Certificate[] fillConnectionInfo(ConnectionInfo info) { // // This can only be called on an open transceiver. // - NativeConnectionInfo info = new NativeConnectionInfo(); + java.security.cert.Certificate[] nativeCerts = null; if(_stream.fd() != null) { java.net.Socket socket = _stream.fd().socket(); @@ -305,10 +316,9 @@ final class TransceiverI implements IceInternal.Transceiver java.security.cert.Certificate[] pcerts = session.getPeerCertificates(); java.security.cert.Certificate[] vcerts = _instance.engine().getVerifiedCertificateChain(pcerts); info.verified = vcerts != null; - info.nativeCerts = vcerts != null ? vcerts : pcerts; - + nativeCerts = vcerts != null ? vcerts : pcerts; java.util.ArrayList<String> certs = new java.util.ArrayList<String>(); - for(java.security.cert.Certificate c : info.nativeCerts) + for(java.security.cert.Certificate c : nativeCerts) { StringBuilder s = new StringBuilder("-----BEGIN CERTIFICATE-----\n"); s.append(IceUtilInternal.Base64.encode(c.getEncoded())); @@ -327,7 +337,7 @@ final class TransceiverI implements IceInternal.Transceiver } info.adapterName = _adapterName; info.incoming = _incoming; - return info; + return nativeCerts; } private int handshakeNonBlocking() diff --git a/java/test/src/main/java/test/Ice/info/AllTests.java b/java/test/src/main/java/test/Ice/info/AllTests.java index f3059ac59c4..ce135750bb4 100644 --- a/java/test/src/main/java/test/Ice/info/AllTests.java +++ b/java/test/src/main/java/test/Ice/info/AllTests.java @@ -53,7 +53,7 @@ public class AllTests test(ipEndpoint.type() == Ice.TCPEndpointType.value && ipEndpoint instanceof Ice.TCPEndpointInfo || ipEndpoint.type() == IceSSL.EndpointType.value && ipEndpoint instanceof IceSSL.EndpointInfo || ipEndpoint.type() == Ice.WSEndpointType.value && ipEndpoint instanceof Ice.WSEndpointInfo || - ipEndpoint.type() == Ice.WSSEndpointType.value && ipEndpoint instanceof Ice.WSEndpointInfo); + ipEndpoint.type() == Ice.WSSEndpointType.value && ipEndpoint instanceof IceSSL.WSSEndpointInfo); Ice.UDPEndpointInfo udpEndpoint = (Ice.UDPEndpointInfo)endps[1].getInfo(); test(udpEndpoint.host.equals("udphost")); @@ -175,12 +175,19 @@ public class AllTests if(base.ice_getConnection().type().equals("ws") || base.ice_getConnection().type().equals("wss")) { - test(info instanceof Ice.WSConnectionInfo); - Ice.WSConnectionInfo wsinfo = (Ice.WSConnectionInfo)info; - test(wsinfo.headers.get("Upgrade").equals("websocket")); - test(wsinfo.headers.get("Connection").equals("Upgrade")); - test(wsinfo.headers.get("Sec-WebSocket-Protocol").equals("ice.zeroc.com")); - test(wsinfo.headers.get("Sec-WebSocket-Accept") != null); + java.util.Map<String, String> headers; + if(info instanceof Ice.WSConnectionInfo) + { + headers = ((Ice.WSConnectionInfo)info).headers; + } + else + { + headers = ((IceSSL.WSSConnectionInfo)info).headers; + } + test(headers.get("Upgrade").equals("websocket")); + test(headers.get("Connection").equals("Upgrade")); + test(headers.get("Sec-WebSocket-Protocol").equals("ice.zeroc.com")); + test(headers.get("Sec-WebSocket-Accept") != null); test(ctx.get("ws.Upgrade").equals("websocket")); test(ctx.get("ws.Connection").equals("Upgrade")); diff --git a/java/test/src/main/java/test/Ice/info/TestI.java b/java/test/src/main/java/test/Ice/info/TestI.java index 6a149c6d53f..d90cf8ed75d 100644 --- a/java/test/src/main/java/test/Ice/info/TestI.java +++ b/java/test/src/main/java/test/Ice/info/TestI.java @@ -70,6 +70,15 @@ public class TestI extends _TestIntfDisp } } + if(info instanceof IceSSL.WSSConnectionInfo) + { + IceSSL.WSSConnectionInfo wssinfo = (IceSSL.WSSConnectionInfo)info; + for(java.util.Map.Entry<String, String> e : wssinfo.headers.entrySet()) + { + ctx.put("ws." + e.getKey(), e.getValue()); + } + } + return ctx; } } diff --git a/java/test/src/main/java/test/IceSSL/configuration/AllTests.java b/java/test/src/main/java/test/IceSSL/configuration/AllTests.java index f53beb315f5..ae632d80947 100644 --- a/java/test/src/main/java/test/IceSSL/configuration/AllTests.java +++ b/java/test/src/main/java/test/IceSSL/configuration/AllTests.java @@ -204,6 +204,7 @@ public class AllTests } catch(Ice.LocalException ex) { + ex.printStackTrace(); test(false); } fact.destroyServer(server); @@ -682,7 +683,7 @@ public class AllTests } fact.destroyServer(server); } - + comm.destroy(); // @@ -2224,6 +2225,48 @@ public class AllTests } out.println("ok"); + out.print("testing system CAs... "); + out.flush(); + { + initData = createClientProps(defaultProperties, defaultDir, defaultHost); + initData.properties.setProperty("IceSSL.VerifyDepthMax", "4"); + initData.properties.setProperty("Ice.Override.Timeout", "5000"); // 5s timeout + Ice.Communicator comm = Ice.Util.initialize(initData); + Ice.ObjectPrx p = comm.stringToProxy("dummy:wss -h demo.zeroc.com -p 5064"); + try + { + p.ice_ping(); + test(false); + } + catch(Ice.SecurityException ex) + { + // Expected, by default we don't check for system CAs. + } + catch(Ice.LocalException ex) + { + test(false); + } + + initData = createClientProps(defaultProperties, defaultDir, defaultHost); + initData.properties.setProperty("IceSSL.VerifyDepthMax", "4"); + initData.properties.setProperty("Ice.Override.Timeout", "5000"); // 5s timeout + initData.properties.setProperty("IceSSL.UsePlatformCAs", "1"); + comm = Ice.Util.initialize(initData); + p = comm.stringToProxy("dummy:wss -h demo.zeroc.com -p 5064"); + IceSSL.WSSConnectionInfo info; + try + { + info = (IceSSL.WSSConnectionInfo)p.ice_getConnection().getInfo(); + test(info.verified); + } + catch(Ice.LocalException ex) + { + test(false); + } + comm.destroy(); + } + out.println("ok"); + return factory; } } diff --git a/js/src/Ice/PropertyNames.js b/js/src/Ice/PropertyNames.js index 49b3c928d1f..4b1fe5b144a 100644 --- a/js/src/Ice/PropertyNames.js +++ b/js/src/Ice/PropertyNames.js @@ -6,7 +6,7 @@ // ICE_LICENSE file included in this distribution. // // ********************************************************************** -// Generated by makeprops.py from file ../config/PropertyNames.xml, Tue Dec 9 12:08:30 2014 +// Generated by makeprops.py from file ./config/PropertyNames.xml, Tue Apr 28 17:34:50 2015 // IMPORTANT: Do not edit this file -- any edits made here will be lost! @@ -189,6 +189,7 @@ PropertyNames.IceProps = new Property("/^Ice\.Warn\.UnusedProperties/", false, null), new Property("/^Ice\.CacheMessageBuffers/", false, null), new Property("/^Ice\.ThreadInterruptSafe/", false, null), + new Property("/^Ice\.Voip/", false, null), ]; /* jshint +W044*/ diff --git a/js/src/Ice/WSEndpoint.js b/js/src/Ice/WSEndpoint.js index 6ccc3dcaf40..42160ed4a05 100644 --- a/js/src/Ice/WSEndpoint.js +++ b/js/src/Ice/WSEndpoint.js @@ -16,7 +16,8 @@ Ice.__M.require(module, "../Ice/StringUtil", "../Ice/EndpointI", "../Ice/LocalException", - "../Ice/WSTransceiver" + "../Ice/WSTransceiver", + "../Ice/EndpointInfo" ]); var HashUtil = Ice.HashUtil; @@ -33,7 +34,7 @@ var WSEndpoint = Class(EndpointI, { }, getInfo: function() { - var info = new Ice.WSEndpointInfo(); + var info = this._delegate.secure() ? new IceSSL.WSSEndpointInfo() : new Ice.WSEndpointInfo(); info.resource = this._resource; this._delegate.fillEndpointInfo(info); return info; diff --git a/js/src/Ice/browser/WSTransceiver.js b/js/src/Ice/browser/WSTransceiver.js index e0a16ed45cb..9cea74acd92 100644 --- a/js/src/Ice/browser/WSTransceiver.js +++ b/js/src/Ice/browser/WSTransceiver.js @@ -18,7 +18,8 @@ Ice.__M.require(module, "../Ice/Connection", "../Ice/Exception", "../Ice/LocalException", - "../Ice/Timer" + "../Ice/Timer", + "../Ice/ConnectionInfo" ]); var Debug = Ice.Debug; @@ -284,7 +285,7 @@ var WSTransceiver = Ice.Class({ getInfo: function() { Debug.assert(this._fd !== null); - var info = new Ice.WSConnectionInfo(); + var info = this._secure ? new IceSSL.WSSConnectionInfo() : new Ice.WSConnectionInfo(); // // The WebSocket API doens't provide this info diff --git a/js/src/Ice/sources.json b/js/src/Ice/sources.json index 953d085b91e..751d4c7cbff 100644 --- a/js/src/Ice/sources.json +++ b/js/src/Ice/sources.json @@ -20,7 +20,8 @@ "Ice/Router.ice", "Ice/SliceChecksumDict.ice", "Ice/Version.ice", - "IceSSL/EndpointInfo.ice"], + "IceSSL/EndpointInfo.ice", + "IceSSL/ConnectionInfo.ice"], "common": [ "ACM.js", diff --git a/js/test/Ice/proxy/Client.js b/js/test/Ice/proxy/Client.js index 38bdd4d2310..d31c1f9b1b1 100644 --- a/js/test/Ice/proxy/Client.js +++ b/js/test/Ice/proxy/Client.js @@ -10,6 +10,7 @@ (function(module, require, exports) { var Ice = require("ice").Ice; + var IceSSL = require("ice").IceSSL; var Test = require("Test").Test; var Promise = Ice.Promise; @@ -916,7 +917,7 @@ } var p1, pstr; - + // Legal TCP endpoint expressed as opaque endpoint p1 = communicator.stringToProxy("test -e 1.1:opaque -e 1.0 -t 1 -v CTEyNy4wLjAuMeouAAAQJwAAAA=="); pstr = communicator.proxyToString(p1); @@ -1019,6 +1020,40 @@ ).then( function() { + var p = communicator.stringToProxy("test:default -p 12010"); + if(defaultProtocol === "tcp") + { + test(p.ice_getEndpoints()[0].getInfo() instanceof Ice.TCPEndpointInfo); + } + else if(defaultProtocol === "ws") + { + test(p.ice_getEndpoints()[0].getInfo() instanceof Ice.WSEndpointInfo); + } + else if(defaultProtocol === "wss") + { + test(p.ice_getEndpoints()[0].getInfo() instanceof IceSSL.WSSEndpointInfo); + } + return p.ice_getConnection() + } + ).then( + function(con) + { + if(defaultProtocol === "tcp") + { + test(con.getInfo() instanceof Ice.TCPConnectionInfo); + } + else if(defaultProtocol === "ws") + { + test(con.getInfo() instanceof Ice.WSConnectionInfo); + } + else if(defaultProtocol === "wss") + { + test(con.getInfo() instanceof IceSSL.WSSConnectionInfo); + } + } + ).then( + function() + { out.writeLine("ok"); var derived = Test.MyDerivedClassPrx.uncheckedCast(communicator.stringToProxy("test:default -p 12010")); return derived.shutdown(); diff --git a/python/modules/IcePy/ConnectionInfo.cpp b/python/modules/IcePy/ConnectionInfo.cpp index 2381bb303ba..455d8a8296b 100644 --- a/python/modules/IcePy/ConnectionInfo.cpp +++ b/python/modules/IcePy/ConnectionInfo.cpp @@ -14,7 +14,6 @@ #include <EndpointInfo.h> #include <Util.h> #include <Ice/Object.h> - using namespace std; using namespace IcePy; diff --git a/scripts/Expect.py b/scripts/Expect.py index cc92aff163e..c367b5b63a9 100755 --- a/scripts/Expect.py +++ b/scripts/Expect.py @@ -18,6 +18,7 @@ import sys import threading import time import traceback +import types __all__ = ["Expect", "EOF", "TIMEOUT" ] @@ -133,7 +134,9 @@ class reader(threading.Thread): content = self._tbuf.getvalue() suppress = False for p in self._tracesuppress: - if p.search(content): + if isinstance(p, types.LambdaType) or isinstance(p, types.FunctionType): + content = p(content) + elif p.search(content): suppress = True break if not suppress: diff --git a/scripts/TestUtil.py b/scripts/TestUtil.py index 2a7003c6ce0..1109cedfde7 100755 --- a/scripts/TestUtil.py +++ b/scripts/TestUtil.py @@ -25,7 +25,8 @@ global x86 x86 = False # Binary distribution is 32-bit cpp11 = False # Binary distribution is c++11 extraArgs = [] - +clientTraceFilters = [] +serverTraceFilters = [] # Default java loader @@ -867,7 +868,7 @@ class InvalidSelectorString(Exception): sslConfigTree = { "cpp" : { "plugin" : " --Ice.Plugin.IceSSL=IceSSL:createIceSSL --IceSSL.Password=password " + - "--IceSSL.DefaultDir=%(certsdir)s --IceSSL.CertAuthFile=cacert.pem --IceSSL.VerifyPeer=%(verifyPeer)s", + "--IceSSL.DefaultDir=%(certsdir)s --IceSSL.CAs=cacert.pem --IceSSL.VerifyPeer=%(verifyPeer)s", "client" : " --IceSSL.CertFile=client.p12", "server" : " --IceSSL.CertFile=server.p12", "colloc" : " --IceSSL.CertFile=client.p12" @@ -880,7 +881,7 @@ sslConfigTree = { "colloc" : " --IceSSL.Keystore=client.jks" }, "csharp" : { - "plugin" : " --Ice.Plugin.IceSSL=%(icesslcs)s:IceSSL.PluginFactory --IceSSL.CertAuthFile=cacert.pem " + + "plugin" : " --Ice.Plugin.IceSSL=%(icesslcs)s:IceSSL.PluginFactory --IceSSL.CAs=cacert.pem " + "--IceSSL.Password=password --IceSSL.DefaultDir=%(certsdir)s --IceSSL.VerifyPeer=%(verifyPeer)s", "client" : " --IceSSL.CertFile=client.p12 --IceSSL.CheckCertName=0", "server" : " --IceSSL.CertFile=server.p12", @@ -1261,7 +1262,7 @@ def spawn(cmd, cwd=None): def spawnClient(cmd, env=None, cwd=None, echo=True, startReader=True, lang=None): client = _spawn(cmd, env, cwd, startReader=startReader, lang=lang) if echo: - client.trace() + client.trace(clientTraceFilters) return client def spawnServer(cmd, env=None, cwd=None, count=1, adapter=None, echo=True, lang=None, mx=False, timeout=60): @@ -1278,7 +1279,7 @@ def spawnServer(cmd, env=None, cwd=None, count=1, adapter=None, echo=True, lang= server.expect("[^\n]+ ready\n", timeout = timeout) count = count - 1 if echo: - server.trace([re.compile("[^\n]+ ready")]) + server.trace([re.compile("[^\n]+ ready")] + serverTraceFilters) return server import subprocess @@ -1821,6 +1822,7 @@ def getTestEnv(lang, testdir): if iceHome: addClasspath(os.path.join(getIceDir("java", testdir), "lib", "db.jar"), env) elif thirdPartyHome: + suffix = "" if x64: suffix = "x64" else: diff --git a/slice/Ice/Connection.ice b/slice/Ice/Connection.ice index bc8db075af4..773ff20c335 100644 --- a/slice/Ice/Connection.ice +++ b/slice/Ice/Connection.ice @@ -22,8 +22,7 @@ module Ice /** * - * Base class providing access to the connection details. - * + * Base class providing access to the connection details. * **/ local class ConnectionInfo { @@ -343,7 +342,7 @@ dictionary<string, string> HeaderDict; * Provides access to the connection details of a WebSocket connection * **/ -local class WSConnectionInfo extends IPConnectionInfo +local class WSConnectionInfo extends TCPConnectionInfo { /** The headers from the HTTP upgrade request. */ HeaderDict headers; diff --git a/slice/Ice/Endpoint.ice b/slice/Ice/Endpoint.ice index 4796b97b6f5..4b5dc03e21d 100644 --- a/slice/Ice/Endpoint.ice +++ b/slice/Ice/Endpoint.ice @@ -196,7 +196,7 @@ local class UDPEndpointInfo extends IPEndpointInfo * Provides access to a WebSocket endpoint information. * **/ -local class WSEndpointInfo extends IPEndpointInfo +local class WSEndpointInfo extends TCPEndpointInfo { /** * diff --git a/slice/IceSSL/ConnectionInfo.ice b/slice/IceSSL/ConnectionInfo.ice index a0dd0b13b1f..fcc1c24597d 100644 --- a/slice/IceSSL/ConnectionInfo.ice +++ b/slice/IceSSL/ConnectionInfo.ice @@ -34,5 +34,16 @@ local class ConnectionInfo extends Ice::IPConnectionInfo bool verified; }; +/** + * + * Provides access to the connection details of a secure WebSocket connection + * + **/ +local class WSSConnectionInfo extends ConnectionInfo +{ + /** The headers from the HTTP upgrade request. */ + Ice::HeaderDict headers; +}; + }; diff --git a/slice/IceSSL/EndpointInfo.ice b/slice/IceSSL/EndpointInfo.ice index 330f6aa5e47..5b1e7c823d2 100644 --- a/slice/IceSSL/EndpointInfo.ice +++ b/slice/IceSSL/EndpointInfo.ice @@ -40,5 +40,20 @@ local class EndpointInfo extends Ice::IPEndpointInfo { }; +/** + * + * Provides access to a secure WebSocket endpoint information. + * + **/ +local class WSSEndpointInfo extends EndpointInfo +{ + /** + * + * The URI configured with the endpoint. + * + **/ + string resource; +}; + }; |