dotnet / Kerberos.NET

A Kerberos implementation built entirely in managed code.
MIT License
520 stars 91 forks source link

Failing to authenticate when used against MIT Kerberos Server #323

Open macsux opened 2 years ago

macsux commented 2 years ago

Describe the bug The library seems to be failing when trying to talk to MIT Kerberos Server. I've setup a test MIT Kerberos Server, create a principal and trying to use Kerberos.NET to try to obtain TGT. This is failing with

System.Threading.Tasks.TaskCanceledException: A task was canceled.
   at Kerberos.NET.Transport.Tcp.ReadFromStream(Memory`1 readResponse, NetworkStream stream, CancellationToken cancellation, TimeSpan readTimeout) in D:\a\1\s\Kerberos.NET\Transport\Tcp.cs:line 57
   at Kerberos.NET.Transport.TcpKerberosTransport.ReadResponse[T](NetworkStream stream, CancellationToken cancellation, TimeSpan readTimeout) in D:\a\1\s\Kerberos.NET\Client\Transport\TcpKerberosTransport.cs:line 129
   at Kerberos.NET.Transport.TcpKerberosTransport.SendMessage[T](String domain, ReadOnlyMemory`1 encoded, CancellationToken cancellation)
   at Kerberos.NET.Transport.KerberosTransportSelector.SendMessage[T](String domain, ReadOnlyMemory`1 encoded, CancellationToken cancellation)
   at Kerberos.NET.Client.KerberosClient.RequestTgt(KerberosCredential credential) in D:\a\1\s\Kerberos.NET\Client\KerberosClient.cs:line 1205
   at Kerberos.NET.Client.KerberosClient.AuthenticateCredential(KerberosCredential credential) in D:\a\1\s\Kerberos.NET\Client\KerberosClient.cs:line 374
   at Kerberos.NET.Client.KerberosClient.Authenticate(KerberosCredential credential) in D:\a\1\s\Kerberos.NET\Client\KerberosClient.cs:line 357
   at UserQuery.Main() in C:\Users\astakhov\AppData\Local\Temp\LINQPad7\_mkttxexs\eshxoi\LINQPadQuery:line 15

I am able to authenticate with kinit without issues

To Reproduce

  1. Create a docker-compose file like this
    version: '3.0'
    services:
    demo:
    image: gcavalcante8808/krb5-server
    ports:
      - '88:88'
      - '749:749'
      - '464:464'
      - '888:888'
    environment:
      KRB5_REALM: MACSUX.DC
      KRB5_KDC: localhost
      KRB5_PASS: password
  2. Launch the server from shell via docker-compose up command
  3. Add user to the kerberos server by executing the following command (adjust container name if necessary) docker exec krb-test-server-demo-1 /bin/sh -c "echo password | kadmin -r MACSUX.DC -p admin/admin@MACSUX.DC -q 'addprinc -pw password iwasvc@MACSUX.DC'"
  4. Try to authenticate to this server via a program like this:
    var realm = "MACSUX.DC";
    var config = Krb5Config.Default();
    config.Realms[realm].Kdc.Add("localhost");
    config.Realms[realm].DefaultDomain = realm.ToLower();
    config.DomainRealm.Add(realm.ToLower(), realm.ToUpper());
    config.DomainRealm.Add($".{realm.ToLower()}", realm.ToUpper());
    var client = new KerberosClient(config);
    var credentials = new Kerberos.NET.Credentials.KerberosPasswordCredential("iwasvc", "password", "macsux.dc");
    await client.Authenticate(credentials);

    Linqpad snippet: http://share.linqpad.net/stcnk6.linq

Expected behavior Authentication successful

Screenshots image

Additional context Tested on Windows with server running in docker. kinit test was performed from WSL and was successful.

SteveSyfuhs commented 2 years ago

Are there any logs from MIT about the request? Can you enable verbose/debug logging?

The error itself is that the client timed out waiting for the KDC to send it a response. The protocol is a length/value pair, so the way the code works is that it reads the length and queries the socket until it can get that length of bytes, OR until the socket is closed intentionally (FIN/RST), OR until the cancellation has triggered from timeout. It's only hitting the last case, which would be a weird behavior on the MIT side of things to let the socket linger open if the client sent a bad request.

Can you capture a wireshark trace of a working/failing attempt?

macsux commented 2 years ago

Ok, some clues...

Oct 25 18:23:56 7ce22535e0f1 krb5kdc[20](info): closing down fd 15
Oct 25 18:24:27 7ce22535e0f1 krb5kdc[20](info): AS_REQ (8 etypes {aes256-cts-hmac-sha1-96(18), aes128-cts-hmac-sha1-96(17), aes256-cts-hmac-sha384-192(20), aes128-cts-hmac-sha256-128(19), DEPRECATED:des3-cbc-sha1(16), DEPRECATED:arcfour-hmac(23), camellia128-cts-cmac(25), camellia256-cts-cmac(26)}) 127.0.0.1: SERVER_NOT_FOUND: admin/admin@MACSUX.DC for kadmin/localhost@MACSUX.DC, Server not found in Kerberos database
Oct 25 18:24:27 7ce22535e0f1 krb5kdc[20](info): AS_REQ (8 etypes {aes256-cts-hmac-sha1-96(18), aes128-cts-hmac-sha1-96(17), aes256-cts-hmac-sha384-192(20), aes128-cts-hmac-sha256-128(19), DEPRECATED:des3-cbc-sha1(16), DEPRECATED:arcfour-hmac(23), camellia128-cts-cmac(25), camellia256-cts-cmac(26)}) 127.0.0.1: NEEDED_PREAUTH: admin/admin@MACSUX.DC for kadmin/admin@MACSUX.DC, Additional pre-authentication required
Oct 25 18:24:27 7ce22535e0f1 krb5kdc[20](info): AS_REQ (8 etypes {aes256-cts-hmac-sha1-96(18), aes128-cts-hmac-sha1-96(17), aes256-cts-hmac-sha384-192(20), aes128-cts-hmac-sha256-128(19), DEPRECATED:des3-cbc-sha1(16), DEPRECATED:arcfour-hmac(23), camellia128-cts-cmac(25), camellia256-cts-cmac(26)}) 127.0.0.1: ISSUE: authtime 1666722267, etypes {rep=aes256-cts-hmac-sha1-96(18), tkt=aes256-cts-hmac-sha1-96(18), ses=aes256-cts-hmac-sha1-96(18)}, admin/admin@MACSUX.DC for kadmin/admin@MACSUX.DC
Oct 25 18:24:59 7ce22535e0f1 krb5kdc[20](info): AS_REQ (4 etypes {aes128-cts-hmac-sha256-128(19), aes256-cts-hmac-sha384-192(20), aes256-cts-hmac-sha1-96(18), aes128-cts-hmac-sha1-96(17)}) 172.25.0.1: CLIENT_NOT_FOUND: iwasvc\@MACSUX.DC@MACSUX.DC for krbtgt/MACSUX.DC@MACSUX.DC, Client not found in Kerberos database
Oct 25 18:24:59 7ce22535e0f1 krb5kdc[20](info): closing down fd 15
macsux commented 2 years ago

This is what the log output looks like when I do same thing successfully via kinit iwasvc@MACSUX.DC

Oct 25 18:27:00 7ce22535e0f1 krb5kdc[20](info): AS_REQ (8 etypes {aes256-cts-hmac-sha1-96(18), aes128-cts-hmac-sha1-96(17), aes256-cts-hmac-sha384-192(20), aes128-cts-hmac-sha256-128(19), DEPRECATED:des3-cbc-sha1(16), DEPRECATED:arcfour-hmac(23), camellia128-cts-cmac(25), camellia256-cts-cmac(26)}) 172.25.0.1: NEEDED_PREAUTH: iwasvc@MACSUX.DC for krbtgt/MACSUX.DC@MACSUX.DC, Additional pre-authentication required
Oct 25 18:27:00 7ce22535e0f1 krb5kdc[20](info): closing down fd 15
Oct 25 18:27:03 7ce22535e0f1 krb5kdc[20](info): AS_REQ (8 etypes {aes256-cts-hmac-sha1-96(18), aes128-cts-hmac-sha1-96(17), aes256-cts-hmac-sha384-192(20), aes128-cts-hmac-sha256-128(19), DEPRECATED:des3-cbc-sha1(16), DEPRECATED:arcfour-hmac(23), camellia128-cts-cmac(25), camellia256-cts-cmac(26)}) 172.25.0.1: ISSUE: authtime 1666722423, etypes {rep=aes256-cts-hmac-sha1-96(18), tkt=aes256-cts-hmac-sha1-96(18), ses=aes256-cts-hmac-sha1-96(18)}, iwasvc@MACSUX.DC for krbtgt/MACSUX.DC@MACSUX.DC
Oct 25 18:27:03 7ce22535e0f1 krb5kdc[20](info): closing down fd 15
SteveSyfuhs commented 2 years ago

The name formatted in the log looks a bit fishy. Can you try setting the DefaultNameType to something like PrincipalNameType.NT_ENTERPRISE

client.Configuration.Defaults.DefaultNameType = PrincipalNameType.NT_ENTERPRISE

SteveSyfuhs commented 2 years ago

CORRECTION: NT_PRINCIPAL


From: Andrew Stakhov @.> Sent: Tuesday, October 25, 2022 11:28:23 AM To: dotnet/Kerberos.NET @.> Cc: Steve Syfuhs @.>; Comment @.> Subject: Re: [dotnet/Kerberos.NET] Failing to authenticate when used against MIT Kerberos Server (Issue #323)

This is what the log output looks like when I do same thing successfully via kinit @.***

Oct 25 18:27:00 7ce22535e0f1 krb5kdc20: AS_REQ (8 etypes {aes256-cts-hmac-sha1-96(18), aes128-cts-hmac-sha1-96(17), aes256-cts-hmac-sha384-192(20), aes128-cts-hmac-sha256-128(19), DEPRECATED:des3-cbc-sha1(16), DEPRECATED:arcfour-hmac(23), camellia128-cts-cmac(25), camellia256-cts-cmac(26)}) 172.25.0.1: NEEDED_PREAUTH: @. for @., Additional pre-authentication required Oct 25 18:27:00 7ce22535e0f1 krb5kdc20: closing down fd 15 Oct 25 18:27:03 7ce22535e0f1 krb5kdc20: AS_REQ (8 etypes {aes256-cts-hmac-sha1-96(18), aes128-cts-hmac-sha1-96(17), aes256-cts-hmac-sha384-192(20), aes128-cts-hmac-sha256-128(19), DEPRECATED:des3-cbc-sha1(16), DEPRECATED:arcfour-hmac(23), camellia128-cts-cmac(25), camellia256-cts-cmac(26)}) 172.25.0.1: ISSUE: authtime 1666722423, etypes {rep=aes256-cts-hmac-sha1-96(18), tkt=aes256-cts-hmac-sha1-96(18), ses=aes256-cts-hmac-sha1-96(18)}, @. for @. Oct 25 18:27:03 7ce22535e0f1 krb5kdc20: closing down fd 15

— Reply to this email directly, view it on GitHubhttps://github.com/dotnet/Kerberos.NET/issues/323#issuecomment-1290975592, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AAJHTYKGFALFUT23RXW7CELWFAREPANCNFSM6AAAAAAROF3UUE. You are receiving this because you commented.Message ID: @.***>

macsux commented 2 years ago

Doesn't seem to affect anything - it still formats the requested principal as iwasvc\@MACSUX.DC@MACSUX.DC

SteveSyfuhs commented 2 years ago

So it looks like it's the same issue as this: https://github.com/dotnet/Kerberos.NET/issues/270

I'd guess there's a bug somewhere along the way that's rewriting the name type back to NT_ENTERPRISE.

SteveSyfuhs commented 2 years ago

Grr, hit submit too early.

Can you try setting the PrincipalNameType on the credential itself?

var credentials = new Kerberos.NET.Credentials.KerberosPasswordCredential("iwasvc", "password", "macsux.dc") { PrincipalNameType = PrincipalNameType.NT_PRINCIPAL };
SteveSyfuhs commented 2 years ago

Also, interestingly we must have hit this basic issue in the past because we already have a way to handle this automatically. Hmm.

https://github.com/dotnet/Kerberos.NET/blob/df33af4cbb33ce6fdc81050f30295c39e30e4060/Kerberos.NET/Client/KerberosClient.cs#L407-L412

macsux commented 2 years ago

Tried it by configuring on credential level - same thing.

macsux commented 2 years ago

Any other suggestions to try, or this will require a code fix in lib?

JanMaier97 commented 4 months ago

Hello, I have a similar setup and the same issue. I am running the kerberos server in a local docker container and I want to authenticate against it using Kerberos.NET running in the windows host. From a second docker container I am able to authenticate against the server with kinit. I used tcpdump to trace the packages received by the server. I compared kinit request with the Kerberos.NET request. I found that the request differ in their cname value. The Kerberos.NET request is sending "bob@EXAMPLE.COM" while kinit is only sending "bob". I debugged the authentication method and edited the cname value, before it is sent. The request is still running into TaskCanceledExceptions. However, when executed multiple times in a loop, it works eventually.

Here are the network traces if any one would like to check them: kerberos_traces.zip

Could this be the root cause for this issue? Or should the server usually be able to handle the Kerberos.NET request properly?

SteveSyfuhs commented 4 months ago

The CName field is a function of the credential you're using:

https://github.com/dotnet/Kerberos.NET/blob/68282664b52d531c89d93e66228d24a6e9d24302/Kerberos.NET/Entities/Krb/KrbAsReq.cs#L135-L149

And the logic for that is somewhat complicated, but can be forced by setting the name type explicitly:

https://github.com/dotnet/Kerberos.NET/blob/develop/Kerberos.NET/Entities/Krb/KrbPrincipalName.cs#L232-L259

If the issue is that the client only works when you're sending just the short name instead of a UPN, this can be handled by setting the credential username to that.

macsux commented 4 months ago

Given that MIT Kerberos is defacto library on Linux side (and one .net core is hardcoded relies on to do IWA on linux), perhaps it makes sense to add some kind of "compatibility" enum where all these minor variances between implementations are automatically accounted for. Consumers of this lib usually don't have in-depth knowledge of these minute differences.

JanMaier97 commented 4 months ago

I tried various combinations of username + domain + PrincipalNameType, however, I am unable to force just the short name and the udp name is always sent. So code like this would also have bob@EXAMPLE.COM as the cname in the AS_REQ request:

var kerbCred = new KerberosPasswordCredential("bob, "***", "EXAMPLE.COM");
kerbCred.PrincipalNameType = PrincipalNameType.NT_PRINCIPAL;
await client.Authenticate(kerbCred);

During debugging I found that SplitAsUpn always combines the username with @<realm-name>, if the username does not already contains an @ character. https://github.com/dotnet/Kerberos.NET/blob/89ae633cad5148d630b4de08cfdeecac21b938af/Kerberos.NET/Entities/Krb/KrbPrincipalName.cs#L339-L362 I looked for information about the kerberos protocol and as far as I can understand, the cname is usually just the username and not the full udp. Could this be the cause of the bug we are encountering here?

Otherwise, are there any other client and credential options that I may have missed to set the username correctly?

A6blpka commented 1 day ago

I tried various combinations of username + domain + PrincipalNameType, however, I am unable to force just the short name and the udp name is always sent.

I tried changing the library source code to send the short name to cname for kRB5-NT-PRINCIPAL, but that did nothing. I checked the sent request via wireshark.

Maybe that's not the problem.