internal static void SetSslOptions(EasyRequest easy, ClientCertificateOption clientCertOption)
{
Debug.Assert(clientCertOption == ClientCertificateOption.Automatic || clientCertOption == ClientCertificateOption.Manual);
// Create a client certificate provider if client certs may be used.
X509Certificate2Collection clientCertificates = easy._handler._clientCertificates;
ClientCertificateProvider certProvider =
clientCertOption == ClientCertificateOption.Automatic ? new ClientCertificateProvider(null) : // automatic
clientCertificates?.Count > 0 ? new ClientCertificateProvider(clientCertificates) : // manual with certs
null; // manual without certs
IntPtr userPointer = IntPtr.Zero;
if (certProvider != null)
{
// The client cert provider needs to be passed through to the callback, and thus
// we create a GCHandle to keep it rooted. This handle needs to be cleaned up
// when the request has completed, and a simple and pay-for-play way to do that
// is by cleaning it up in a continuation off of the request.
userPointer = GCHandle.ToIntPtr(certProvider._gcHandle);
easy.Task.ContinueWith((_, state) => ((IDisposable)state).Dispose(), certProvider,
CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
// Register the callback with libcurl. We need to register even if there's no user-provided
// server callback and even if there are no client certificates, because we support verifying
// server certificates against more than those known to OpenSSL.
CURLcode answer = easy.SetSslCtxCallback(s_sslCtxCallback, userPointer);
switch (answer)
{
case CURLcode.CURLE_OK:
// We successfully registered. If we'll be invoking a user-provided callback to verify the server
// certificate as part of that, disable libcurl's verification of the host name. The user's callback
// needs to be given the opportunity to examine the cert, and our logic will determine whether
// the host name matches and will inform the callback of that.
if (easy._handler.ServerCertificateValidationCallback != null)
{
easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_SSL_VERIFYHOST, 0); // don't verify the peer cert's hostname
// We don't change the SSL_VERIFYPEER setting, as setting it to 0 will cause
// SSL and libcurl to ignore the result of the server callback.
}
// The allowed SSL protocols will be set in the configuration callback.
break;
case CURLcode.CURLE_UNKNOWN_OPTION: // Curl 7.38 and prior
case CURLcode.CURLE_NOT_BUILT_IN: // Curl 7.39 and later
// It's ok if we failed to register the callback if all of the defaults are in play
// with relation to handling of certificates. But if that's not the case, failing to
// register the callback will result in those options not being factored in, which is
// a significant enough error that we need to fail.
EventSourceTrace("CURLOPT_SSL_CTX_FUNCTION not supported: {0}", answer, easy: easy);
if (certProvider != null ||
easy._handler.ServerCertificateValidationCallback != null ||
easy._handler.CheckCertificateRevocationList)
{
throw new PlatformNotSupportedException(
SR.Format(SR.net_http_unix_invalid_certcallback_option, CurlVersionDescription, CurlSslVersionDescription));
}
// Since there won't be a callback to configure the allowed SSL protocols, configure them here.
SetSslVersion(easy);
break;
default:
ThrowIfCURLEError(answer);
break;
}
}