Allow QuicListener to accept files paths for options (#32088)
authorJustin Kotalik <jkotalik12@gmail.com>
Fri, 14 Feb 2020 05:26:48 +0000 (21:26 -0800)
committerGitHub <noreply@github.com>
Fri, 14 Feb 2020 05:26:48 +0000 (21:26 -0800)
src/libraries/System.Net.Quic/ref/System.Net.Quic.cs
src/libraries/System.Net.Quic/src/Interop/MsQuicNativeMethods.cs
src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/Internal/MsQuicApi.cs
src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs
src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicListener.cs
src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs

index 0c97e63..751d8c0 100644 (file)
@@ -74,6 +74,8 @@ namespace System.Net.Quic
     public class QuicListenerOptions
     {
         public SslServerAuthenticationOptions ServerAuthenticationOptions { get => throw null; set => throw null; }
+        public string CertificateFilePath { get => throw null; set => throw null; }
+        public string PrivateKeyFilePath { get => throw null; set => throw null; }
         public IPEndPoint ListenEndPoint { get => throw null; set => throw null; }
         public int ListenBacklog { get => throw null; set => throw null; }
         public long MaxBidirectionalStreams { get => throw null; set => throw null; }
index 5f18383..136051c 100644 (file)
@@ -477,5 +477,12 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal
             internal uint Length;
             internal byte* Buffer;
         }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct CertFileParams
+        {
+            internal IntPtr CertificateFilePath;
+            internal IntPtr PrivateKeyFilePath;
+        }
     }
 }
index 653e16a..30e5cba 100644 (file)
@@ -227,54 +227,95 @@ namespace System.Net.Quic.Implementations.MsQuic.Internal
                 buf);
         }
 
-        public async ValueTask<MsQuicSecurityConfig> CreateSecurityConfig(X509Certificate certificate)
+        public async ValueTask<MsQuicSecurityConfig> CreateSecurityConfig(X509Certificate certificate, string certFilePath, string privateKeyFilePath)
         {
             MsQuicSecurityConfig secConfig = null;
             var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
             uint secConfigCreateStatus = MsQuicStatusCodes.InternalError;
             uint createConfigStatus;
+            IntPtr unmanagedAddr = IntPtr.Zero;
+            MsQuicNativeMethods.CertFileParams fileParams = default;
 
-            // If no certificate is provided, provide a null one.
-            if (certificate != null)
-            {
-                createConfigStatus = SecConfigCreateDelegate(
-                    _registrationContext,
-                    (uint)QUIC_SEC_CONFIG_FLAG.CERT_CONTEXT,
-                    certificate.Handle,
-                    null,
-                    IntPtr.Zero,
-                    SecCfgCreateCallbackHandler);
-            }
-            else
+            try
             {
-                createConfigStatus = SecConfigCreateDelegate(
-                    _registrationContext,
-                    (uint)QUIC_SEC_CONFIG_FLAG.CERT_NULL,
-                    IntPtr.Zero,
-                    null,
-                    IntPtr.Zero,
-                    SecCfgCreateCallbackHandler);
-            }
+                if (certFilePath != null && privateKeyFilePath != null)
+                {
+                    fileParams = new MsQuicNativeMethods.CertFileParams
+                    {
+                        CertificateFilePath = Marshal.StringToCoTaskMemUTF8(certFilePath),
+                        PrivateKeyFilePath = Marshal.StringToCoTaskMemUTF8(privateKeyFilePath)
+                    };
+
+                    unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(fileParams));
+                    Marshal.StructureToPtr(fileParams, unmanagedAddr, fDeleteOld: false);
+
+                    createConfigStatus = SecConfigCreateDelegate(
+                        _registrationContext,
+                        (uint)QUIC_SEC_CONFIG_FLAG.CERT_FILE,
+                        certificate.Handle,
+                        null,
+                        IntPtr.Zero,
+                        SecCfgCreateCallbackHandler);
+                }
+                else if (certificate != null)
+                {
+                    createConfigStatus = SecConfigCreateDelegate(
+                        _registrationContext,
+                        (uint)QUIC_SEC_CONFIG_FLAG.CERT_CONTEXT,
+                        certificate.Handle,
+                        null,
+                        IntPtr.Zero,
+                        SecCfgCreateCallbackHandler);
+                }
+                else
+                {
+                    // If no certificate is provided, provide a null one.
+                    createConfigStatus = SecConfigCreateDelegate(
+                        _registrationContext,
+                        (uint)QUIC_SEC_CONFIG_FLAG.CERT_NULL,
+                        IntPtr.Zero,
+                        null,
+                        IntPtr.Zero,
+                        SecCfgCreateCallbackHandler);
+                }
 
-            QuicExceptionHelpers.ThrowIfFailed(
-                createConfigStatus,
-                "Could not create security configuration.");
+                QuicExceptionHelpers.ThrowIfFailed(
+                    createConfigStatus,
+                    "Could not create security configuration.");
 
-            void SecCfgCreateCallbackHandler(
-                IntPtr context,
-                uint status,
-                IntPtr securityConfig)
-            {
-                secConfig = new MsQuicSecurityConfig(this, securityConfig);
-                secConfigCreateStatus = status;
-                tcs.SetResult(null);
+                void SecCfgCreateCallbackHandler(
+                    IntPtr context,
+                    uint status,
+                    IntPtr securityConfig)
+                {
+                    secConfig = new MsQuicSecurityConfig(this, securityConfig);
+                    secConfigCreateStatus = status;
+                    tcs.SetResult(null);
+                }
+
+                await tcs.Task.ConfigureAwait(false);
+
+                QuicExceptionHelpers.ThrowIfFailed(
+                    secConfigCreateStatus,
+                    "Could not create security configuration.");
             }
+            finally
+            {
+                if (fileParams.CertificateFilePath != IntPtr.Zero)
+                {
+                    Marshal.FreeCoTaskMem(fileParams.CertificateFilePath);
+                }
 
-            await tcs.Task.ConfigureAwait(false);
+                if (fileParams.PrivateKeyFilePath != IntPtr.Zero)
+                {
+                    Marshal.FreeCoTaskMem(fileParams.PrivateKeyFilePath);
+                }
 
-            QuicExceptionHelpers.ThrowIfFailed(
-                secConfigCreateStatus,
-                "Could not create security configuration.");
+                if (unmanagedAddr != IntPtr.Zero)
+                {
+                    Marshal.FreeHGlobal(unmanagedAddr);
+                }
+            }
 
             return secConfig;
         }
index 76325b1..1d914c2 100644 (file)
@@ -86,9 +86,9 @@ namespace System.Net.Quic.Implementations.MsQuic
             }
         }
 
-        internal async ValueTask SetSecurityConfigForConnection(X509Certificate cert)
+        internal async ValueTask SetSecurityConfigForConnection(X509Certificate cert, string certFilePath, string privateKeyFilePath)
         {
-            _securityConfig = await MsQuicApi.Api.CreateSecurityConfig(cert);
+            _securityConfig = await MsQuicApi.Api.CreateSecurityConfig(cert, certFilePath, privateKeyFilePath);
             // TODO this isn't being set correctly
             MsQuicParameterHelpers.SetSecurityConfig(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.SEC_CONFIG, _securityConfig.NativeObjPtr);
         }
index caf5716..20d677a 100644 (file)
@@ -30,6 +30,7 @@ namespace System.Net.Quic.Implementations.MsQuic
         // Ssl listening options (ALPN, cert, etc)
         private SslServerAuthenticationOptions _sslOptions;
 
+        private QuicListenerOptions _options;
         private volatile bool _disposed;
         private IPEndPoint _listenEndPoint;
 
@@ -44,11 +45,11 @@ namespace System.Net.Quic.Implementations.MsQuic
                 SingleWriter = true
             });
 
+            _options = options;
             _sslOptions = options.ServerAuthenticationOptions;
             _listenEndPoint = options.ListenEndPoint;
 
             _ptr = _session.ListenerOpen(options);
-
         }
 
         internal override IPEndPoint ListenEndPoint
@@ -76,7 +77,9 @@ namespace System.Net.Quic.Implementations.MsQuic
                 throw new QuicOperationAbortedException();
             }
 
-            await connection.SetSecurityConfigForConnection(_sslOptions.ServerCertificate);
+            await connection.SetSecurityConfigForConnection(_sslOptions.ServerCertificate,
+                _options.CertificateFilePath,
+                _options.PrivateKeyFilePath);
 
             if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
             return connection;
index 56e3209..f315945 100644 (file)
@@ -17,6 +17,16 @@ namespace System.Net.Quic
         public SslServerAuthenticationOptions ServerAuthenticationOptions { get; set; }
 
         /// <summary>
+        /// Optional path to certificate file to configure the security configuration.
+        /// </summary>
+        public string CertificateFilePath { get; set; }
+
+        /// <summary>
+        /// Optional path to private key file to configure the security configuration.
+        /// </summary>
+        public string PrivateKeyFilePath { get; set; }
+
+        /// <summary>
         /// The endpoint to listen on.
         /// </summary>
         public IPEndPoint ListenEndPoint { get; set; }