-
Notifications
You must be signed in to change notification settings - Fork 593
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use the platform native APIs with C++ SSL transport #2063
Conversation
@@ -26,6 +27,7 @@ namespace IceSSL | |||
|
|||
Ice::LoggerPtr getLogger() const; | |||
Ice::PropertiesPtr getProperties() const; | |||
Ice::InitializationData getInitializationData() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me who is the client for this public exported class.
Is it a public / user-documented API even though there is no doc-comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should move this to IceInternal, it is only used by the SSL transport implementation.
cpp/include/Ice/SSL.h
Outdated
# undef SECURITY_WIN32 | ||
#endif | ||
|
||
namespace Ice::SSL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the idea to rename IceSSL -> Ice::SSL in another PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and do the same in C#/Java replacing the top-level IceSSL namespace with Ice::SSL.
/** | ||
* The server's certificate chain. | ||
*/ | ||
CFArrayRef serverCertificateChain; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the simplest when you want to always use the same certificate chain.
It is used to call SSLSetCertificate
before the handshake starts.
* @param host The server's host name. | ||
* @return The server's certificate chain. | ||
*/ | ||
std::function<CFArrayRef(const std::string& host)> serverCertificateSelectionCallback; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This callback allows selecting the certificate before SSL handshake starts, host parameter should probably be the adapter name, a bit more convenient way when you want to be able to upgrade the certificate without restart.
* A callback that is called before a new SSL handshake starts. The callback can be used to set additional SSL | ||
* context parameters. | ||
*/ | ||
std::function<void(SSLContextRef)> sslNewSessionCallback; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can also set the certificates from this callback, but the other certificate properties simplify it, and the use case for this callback is for more advanced or less used settings for which we don't provide a property.
std::function<int(int ok, X509_STORE_CTX*, const IceSSL::ConnectionInfoPtr& info)> | ||
clientCertificateVerificationCallback; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation wraps this callback and uses it as the OpenSSL verification callback. The signature is the same with the additional ConnectionInfo
. The info param is required for the trust verifier, but I plan to remove this dependency in a follow-up PR.
The wrapper saves exceptions thrown from the callback, and rethrows them from the transceiver initialization.
* A callback that is called before a new SSL handshake starts. The callback can be used to set additional SSL | ||
* parameters. | ||
*/ | ||
std::function<void(::SSL* ssl, const std::string& host)> sslNewSessionCallback; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allows configuring the SSL session before the handshake starts, the SSL object is created from the provided SSL_CTX.
Need to think about the defaults we should use here, or if we can count on OpenSSL defaults being always appropriate.
While more of the parameters can be set in both SSL_CTX and SSL, a few like SNI are only available for SSL object, that is why we use to callbacks.
/** | ||
* The server's SSL context, this objects holds configuration relevant the SSL session establishment. | ||
*/ | ||
SSL_CTX* serverSslContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A callback would be more convenient here too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me! I may have been a bit overzealous with nullptr
comments, please ignore any wrong ones :)
@@ -9,7 +9,7 @@ RUN set -eux \ | |||
&& sudo dpkg -i packages-microsoft-prod.deb \ | |||
&& rm packages-microsoft-prod.deb \ | |||
&& sudo apt update \ | |||
&& sudo apt-get install -y python3 python3-dev python3-passlib ruby-full \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is gdb
necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use it to debug the tests, I think it is fine for a development container.
* @remarks The trusted root certificates are used by both the default validation callback, and by custom | ||
* validation callback set in clientCertificateValidationCallback. | ||
* | ||
* The application must ensure that the certificate store remains valid during the setup of the Communicator. It |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should setup be initialization?
* The application must ensure that the certificate store remains valid during the setup of the Communicator. It | |
* The application must ensure that the certificate store remains valid during the setup of the Communicator. It |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's actually obvious and not necessary to document. initialize() is a synchronous function. While it runs, you should not free the arguments you passed via initData.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about the second part about releasing the certificate store?
* | ||
* @remarks This callback is invoked by the SSL transport for each new outgoing connection before starting the | ||
* SSL handshake to determine the appropriate client certificate chain. The callback should return a CFArrayRef | ||
* that represents the client's certificate chain, or NULL if no certificate chain should be used for the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nullptr?
* that represents the client's certificate chain, or NULL if no certificate chain should be used for the | |
* that represents the client's certificate chain, or nullptr if no certificate chain should be used for the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to check if nullptr
works with CFArrayRef
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought that nullptr
as a drop in replacement for 0
or NULL
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice API!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the API but I am concerned about the generated C++ API reference. How is it going to work with all these ifdef?
When you look-up Ice::Ssl::ClientAutenticationOptions::clientCertificateSelectionCallback
, which platform-specific doc-comment do you see? All of them?
It would be clearer for the API reference to have different names, like:
#if defined (ICE_SCHANNEL)
struct SchannelClientAuthenticationOptions
{
// lots of Schannel-specific doc-comment
...
};
using ClientAuthenticationOptions = SchannelClientAuthenticationOptions; // alias for portable code
#elif defined(ICE_SECURE_TRANSPORT)
...
{ | ||
#if defined(_WIN32) | ||
/** | ||
* A callback that allows selecting the client's SSL certificate based on the target server host name. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This target server host name is the host name in the endpoint you're using to establish the connection?
If yes, I think something like "based on the hostname or IP address in the endpoint used to establish the connection" would be clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is.
* server's certificate chain is validated against these certificates; otherwise, the system's default root | ||
* certificates are used. | ||
* | ||
* @remarks The trusted root certificates are used by both the default validation callback, and by custom |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are used by both the default validation callback and by the custom validation ... (no comma, and the).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean serverCertificateValidationCallback?
Since these fields are not sorted in alphabetical order, I would put this trustedRootCertificates next to serverCertificateValidationCallback.
Also, do we document what the default validation callback is?
* @remarks The trusted root certificates are used by both the default validation callback, and by custom | ||
* validation callback set in clientCertificateValidationCallback. | ||
* | ||
* The application must ensure that the certificate store remains valid during the setup of the Communicator. It |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's actually obvious and not necessary to document. initialize() is a synchronous function. While it runs, you should not free the arguments you passed via initData.
Co-authored-by: Joe George <joe@externl.com>
Co-authored-by: Joe George <joe@externl.com>
Co-authored-by: Joe George <joe@externl.com>
Co-authored-by: Joe George <joe@externl.com>
Co-authored-by: Joe George <joe@externl.com>
Co-authored-by: Joe George <joe@externl.com>
That makes sense, assuming we can still use the alias in ObjectAdapter/InitializationData declarations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
This PR adds the ability to programmatically configure the C++ SSL transport using the platform native API.
This PR adds two structs for configuring the SSL transport,
Ice::SSL::ClientAuthenticationOptions
andIce::SSL::ServerAuthenticationOptions
.I considered two important use cases for this PR:
The new configuration API also allow for using the native API for setting lower level details.
The PR reworks the SSL engines to create the
ClientAuthenticationOptions
andServerAuthenticationOptions
based on the IceSSL properties, when the user doesn't explicitly provide its own options. This shows that the new option structs are enough to replace the IceSSL properties currently supported.Things to consider for follow up PRs:
info.certs
and rely on custom validation callbacks to obtain the peer certificates, but I think we use this in a Glacier2 and IceGrid implementations to fill SSLInfo forwarder to session managers.