From f3e44c390c0082e585aec83372cdcdde19d76016 Mon Sep 17 00:00:00 2001 From: Kai Blin Date: Mon, 3 Sep 2012 08:06:55 +0200 Subject: [PATCH] s4 dns: Verify incoming TSIG signatures --- source4/dns_server/dns_crypto.c | 93 +++++++++++++++++++++++++++++++++++++---- source4/dns_server/dns_server.c | 8 +++- source4/dns_server/dns_server.h | 1 + source4/dns_server/dns_utils.c | 2 + 4 files changed, 95 insertions(+), 9 deletions(-) diff --git a/source4/dns_server/dns_crypto.c b/source4/dns_server/dns_crypto.c index ab422221c05..74cbffb2525 100644 --- a/source4/dns_server/dns_crypto.c +++ b/source4/dns_server/dns_crypto.c @@ -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, diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c index cd121f9d8b2..6b78b6d568e 100644 --- a/source4/dns_server/dns_server.c +++ b/source4/dns_server/dns_server.c @@ -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; diff --git a/source4/dns_server/dns_server.h b/source4/dns_server/dns_server.h index 6cd60267c00..f807cd1536a 100644 --- a/source4/dns_server/dns_server.h +++ b/source4/dns_server/dns_server.h @@ -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, diff --git a/source4/dns_server/dns_utils.c b/source4/dns_server/dns_utils.c index 7d95bdd1518..11ded682048 100644 --- a/source4/dns_server/dns_utils.c +++ b/source4/dns_server/dns_utils.c @@ -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; -- 2.11.4.GIT