s4 dns: Handle GSS-TSIG signature creation
authorKai Blin <kai@samba.org>
Wed, 5 Sep 2012 06:34:04 +0000 (08:34 +0200)
committerKai Blin <kai@samba.org>
Wed, 5 Sep 2012 17:02:17 +0000 (19:02 +0200)
source4/dns_server/dns_crypto.c
source4/dns_server/dns_server.c
source4/dns_server/dns_server.h

index 4be61f6aac857905e90a8e484f56f14aef85f482..87d82b86a686835c83aad61c26bf4fff98134176 100644 (file)
@@ -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;
+}
index 887fc8ee1d6391109e628e78729c79aa5c587cca..795b7198aa9c61909f155d261b16a68dae508b24 100644 (file)
@@ -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);
index 8007bcbc08acdff42556e7abbe0e7b62786cfaae..0093879451cc9686bf808a417ccc53055b397499 100644 (file)
@@ -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__ */