During signed dyn. DNS updates with the internal DNS server a TSIG error is flagged

Günter Kukkukk linux at kukkukk.com
Sat Feb 9 23:10:01 MST 2013


Hi all,

in the following i'll describe my findings about the dyn. DNS update
failure message, seen when the internal DNS is used.

Be warned - it will be a longer story, cause when i started the investigation
i had only very few background knowledge about dns and crypto at all.
Again - all this is only related to the internal DNS server!
-------
In our wiki in the samba-4.x.x howto, section "Testing/Debugging Dynamic DNS Updates"
we offer a test to check dyn. DNS updates:

/usr/local/samba/sbin/samba_dnsupdate --verbose --all-names

Lots of users ran that command and were/are complaining (mostly on IRC),
that they get more than 20 "error messages".

Atm samba uses the ISC supplied "nsupdate" tool, which can also be listed
as the internal default with:

samba-tool testparm --suppress-prompt -v | grep "nsupdate command"
        nsupdate command = /usr/bin/nsupdate -g

Nsupdate is run with the "-g" options, which results in "...GSS-TSIG uses 
Kerberos credentials...". One can also run that tool interactive:
nsupdate -g
> update add huhu1.intranet01.hom 86400 A 192.168.200.182
> send
; TSIG error with server: tsig verify failure
>
So the TSIG error is also seen here. Btw - the requested update _is_ done
on the dns server, but then something goes wrong .....

I now did the following:
   - downloaded the recent ISC bind source package (i do that for years)
   - installed all krb5 related debug packages
   - built and installed the bind package to be able to run
     nsupdate inside gdb with all sources available
   - added DEBUG (); and some dump-hex-and ascii stuff at some places
     in some samba sources (mainly dsn_crypto.c and gensec_gssapi.c, ...)

During my first gdb sessions (btw - i used DDD as the gui-frontend), i
realized that "this crypto stuff can easily drive you crazy :-) cause
gdb got deeper and deeper .... and deeper into krb5 libraries.

A big THANKS here to abartlet, who noticed on IRC "...Guenter, look at the
"plain" data ... on both the samba and the ISC nsupdate side...".

The following was one of the first stuff i put into my internal docu:
GKKKK: gensec_gssapi_check_packet: sig
 data: 0x85ffce0 len: 28
[0000] 04 04 04 FF FF FF FF FF   00 00 00 00 07 8F B9 C0   ........ ........
[0010] 68 B6 A0 E6 1A 9F B6 32   58 C1 C9 19              h......2 X...
CCC2: input_message: value: 0x8aa1f90 len: 123
CCC3: input_token: value: 0x85ffce0 len: 28
CCC4: input_message: value: 0x8aa1f90 len: 123
CCC5: input_token:   value: 0x85ffce0 len: 28    (REQUEST FROM CLIENT checked here)

GKKKK: gensec_gssapi_check_packet: MESSAGE
 data: 0x8aa1f90 len: 123
[0000] F1 42 28 00 00 01 00 00   00 01 00 00 0A 69 6E 74   .B(..... .....int
[0010] 72 61 6E 65 74 30 31 03   68 6F 6D 00 00 06 00 01   ranet01. hom.....
[0020] 06 68 75 68 75 38 38 C0   0C 00 01 00 01 00 01 51   .huhu88. .......Q
[0030] 80 00 04 C0 A8 C8 BC 0A   33 39 31 38 30 39 31 39   ........ 39180919
[0040] 37 38 0C 73 69 67 2D 6C   69 6E 75 78 33 30 30 0A   78.sig-l inux300.
[0050] 69 6E 74 72 61 6E 65 74   30 31 03 68 6F 6D 00 00   intranet 01.hom..
[0060] FF 00 00 00 00 08 67 73   73 2D 74 73 69 67 00 00   ......gs s-tsig..
[0070] 00 50 FB 61 73 01 2C 00   00 00 00                 .P.ar.,. ...

(gdb) x/169xb plain.data
0x81492d8:    0x00    0x1c	
              0x04    0x04    0x04    0xff    0xff    0xff
0x81492e0:    0xff    0xff    0x00    0x00    0x00    0x00    0x07    0x8f
0x81492e8:    0xb9    0xc0    0x68    0xb6    0xa0    0xe6    0x1a    0x9f
0x81492f0:    0xb6    0x32    0x58    0xc1    0xc9    0x19
              0xf1    0x42
0x81492f8:    0xa8    0x80    0x00    0x01    0x00    0x00    0x00    0x01
              ^^^^^   ^^^^^
0x8149300:    0x00    0x00    0x0a    0x69    0x6e    0x74    0x72    0x61
0x8149308:    0x6e    0x65    0x74    0x30    0x31    0x03    0x68    0x6f
0x8149310:    0x6d    0x00    0x00    0x06    0x00    0x01    0x06    0x68
0x8149318:    0x75    0x68    0x75    0x38    0x38    0xc0    0x0c    0x00
0x8149320:    0x01    0x00    0x01    0x00    0x01    0x51    0x80    0x00
0x8149328:    0x04    0xc0    0xa8    0xc8    0xbc    0x0a    0x33    0x39
0x8149330:    0x31    0x38    0x30    0x39    0x31    0x39    0x37    0x38
0x8149338:    0x0c    0x73    0x69    0x67    0x2d    0x6c    0x69    0x6e
0x8149340:    0x75    0x78    0x33    0x30    0x30    0x0a    0x69    0x6e
0x8149348:    0x74    0x72    0x61    0x6e    0x65    0x74    0x30    0x31
0x8149350:    0x03    0x68    0x6f    0x6d    0x00    0x00    0xff    0x00
0x8149358:    0x00    0x00    0x00    0x08    0x67    0x73    0x73    0x2d
0x8149360:    0x74    0x73    0x69    0x67    0x00    0x00    0x00    0x50
0x8149368:    0xfb    0x61    0x73    0x01    0x2c    0x00    0x00    0x00
0x8149370:    0x00
                      0x04    0x04    0x05    0xff    0xff    0xff    0xff
0x8149378:    0xff    0x00    0x00    0x00    0x00    0x0f    0xb9    0xb6
0x8149380:    0xd9
(gdb) 
-
BBB2: input: value: 0x847a420 len: 125
BBB3: input;  value: 0x847a420 len: 125
BBB4: output; value: 0x8728fc8 len: 28
GKKKK: gensec_gssapi_sign_packet: INPUT     (SIGNED RESPONSE)
 data: 0x847a420 len: 125
[0000] F1 42 A8 80 00 01 00 00   00 01 00 00 0A 69 6E 74   .B...... .....int
[0010] 72 61 6E 65 74 30 31 03   68 6F 6D 00 00 06 00 01   ranet01. hom.....
[0020] 06 68 75 68 75 38 38 C0   0C 00 01 00 01 00 01 51   .huhu88. .......Q
[0030] 80 00 04 C0 A8 C8 BC 0A   33 39 31 38 30 39 31 39   ........ 39180919
[0040] 37 38 0C 73 69 67 2D 6C   69 6E 75 78 33 30 30 0A   78.sig-l inux300.
[0050] 69 6E 74 72 61 6E 65 74   30 31 03 68 6F 6D 00 00   intranet 01.hom..
[0060] FF 00 00 00 00 08 67 73   73 2D 74 73 69 67 00 00   ......gs s-tsig..
[0070] 00 50 FB 61 73 01 2C 00   00 00 00 00 00           .P.as.,. .....
GKKKK: gensec_gssapi_sign_packet: sig
 data: 0x8ee6770 len: 28
[0000] 04 04 05 FF FF FF FF FF   00 00 00 00 0F B9 B6 D9   ........ ........
                                 ======================= seqnum
[0010] DF 71 D4 93 3A AB D6 0F   E8 E8 33 FD              .q..:... ..3.   <==checksum
--------------------
The difference between both sides was obvious:
  - there were 2 + 28 = 30 additional bytes at the beginning
    (later i realized that "0x00    0x1c" is 28 in big-endian ..)
    I also realized that the 28 bytes were the MIC from the _former_ request!
  - 16 additonal bytes at the end

Fortunately http://tools.ietf.org/html/rfc4121 describes in section
   4.2.4.  Encryption and Checksum Operations
that the first 16 bytes of the new signed MIC are added at the end of "plain" data.
Btw - the remaining 12 MIC bytes are the checksum itself.

Puuuh, so the trailing 16 bytes were explained now!  :-)
But WHY was the former request MIC (and the big-endian length) placed
in front of the plain data?  &%(&/@%&( - grumble.

I started hours of reading RFCs and other docus ......
This one caught me attention:
http://docs.oracle.com/cd/E19253-01/816-4863/overview-49/index.html
At the bottom in "Confirming Message Transmission in GSS-API" both
sign and seal cases are described. In the signed MIC only case in step 4:
     4. The acceptor sends the MIC back to the initiator.

Okay, but HOW is that done in the DNS case?

I also remembered that i did not understand, which info was used
to create the struct dns_fake_tsig_rec inside samba ... ?

Again hours of searching for RFCs to explain that.....
I knew that the smb/cifs protocol was not really well documented
over years - but i couldn't believe that the same could be true
for the DNS (crypto) protocol.
In my head thoughts like "you are just to stupid to find the right
RFCs" were rotating.

Aaah, why not looking more carefully at the sources from ISC?
They had crafted the plain buffer, which was then send deep
down to the krb5 checksum verify function gss_verify_mic().
When not me, then the devs at ISC had surely a RFC which they
used during coding:

In tsig.c ---> dns_tsig_verify()
.....
	if (tsig.siglen > 0) {
		sig_r.base = tsig.signature;
		sig_r.length = tsig.siglen;

		ret = dst_context_create(key, mctx, &ctx);
		if (ret != ISC_R_SUCCESS)
			return (ret);

		if (response) {
			isc_buffer_init(&databuf, data, sizeof(data));
			isc_buffer_putuint16(&databuf, querytsig.siglen); !!!!!!
			isc_buffer_usedregion(&databuf, &r);
			ret = dst_context_adddata(ctx, &r);
			if (ret != ISC_R_SUCCESS)
				goto cleanup_context;
			if (querytsig.siglen > 0) {
				r.length = querytsig.siglen;    !!!!!!
				r.base = querytsig.signature;   !!!!!!
				ret = dst_context_adddata(ctx, &r);
				if (ret != ISC_R_SUCCESS)
					goto cleanup_context;
			}
		}
..........
Oh well, as the very first bytes they put the former query (!) MIC data
into the plain buffer (was expected, cause my "gdb nsupdate"
sessions were showing that always).

That was the time when i added my first UGLY test code to samba:
  - when the update request was seen, i just copied THAT MIC 
    into a static buffer (Oh well...) Ugly as ugly can be ...
  - later when samba was crafting the to be signed buffer response
    buffer, i just copied the formerly saved MIC back in front of it.

WOW - that infamous
   ; TSIG error with server: tsig verify failure
WAS GONE!
 
I wrote a 2nd UGLY test .... posted it somewhere to a pastebin....
   http://pastie.org/6100631
to let Andrew and Kai have a look.
This code was _never_ meant as a patch to the samba source!

But the best news FOR ME is, that i think that today (!!!) i found the RFC
where all that is written down:
   http://www.ietf.org/rfc/rfc2845.txt

Could have saved me hours - but i don't count them .... :-)

I hope, that these findings can help to improve the samba code.

Cheers, Günter

Remember - you have been warned, that this story would possibly
never end ....


More information about the samba-technical mailing list