s4 dns: Verify incoming TSIG signatures
authorKai Blin <kai@samba.org>
Mon, 3 Sep 2012 06:06:55 +0000 (08:06 +0200)
committerKai Blin <kai@samba.org>
Wed, 5 Sep 2012 06:41:23 +0000 (08:41 +0200)
source4/dns_server/dns_crypto.c
source4/dns_server/dns_server.c
source4/dns_server/dns_server.h
source4/dns_server/dns_utils.c

index ab422221c05cc8a0490e01be0da77c0ec9c168e8..74cbffb2525c910249e7a3d15736446dc8f63398 100644 (file)
@@ -89,14 +89,22 @@ struct dns_server_tkey *dns_find_tkey(struct dns_server_tkey_store *store,
 }
 
 WERROR dns_verify_tsig(struct dns_server *dns,
+                      TALLOC_CTX *mem_ctx,
                       struct dns_request_state *state,
                       struct dns_name_packet *packet)
 {
        WERROR werror;
+       NTSTATUS status;
+       enum ndr_err_code ndr_err;
        bool found_tsig = false;
-       uint16_t i, mac_length = 0;
+       uint16_t i;
+       DATA_BLOB packet_blob, tsig_blob, sig;
+       uint8_t *buffer = NULL;
+       size_t buffer_len = 0;
        struct dns_server_tkey *tkey = NULL;
-       uint8_t *mac = NULL;
+       struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx,
+                       struct dns_fake_tsig_rec);
+
 
        /* Find the first TSIG record in the additional records */
        for (i=0; i < packet->arcount; i++) {
@@ -110,6 +118,7 @@ WERROR dns_verify_tsig(struct dns_server *dns,
                return WERR_OK;
        }
 
+       DEBUG(0, ("got here\n"));
        /* The TSIG record needs to be the last additional record */
        if (found_tsig && i + 1 != packet->arcount) {
                DEBUG(0, ("TSIG record not the last additional record!\n"));
@@ -119,12 +128,16 @@ WERROR dns_verify_tsig(struct dns_server *dns,
        /* We got a TSIG, so we need to sign our reply */
        state->sign = true;
 
-       state->tsig = talloc_zero(state, struct dns_res_rec);
-       W_ERROR_HAVE_NO_MEMORY(state->tsig);
+       state->tsig = talloc_zero(mem_ctx, struct dns_res_rec);
+       if (state->tsig == NULL) {
+               return WERR_NOMEM;
+       }
 
        werror = dns_copy_tsig(state->tsig, &packet->additional[i],
                               state->tsig);
-       W_ERROR_NOT_OK_RETURN(werror);
+       if (!W_ERROR_IS_OK(werror)) {
+               return werror;
+       }
 
        packet->arcount--;
 
@@ -135,10 +148,74 @@ WERROR dns_verify_tsig(struct dns_server *dns,
        }
 
        /* FIXME: check TSIG here */
+       if (check_rec == NULL) {
+               return WERR_NOMEM;
+       }
 
-       dump_data(1, mac, mac_length);
-       state->tsig_error = DNS_RCODE_BADKEY;
-       return DNS_ERR(NOTAUTH);
+       /* first build and verify check packet */
+       check_rec->name = talloc_strdup(check_rec, tkey->name);
+       if (check_rec->name == NULL) {
+               return WERR_NOMEM;
+       }
+       check_rec->rr_class = DNS_QCLASS_ANY;
+       check_rec->ttl = 0;
+       check_rec->algorithm_name = talloc_strdup(check_rec, tkey->algorithm);
+       if (check_rec->algorithm_name == NULL) {
+               return WERR_NOMEM;
+       }
+       check_rec->time_prefix = 0;
+       check_rec->time = state->tsig->rdata.tsig_record.time;
+       check_rec->fudge = state->tsig->rdata.tsig_record.fudge;
+       check_rec->error = 0;
+       check_rec->other_size = 0;
+       check_rec->other_data = NULL;
+
+       ndr_err = ndr_push_struct_blob(&packet_blob, mem_ctx, packet,
+               (ndr_push_flags_fn_t)ndr_push_dns_name_packet);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(1, ("Failed to push packet: %s!\n",
+                         ndr_errstr(ndr_err)));
+               return DNS_ERR(SERVER_FAILURE);
+       }
+
+       ndr_err = ndr_push_struct_blob(&tsig_blob, mem_ctx, check_rec,
+               (ndr_push_flags_fn_t)ndr_push_dns_fake_tsig_rec);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(1, ("Failed to push packet: %s!\n",
+                         ndr_errstr(ndr_err)));
+               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;
+       }
+
+       memcpy(buffer, packet_blob.data, packet_blob.length);
+       memcpy(buffer, tsig_blob.data, tsig_blob.length);
+
+       sig.length = state->tsig->rdata.tsig_record.mac_size;
+       sig.data = talloc_memdup(mem_ctx, state->tsig->rdata.tsig_record.mac, sig.length);
+       if (sig.data == NULL) {
+               return WERR_NOMEM;
+       }
+
+
+       status = gensec_check_packet(tkey->gensec, buffer, buffer_len,
+                                   buffer, buffer_len, &sig);
+       if (NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
+               return DNS_ERR(BADKEY);
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("Verifying tsig failed: %s\n", nt_errstr(status)));
+               return ntstatus_to_werror(status);
+       }
+
+       state->authenticated = true;
+
+       return WERR_OK;
 }
 
 WERROR dns_sign_tsig(struct dns_server *dns,
index cd121f9d8b2418471053db35383a3d5ec0ac74c1..6b78b6d568eec1b690a05711a140411765f83812 100644 (file)
@@ -145,7 +145,13 @@ static struct tevent_req *dns_process_send(TALLOC_CTX *mem_ctx,
                NDR_PRINT_DEBUG(dns_name_packet, &state->in_packet);
        }
 
-       ret = dns_verify_tsig(dns, &state->state, &state->in_packet);
+       ret = dns_verify_tsig(dns, state, &state->state, &state->in_packet);
+       if (!W_ERROR_IS_OK(ret)) {
+               DEBUG(0, ("Bailing out early!\n"));
+               state->dns_err = werr_to_dns_err(ret);
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
 
        state->state.flags = state->in_packet.operation;
        state->state.flags |= DNS_FLAG_REPLY;
index 6cd60267c00dd6e50b87ac8e49a53ae8eea2715a..f807cd1536a9626e75c66e14b30d695d69ce16ff 100644 (file)
@@ -110,6 +110,7 @@ WERROR dns_name2dn(struct dns_server *dns,
 struct dns_server_tkey *dns_find_tkey(struct dns_server_tkey_store *store,
                                      const char *name);
 WERROR dns_verify_tsig(struct dns_server *dns,
+                      TALLOC_CTX *mem_ctx,
                       struct dns_request_state *state,
                       struct dns_name_packet *packet);
 WERROR dns_sign_tsig(struct dns_server *dns,
index 7d95bdd1518a1da409c4c7e4f73caee053135081..11ded6820487af5c450c5763af7d4515a019448d 100644 (file)
@@ -54,6 +54,8 @@ uint8_t werr_to_dns_err(WERROR werr)
                return DNS_RCODE_NOTAUTH;
        } else if (W_ERROR_EQUAL(DNS_ERR(NOTZONE), werr)) {
                return DNS_RCODE_NOTZONE;
+       } else if (W_ERROR_EQUAL(DNS_ERR(BADKEY), werr)) {
+               return DNS_RCODE_BADKEY;
        }
        DEBUG(5, ("No mapping exists for %s\n", win_errstr(werr)));
        return DNS_RCODE_SERVFAIL;