s4 dns: Handle GSS-TSIG signature creation
[Samba/bb.git] / source4 / dns_server / dns_crypto.c
blob87d82b86a686835c83aad61c26bf4fff98134176
1 /*
2 Unix SMB/CIFS implementation.
4 DNS server handler for signed packets
6 Copyright (C) 2012 Kai Blin <kai@samba.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "lib/crypto/hmacmd5.h"
24 #include "system/network.h"
25 #include "librpc/ndr/libndr.h"
26 #include "librpc/gen_ndr/ndr_dns.h"
27 #include "dns_server/dns_server.h"
28 #include "libcli/util/ntstatus.h"
29 #include "auth/auth.h"
30 #include "auth/gensec/gensec.h"
32 struct dns_server_tkey *dns_find_tkey(struct dns_server_tkey_store *store,
33 const char *name)
35 struct dns_server_tkey *tkey = NULL;
36 uint16_t i = 0;
38 do {
39 struct dns_server_tkey *tmp_key = store->tkeys[i];
41 i++;
42 i %= TKEY_BUFFER_SIZE;
44 if (tmp_key == NULL) {
45 continue;
47 if (dns_name_equal(name, tmp_key->name)) {
48 tkey = tmp_key;
49 break;
51 } while (i != 0);
53 return tkey;
56 WERROR dns_sign_tsig(struct dns_server *dns,
57 TALLOC_CTX *mem_ctx,
58 struct dns_request_state *state,
59 struct dns_name_packet *packet,
60 uint16_t error)
62 WERROR werror;
63 NTSTATUS status;
64 enum ndr_err_code ndr_err;
65 time_t current_time = time(NULL);
66 DATA_BLOB packet_blob, tsig_blob, sig;
67 uint8_t *buffer = NULL;
68 size_t buffer_len = 0;
69 struct dns_server_tkey * tkey = NULL;
70 struct dns_res_rec *tsig = talloc_zero(mem_ctx, struct dns_res_rec);
72 struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx,
73 struct dns_fake_tsig_rec);
75 if (tsig == NULL) {
76 return WERR_NOMEM;
79 if (check_rec == NULL) {
80 return WERR_NOMEM;
83 tkey = dns_find_tkey(dns->tkeys, state->key_name);
84 if (tkey == NULL) {
85 /* FIXME: read up on what to do when we can't find a key */
86 return WERR_OK;
89 /* first build and verify check packet */
90 check_rec->name = talloc_strdup(check_rec, tkey->name);
91 if (check_rec->name == NULL) {
92 return WERR_NOMEM;
94 check_rec->rr_class = DNS_QCLASS_ANY;
95 check_rec->ttl = 0;
96 check_rec->algorithm_name = talloc_strdup(check_rec, tkey->algorithm);
97 if (check_rec->algorithm_name == NULL) {
98 return WERR_NOMEM;
100 check_rec->time_prefix = 0;
101 check_rec->time = current_time;
102 check_rec->fudge = 300;
103 check_rec->error = state->tsig_error;
104 check_rec->other_size = 0;
105 check_rec->other_data = NULL;
107 ndr_err = ndr_push_struct_blob(&packet_blob, mem_ctx, packet,
108 (ndr_push_flags_fn_t)ndr_push_dns_name_packet);
109 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
110 DEBUG(1, ("Failed to push packet: %s!\n",
111 ndr_errstr(ndr_err)));
112 return DNS_ERR(SERVER_FAILURE);
115 ndr_err = ndr_push_struct_blob(&tsig_blob, mem_ctx, check_rec,
116 (ndr_push_flags_fn_t)ndr_push_dns_fake_tsig_rec);
117 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
118 DEBUG(1, ("Failed to push packet: %s!\n",
119 ndr_errstr(ndr_err)));
120 return DNS_ERR(SERVER_FAILURE);
123 buffer_len = packet_blob.length + tsig_blob.length;
124 buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
125 if (buffer == NULL) {
126 return WERR_NOMEM;
129 memcpy(buffer, packet_blob.data, packet_blob.length);
130 memcpy(buffer+packet_blob.length, tsig_blob.data, tsig_blob.length);
133 status = gensec_sign_packet(tkey->gensec, mem_ctx, buffer, buffer_len,
134 buffer, buffer_len, &sig);
135 if (!NT_STATUS_IS_OK(status)) {
136 return ntstatus_to_werror(status);
139 tsig->name = talloc_strdup(tsig, check_rec->name);
140 if (tsig->name == NULL) {
141 return WERR_NOMEM;
143 tsig->rr_class = check_rec->rr_class;
144 tsig->rr_type = DNS_QTYPE_TSIG;
145 tsig->ttl = 0;
146 tsig->length = UINT16_MAX;
147 tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig,
148 check_rec->algorithm_name);
149 tsig->rdata.tsig_record.time_prefix = check_rec->time_prefix;
150 tsig->rdata.tsig_record.time = check_rec->time;
151 tsig->rdata.tsig_record.fudge = check_rec->fudge;
152 tsig->rdata.tsig_record.error = state->tsig_error;
153 tsig->rdata.tsig_record.original_id = packet->id;
154 tsig->rdata.tsig_record.other_size = 0;
155 tsig->rdata.tsig_record.other_data = NULL;
156 tsig->rdata.tsig_record.mac_size = sig.length;
157 tsig->rdata.tsig_record.mac = talloc_memdup(tsig, sig.data, sig.length);
160 if (packet->arcount == 0) {
161 packet->additional = talloc_zero(mem_ctx, struct dns_res_rec);
162 if (packet->additional == NULL) {
163 return WERR_NOMEM;
166 packet->additional = talloc_realloc(mem_ctx, packet->additional,
167 struct dns_res_rec,
168 packet->arcount + 1);
169 if (packet->additional == NULL) {
170 return WERR_NOMEM;
173 werror = dns_copy_tsig(mem_ctx, tsig,
174 &packet->additional[packet->arcount]);
175 if (!W_ERROR_IS_OK(werror)) {
176 return werror;
178 packet->arcount++;
180 return WERR_OK;