Comments (18)
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.
The flags are set on this line:
Line 137 in b762b59
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.
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:
Line 55 in b762b59
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.
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.
Excellent news! Did you just have to set the flag here...
Line 137 in b762b59
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.
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.
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:
Line 126 in 9ee2b4a
Does this give you what you need to integrate? If so, I will raise a PR into master.
from gokrb5.
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.
I've merged the branch into master.
I've not been able to look into the GSS_{Get,Verify}MIC() part.
from gokrb5.
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.
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.
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.
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.
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.
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.
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:
- 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? - 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.
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.
@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)
- is POST supported or just GET? HOT 1
- How can I get a kvno for keytab, by a separate request, or in another way? HOT 1
- Getting valid service KVNO HOT 2
- dns_canonicalize_hostname is not always a boolean value
- [bug]: out of range
- Principal name parsing does not handle backslash escapes/quoting HOT 1
- LoadCCache does not check file length
- Encryption Type Parsing of krb5.conf - Case Sensitive
- Go 1.20.x: Inconsistent LookupCNAME breaks TGS Exchange (KDC_ERR_S_PRINCIPAL_UNKNOWN)
- Use of hard-coded security token reported by a static analysis tool
- Decrypting ticket on initiator/client side
- Correctly-working redirects being interpreted as redirect loops in SPNEGO HTTP client.
- client login using keytab file
- Creating a keytabFile for FreeIPA HOT 2
- KVN is always 0 HOT 2
- No way to change context flags for SPNEGO's AP REQ
- Unable to pass authentication HOT 2
- Should `DNSLookupKDC` default to `true` to match `man krb5.conf` ? HOT 2
- SPNEGO validation error: defective token detected: PAC Info Buffers does not contain a KerbValidationInfo HOT 2
- Enhancement proposal: support dialer for TCP
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from gokrb5.