Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
18ae7e0
Implement server-side channel bindings in NegotiateAuthentication on …
rzikm Apr 28, 2026
0b1fdff
Add tests for server-side Kerberos channel bindings
rzikm Apr 28, 2026
2176b23
Address review feedback: harden channel binding token handling
rzikm Jul 15, 2026
826e9bf
Merge AcceptSecContextEx into AcceptSecContext
rzikm Jul 15, 2026
21ca61a
Validate channel binding token size in managed code and initiator nat…
rzikm Jul 15, 2026
20ae580
Address review feedback: init out params on native fail-fast, ref-cou…
rzikm Jul 17, 2026
6b5315d
Pass channel binding as SafeHandle; offset the CBT pointer in native
rzikm Jul 17, 2026
5cb10a6
Move CBT ref-counting into interop wrappers; keep native at ptr+size
rzikm Jul 17, 2026
64e23f1
Move CBT offset/size computation into interop wrappers
rzikm Jul 17, 2026
0ad9a3c
Address review feedback: inline out params, assert consistency, dispo…
rzikm Jul 17, 2026
ac43cdc
Add final newline and clear exec bit on CBT test file
rzikm Jul 20, 2026
1b6ae7f
Restrict negative channel-binding tests to Linux
rzikm Jul 20, 2026
e8456ca
Throw on unsupported platforms
rzikm Jul 20, 2026
548a933
Harden channel binding app-data resolution in GSSAPI interop
rzikm Jul 20, 2026
8498cbf
Merge remote-tracking branch 'upstream/main' into feature/gssapi-serv…
rzikm Jul 20, 2026
136b2ff
Merge branch 'main' into feature/gssapi-server-channel-bindings
rzikm Jul 22, 2026
4ac04ed
Remove failing test
rzikm Jul 22, 2026
f18774e
Apply suggestion from @rzikm
rzikm Jul 22, 2026
6e858fe
Potential fix for pull request finding
rzikm Jul 22, 2026
17ee3f8
Potential fix for pull request finding
rzikm Jul 22, 2026
818e21e
Fix build
rzikm Jul 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Net.Security;
using System.Runtime.InteropServices;
using System.Security.Authentication.ExtendedProtection;
using System.Text;
using Microsoft.Win32.SafeHandles;

Expand Down Expand Up @@ -133,60 +134,155 @@ internal static Status InitSecContext(
SafeGssCredHandle initiatorCredHandle,
ref SafeGssContextHandle contextHandle,
PackageType packageType,
IntPtr cbt,
int cbtSize,
ChannelBinding channelBinding,
SafeGssNameHandle? targetName,
uint reqFlags,
ReadOnlySpan<byte> inputBytes,
ref GssBuffer token,
out uint retFlags,
out bool isNtlmUsed)
{
return InitSecContext(
out minorStatus,
initiatorCredHandle,
ref contextHandle,
packageType,
cbt,
cbtSize,
targetName,
reqFlags,
ref MemoryMarshal.GetReference(inputBytes),
inputBytes.Length,
ref token,
out retFlags,
out isNtlmUsed);
// Ref-count the channel binding handle so it cannot be released while the native
// call, which receives a raw pointer into the application-specific data, is in flight.
bool refAdded = false;
try
{
channelBinding.DangerousAddRef(ref refAdded);
if (!TryGetChannelBindingApplicationData(channelBinding, out IntPtr cbtAppData, out int cbtAppDataSize))
{
// Invalid or malformed channel binding; fail rather than passing a bogus pointer to interop.
minorStatus = Status.GSS_S_COMPLETE;
retFlags = 0;
isNtlmUsed = false;
return Status.GSS_S_BAD_BINDINGS;
}

return InitSecContext(
out minorStatus,
initiatorCredHandle,
ref contextHandle,
packageType,
cbtAppData,
cbtAppDataSize,
targetName,
reqFlags,
ref MemoryMarshal.GetReference(inputBytes),
inputBytes.Length,
ref token,
out retFlags,
out isNtlmUsed);
}
finally
{
if (refAdded)
{
channelBinding.DangerousRelease();
}
}
}

// Resolves the application-specific data inside a SecChannelBindings buffer, honoring the
// ApplicationDataOffset/ApplicationDataLength fields rather than assuming the data starts
// immediately after the header. Returns false for invalid handles or out-of-bounds ranges.
// The caller must hold a ref on the handle (DangerousAddRef) while using the returned pointer.
private static unsafe bool TryGetChannelBindingApplicationData(ChannelBinding channelBinding, out IntPtr applicationData, out int applicationDataSize)
{
applicationData = IntPtr.Zero;
applicationDataSize = 0;

int size = channelBinding.Size;
if (channelBinding.IsInvalid || size < sizeof(SecChannelBindings))
{
return false;
}

SecChannelBindings* bindings = (SecChannelBindings*)channelBinding.DangerousGetHandle();
int offset = bindings->ApplicationDataOffset;
int length = bindings->ApplicationDataLength;

// The application data must lie entirely within the buffer, after the fixed header.
if (offset < sizeof(SecChannelBindings) || length < 0 || (long)offset + length > size)
{
return false;
}

applicationData = (IntPtr)((byte*)bindings + offset);
applicationDataSize = length;
return true;
}

[LibraryImport(Interop.Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_AcceptSecContext")]
private static partial Status AcceptSecContext(
out Status minorStatus,
SafeGssCredHandle acceptorCredHandle,
ref SafeGssContextHandle acceptContextHandle,
IntPtr cbt,
Comment thread
rzikm marked this conversation as resolved.
int cbtSize,
ref byte inputBytes,
int inputLength,
ref GssBuffer token,
out uint retFlags,
[MarshalAs(UnmanagedType.Bool)] out bool isNtlmUsed);

internal static Status AcceptSecContext(
internal static unsafe Status AcceptSecContext(
out Status minorStatus,
SafeGssCredHandle acceptorCredHandle,
ref SafeGssContextHandle acceptContextHandle,
ChannelBinding? channelBinding,
ReadOnlySpan<byte> inputBytes,
ref GssBuffer token,
out uint retFlags,
out bool isNtlmUsed)
{
return AcceptSecContext(
out minorStatus,
acceptorCredHandle,
ref acceptContextHandle,
ref MemoryMarshal.GetReference(inputBytes),
inputBytes.Length,
ref token,
out retFlags,
out isNtlmUsed);
if (channelBinding is null)
{
return AcceptSecContext(
out minorStatus,
acceptorCredHandle,
ref acceptContextHandle,
IntPtr.Zero,
0,
ref MemoryMarshal.GetReference(inputBytes),
inputBytes.Length,
ref token,
out retFlags,
out isNtlmUsed);
}

// Ref-count the channel binding handle so it cannot be released while the native
// call, which receives a raw pointer into the application-specific data, is in flight.
bool refAdded = false;
try
{
channelBinding.DangerousAddRef(ref refAdded);
if (!TryGetChannelBindingApplicationData(channelBinding, out IntPtr cbtAppData, out int cbtAppDataSize))
{
// Invalid or malformed channel binding; fail rather than passing a bogus pointer to interop.
minorStatus = Status.GSS_S_COMPLETE;
retFlags = 0;
isNtlmUsed = false;
return Status.GSS_S_BAD_BINDINGS;
}

return AcceptSecContext(
out minorStatus,
acceptorCredHandle,
ref acceptContextHandle,
cbtAppData,
cbtAppDataSize,
ref MemoryMarshal.GetReference(inputBytes),
inputBytes.Length,
ref token,
out retFlags,
out isNtlmUsed);
}
finally
{
if (refAdded)
{
channelBinding.DangerousRelease();
}
}
}

[LibraryImport(Interop.Libraries.NetSecurityNative, EntryPoint = "NetSecurityNative_DeleteSecContext")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,12 @@ public override void Dispose()
}
else
{
// TODO: We don't currently check channel bindings.

// Server session.
statusCode = AcceptSecurityContext(
_credentialsHandle,
ref _securityContext,
incomingBlob,
_channelBinding,
ref _tokenBuffer,
out resultBlobLength,
ref _contextFlags);
Expand Down Expand Up @@ -540,7 +539,7 @@ Interop.NetSecurityNative.Status status
}
}

private unsafe NegotiateAuthenticationStatusCode InitializeSecurityContext(
private NegotiateAuthenticationStatusCode InitializeSecurityContext(
ref SafeGssCredHandle credentialsHandle,
ref SafeGssContextHandle? contextHandle,
ref SafeGssNameHandle? targetNameHandle,
Expand Down Expand Up @@ -582,18 +581,11 @@ private unsafe NegotiateAuthenticationStatusCode InitializeSecurityContext(

if (channelBinding != null)
{
// If a TLS channel binding token (cbt) is available then get the pointer
// to the application specific data.
int appDataOffset = sizeof(SecChannelBindings);
Debug.Assert(appDataOffset < channelBinding.Size);
IntPtr cbtAppData = channelBinding.DangerousGetHandle() + appDataOffset;
int cbtAppDataSize = channelBinding.Size - appDataOffset;
status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
credentialsHandle,
ref contextHandle,
_packageType,
cbtAppData,
cbtAppDataSize,
channelBinding,
targetNameHandle,
(uint)requestedContextFlags,
incomingBlob,
Expand Down Expand Up @@ -670,9 +662,8 @@ private unsafe NegotiateAuthenticationStatusCode InitializeSecurityContext(
private NegotiateAuthenticationStatusCode AcceptSecurityContext(
SafeGssCredHandle credentialsHandle,
ref SafeGssContextHandle? contextHandle,
//ContextFlagsPal requestedContextFlags,
ReadOnlySpan<byte> incomingBlob,
//ChannelBinding? channelBinding,
ChannelBinding? channelBinding,
ref byte[]? resultBlob,
out int resultBlobLength,
ref Interop.NetSecurityNative.GssFlags contextFlags)
Expand All @@ -684,9 +675,11 @@ private NegotiateAuthenticationStatusCode AcceptSecurityContext(
{
Interop.NetSecurityNative.Status status;
Interop.NetSecurityNative.Status minorStatus;

status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
credentialsHandle,
ref contextHandle,
channelBinding,
incomingBlob,
ref token,
out uint outputFlags,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ public static bool OSSupportsExtendedProtection
get
{
// ExtendedProtection is supported on all Windows versions supported by current .NET version.
return OperatingSystem.IsWindows();
// Linux is supported via GSSAPI (Managed implements only client-side).
// MacOS's Heimdal-derived GSS.framework does not reject a channel binding mismatch (the exchange completes successfully)
return OperatingSystem.IsWindows() || OperatingSystem.IsLinux();
Comment thread
rzikm marked this conversation as resolved.
}
Comment thread
rzikm marked this conversation as resolved.
}
}
Expand Down
Loading
Loading