Skip to content

Comments

Add GSS channel binding support per RFC 4121 section 4.1.1.2 for AP-REQ and TGS-REQ#422

Open
thadumi wants to merge 3 commits intodotnet:developfrom
thadumi:cbt-validation
Open

Add GSS channel binding support per RFC 4121 section 4.1.1.2 for AP-REQ and TGS-REQ#422
thadumi wants to merge 3 commits intodotnet:developfrom
thadumi:cbt-validation

Conversation

@thadumi
Copy link

@thadumi thadumi commented Feb 20, 2026

What's the problem?

TLS Channel Binding Tokens solve authentication relay attacks (MITM).

Without CBT attacker intercepts a client's connection, terminates the TLS session, and establishes a separate TLS session to the real server. The attacker then relays the client's Kerberos AP-REQ (which is valid and properly encrypted) to the server. The server accepts it because the Kerberos ticket itself is perfectly legitimate and it has no knowledge that it arrived through an intermediary.

RFC 4121 describes Channel Binding Tokens for Kerberos.

  • Bugfix
  • New Feature

What's the solution?

CBT ties the Kerberos authentication to the specific TLS channel it was intended for:

  • the client hashes the TLS server certificate (or other channel-unique material) into the application_data of the gss_channel_bindings_struct, computes the MD5 Bnd hash, and embeds it inside the authenticator checksum of the AP-REQ (encrypted, so the attacker can't modify it).
  • the server independently computes the same hash from its own TLS certificate and compares it against the hash in the AP-REQ.
    If an attacker is relaying, the two TLS sessions have different certificates, the attacker's certificate on the client side vs. the real server's certificate on the server side. The hashes won't match, and the server rejects the request.

As such we are introducing:

  • a new property DecryptedKrbApReq.ExpectedChannelBindings to allow the server to specify the expected Channel Binding information in the AP-REQ
  • a new property KerberosValidator.ExpectedChannelBindings which is symmetrical to DecryptedKrbApReq to facilitate the usage of this feature
  • a new validation flag ValidationActions.ChannelBinding that triggers ValidateChannelBinding() during Validate()
    ValidateChannelBinding() uses the property DecryptedKrbApReq.ExpectedChannelBindings to ensure that the unique material present in the AP-REQ.

On server side the intended usage is

// Option 1: Using KerberosValidator 
var validator = new KerberosValidator(serviceKey)
{
    ExpectedChannelBindings = new GssChannelBindings
    {
        ApplicationData = tlsServerEndpointBinding
    }
};

var identity = await validator.Validate(apReqBytes);

// Option 2: Using DecryptedKrbApReq 
var decrypted = new DecryptedKrbApReq(apReq);
decrypted.Decrypt(serviceKey);

decrypted.ExpectedChannelBindings = new GssChannelBindings
{
    ApplicationData = tlsServerEndpointBinding
};

decrypted.Validate(ValidationActions.All);
// Throws KerberosValidationException if bindings don't match

On client side

// Build the TLS channel binding from the server's certificate hash
var channelBindings = new GssChannelBindings
{
    ApplicationData = client.GetTlsServerEndpointBinding()
};

var rst = new RequestServiceTicket
{
    ServicePrincipalName = "host/server.example.com",
    GssContextFlags = GssContextEstablishmentFlag.GSS_C_MUTUAL_FLAG,
    ChannelBindings = channelBindings
};

var apReq = await client.GetServiceTicket(rst);

As of this PR, Channel Binding validation is enabled by default and follow an opt‑out model. Validation is bypassed only when ExpectedChannelBindings is unset, or when Channel Bindings are explicitly excluded through flags: decrypted.Validate(ValidationActions.All & ~ValidationActions.ChannelBinding);

Describe the solution here.

  • Includes unit tests
  • Requires manual test

@thadumi
Copy link
Author

thadumi commented Feb 20, 2026

@dotnet-policy-service agree company="Microsoft"

@thadumi thadumi marked this pull request as ready for review February 20, 2026 15:47
@thadumi thadumi changed the title Add GSS channel binding support per RFC 4121 section 4.1.1.2 for AP-REQ Add GSS channel binding support per RFC 4121 section 4.1.1.2 for AP-REQ and TGS-REQ Feb 20, 2026
/// Accepts a raw SEC_CHANNEL_BINDINGS buffer
/// and converts it to <see cref="ExpectedChannelBindings"/>.
/// </summary>
public ReadOnlyMemory<byte> ExpectedRawChannelBindings
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I feel like intention might be clearer with a setter method instead of a setter-only field. Or you could just make the caller call GssChannelBindings.FromSecChannelBindings. But no strong opinion on this.


protected abstract MessageType MessageType { get; }

protected KdcMessageHandlerBase(ReadOnlyMemory<byte> message, KdcServerOptions options)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to add expected channel bindings here? That way, it's harder to miss passing it in


protected virtual void ValidateChannelBinding()
{
if (this.ExpectedChannelBindings == null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you passed ValidationActions.ChannelBinding but forgot to pass ExpectedChannelBindings -- should this be an exception? Or maybe it's not a wise idea -- but just trying to see if there's a way to not fail silently

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe you could make ExpectedChannelBindings a mandatory param of Validate?


public int Length { get; set; }

public ReadOnlyMemory<byte> ChannelBinding { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to ChannelBindingHash to avoid confusion?


using System;
using System.Globalization;
using System.Runtime.Versioning;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not needed?

/// <summary>
/// Expected channel bindings for this request's TGS-REQ validation.
/// </summary>
public GssChannelBindings ExpectedChannelBindings { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe makes sense to just store the hash, instead of the full object? Seems like you just need the hash

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants