From 27b732f6bfcdcd96fd76e89c624f5f18ca944531 Mon Sep 17 00:00:00 2001
From: Guenter Kukkukk <kukks@samba.org>
Date: Sat, 16 Feb 2013 16:53:16 +0100
Subject: [PATCH] Fix internal DNS dyn. update, seen as: ; TSIG error with server: tsig verify failure

The dns update signed response must be handled differently than the TKEY response

Signed-off-by: Guenter Kukkukk <kukks@samba.org>
---
 source4/dns_server/dns_crypto.c |   44 ++++++++++++++++++++++++++++++++------
 1 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/source4/dns_server/dns_crypto.c b/source4/dns_server/dns_crypto.c
index 7604a05..71adf68 100644
--- ./source4/dns_server/dns_crypto.c
+++ ./source4/dns_server/dns_crypto.c
@@ -244,6 +244,8 @@ WERROR dns_sign_tsig(struct dns_server *dns,
 	DATA_BLOB packet_blob, tsig_blob, sig;
 	uint8_t *buffer = NULL;
 	size_t buffer_len = 0;
+	size_t miclen_bytes = 0;
+	size_t mic_size = 0;
 	struct dns_server_tkey * tkey = NULL;
 	struct dns_res_rec *tsig = talloc_zero(mem_ctx, struct dns_res_rec);
 
@@ -298,16 +300,44 @@ WERROR dns_sign_tsig(struct dns_server *dns,
 		return DNS_ERR(SERVER_FAILURE);
 	}
 
-	buffer_len = packet_blob.length + tsig_blob.length;
-	buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
-	if (buffer == NULL) {
-		return WERR_NOMEM;
-	}
+	/* DNS update must be handled differently than the TKEY case */ 
+	if ((packet->operation & DNS_OPCODE) == DNS_OPCODE_UPDATE) {
+		/* Here the request MIC must be placed in front of the buffer.
+		   Calculate the length of the buffer used for the request MIC:
+		   2 bytes (sizeof uint16_t) for the length itself
+		   length bytes of the MIC (here 16 + 12 = 28 bytes) */
+		miclen_bytes = sizeof(state->tsig->rdata.tsig_record.mac_size);
+		mic_size     = miclen_bytes + state->tsig->rdata.tsig_record.mac_size;
+
+		buffer_len = mic_size + packet_blob.length + tsig_blob.length;
+		buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
+		if (buffer == NULL) {
+			return WERR_NOMEM;
+		}
 
-	memcpy(buffer, packet_blob.data, packet_blob.length);
-	memcpy(buffer+packet_blob.length, tsig_blob.data, tsig_blob.length);
+		/* copy the 2 length bytes of request MIC in big-endian order */
+		RSSVAL(buffer,0,state->tsig->rdata.tsig_record.mac_size);
 
+		/* copy the request MIC itself */
+		memcpy(buffer + miclen_bytes, state->tsig->rdata.tsig_record.mac,
+			  state->tsig->rdata.tsig_record.mac_size);
+
+		/* copy the remaining data */
+		memcpy(buffer + mic_size, packet_blob.data, packet_blob.length);
+		memcpy(buffer + mic_size + packet_blob.length, tsig_blob.data, tsig_blob.length);
+	} else {
+		buffer_len = packet_blob.length + tsig_blob.length;
+		buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
+		if (buffer == NULL) {
+			return WERR_NOMEM;
+		}
+
+		memcpy(buffer, packet_blob.data, packet_blob.length);
+		memcpy(buffer+packet_blob.length, tsig_blob.data, tsig_blob.length);
+	}
 
+	/* FIXME: as in the verify case, some padding is wrong */
+	buffer_len -=2;
 	status = gensec_sign_packet(tkey->gensec, mem_ctx, buffer, buffer_len,
 				    buffer, buffer_len, &sig);
 	if (!NT_STATUS_IS_OK(status)) {
-- 
1.7.3.4
