Kerberos.NET icon indicating copy to clipboard operation
Kerberos.NET copied to clipboard

Windows Extended Protection support

Open techmikal opened this issue 11 months ago • 4 comments

Is your feature request related to a problem? Please describe. When using the library together the sample code provided to create a "Negotiate" header and then using that header with httpClient to make a request to a website, it fails if the receiving IIS has Windows Extended Protection set to required.

Describe the solution you'd like It would be great if there was a way to add channel binding information when the apReq (i think) is created.

techmikal avatar Mar 12 '24 16:03 techmikal

Yeah, good feature. In theory this could be built out by an application directly by filling in the DelegationInfo object during AP-REQ creation, but that's certainly a pain.

SteveSyfuhs avatar Mar 12 '24 19:03 SteveSyfuhs

First of all, thanks for taking the time to answer and to point me in the right direction. I did give it a go (well, a lot of go's actually) but unfortunately I wasn't able to get it working. While this is not a support channel perhaps you can easily see where i went wrong.

By your direction I made the changes to DelegationInfo.cs

I started with declaring:

public const int ChannelBindingLength = 0x10;
            public readonly byte[] cbt_zeros = new byte[] {
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
            };
            public readonly byte[] tls_server_end_point = new byte[] {
                0x74, 0x6c, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2d, 0x65, 0x6e, 0x64, 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x3a
            };

And then I attempted to change the Encode method like below but when i add "Negotiate " + Convert.ToBase64String(ticket.EncodeGssApi().ToArray()) to the Authorization: header of a http request the binding fails.

public ReadOnlyMemory<byte> Encode()
        {
            using (var stream = new MemoryStream())
            using (var writer = new BinaryWriter(stream))
            {
                X509Certificate cert = new X509Certificate("C:\\Temp\\cert\\test\\publicKey.cer");

                using (var hasher = SHA256.Create())
                {
                    var hash = hasher.ComputeHash(cert.GetRawCertData());
                    int cbtLength = cbt_zeros.Length + tls_server_end_point.Length + hash.Length + 4;
                    var cbt = new byte[cbtLength];

                    int pos = 0;
                    //first 16 bytes zeros
                    Array.Copy(cbt_zeros, 0, cbt, pos, cbt_zeros.Length);

                    //add 4 bytes of app data length (tls prefix + cert hash)
                    byte[] dataLength = new byte[] { (byte)(tls_server_end_point.Length + hash.Length), 0, 0, 0 };
                    pos += cbt_zeros.Length;
                    Array.Copy(dataLength, 0, cbt, pos, 4);

                    //add tls prefix
                    pos += 4;
                    Array.Copy(tls_server_end_point, 0, cbt, pos, tls_server_end_point.Length);

                    //add cert hash
                    pos += tls_server_end_point.Length;
                    Array.Copy(hash, 0, cbt, pos, hash.Length);

                    this.ChannelBinding = cbt;
                }

                if (this.ChannelBinding.Length == 0)
                {
                    this.ChannelBinding = new byte[ChannelBindingLength];
                }

                writer.Write(this.ChannelBinding.Length);
                writer.Write(this.ChannelBinding.ToArray());

                if (this.DelegationTicket != null)
                {
                    this.Flags |= GssContextEstablishmentFlag.GSS_C_DELEG_FLAG;
                }

                writer.Write((int)this.Flags);

                if (this.DelegationTicket != null)
                {
                    writer.Write((short)this.DelegationOption);

                    var deleg = this.DelegationTicket.EncodeApplication();

                    writer.Write((short)deleg.Length);

                    writer.Write(deleg.ToArray());
                }

                return stream.ToArray();
            }
        }

techmikal avatar Mar 14 '24 14:03 techmikal

I'm just eyeballing this so I haven’t looked at the Windows side, but I believe you’re missing the MD5 hash and/or MIC to bind the data to the session.

https://www.rfc-editor.org/rfc/rfc6542.html#section-3.1

https://www.rfc-editor.org/rfc/rfc4121#section-4.1.1.2


From: Mika @.> Sent: Thursday, March 14, 2024 7:00:31 AM To: dotnet/Kerberos.NET @.> Cc: Comment @.>; Subscribed @.> Subject: Re: [dotnet/Kerberos.NET] Windows Extended Protection support (Issue #366)

First of all, thanks for taking the time to answer and to point me in the right direction. I did give it a go (well, a lot of go's actually) but unfortunately I wasn't able to get it working. While this is not a support channel perhaps you can easily see where i went wrong.

By your direction I made the changes to DelegationInfo.cs

I started with declaring:

public const int ChannelBindingLength = 0x10; public readonly byte[] cbt_zeros = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; public readonly byte[] tls_server_end_point = new byte[] { 0x74, 0x6c, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2d, 0x65, 0x6e, 0x64, 0x2d, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x3a };

And then I attempted to change the Encode method like below but when i add "Negotiate " + Convert.ToBase64String(ticket.EncodeGssApi().ToArray()) to the Authorization: header of a http request the binding fails.

public ReadOnlyMemory Encode() { using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { X509Certificate cert = new X509Certificate("C:\Temp\cert\test\publicKey.cer");

            using (var hasher = SHA256.Create())
            {
                var hash = hasher.ComputeHash(cert.GetRawCertData());
                int cbtLength = cbt_zeros.Length + tls_server_end_point.Length + hash.Length + 4;
                var cbt = new byte[cbtLength];

                int pos = 0;
                //first 16 bytes zeros
                Array.Copy(cbt_zeros, 0, cbt, pos, cbt_zeros.Length);

                //add 4 bytes of app data length (tls prefix + cert hash)
                byte[] dataLength = new byte[] { (byte)(tls_server_end_point.Length + hash.Length), 0, 0, 0 };
                pos += cbt_zeros.Length;
                Array.Copy(dataLength, 0, cbt, pos, 4);

                //add tls prefix
                pos += 4;
                Array.Copy(tls_server_end_point, 0, cbt, pos, tls_server_end_point.Length);

                //add cert hash
                pos += tls_server_end_point.Length;
                Array.Copy(hash, 0, cbt, pos, hash.Length);

                this.ChannelBinding = cbt;
            }

            if (this.ChannelBinding.Length == 0)
            {
                this.ChannelBinding = new byte[ChannelBindingLength];
            }

            writer.Write(this.ChannelBinding.Length);
            writer.Write(this.ChannelBinding.ToArray());

            if (this.DelegationTicket != null)
            {
                this.Flags |= GssContextEstablishmentFlag.GSS_C_DELEG_FLAG;
            }

            writer.Write((int)this.Flags);

            if (this.DelegationTicket != null)
            {
                writer.Write((short)this.DelegationOption);

                var deleg = this.DelegationTicket.EncodeApplication();

                writer.Write((short)deleg.Length);

                writer.Write(deleg.ToArray());
            }

            return stream.ToArray();
        }
    }

— Reply to this email directly, view it on GitHubhttps://github.com/dotnet/Kerberos.NET/issues/366#issuecomment-1997528881 or unsubscribehttps://github.com/notifications/unsubscribe-auth/AAJHTYI5RCJVKATAFXVFBNDYYGUQBBFKMF2HI4TJMJ2XIZLTSSBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDUOJ2WLJDOMFWWLLTXMF2GG2C7MFRXI2LWNF2HTAVFOZQWY5LFUVUXG43VMWSG4YLNMWVXI2DSMVQWIX3UPFYGLAVFOZQWY5LFVE2TMNBSG4ZDGNJXURXGC3LFVFUGC427NRQWEZLMVRZXKYTKMVRXIX3UPFYGLLCJONZXKZKDN5WW2ZLOOSTHI33QNFRXHE4CUR2HS4DFVJZGK4DPONUXI33SPGSXMYLMOVS2QOBVGQ4DSMJTHCBKI5DZOBS2K2LTON2WLJLWMFWHKZNKGIYTQMRRGU2TMMZRQKSHI6LQMWSWYYLCMVWKK5TBNR2WLKJVGY2DENZSGM2TPJ3UOJUWOZ3FOKTGG4TFMF2GK. You are receiving this email because you commented on the thread.

Triage notifications on the go with GitHub Mobile for iOShttps://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Androidhttps://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

SteveSyfuhs avatar Mar 14 '24 15:03 SteveSyfuhs

Hi, you were absolutely right about the MD5 hash. For some reason I thought that part was only for NTLM. Thanks a ton for taking the time to help out. Hopefully this helps someone else.

techmikal avatar Mar 17 '24 22:03 techmikal