Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segmentation violation when doing server-side Kerberos authentication on macOS #71463

Closed
filipnavara opened this issue Jun 29, 2022 · 9 comments · Fixed by #71484
Closed

Segmentation violation when doing server-side Kerberos authentication on macOS #71463

filipnavara opened this issue Jun 29, 2022 · 9 comments · Fixed by #71484
Assignees
Milestone

Comments

@filipnavara
Copy link
Member

filipnavara commented Jun 29, 2022

The following repo crashes with segmentation violation in gss_delete_sec_context.

Steps:

  1. Compile and run Kerberos.NET KerberosKdcHostApp sample. Note that it may require some tweaks since it currently has internal project dependencies that prevent it from being built on non-Windows.
  2. Create a krb5.conf file with the following content:
[realms]
    corp2.identityintervention.com = {
        kdc = tcp/127.0.0.1:8888
    }
  1. Create a krb5.keytab file with the following command:
ktutil --keytab=./keytab add -p HTTP/corp2.identityintervention.com@corp2.identityintervention.com
Encryption type: aes256-cts-hmac-sha1-96
Key version: 1
Password: P@ssw0rd!
  1. Set KRB5_CONFIG environment variable to path to krb5.conf from step 2
  2. Set KRB5_KTNAME environment variable to path to krb5.keytab from step 3
  3. Create a new dotnet program with dotnet new console and replace the content of Program.cs with
using System.Net;
using System.Net.Security;

NegotiateAuthenticationClientOptions clientOptions = new NegotiateAuthenticationClientOptions {
    Credential = new NetworkCredential("user", "P@ssw0rd!", "corp2.identityintervention.com"),
    TargetName = "HTTP/corp2.identityintervention.com"
};
NegotiateAuthenticationServerOptions serverOptions = new NegotiateAuthenticationServerOptions { };
NegotiateAuthentication clientNegotiateAuthentication = new NegotiateAuthentication(clientOptions);
NegotiateAuthentication serverNegotiateAuthentication = new NegotiateAuthentication(serverOptions);
NegotiateAuthenticationStatusCode statusCode;

byte[]? serverBlob = null;
byte[]? clientBlob = null;
do
{
    clientBlob = clientNegotiateAuthentication.GetOutgoingBlob(serverBlob, out statusCode);
    Console.WriteLine("client status: " + statusCode);
    Console.WriteLine("client blob: " + (clientBlob == null ? "null" : Convert.ToHexString(clientBlob)));
    if (clientBlob != null)
    {
        Console.WriteLine("client -> server ");
        serverBlob = serverNegotiateAuthentication.GetOutgoingBlob(clientBlob, out statusCode);
        Console.WriteLine("client -> server 2");
        Console.WriteLine("server status: " + statusCode);
        Console.WriteLine("server blob: " + (serverBlob == null ? "null" : Convert.ToHexString(serverBlob)));
    }
}
while (serverBlob != null);
  1. Update the .csproj file from step 6 to target net7.0 (preview6+ is required)
  2. Run the program with dotnet run

The output is

client status: ContinueNeeded
client blob: 607406062B0601050502A06A3068A01E301C06062B060105050E060A2B06010401823702020A06062B0601050205A2460444604206062B060105050E6E2C6E3D7573657240636F7270322E6964656E74697479696E74657276656E74696F6E2E636F6D2C723D4677774564416832684E687164484B35
client -> server 
client -> server 2
server status: ContinueNeeded
server blob: A1153013A0030A0101A10C060A2B06010401823702020A
client status: ContinueNeeded
client blob: A12E302CA22A04284E544C4D535350000100000005028862000000000000000000000000000000000601B01D0000000F
client -> server 
zsh: segmentation fault  ./bin/Debug/net7.0/negotest
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jun 29, 2022
@ghost
Copy link

ghost commented Jun 29, 2022

Tagging subscribers to this area: @dotnet/ncl, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

The following repo crashes with segmentation violation in gss_delete_sec_context.

Steps:

  1. Compile and run Kerberos.NET KerberosKdcHostApp sample
  2. Create a krb5.conf file with the following content:
[realms]
    corp2.identityintervention.com = {
        kdc = tcp/127.0.0.1:8888
    }
  1. Create a krb5.keytab file with the following command:
ktutil --keytab=./keytab add -p HTTP/corp2.identityintervention.com@corp2.identityintervention.com
Encryption type: aes256-cts-hmac-sha1-96
Key version: 1
Password: P@ssw0rd!
  1. Set KRB5_CONFIG environment variable to path to krb5.conf from step 2
  2. Set KRB5_KTNAME environment variable to path to krb5.keytab from step 3
  3. Create a new dotnet program with dotnet new console and replace the content of Program.cs with
using System.Net;
using System.Net.Security;

NegotiateAuthenticationClientOptions clientOptions = new NegotiateAuthenticationClientOptions {
    Credential = new NetworkCredential("user", "P@ssw0rd!", "corp2.identityintervention.com"),
    TargetName = "HTTP/corp2.identityintervention.com"
};
NegotiateAuthenticationServerOptions serverOptions = new NegotiateAuthenticationServerOptions { };
NegotiateAuthentication clientNegotiateAuthentication = new NegotiateAuthentication(clientOptions);
NegotiateAuthentication serverNegotiateAuthentication = new NegotiateAuthentication(serverOptions);
NegotiateAuthenticationStatusCode statusCode;

byte[]? serverBlob = null;
byte[]? clientBlob = null;
do
{
    clientBlob = clientNegotiateAuthentication.GetOutgoingBlob(serverBlob, out statusCode);
    Console.WriteLine("client status: " + statusCode);
    Console.WriteLine("client blob: " + (clientBlob == null ? "null" : Convert.ToHexString(clientBlob)));
    if (clientBlob != null)
    {
        Console.WriteLine("client -> server ");
        serverBlob = serverNegotiateAuthentication.GetOutgoingBlob(clientBlob, out statusCode);
        Console.WriteLine("client -> server 2");
        Console.WriteLine("server status: " + statusCode);
        Console.WriteLine("server blob: " + (serverBlob == null ? "null" : Convert.ToHexString(serverBlob)));
    }
}
while (serverBlob != null);
  1. Update the .csproj file from step 6 to target net7.0 (preview6+ is required)
  2. Run the program with dotnet run

The output is

client status: ContinueNeeded
client blob: 607406062B0601050502A06A3068A01E301C06062B060105050E060A2B06010401823702020A06062B0601050205A2460444604206062B060105050E6E2C6E3D7573657240636F7270322E6964656E74697479696E74657276656E74696F6E2E636F6D2C723D4677774564416832684E687164484B35
client -> server 
client -> server 2
server status: ContinueNeeded
server blob: A1153013A0030A0101A10C060A2B06010401823702020A
client status: ContinueNeeded
client blob: A12E302CA22A04284E544C4D535350000100000005028862000000000000000000000000000000000601B01D0000000F
client -> server 
zsh: segmentation fault  ./bin/Debug/net7.0/negotest
Author: filipnavara
Assignees: -
Labels:

area-System.Net.Security

Milestone: -

@filipnavara filipnavara added the os-mac-os-x macOS aka OSX label Jun 29, 2022
@filipnavara
Copy link
Member Author

I'm trying to make a standalone repro but I wanted to log the issue first.

@filipnavara filipnavara changed the title Segmentation violation when doing Kerberos authentication on macOS Segmentation violation when doing server-side Kerberos authentication on macOS Jun 29, 2022
@filipnavara
Copy link
Member Author

Standalone repro: https://github.com/filipnavara/KerberosLoopback, just dotnet run.

@filipnavara
Copy link
Member Author

Native stack:

  * frame #0: 0x00000001ca9aa420 GSS`gss_delete_sec_context + 192
    frame #1: 0x0000000103566238 libSystem.Net.Security.Native.dylib`NetSecurityNative_DeleteSecContext + 148

CLR stack:

000000016FDFD630 00000001ca9aa420 [InlinedCallFrame: 000000016fdfd630] Interop+NetSecurityNative.<DeleteSecContext>g____PInvoke|21_0(Status*, IntPtr*)
000000016FDFD630 000000012a44f54c [InlinedCallFrame: 000000016fdfd630] Interop+NetSecurityNative.<DeleteSecContext>g____PInvoke|21_0(Status*, IntPtr*)
000000016FDFD610 000000012A44F54C ILStubClass.IL_STUB_PInvoke(Status*, IntPtr*)
000000016FDFD710 000000012A46B854 Interop+NetSecurityNative.DeleteSecContext(Status ByRef, IntPtr ByRef)
000000016FDFD770 000000012A46B4B4 Microsoft.Win32.SafeHandles.SafeGssContextHandle.ReleaseHandle()
000000016FDFD7A0 00000001285663D8 System.Runtime.InteropServices.SafeHandle.InternalRelease(Boolean)
000000016FDFD7E0 00000001285660D0 System.Runtime.InteropServices.SafeHandle.Dispose(Boolean)
000000016FDFD800 000000012856606C System.Runtime.InteropServices.SafeHandle.Dispose()
000000016FDFD820 000000012A46B354 System.Net.Security.SafeDeleteNegoContext.Dispose(Boolean)
000000016FDFD850 000000012856606C System.Runtime.InteropServices.SafeHandle.Dispose()
000000016FDFD870 000000012A46B298 System.Net.NTAuthentication.CloseContext()
000000016FDFD8A0 000000012A4509B4 System.Net.NTAuthentication.GetOutgoingBlob(System.ReadOnlySpan`1<Byte>, Boolean, System.Net.SecurityStatusPal ByRef)
000000016FDFDB00 000000012A44FF50 System.Net.Security.NegotiateAuthentication.GetOutgoingBlob(System.ReadOnlySpan`1<Byte>, System.Net.Security.NegotiateAuthenticationStatusCode ByRef)
000000016FDFDB70 0000000129123014 KerberosLoopback.Program+<Main>d__1.MoveNext()
000000016FDFDFC0 00000001288625B8 System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[System.__Canon, System.Private.CoreLib]](System.__Canon ByRef)
000000016FDFE000 00000001288626CC System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start[[System.__Canon, System.Private.CoreLib]](System.__Canon ByRef)
000000016FDFE020 0000000129121904 KerberosLoopback.Program.Main()
000000016FDFE060 00000001291217C8 KerberosLoopback.Program.<Main>()

@filipnavara
Copy link
Member Author

It seems to be double-free. gss_accept_sec_context frees the context itself, it sets *contextHandle = NULL but that's somehow not reflected on the managed side. It then tries to close the same SafeHandle.

@wfurt
Copy link
Member

wfurt commented Jun 30, 2022

does it happen always or only on failure? This seems like some discrepancy between MIT and Heimdal?

@filipnavara
Copy link
Member Author

does it happen always or only on failure?

Not sure if it failed. I assume it did based on the Apple source code but I would need to add traces to see. It crashes before the status code is returned to the application.

This seems like some discrepancy between MIT and Heimdal?

Quite possibly. The gss_accept_sec_context method is allowed to replace the context. Apple/Heimdal apparently does that in error conditions in addition to the normal ones (first call) and it's not properly reflected in the interop or the layers above it.

@filipnavara
Copy link
Member Author

filipnavara commented Jun 30, 2022

It could be something that was introduced by the new interop generator. I added traces both to the native layer in NetSecurityNative_AcceptSecContext (for *contextHandle) and to the managed AcceptSecContext (for acceptorCredHandle.DangerousGetHandle()):

AcceptSecContext: in 105553118441056
NetSecurityNative_AcceptSecContext: in 0x600000212ea0
NetSecurityNative_AcceptSecContext: out 0x0
AcceptSecContext: out 105553118441056

(The decompiled P/Invoke for AcceptSecContext looks okay to me though :-/ )

Nevermind, I dumped a wrong variable.

@filipnavara
Copy link
Member Author

filipnavara commented Jun 30, 2022

The problem is here:

if (null == negoContext.GssContext)
{
negoContext.SetGssContext(contextHandle!);
}

The handle doesn't get updated if the native code released it. Also, GssAcceptSecurityContext directly throws an exception so that handle update and asserts around it never get a chance to take place.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jun 30, 2022
@wfurt wfurt removed the untriaged New issue has not been triaged by the area owner label Jun 30, 2022
@wfurt wfurt added this to the 7.0.0 milestone Jun 30, 2022
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jul 1, 2022
filipnavara added a commit to filipnavara/runtime that referenced this issue Jul 12, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Jul 31, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants