From 7fe5e2cdcb17cee06ebde2717439c0aa964ac026 Mon Sep 17 00:00:00 2001 From: Kai Blin Date: Wed, 5 Sep 2012 08:34:04 +0200 Subject: [PATCH] s4 dns: Handle GSS-TSIG signature creation --- source4/dns_server/dns_crypto.c | 127 ++++++++++++++++++++++++++++++++++++++++ source4/dns_server/dns_server.c | 12 ++++ source4/dns_server/dns_server.h | 5 ++ 3 files changed, 144 insertions(+) diff --git a/source4/dns_server/dns_crypto.c b/source4/dns_server/dns_crypto.c index 4be61f6aac8..87d82b86a68 100644 --- a/source4/dns_server/dns_crypto.c +++ b/source4/dns_server/dns_crypto.c @@ -52,3 +52,130 @@ struct dns_server_tkey *dns_find_tkey(struct dns_server_tkey_store *store, return tkey; } + +WERROR dns_sign_tsig(struct dns_server *dns, + TALLOC_CTX *mem_ctx, + struct dns_request_state *state, + struct dns_name_packet *packet, + uint16_t error) +{ + WERROR werror; + NTSTATUS status; + enum ndr_err_code ndr_err; + time_t current_time = time(NULL); + DATA_BLOB packet_blob, tsig_blob, sig; + uint8_t *buffer = NULL; + size_t buffer_len = 0; + struct dns_server_tkey * tkey = NULL; + struct dns_res_rec *tsig = talloc_zero(mem_ctx, struct dns_res_rec); + + struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx, + struct dns_fake_tsig_rec); + + if (tsig == NULL) { + return WERR_NOMEM; + } + + if (check_rec == NULL) { + return WERR_NOMEM; + } + + tkey = dns_find_tkey(dns->tkeys, state->key_name); + if (tkey == NULL) { + /* FIXME: read up on what to do when we can't find a key */ + return WERR_OK; + } + + /* 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 = current_time; + check_rec->fudge = 300; + check_rec->error = state->tsig_error; + 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+packet_blob.length, tsig_blob.data, tsig_blob.length); + + + status = gensec_sign_packet(tkey->gensec, mem_ctx, buffer, buffer_len, + buffer, buffer_len, &sig); + if (!NT_STATUS_IS_OK(status)) { + return ntstatus_to_werror(status); + } + + tsig->name = talloc_strdup(tsig, check_rec->name); + if (tsig->name == NULL) { + return WERR_NOMEM; + } + tsig->rr_class = check_rec->rr_class; + tsig->rr_type = DNS_QTYPE_TSIG; + tsig->ttl = 0; + tsig->length = UINT16_MAX; + tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig, + check_rec->algorithm_name); + tsig->rdata.tsig_record.time_prefix = check_rec->time_prefix; + tsig->rdata.tsig_record.time = check_rec->time; + tsig->rdata.tsig_record.fudge = check_rec->fudge; + tsig->rdata.tsig_record.error = state->tsig_error; + tsig->rdata.tsig_record.original_id = packet->id; + tsig->rdata.tsig_record.other_size = 0; + tsig->rdata.tsig_record.other_data = NULL; + tsig->rdata.tsig_record.mac_size = sig.length; + tsig->rdata.tsig_record.mac = talloc_memdup(tsig, sig.data, sig.length); + + + if (packet->arcount == 0) { + packet->additional = talloc_zero(mem_ctx, struct dns_res_rec); + if (packet->additional == NULL) { + return WERR_NOMEM; + } + } + packet->additional = talloc_realloc(mem_ctx, packet->additional, + struct dns_res_rec, + packet->arcount + 1); + if (packet->additional == NULL) { + return WERR_NOMEM; + } + + werror = dns_copy_tsig(mem_ctx, tsig, + &packet->additional[packet->arcount]); + if (!W_ERROR_IS_OK(werror)) { + return werror; + } + packet->arcount++; + + return WERR_OK; +} diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c index 887fc8ee1d6..795b7198aa9 100644 --- a/source4/dns_server/dns_server.c +++ b/source4/dns_server/dns_server.c @@ -98,6 +98,7 @@ static void dns_tcp_send(struct stream_connection *conn, uint16_t flags) struct dns_process_state { DATA_BLOB *in; + struct dns_server *dns; struct dns_name_packet in_packet; struct dns_request_state state; uint16_t dns_err; @@ -123,6 +124,8 @@ static struct tevent_req *dns_process_send(TALLOC_CTX *mem_ctx, } state->in = in; + state->dns = dns; + if (in->length < 12) { tevent_req_werror(req, WERR_INVALID_PARAM); return tevent_req_post(req, ev); @@ -215,6 +218,15 @@ static WERROR dns_process_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, } state->out_packet.operation |= state->state.flags; + if (state->state.sign) { + ret = dns_sign_tsig(state->dns, mem_ctx, &state->state, + &state->out_packet, 0); + if (!W_ERROR_IS_OK(ret)) { + state->dns_err = DNS_RCODE_SERVFAIL; + goto drop; + } + } + ndr_err = ndr_push_struct_blob( out, mem_ctx, &state->out_packet, (ndr_push_flags_fn_t)ndr_push_dns_name_packet); diff --git a/source4/dns_server/dns_server.h b/source4/dns_server/dns_server.h index 8007bcbc08a..0093879451c 100644 --- a/source4/dns_server/dns_server.h +++ b/source4/dns_server/dns_server.h @@ -109,6 +109,11 @@ WERROR dns_name2dn(struct dns_server *dns, struct ldb_dn **_dn); struct dns_server_tkey *dns_find_tkey(struct dns_server_tkey_store *store, const char *name); +WERROR dns_sign_tsig(struct dns_server *dns, + TALLOC_CTX *mem_ctx, + struct dns_request_state *state, + struct dns_name_packet *packet, + uint16_t error); #define DNS_ERR(err_str) WERR_DNS_ERROR_RCODE_##err_str #endif /* __DNS_SERVER_H__ */ -- 2.11.4.GIT