internal static partial class OpenSsl
{
private static readonly Ssl.SslCtxSetVerifyCallback s_verifyClientCertificate = VerifyClientCertificate;
- private static readonly Ssl.SslCtxSetAlpnCallback s_alpnServerCallback = AlpnServerSelectCallback;
+ private unsafe static readonly Ssl.SslCtxSetAlpnCallback s_alpnServerCallback = AlpnServerSelectCallback;
#region internal methods
//update the client CA list
UpdateCAListFromRootStore(innerContext);
}
-
- if (sslAuthenticationOptions.ApplicationProtocols != null)
+ GCHandle alpnHandle = default;
+ try
{
- if (sslAuthenticationOptions.IsServer)
+ if (sslAuthenticationOptions.ApplicationProtocols != null)
{
- byte[] protos = Interop.Ssl.ConvertAlpnProtocolListToByteArray(sslAuthenticationOptions.ApplicationProtocols);
- sslAuthenticationOptions.AlpnProtocolsHandle = GCHandle.Alloc(protos, GCHandleType.Pinned);
- Interop.Ssl.SslCtxSetAlpnSelectCb(innerContext, s_alpnServerCallback, GCHandle.ToIntPtr(sslAuthenticationOptions.AlpnProtocolsHandle));
- }
- else
- {
- if (Interop.Ssl.SslCtxSetAlpnProtos(innerContext, sslAuthenticationOptions.ApplicationProtocols) != 0)
+ if (sslAuthenticationOptions.IsServer)
{
- throw CreateSslException(SR.net_alpn_config_failed);
+ alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols);
+ Interop.Ssl.SslCtxSetAlpnSelectCb(innerContext, s_alpnServerCallback, GCHandle.ToIntPtr(alpnHandle));
+ }
+ else
+ {
+ if (Interop.Ssl.SslCtxSetAlpnProtos(innerContext, sslAuthenticationOptions.ApplicationProtocols) != 0)
+ {
+ throw CreateSslException(SR.net_alpn_config_failed);
+ }
}
}
- }
- context = SafeSslHandle.Create(innerContext, sslAuthenticationOptions.IsServer);
- Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create");
- if (context.IsInvalid)
- {
- context.Dispose();
- throw CreateSslException(SR.net_allocate_ssl_context_failed);
- }
+ context = SafeSslHandle.Create(innerContext, sslAuthenticationOptions.IsServer);
+ Debug.Assert(context != null, "Expected non-null return value from SafeSslHandle.Create");
+ if (context.IsInvalid)
+ {
+ context.Dispose();
+ throw CreateSslException(SR.net_allocate_ssl_context_failed);
+ }
- if (hasCertificateAndKey)
- {
- bool hasCertReference = false;
- try
+ if (hasCertificateAndKey)
{
- certHandle.DangerousAddRef(ref hasCertReference);
- using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle()))
+ bool hasCertReference = false;
+ try
{
- using (X509Chain chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false))
+ certHandle.DangerousAddRef(ref hasCertReference);
+ using (X509Certificate2 cert = new X509Certificate2(certHandle.DangerousGetHandle()))
{
- if (chain != null && !Ssl.AddExtraChainCertificates(context, chain))
- throw CreateSslException(SR.net_ssl_use_cert_failed);
+ using (X509Chain chain = TLSCertificateExtensions.BuildNewChain(cert, includeClientApplicationPolicy: false))
+ {
+ if (chain != null && !Ssl.AddExtraChainCertificates(context, chain))
+ throw CreateSslException(SR.net_ssl_use_cert_failed);
+ }
}
}
+ finally
+ {
+ if (hasCertReference)
+ certHandle.DangerousRelease();
+ }
}
- finally
+ context.AlpnHandle = alpnHandle;
+ }
+ catch
+ {
+ if (alpnHandle.IsAllocated)
{
- if (hasCertReference)
- certHandle.DangerousRelease();
+ alpnHandle.Free();
}
+
+ throw;
}
}
return OpenSslSuccess;
}
- private static unsafe int AlpnServerSelectCallback(IntPtr ssl, out IntPtr outp, out byte outlen, IntPtr inp, uint inlen, IntPtr arg)
+ private static unsafe int AlpnServerSelectCallback(IntPtr ssl, out byte* outp, out byte outlen, byte* inp, uint inlen, IntPtr arg)
{
- outp = IntPtr.Zero;
+ outp = null;
outlen = 0;
-
- GCHandle protocols = GCHandle.FromIntPtr(arg);
- Debug.Assert(protocols.IsAllocated && protocols.Target != null);
- byte[] server = (byte[])protocols.Target;
+ GCHandle protocolHandle = GCHandle.FromIntPtr(arg);
+ if (!(protocolHandle.Target is List<SslApplicationProtocol> protocolList))
+ {
+ return Ssl.SSL_TLSEXT_ERR_NOACK;
+ }
+
+ try
+ {
+ for (int i = 0; i < protocolList.Count; i++)
+ {
+ Span<byte> clientList = new Span<byte>(inp, (int)inlen);
+ while (clientList.Length > 0)
+ {
+ byte length = clientList[0];
+ Span<byte> clientProto = clientList.Slice(1, length);
+ if (clientProto.SequenceEqual(protocolList[i].Protocol.Span))
+ {
+ outp = inp + (int)inlen - clientList.Length + 1;
+ outlen = length;
+ return Ssl.SSL_TLSEXT_ERR_OK;
+ }
+
+ clientList = clientList.Slice(1 + length);
+ }
+ }
+ }
+ catch
+ {
+ return Ssl.SSL_TLSEXT_ERR_NOACK;
+ }
- return Ssl.SslSelectNextProto(out outp, out outlen, protocols.AddrOfPinnedObject(), (uint)server.Length, inp, inlen) == Ssl.OPENSSL_NPN_NEGOTIATED ?
- Ssl.SSL_TLSEXT_ERR_OK : Ssl.SSL_TLSEXT_ERR_NOACK;
+ return Ssl.SSL_TLSEXT_ERR_NOACK;
}
private static void UpdateCAListFromRootStore(SafeSslContextHandle context)
unsigned int inlen,
void *arg), void *arg);
void SSL_get0_alpn_selected(const SSL* ssl, const unsigned char** protocol, unsigned int* len);
-int32_t SSL_select_next_proto(unsigned char** out, unsigned char* outlen, const unsigned char* server, unsigned int server_len, const unsigned char* client, unsigned int client_len);
#endif
#define API_EXISTS(fn) (fn != nullptr)
PER_FUNCTION_BLOCK(SSL_new, true) \
PER_FUNCTION_BLOCK(SSL_read, true) \
PER_FUNCTION_BLOCK(SSL_renegotiate_pending, true) \
- PER_FUNCTION_BLOCK(SSL_select_next_proto, false) \
PER_FUNCTION_BLOCK(SSL_set_accept_state, true) \
PER_FUNCTION_BLOCK(SSL_set_bio, true) \
PER_FUNCTION_BLOCK(SSL_set_connect_state, true) \
#define SSL_new SSL_new_ptr
#define SSL_read SSL_read_ptr
#define SSL_renegotiate_pending SSL_renegotiate_pending_ptr
-#define SSL_select_next_proto SSL_select_next_proto_ptr
#define SSL_set_accept_state SSL_set_accept_state_ptr
#define SSL_set_bio SSL_set_bio_ptr
#define SSL_set_connect_state SSL_set_connect_state_ptr