Giter Club home page Giter Club logo

Comments (18)

bodgit avatar bodgit commented on August 22, 2024

Here's what I have currently which is getting a TKEY response back from the server, but is getting a DNS return code of REFUSED, and the TKEY error indicates 17 (BADKEY).

If I compare captured traffic with a known working capture from running nsupdate, the only thing that seems to be different in the AP_REQ is that mine does not have the mutual flag (GSS_C_MUTUAL_FLAG?) set to true. I can't see how to set that.

package main

import (
        "encoding/binary"
        "encoding/hex"
        "fmt"
        "github.com/miekg/dns"
        "gopkg.in/jcmturner/gokrb5.v2/client"
        "gopkg.in/jcmturner/gokrb5.v2/config"
        "gopkg.in/jcmturner/gokrb5.v2/gssapi"
        "gopkg.in/jcmturner/gokrb5.v2/keytab"
        "log"
        "math/rand"
        "os"
        "time"
)

const (
        DefaultTimeout time.Duration = 5 * time.Second
)

func main() {
        l := log.New(os.Stderr, "GOKRB5 Client: ", log.Ldate|log.Ltime|log.Lshortfile)

        cfg, err := config.Load("/etc/krb5.conf")
        if err != nil {
                l.Printf("Error on config: %v\n", err)
        }

        kt, err := keytab.Load("/path/to/keytab")
        if err != nil {
                l.Printf("Error on keytab: %v\n", err)
        }

        cl := client.NewClientWithKeytab("user", "EXAMPLE.COM", kt)
        cl.WithConfig(cfg)
        cl.GoKrb5Conf.DisablePAFXFast = true
        err = cl.Login()
        if err != nil {
                l.Printf("Error on AS_REQ: %v\n", err)
        }

        tkt, key, err := cl.GetServiceTicket("DNS/[email protected]")
        if err != nil {
                l.Printf("Error on service ticket: %v\n", err)
        }

        apreq, err := gssapi.NewKRB5APREQMechToken(*cl.Credentials, tkt, key)
        if err != nil {
                l.Printf("Error on AP_REQ: %v\n", err)
        }

        s := rand.NewSource(time.Now().UnixNano())
        r := rand.New(s)

        m := &dns.Msg{
                MsgHdr: dns.MsgHdr{
                        RecursionDesired: false,
                },
                Question: make([]dns.Question, 1),
                Extra:    make([]dns.RR, 1),
        }
        c := &dns.Client{
                Net:         "tcp",
                ReadTimeout: DefaultTimeout,
        }

        keyname := dns.Fqdn(fmt.Sprintf("%d.sig-%s", r.Int31(), "dns.example.com"))

        m.Question[0] = dns.Question{
                Name:   keyname,
                Qtype:  dns.TypeTKEY,
                Qclass: dns.ClassANY,
        }

        now := time.Now().Unix()
        m.Extra[0] = &dns.TKEY{
                Hdr: dns.RR_Header{
                        Name:   keyname,
                        Rrtype: dns.TypeTKEY,
                        Class:  dns.ClassANY,
                        Ttl:    0,
                },
                Algorithm:  dns.Fqdn("gss-tsig"),
                Mode:       3,
                Inception:  uint32(now),
                Expiration: uint32(now),
                KeySize:    uint16(binary.Size(apreq)),
                Key:        hex.EncodeToString(apreq),
        }
        tkey, _, err := c.Exchange(m, "192.0.2.1:53")

        l.Printf("%v\n", tkey)
}

from gokrb5.

jcmturner avatar jcmturner commented on August 22, 2024

The flags are set on this line:

Checksum: newAuthenticatorChksum([]int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}),

There isn't currently a public method that allows you to specify the flags. Perhaps you try hacking this line as below to see if it works for you and if so I can look to create a new public method that you can specify flags for a proper fix.

Checksum:  newAuthenticatorChksum([]int{GSS_C_INTEG_FLAG, GSS_C_MUTUAL_FLAG, GSS_C_CONF_FLAG}),

from gokrb5.

bodgit avatar bodgit commented on August 22, 2024

I tried making that change and it didn't seem to have any effect on the resulting capture, the mutual flag was still unset. I did however get it to work by hacking in the flag change here after creating the new AP_REQ struct:

a = APReq{

with:

types.SetFlag(&a.APOptions, 2) // import cycle if I try and use gssapi.GSS_C_MUTUAL_FLAG

This has the desired effect that the AP_REQ has the right flags set however the the server returns KRB5KRB_AP_ERR_MODIFIED which makes me think I've invalidated an integrity checksum somewhere.

from gokrb5.

bodgit avatar bodgit commented on August 22, 2024

This has the desired effect that the AP_REQ has the right flags set however the the server returns KRB5KRB_AP_ERR_MODIFIED which makes me think I've invalidated an integrity checksum somewhere.

My bad, KRB5KRB_AP_ERR_MODIFIED meant something else entirely which I think was down to requesting the wrong service ticket. I've fixed the code and I now have the server accepting my request and responding back with a TSIG key 🎉, (with the hack above to set the mutual flag).

I now need to verify this response so I need the equivalent of the GSS_VerifyMIC() function call to which I then feed this DNS response (adjusted as per normal TSIG validation rules) as the message and the signature from the TSIG RR as the message token. Is this implemented anywhere?

from gokrb5.

jcmturner avatar jcmturner commented on August 22, 2024

Excellent news! Did you just have to set the flag here...

Checksum: newAuthenticatorChksum([]int{GSS_C_INTEG_FLAG, GSS_C_CONF_FLAG}),

If so then check out the updates I have put on the issue-65 branch.

You can now use gssapi.NewAPREQMechToken method to create a mechtoken and it takes in an []int for the GSS flags you want.
Then call the Marshal() method on the token to get it in []byte

I'll have a look at the GSS_VerifyMIC() bit.

from gokrb5.

bodgit avatar bodgit commented on August 22, 2024

Sadly no, that change doesn't seem to have any effect whether I pass in GSS_C_MUTUAL_FLAG or not. I ended up applying this:

diff --git a/gssapi/krb5Token.go b/gssapi/krb5Token.go
index acc8f50..da978d8 100644
--- a/gssapi/krb5Token.go
+++ b/gssapi/krb5Token.go
@@ -141,6 +141,7 @@ func NewAPREQMechToken(creds credentials.Credentials, tkt messages.Ticket, sessi
        if err != nil {
                return m, err
        }
+       types.SetFlag(&APReq.APOptions, GSS_C_MUTUAL_FLAG)
        m.APReq = APReq
        tb, err = APReq.Marshal()
        return m, nil

With this change it works.

Edit: I also set GSS_C_REPLAY_FLAG too, the RFC mentions that should be enabled as well.

from gokrb5.

jcmturner avatar jcmturner commented on August 22, 2024

I have update the method for the generating the AP_REQ mech token to take input of both GSSAPI flags and AP Option flags. This is on the issue-65 branch:

func NewAPREQMechToken(creds credentials.Credentials, tkt messages.Ticket, sessionKey types.EncryptionKey, GSSAPIFlags []int, APOptions []int) (MechToken, error) {

Does this give you what you need to integrate? If so, I will raise a PR into master.

from gokrb5.

bodgit avatar bodgit commented on August 22, 2024

Yes, that seems to work fine, using something like:

apreq, err := gssapi.NewAPREQMechToken(*cl.Credentials, tkt, key, []int{gssapi.GSS_C_INTEG_FLAG, gssapi.GSS_C_CONF_FLAG}, []int{gssapi.GSS_C_MUTUAL_FLAG, gssapi.GSS_C_REPLAY_FLAG})

I still get a successful AP_REP back from the server. Have you managed to have any luck with the GSS_{Get,Verify}MIC() side of things?

from gokrb5.

jcmturner avatar jcmturner commented on August 22, 2024

I've merged the branch into master.

I've not been able to look into the GSS_{Get,Verify}MIC() part.

from gokrb5.

Shastick avatar Shastick commented on August 22, 2024

Hello,
(As I have a question similar to bodgit I'll post here rather than open a new issue).

I can successfully* use gokrb5 to authenticate to a service (hadoop via hrpc, in this case, through NewNegTokenInitKrb5) but am left wondering how to authenticate the challenge sent back by the server. As bodgit, I've been looking around the code, but might have missed something (I'm relatively new to kerberos, so I might be greping the wrong terms?)

Anyway, many thanks for this library, it's been very helpful so far!

*edit: by successfully I mean that the server is happy with the received token, but I currently can't complete the handshake

from gokrb5.

jcmturner avatar jcmturner commented on August 22, 2024

I implemented just enough of GSSAPI to support HTTP SPNEGO authentication so there isn't a full implementation. Therefore the code may not have what you need. GSSAPI is not an area I know in depth so help is appreciated.

from gokrb5.

Shastick avatar Shastick commented on August 22, 2024

Thanks for the info. I have a few days to play around with this. I hope something useful will come out of it :)

from gokrb5.

Shastick avatar Shastick commented on August 22, 2024

I made some progress. Before I try to distillate this into a pull request, I had a question: is there any reason you chose to generate a subkey when building the Authenticator?

In gssapi.krb5Token.go's NewAuthenticator function, GenerateSeqNumberAndSubKey is called on auth.

Is there any particular reason a subkey is generated (as this is not required per the RFC, at lest these pertaining to GSSApi related exchanges) instead of using the session key, and the sequence number regenerated? (NewAuthenticator already takes care of setting SeqNumber).

Just wondering. It took me some time to realise the session key was not used for the checksums :)

from gokrb5.

jcmturner avatar jcmturner commented on August 22, 2024

Good question. I think I may have wrote the method GenerateSeqNumberAndSubKey before doing the GSSAPI work. Perhaps there should be a GenerateSeqNumber method and for this to be used. I did a lot of the GSSAPI work by inspecting wireshark traces. The RFC was not the easiest to follow ;-)

It would be great if you could pull a PR together and we can continue the discussion there. Please note I recently adding contributing guidelines.

from gokrb5.

bodgit avatar bodgit commented on August 22, 2024

I managed to create https://github.com/bodgit/tsig which works as expected however, because of the lack of MIC tokens, I ended up using https://github.com/apcera/gssapi for the GSSAPI bit. This however doesn't implement any of the Kerberos-specific functions for setting up the session with credentials so I have to make sure I've run kinit appropriately beforehand. It also relies on CGo which has become a problem in the project I'd like to ultimately use my code with so I'm back to looking at this library again.

Thanks to the work @Shastick has done with adding support for Wrap tokens, the combination of their code, the RFC and the MIT source, I should hopefully be able to add the missing MIC token support.

from gokrb5.

bodgit avatar bodgit commented on August 22, 2024

So I think I've finally gotten MIC token support implemented in #238 based heavily on the original Wrap token support added in #83 by @Shastick.

However, it doesn't work. As per #65 (comment) I have a small library to do all of the DNS heavy lifting and I have made a branch with an implementation based on this library (including my pending PR) here: https://github.com/bodgit/tsig/blob/1a0a2b75fddcc4d78d84593b7faf31b7c298c1b0/gss/gokrb5.go ; compiling with CGO_ENABLED=0 uses this new implementation. I have a small test application using the example in the docs that just tries to create/update a DNS record on a Windows 2012r2 server.

The initial negotiation exchange works ok, but when I try and sign a DNS update using a token, the server rejects it, no useful logging apart from the DNS-level rcode. If I recompile to use the original Apcera-based GSSAPI implementation again then it works and so I've made packet captures from both implementations to try and spot what's different in the packets; Wireshark is unable to fully decipher the specifics of the tokens so I'm just left comparing arrays of bytes however I can see some obvious differences:

  1. In the Apcera implementation, the AcceptorSubkey flag on the token is set (0x04), I've tried manually setting that flag in my code but that made no difference, I suspect I might need to create a subkey?
  2. The Apcera implementation is setting the sequence number field in the token, whereas my code doesn't and I'm not sure how I can even do that, I can't see where to generate them.

I think I have the implementation mostly correct though; the token is the same size in both cases, has the same ID field and filler bytes and if I verify the token I generate before sending it, it verifies OK so the crypto looks sound, I think it's possibly just this lack of a sequence number and perhaps the flag.

Any ideas?

from gokrb5.

bodgit avatar bodgit commented on August 22, 2024

Okay, I think I've figured it out. What I wasn't doing was extracting the encrypted part of the KRB_AP_REP response and using the key contained within for the subsequent signing, it took some guesswork and poring over other Kerberos implementations to work out what I should be doing.

It turns out I do need to set the AcceptorSubkey flag on the token but so far I've not explicitly set any sequence number and it seems to work, my original MIC token implementation seems to be behaving so I will add the missing documentation and tests if you're happy with the implementation.

from gokrb5.

jcmturner avatar jcmturner commented on August 22, 2024

@bodgit thanks for all the hard work on this. I know what it's like poring over wireshark traces to figure this kind of thing out. I'll take a look at the current PR, however I won't be able to merge until there is test coverage.

from gokrb5.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.