From ea3e527bdf4fd6180577a0e44c31e2326536e110 Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 21 Mar 2025 16:26:03 -0700 Subject: [PATCH 1/5] Add support for setting security attributes on Http.Sys RequestQueue --- src/Servers/HttpSys/src/HttpSysListener.cs | 3 +- src/Servers/HttpSys/src/HttpSysOptions.cs | 9 ++ .../HttpSys/src/NativeInterop/RequestQueue.cs | 46 +++++++-- src/Servers/HttpSys/src/NativeMethods.txt | 1 + .../HttpSys/src/PublicAPI.Unshipped.txt | 2 + .../test/FunctionalTests/DelegateTests.cs | 95 ++++++++++++++++++- ...Core.Server.HttpSys.FunctionalTests.csproj | 11 ++- .../test/FunctionalTests/NativeMethods.txt | 7 ++ 8 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 src/Servers/HttpSys/test/FunctionalTests/NativeMethods.txt diff --git a/src/Servers/HttpSys/src/HttpSysListener.cs b/src/Servers/HttpSys/src/HttpSysListener.cs index 0f168e53bd2f..02f595c98fe6 100644 --- a/src/Servers/HttpSys/src/HttpSysListener.cs +++ b/src/Servers/HttpSys/src/HttpSysListener.cs @@ -73,7 +73,8 @@ public HttpSysListener(HttpSysOptions options, ILoggerFactory loggerFactory) try { _serverSession = new ServerSession(); - _requestQueue = new RequestQueue(options.RequestQueueName, options.RequestQueueMode, Logger); + _requestQueue = new RequestQueue(options.RequestQueueName, options.RequestQueueMode, + options.RequestQueueSecurityDescriptor, Logger); _urlGroup = new UrlGroup(_serverSession, _requestQueue, Logger); _disconnectListener = new DisconnectListener(_requestQueue, Logger); diff --git a/src/Servers/HttpSys/src/HttpSysOptions.cs b/src/Servers/HttpSys/src/HttpSysOptions.cs index 3e83e10212f9..9b5958b595ba 100644 --- a/src/Servers/HttpSys/src/HttpSysOptions.cs +++ b/src/Servers/HttpSys/src/HttpSysOptions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Globalization; +using System.Security.AccessControl; using System.Text; using Microsoft.AspNetCore.Http.Features; @@ -167,6 +168,14 @@ public long RequestQueueLimit } } + /// + /// Gets or sets the security descriptor for the request queue. + /// + /// + /// Only applies when creating a new request queue. + /// + public GenericSecurityDescriptor? RequestQueueSecurityDescriptor { get; set; } + /// /// Gets or sets the maximum allowed size of any request body in bytes. /// When set to null, the maximum request body size is unlimited. diff --git a/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs b/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs index 810bdcce3c57..b9dce898a1f3 100644 --- a/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs +++ b/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs @@ -3,9 +3,11 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using System.Security.AccessControl; using Microsoft.Extensions.Logging; using Windows.Win32; using Windows.Win32.Networking.HttpServer; +using Windows.Win32.Security; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -16,15 +18,15 @@ internal sealed partial class RequestQueue private bool _disposed; internal RequestQueue(string requestQueueName, ILogger logger) - : this(requestQueueName, RequestQueueMode.Attach, logger, receiver: true) + : this(requestQueueName, RequestQueueMode.Attach, securityDescriptor: null, logger, receiver: true) { } - internal RequestQueue(string? requestQueueName, RequestQueueMode mode, ILogger logger) - : this(requestQueueName, mode, logger, false) + internal RequestQueue(string? requestQueueName, RequestQueueMode mode, GenericSecurityDescriptor? securityDescriptor, ILogger logger) + : this(requestQueueName, mode, securityDescriptor, logger, false) { } - private RequestQueue(string? requestQueueName, RequestQueueMode mode, ILogger logger, bool receiver) + private RequestQueue(string? requestQueueName, RequestQueueMode mode, GenericSecurityDescriptor? securityDescriptor, ILogger logger, bool receiver) { _mode = mode; _logger = logger; @@ -32,6 +34,9 @@ private RequestQueue(string? requestQueueName, RequestQueueMode mode, ILogger lo var flags = 0u; Created = true; + SECURITY_ATTRIBUTES? securityAttributes = null; + nint? pSecurityDescriptor = null; + if (_mode == RequestQueueMode.Attach) { flags = PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING; @@ -41,11 +46,31 @@ private RequestQueue(string? requestQueueName, RequestQueueMode mode, ILogger lo flags |= PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_DELEGATION; } } + else if (securityDescriptor is not null) // Create or CreateOrAttach + { + // Convert the security descriptor to a byte array + byte[] securityDescriptorBytes = new byte[securityDescriptor.BinaryLength]; + securityDescriptor.GetBinaryForm(securityDescriptorBytes, 0); + + // Allocate native memory for the security descriptor + pSecurityDescriptor = Marshal.AllocHGlobal(securityDescriptorBytes.Length); + Marshal.Copy(securityDescriptorBytes, 0, pSecurityDescriptor.Value, securityDescriptorBytes.Length); + + unsafe + { + securityAttributes = new SECURITY_ATTRIBUTES + { + nLength = (uint)Marshal.SizeOf(), + lpSecurityDescriptor = pSecurityDescriptor.Value.ToPointer(), + bInheritHandle = false + }; + } + } var statusCode = PInvoke.HttpCreateRequestQueue( HttpApi.Version, requestQueueName, - default, + securityAttributes, flags, out var requestQueueHandle); @@ -57,11 +82,17 @@ private RequestQueue(string? requestQueueName, RequestQueueMode mode, ILogger lo statusCode = PInvoke.HttpCreateRequestQueue( HttpApi.Version, requestQueueName, - default, + SecurityAttributes: default, // Attaching should not pass any security attributes flags, out requestQueueHandle); } + if (pSecurityDescriptor is not null) + { + // Free the allocated memory for the security descriptor + Marshal.FreeHGlobal(pSecurityDescriptor.Value); + } + if ((flags & PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING) != 0 && statusCode == ErrorCodes.ERROR_FILE_NOT_FOUND) { throw new HttpSysException((int)statusCode, $"Failed to attach to the given request queue '{requestQueueName}', the queue could not be found."); @@ -143,6 +174,9 @@ public void Dispose() } _disposed = true; + + PInvoke.HttpCloseRequestQueue(Handle); + BoundHandle.Dispose(); Handle.Dispose(); } diff --git a/src/Servers/HttpSys/src/NativeMethods.txt b/src/Servers/HttpSys/src/NativeMethods.txt index 49222f530b01..649cb82404f4 100644 --- a/src/Servers/HttpSys/src/NativeMethods.txt +++ b/src/Servers/HttpSys/src/NativeMethods.txt @@ -42,6 +42,7 @@ HTTPAPI_VERSION HttpCancelHttpRequest HttpCloseServerSession HttpCloseUrlGroup +HttpCloseRequestQueue HttpCreateRequestQueue HttpCreateServerSession HttpCreateUrlGroup diff --git a/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt b/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt index e18d576e45d3..393f7b26a8f3 100644 --- a/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt +++ b/src/Servers/HttpSys/src/PublicAPI.Unshipped.txt @@ -1,3 +1,5 @@ #nullable enable Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.TlsClientHelloBytesCallback.get -> System.Action>? Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.TlsClientHelloBytesCallback.set -> void +Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.RequestQueueSecurityDescriptor.get -> System.Security.AccessControl.GenericSecurityDescriptor? +Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions.RequestQueueSecurityDescriptor.set -> void diff --git a/src/Servers/HttpSys/test/FunctionalTests/DelegateTests.cs b/src/Servers/HttpSys/test/FunctionalTests/DelegateTests.cs index 8cb6332a8f6d..daa5dd7d466c 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/DelegateTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/DelegateTests.cs @@ -3,13 +3,17 @@ using System.Net.Http; using System.Runtime.InteropServices; +using System.Security.AccessControl; +using System.Security.Principal; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.AspNetCore.InternalTesting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Security; namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests; @@ -266,6 +270,95 @@ public async Task DelegateAfterReceiverRestart() destination?.Dispose(); } + [ConditionalFact] + [DelegateSupportedCondition(true)] + public async Task DelegateRequestTestCanSetSecurityDescriptor() + { + // Create a new security descriptor + CommonSecurityDescriptor securityDescriptor = new CommonSecurityDescriptor(false, false, string.Empty); + + // Create a discretionary access control list (DACL) + DiscretionaryAcl dacl = new DiscretionaryAcl(false, false, 2); + dacl.AddAccess(AccessControlType.Allow, new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null), -1, InheritanceFlags.None, PropagationFlags.None); + dacl.AddAccess(AccessControlType.Deny, new SecurityIdentifier(WellKnownSidType.BuiltinGuestsSid, null), -1, InheritanceFlags.None, PropagationFlags.None); + + // Assign the DACL to the security descriptor + securityDescriptor.DiscretionaryAcl = dacl; + + var queueName = Guid.NewGuid().ToString(); + using var receiver = Utilities.CreateHttpServer(out var receiverAddress, async httpContext => + { + await httpContext.Response.WriteAsync(_expectedResponseString); + }, + options => + { + options.RequestQueueName = queueName; + options.RequestQueueSecurityDescriptor = securityDescriptor; + }, LoggerFactory); + + DelegationRule destination = default; + + using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, httpContext => + { + var delegateFeature = httpContext.Features.Get(); + delegateFeature.DelegateRequest(destination); + return Task.CompletedTask; + }, LoggerFactory); + + var delegationProperty = delegator.Features.Get(); + destination = delegationProperty.CreateDelegationRule(queueName, receiverAddress); + + AssertPermissions(destination.Queue.Handle); + unsafe void AssertPermissions(SafeHandle handle) + { + PSECURITY_DESCRIPTOR pSecurityDescriptor = new(); + + WIN32_ERROR result = PInvoke.GetSecurityInfo( + handle, + Windows.Win32.Security.Authorization.SE_OBJECT_TYPE.SE_KERNEL_OBJECT, + 4, // DACL_SECURITY_INFORMATION + null, + null, + null, + null, + &pSecurityDescriptor); + + var length = (int)PInvoke.GetSecurityDescriptorLength(pSecurityDescriptor); + + // Copy the security descriptor to a managed byte array + byte[] securityDescriptorBytes = new byte[length]; + Marshal.Copy(new IntPtr(pSecurityDescriptor.Value), securityDescriptorBytes, 0, length); + + // Convert the byte array to a RawSecurityDescriptor + var securityDescriptor = new RawSecurityDescriptor(securityDescriptorBytes, 0); + + var checkedAllowUser = false; + var checkedDenyGuest = false; + + foreach (CommonAce ace in securityDescriptor.DiscretionaryAcl) + { + if (ace.SecurityIdentifier.IsWellKnown(WellKnownSidType.BuiltinGuestsSid)) + { + Assert.Equal(AceType.AccessDenied, ace.AceType); + checkedDenyGuest = true; + } + else if (ace.SecurityIdentifier.IsWellKnown(WellKnownSidType.BuiltinUsersSid)) + { + Assert.Equal(AceType.AccessAllowed, ace.AceType); + checkedAllowUser = true; + } + } + + PInvoke.LocalFree((HLOCAL)pSecurityDescriptor.Value); + + Assert.True(checkedDenyGuest && checkedAllowUser, "DACL does not contain the expected ACEs"); + } + + var responseString = await SendRequestAsync(delegatorAddress); + Assert.Equal(_expectedResponseString, responseString); + destination?.Dispose(); + } + private async Task SendRequestAsync(string uri) { using var client = new HttpClient(); diff --git a/src/Servers/HttpSys/test/FunctionalTests/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj b/src/Servers/HttpSys/test/FunctionalTests/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj index 56f300b89198..b7a9753e0b4e 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj +++ b/src/Servers/HttpSys/test/FunctionalTests/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj @@ -1,4 +1,4 @@ - + $(DefaultNetCoreTargetFramework) @@ -20,7 +20,7 @@ - + @@ -35,6 +35,13 @@ + + + all + runtime; build; native; contentfiles; analyzers + + + 214124cd-d05b-4309-9af9-9caa44b2b74a diff --git a/src/Servers/HttpSys/test/FunctionalTests/NativeMethods.txt b/src/Servers/HttpSys/test/FunctionalTests/NativeMethods.txt new file mode 100644 index 000000000000..6b638dfa8d52 --- /dev/null +++ b/src/Servers/HttpSys/test/FunctionalTests/NativeMethods.txt @@ -0,0 +1,7 @@ +// https://github.com/microsoft/cswin32 +// Listing specific types reduces the size of the final dll. +// Uncomment the next line to import all definitions during development. +Windows.Win32.Networking.HttpServer +GetSecurityInfo +GetSecurityDescriptorLength +LocalFree From 0c2197cf5b58daa4eba3544171fca542b9cec709 Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 4 Apr 2025 11:35:25 -0700 Subject: [PATCH 2/5] Update HttpSysOptions.cs --- src/Servers/HttpSys/src/HttpSysOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Servers/HttpSys/src/HttpSysOptions.cs b/src/Servers/HttpSys/src/HttpSysOptions.cs index 9b5958b595ba..9497d00d67a4 100644 --- a/src/Servers/HttpSys/src/HttpSysOptions.cs +++ b/src/Servers/HttpSys/src/HttpSysOptions.cs @@ -172,7 +172,7 @@ public long RequestQueueLimit /// Gets or sets the security descriptor for the request queue. /// /// - /// Only applies when creating a new request queue. + /// Only applies when creating a new request queue, see . /// public GenericSecurityDescriptor? RequestQueueSecurityDescriptor { get; set; } From 0037152d8bd84c3a05204c8ef98860c8210fc5a7 Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 4 Apr 2025 12:05:50 -0700 Subject: [PATCH 3/5] Update src/Servers/HttpSys/src/HttpSysOptions.cs --- src/Servers/HttpSys/src/HttpSysOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Servers/HttpSys/src/HttpSysOptions.cs b/src/Servers/HttpSys/src/HttpSysOptions.cs index 9497d00d67a4..bb9cdc6954e4 100644 --- a/src/Servers/HttpSys/src/HttpSysOptions.cs +++ b/src/Servers/HttpSys/src/HttpSysOptions.cs @@ -172,7 +172,7 @@ public long RequestQueueLimit /// Gets or sets the security descriptor for the request queue. /// /// - /// Only applies when creating a new request queue, see . + /// Only applies when creating a new request queue, see . /// public GenericSecurityDescriptor? RequestQueueSecurityDescriptor { get; set; } From cabcc2f3acf8c20d0b2bfd5bb159cae25b8466c6 Mon Sep 17 00:00:00 2001 From: Brennan Date: Fri, 18 Apr 2025 10:18:46 -0700 Subject: [PATCH 4/5] internals --- src/Servers/HttpSys/src/NativeMethods.txt | 3 +++ ...rosoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj | 7 ------- src/Servers/HttpSys/test/FunctionalTests/NativeMethods.txt | 7 ------- 3 files changed, 3 insertions(+), 14 deletions(-) delete mode 100644 src/Servers/HttpSys/test/FunctionalTests/NativeMethods.txt diff --git a/src/Servers/HttpSys/src/NativeMethods.txt b/src/Servers/HttpSys/src/NativeMethods.txt index 649cb82404f4..0ad43a083c5d 100644 --- a/src/Servers/HttpSys/src/NativeMethods.txt +++ b/src/Servers/HttpSys/src/NativeMethods.txt @@ -60,3 +60,6 @@ HttpSetUrlGroupProperty SetFileCompletionNotificationModes SOCKADDR_IN SOCKADDR_IN6 +GetSecurityInfo +GetSecurityDescriptorLength +LocalFree diff --git a/src/Servers/HttpSys/test/FunctionalTests/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj b/src/Servers/HttpSys/test/FunctionalTests/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj index b7a9753e0b4e..f59ba8ab7c22 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj +++ b/src/Servers/HttpSys/test/FunctionalTests/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj @@ -35,13 +35,6 @@ - - - all - runtime; build; native; contentfiles; analyzers - - - 214124cd-d05b-4309-9af9-9caa44b2b74a diff --git a/src/Servers/HttpSys/test/FunctionalTests/NativeMethods.txt b/src/Servers/HttpSys/test/FunctionalTests/NativeMethods.txt deleted file mode 100644 index 6b638dfa8d52..000000000000 --- a/src/Servers/HttpSys/test/FunctionalTests/NativeMethods.txt +++ /dev/null @@ -1,7 +0,0 @@ -// https://github.com/microsoft/cswin32 -// Listing specific types reduces the size of the final dll. -// Uncomment the next line to import all definitions during development. -Windows.Win32.Networking.HttpServer -GetSecurityInfo -GetSecurityDescriptorLength -LocalFree From e38f7d5b23a72c81600cf80451ab62a64e75359c Mon Sep 17 00:00:00 2001 From: Brennan Date: Mon, 28 Apr 2025 10:38:45 -0700 Subject: [PATCH 5/5] finally --- .../HttpSys/src/NativeInterop/RequestQueue.cs | 143 +++++++++--------- 1 file changed, 74 insertions(+), 69 deletions(-) diff --git a/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs b/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs index b9dce898a1f3..a2e86245de75 100644 --- a/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs +++ b/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs @@ -37,92 +37,97 @@ private RequestQueue(string? requestQueueName, RequestQueueMode mode, GenericSec SECURITY_ATTRIBUTES? securityAttributes = null; nint? pSecurityDescriptor = null; - if (_mode == RequestQueueMode.Attach) + try { - flags = PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING; - Created = false; - if (receiver) + if (_mode == RequestQueueMode.Attach) { - flags |= PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_DELEGATION; + flags = PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING; + Created = false; + if (receiver) + { + flags |= PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_DELEGATION; + } } - } - else if (securityDescriptor is not null) // Create or CreateOrAttach - { - // Convert the security descriptor to a byte array - byte[] securityDescriptorBytes = new byte[securityDescriptor.BinaryLength]; - securityDescriptor.GetBinaryForm(securityDescriptorBytes, 0); + else if (securityDescriptor is not null) // Create or CreateOrAttach + { + // Convert the security descriptor to a byte array + byte[] securityDescriptorBytes = new byte[securityDescriptor.BinaryLength]; + securityDescriptor.GetBinaryForm(securityDescriptorBytes, 0); - // Allocate native memory for the security descriptor - pSecurityDescriptor = Marshal.AllocHGlobal(securityDescriptorBytes.Length); - Marshal.Copy(securityDescriptorBytes, 0, pSecurityDescriptor.Value, securityDescriptorBytes.Length); + // Allocate native memory for the security descriptor + pSecurityDescriptor = Marshal.AllocHGlobal(securityDescriptorBytes.Length); + Marshal.Copy(securityDescriptorBytes, 0, pSecurityDescriptor.Value, securityDescriptorBytes.Length); - unsafe - { - securityAttributes = new SECURITY_ATTRIBUTES + unsafe { - nLength = (uint)Marshal.SizeOf(), - lpSecurityDescriptor = pSecurityDescriptor.Value.ToPointer(), - bInheritHandle = false - }; + securityAttributes = new SECURITY_ATTRIBUTES + { + nLength = (uint)Marshal.SizeOf(), + lpSecurityDescriptor = pSecurityDescriptor.Value.ToPointer(), + bInheritHandle = false + }; + } } - } - - var statusCode = PInvoke.HttpCreateRequestQueue( - HttpApi.Version, - requestQueueName, - securityAttributes, - flags, - out var requestQueueHandle); - if (_mode == RequestQueueMode.CreateOrAttach && statusCode == ErrorCodes.ERROR_ALREADY_EXISTS) - { - // Tried to create, but it already exists so attach to it instead. - Created = false; - flags = PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING; - statusCode = PInvoke.HttpCreateRequestQueue( + var statusCode = PInvoke.HttpCreateRequestQueue( HttpApi.Version, requestQueueName, - SecurityAttributes: default, // Attaching should not pass any security attributes + securityAttributes, flags, - out requestQueueHandle); - } + out var requestQueueHandle); - if (pSecurityDescriptor is not null) - { - // Free the allocated memory for the security descriptor - Marshal.FreeHGlobal(pSecurityDescriptor.Value); - } + if (_mode == RequestQueueMode.CreateOrAttach && statusCode == ErrorCodes.ERROR_ALREADY_EXISTS) + { + // Tried to create, but it already exists so attach to it instead. + Created = false; + flags = PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING; + statusCode = PInvoke.HttpCreateRequestQueue( + HttpApi.Version, + requestQueueName, + SecurityAttributes: default, // Attaching should not pass any security attributes + flags, + out requestQueueHandle); + } - if ((flags & PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING) != 0 && statusCode == ErrorCodes.ERROR_FILE_NOT_FOUND) - { - throw new HttpSysException((int)statusCode, $"Failed to attach to the given request queue '{requestQueueName}', the queue could not be found."); - } - else if (statusCode == ErrorCodes.ERROR_INVALID_NAME) - { - throw new HttpSysException((int)statusCode, $"The given request queue name '{requestQueueName}' is invalid."); - } - else if (statusCode != ErrorCodes.ERROR_SUCCESS) - { - throw new HttpSysException((int)statusCode); - } + if ((flags & PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING) != 0 && statusCode == ErrorCodes.ERROR_FILE_NOT_FOUND) + { + throw new HttpSysException((int)statusCode, $"Failed to attach to the given request queue '{requestQueueName}', the queue could not be found."); + } + else if (statusCode == ErrorCodes.ERROR_INVALID_NAME) + { + throw new HttpSysException((int)statusCode, $"The given request queue name '{requestQueueName}' is invalid."); + } + else if (statusCode != ErrorCodes.ERROR_SUCCESS) + { + throw new HttpSysException((int)statusCode); + } - // Disabling callbacks when IO operation completes synchronously (returns ErrorCodes.ERROR_SUCCESS) - if (HttpSysListener.SkipIOCPCallbackOnSuccess && - !PInvoke.SetFileCompletionNotificationModes( - requestQueueHandle, - (byte)(PInvoke.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | - PInvoke.FILE_SKIP_SET_EVENT_ON_HANDLE))) - { - requestQueueHandle.Dispose(); - throw new HttpSysException(Marshal.GetLastWin32Error()); - } + // Disabling callbacks when IO operation completes synchronously (returns ErrorCodes.ERROR_SUCCESS) + if (HttpSysListener.SkipIOCPCallbackOnSuccess && + !PInvoke.SetFileCompletionNotificationModes( + requestQueueHandle, + (byte)(PInvoke.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | + PInvoke.FILE_SKIP_SET_EVENT_ON_HANDLE))) + { + requestQueueHandle.Dispose(); + throw new HttpSysException(Marshal.GetLastWin32Error()); + } - Handle = requestQueueHandle; - BoundHandle = ThreadPoolBoundHandle.BindHandle(Handle); + Handle = requestQueueHandle; + BoundHandle = ThreadPoolBoundHandle.BindHandle(Handle); - if (!Created) + if (!Created) + { + Log.AttachedToQueue(_logger, requestQueueName); + } + } + finally { - Log.AttachedToQueue(_logger, requestQueueName); + if (pSecurityDescriptor is not null) + { + // Free the allocated memory for the security descriptor + Marshal.FreeHGlobal(pSecurityDescriptor.Value); + } } }