Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : DNS server handler for signed packets
5 :
6 : Copyright (C) 2012 Kai Blin <kai@samba.org>
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "system/network.h"
24 : #include "librpc/ndr/libndr.h"
25 : #include "librpc/gen_ndr/ndr_dns.h"
26 : #include "dns_server/dns_server.h"
27 : #include "libcli/util/ntstatus.h"
28 : #include "auth/auth.h"
29 : #include "auth/gensec/gensec.h"
30 :
31 : #undef DBGC_CLASS
32 : #define DBGC_CLASS DBGC_DNS
33 :
34 615 : static WERROR dns_copy_tsig(TALLOC_CTX *mem_ctx,
35 : struct dns_res_rec *old,
36 : struct dns_res_rec *new_rec)
37 : {
38 615 : new_rec->start_ndr_offset = 0;
39 :
40 615 : new_rec->name = talloc_strdup(mem_ctx, old->name);
41 615 : W_ERROR_HAVE_NO_MEMORY(new_rec->name);
42 :
43 615 : new_rec->rr_type = old->rr_type;
44 615 : new_rec->rr_class = old->rr_class;
45 615 : new_rec->ttl = old->ttl;
46 615 : new_rec->length = old->length;
47 615 : new_rec->rdata.tsig_record.algorithm_name = talloc_strdup(mem_ctx,
48 : old->rdata.tsig_record.algorithm_name);
49 615 : W_ERROR_HAVE_NO_MEMORY(new_rec->rdata.tsig_record.algorithm_name);
50 :
51 615 : new_rec->rdata.tsig_record.time_prefix = old->rdata.tsig_record.time_prefix;
52 615 : new_rec->rdata.tsig_record.time = old->rdata.tsig_record.time;
53 615 : new_rec->rdata.tsig_record.fudge = old->rdata.tsig_record.fudge;
54 615 : new_rec->rdata.tsig_record.mac_size = old->rdata.tsig_record.mac_size;
55 615 : new_rec->rdata.tsig_record.mac = talloc_memdup(mem_ctx,
56 : old->rdata.tsig_record.mac,
57 : old->rdata.tsig_record.mac_size);
58 615 : W_ERROR_HAVE_NO_MEMORY(new_rec->rdata.tsig_record.mac);
59 :
60 615 : new_rec->rdata.tsig_record.original_id = old->rdata.tsig_record.original_id;
61 615 : new_rec->rdata.tsig_record.error = old->rdata.tsig_record.error;
62 615 : new_rec->rdata.tsig_record.other_size = old->rdata.tsig_record.other_size;
63 615 : new_rec->rdata.tsig_record.other_data = talloc_memdup(mem_ctx,
64 : old->rdata.tsig_record.other_data,
65 : old->rdata.tsig_record.other_size);
66 615 : W_ERROR_HAVE_NO_MEMORY(new_rec->rdata.tsig_record.other_data);
67 :
68 615 : return WERR_OK;
69 : }
70 :
71 1017 : struct dns_server_tkey *dns_find_tkey(struct dns_server_tkey_store *store,
72 : const char *name)
73 : {
74 1017 : struct dns_server_tkey *tkey = NULL;
75 1017 : uint16_t i = 0;
76 :
77 : do {
78 37196 : struct dns_server_tkey *tmp_key = store->tkeys[i];
79 :
80 37196 : i++;
81 37196 : i %= TKEY_BUFFER_SIZE;
82 :
83 37196 : if (tmp_key == NULL) {
84 24010 : continue;
85 : }
86 13186 : if (samba_dns_name_equal(name, tmp_key->name)) {
87 810 : tkey = tmp_key;
88 810 : break;
89 : }
90 36386 : } while (i != 0);
91 :
92 1017 : return tkey;
93 : }
94 :
95 33075 : WERROR dns_verify_tsig(struct dns_server *dns,
96 : TALLOC_CTX *mem_ctx,
97 : struct dns_request_state *state,
98 : struct dns_name_packet *packet,
99 : DATA_BLOB *in)
100 : {
101 : WERROR werror;
102 : NTSTATUS status;
103 : enum ndr_err_code ndr_err;
104 33075 : uint16_t i, arcount = 0;
105 : DATA_BLOB fake_tsig_blob, sig;
106 33075 : uint8_t *buffer = NULL;
107 33075 : size_t buffer_len = 0, packet_len = 0;
108 33075 : struct dns_server_tkey *tkey = NULL;
109 33075 : struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx,
110 : struct dns_fake_tsig_rec);
111 :
112 :
113 : /* Find the first TSIG record in the additional records */
114 33294 : for (i=0; i < packet->arcount; i++) {
115 426 : if (packet->additional[i].rr_type == DNS_QTYPE_TSIG) {
116 207 : break;
117 : }
118 : }
119 :
120 33075 : if (i == packet->arcount) {
121 : /* no TSIG around */
122 32868 : return WERR_OK;
123 : }
124 :
125 : /* The TSIG record needs to be the last additional record */
126 207 : if (i + 1 != packet->arcount) {
127 0 : DEBUG(1, ("TSIG record not the last additional record!\n"));
128 0 : return DNS_ERR(FORMAT_ERROR);
129 : }
130 :
131 : /* We got a TSIG, so we need to sign our reply */
132 207 : state->sign = true;
133 :
134 : /*
135 : * We need to keep the input packet exactly like we got it,
136 : * but we need to cut off the tsig record.
137 : *
138 : * DNS packets can not be larger than UINT16_MAX,
139 : * the size of the UDP payload is uint16_t and
140 : * there's a uint16_t length header for TCP.
141 : *
142 : * And the start offset of the last
143 : * additional record can't be larger than
144 : * the whole packet.
145 : */
146 207 : packet_len = packet->additional[i].start_ndr_offset;
147 207 : SMB_ASSERT(in->length <= UINT16_MAX);
148 207 : SMB_ASSERT(in->length > packet_len);
149 :
150 207 : state->tsig = talloc_zero(state->mem_ctx, struct dns_res_rec);
151 207 : if (state->tsig == NULL) {
152 0 : return WERR_NOT_ENOUGH_MEMORY;
153 : }
154 :
155 207 : werror = dns_copy_tsig(state->tsig, &packet->additional[i],
156 : state->tsig);
157 207 : if (!W_ERROR_IS_OK(werror)) {
158 0 : return werror;
159 : }
160 :
161 207 : packet->arcount--;
162 :
163 207 : tkey = dns_find_tkey(dns->tkeys, state->tsig->name);
164 207 : if (tkey == NULL) {
165 : /*
166 : * We must save the name for use in the TSIG error
167 : * response and have no choice here but to save the
168 : * keyname from the TSIG request.
169 : */
170 3 : state->key_name = talloc_strdup(state->mem_ctx,
171 2 : state->tsig->name);
172 2 : if (state->key_name == NULL) {
173 0 : return WERR_NOT_ENOUGH_MEMORY;
174 : }
175 2 : state->tsig_error = DNS_RCODE_BADKEY;
176 2 : return DNS_ERR(NOTAUTH);
177 : }
178 :
179 : /*
180 : * Remember the keyname that found an existing tkey, used
181 : * later to fetch the key with dns_find_tkey() when signing
182 : * and adding a TSIG record with MAC.
183 : */
184 205 : state->key_name = talloc_strdup(state->mem_ctx, tkey->name);
185 205 : if (state->key_name == NULL) {
186 0 : return WERR_NOT_ENOUGH_MEMORY;
187 : }
188 :
189 : /* FIXME: check TSIG here */
190 205 : if (check_rec == NULL) {
191 0 : return WERR_NOT_ENOUGH_MEMORY;
192 : }
193 :
194 : /* first build and verify check packet */
195 205 : check_rec->name = talloc_strdup(check_rec, tkey->name);
196 205 : if (check_rec->name == NULL) {
197 0 : return WERR_NOT_ENOUGH_MEMORY;
198 : }
199 205 : check_rec->rr_class = DNS_QCLASS_ANY;
200 205 : check_rec->ttl = 0;
201 205 : check_rec->algorithm_name = talloc_strdup(check_rec, tkey->algorithm);
202 205 : if (check_rec->algorithm_name == NULL) {
203 0 : return WERR_NOT_ENOUGH_MEMORY;
204 : }
205 205 : check_rec->time_prefix = 0;
206 205 : check_rec->time = state->tsig->rdata.tsig_record.time;
207 205 : check_rec->fudge = state->tsig->rdata.tsig_record.fudge;
208 205 : check_rec->error = 0;
209 205 : check_rec->other_size = 0;
210 205 : check_rec->other_data = NULL;
211 :
212 205 : ndr_err = ndr_push_struct_blob(&fake_tsig_blob, mem_ctx, check_rec,
213 : (ndr_push_flags_fn_t)ndr_push_dns_fake_tsig_rec);
214 205 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
215 0 : DEBUG(1, ("Failed to push packet: %s!\n",
216 : ndr_errstr(ndr_err)));
217 0 : return DNS_ERR(SERVER_FAILURE);
218 : }
219 :
220 : /*
221 : * We already asserted packet_len < UINT16_MAX
222 : * above, and struct ndr_push has alloc_size and
223 : * offset as uint32_t, so it's really unlikely
224 : * to overflow buffer_len. For SIZE_MAX == UINT32_MAX,
225 : * the following check might be important, but
226 : * just lets do it always.
227 : */
228 205 : if (fake_tsig_blob.length > (SIZE_MAX - UINT16_MAX)) {
229 0 : DBG_WARNING("fake_tsig_blob.length=%zu too large!\n",
230 : fake_tsig_blob.length);
231 0 : return DNS_ERR(SERVER_FAILURE);
232 : }
233 205 : buffer_len = packet_len + fake_tsig_blob.length;
234 205 : buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
235 205 : if (buffer == NULL) {
236 0 : return WERR_NOT_ENOUGH_MEMORY;
237 : }
238 :
239 205 : memcpy(buffer, in->data, packet_len);
240 205 : memcpy(buffer + packet_len, fake_tsig_blob.data, fake_tsig_blob.length);
241 :
242 205 : sig.length = state->tsig->rdata.tsig_record.mac_size;
243 205 : sig.data = talloc_memdup(mem_ctx, state->tsig->rdata.tsig_record.mac, sig.length);
244 205 : if (sig.data == NULL) {
245 0 : return WERR_NOT_ENOUGH_MEMORY;
246 : }
247 :
248 : /* Now we also need to count down the additional record counter */
249 205 : arcount = RSVAL(buffer, 10);
250 205 : RSSVAL(buffer, 10, arcount-1);
251 :
252 205 : status = gensec_check_packet(tkey->gensec, buffer, buffer_len,
253 : buffer, buffer_len, &sig);
254 205 : if (NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
255 2 : state->tsig_error = DNS_RCODE_BADSIG;
256 2 : return DNS_ERR(NOTAUTH);
257 : }
258 :
259 203 : if (!NT_STATUS_IS_OK(status)) {
260 0 : DEBUG(1, ("Verifying tsig failed: %s\n", nt_errstr(status)));
261 0 : return ntstatus_to_werror(status);
262 : }
263 :
264 203 : state->authenticated = true;
265 :
266 203 : return WERR_OK;
267 : }
268 :
269 404 : static WERROR dns_tsig_compute_mac(TALLOC_CTX *mem_ctx,
270 : struct dns_request_state *state,
271 : struct dns_name_packet *packet,
272 : struct dns_server_tkey *tkey,
273 : time_t current_time,
274 : DATA_BLOB *_psig)
275 : {
276 : NTSTATUS status;
277 : enum ndr_err_code ndr_err;
278 : DATA_BLOB packet_blob, tsig_blob, sig;
279 404 : uint8_t *buffer = NULL;
280 404 : uint8_t *p = NULL;
281 404 : size_t buffer_len = 0;
282 404 : struct dns_fake_tsig_rec *check_rec = talloc_zero(mem_ctx,
283 : struct dns_fake_tsig_rec);
284 404 : size_t mac_size = 0;
285 :
286 404 : if (check_rec == NULL) {
287 0 : return WERR_NOT_ENOUGH_MEMORY;
288 : }
289 :
290 : /* first build and verify check packet */
291 404 : check_rec->name = talloc_strdup(check_rec, tkey->name);
292 404 : if (check_rec->name == NULL) {
293 0 : return WERR_NOT_ENOUGH_MEMORY;
294 : }
295 404 : check_rec->rr_class = DNS_QCLASS_ANY;
296 404 : check_rec->ttl = 0;
297 404 : check_rec->algorithm_name = talloc_strdup(check_rec, tkey->algorithm);
298 404 : if (check_rec->algorithm_name == NULL) {
299 0 : return WERR_NOT_ENOUGH_MEMORY;
300 : }
301 404 : check_rec->time_prefix = 0;
302 404 : check_rec->time = current_time;
303 404 : check_rec->fudge = 300;
304 404 : check_rec->error = state->tsig_error;
305 404 : check_rec->other_size = 0;
306 404 : check_rec->other_data = NULL;
307 :
308 404 : ndr_err = ndr_push_struct_blob(&packet_blob, mem_ctx, packet,
309 : (ndr_push_flags_fn_t)ndr_push_dns_name_packet);
310 404 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
311 0 : DEBUG(1, ("Failed to push packet: %s!\n",
312 : ndr_errstr(ndr_err)));
313 0 : return DNS_ERR(SERVER_FAILURE);
314 : }
315 :
316 404 : ndr_err = ndr_push_struct_blob(&tsig_blob, mem_ctx, check_rec,
317 : (ndr_push_flags_fn_t)ndr_push_dns_fake_tsig_rec);
318 404 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
319 0 : DEBUG(1, ("Failed to push packet: %s!\n",
320 : ndr_errstr(ndr_err)));
321 0 : return DNS_ERR(SERVER_FAILURE);
322 : }
323 :
324 404 : if (state->tsig != NULL) {
325 199 : mac_size = state->tsig->rdata.tsig_record.mac_size;
326 : }
327 :
328 404 : buffer_len = mac_size;
329 :
330 404 : buffer_len += packet_blob.length;
331 404 : if (buffer_len < packet_blob.length) {
332 0 : return WERR_INVALID_PARAMETER;
333 : }
334 404 : buffer_len += tsig_blob.length;
335 404 : if (buffer_len < tsig_blob.length) {
336 0 : return WERR_INVALID_PARAMETER;
337 : }
338 :
339 404 : buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len);
340 404 : if (buffer == NULL) {
341 0 : return WERR_NOT_ENOUGH_MEMORY;
342 : }
343 :
344 404 : p = buffer;
345 :
346 : /*
347 : * RFC 2845 "4.2 TSIG on Answers", how to lay out the buffer
348 : * that we're going to sign:
349 : * 1. MAC of request (if present)
350 : * 2. Outgoing packet
351 : * 3. TSIG record
352 : */
353 404 : if (mac_size > 0) {
354 199 : memcpy(p, state->tsig->rdata.tsig_record.mac, mac_size);
355 199 : p += mac_size;
356 : }
357 :
358 404 : memcpy(p, packet_blob.data, packet_blob.length);
359 404 : p += packet_blob.length;
360 :
361 404 : memcpy(p, tsig_blob.data, tsig_blob.length);
362 :
363 404 : status = gensec_sign_packet(tkey->gensec, mem_ctx, buffer, buffer_len,
364 : buffer, buffer_len, &sig);
365 404 : if (!NT_STATUS_IS_OK(status)) {
366 0 : return ntstatus_to_werror(status);
367 : }
368 :
369 404 : *_psig = sig;
370 404 : return WERR_OK;
371 : }
372 :
373 408 : WERROR dns_sign_tsig(struct dns_server *dns,
374 : TALLOC_CTX *mem_ctx,
375 : struct dns_request_state *state,
376 : struct dns_name_packet *packet,
377 : uint16_t error)
378 : {
379 : WERROR werror;
380 408 : time_t current_time = time(NULL);
381 408 : struct dns_res_rec *tsig = NULL;
382 408 : DATA_BLOB sig = (DATA_BLOB) {
383 : .data = NULL,
384 : .length = 0
385 : };
386 :
387 408 : tsig = talloc_zero(mem_ctx, struct dns_res_rec);
388 408 : if (tsig == NULL) {
389 0 : return WERR_NOT_ENOUGH_MEMORY;
390 : }
391 :
392 408 : if (state->tsig_error == DNS_RCODE_OK) {
393 404 : struct dns_server_tkey *tkey = dns_find_tkey(
394 404 : dns->tkeys, state->key_name);
395 404 : if (tkey == NULL) {
396 0 : return DNS_ERR(SERVER_FAILURE);
397 : }
398 :
399 404 : werror = dns_tsig_compute_mac(mem_ctx, state, packet,
400 : tkey, current_time, &sig);
401 404 : if (!W_ERROR_IS_OK(werror)) {
402 0 : return werror;
403 : }
404 : }
405 :
406 408 : tsig->name = talloc_strdup(tsig, state->key_name);
407 408 : if (tsig->name == NULL) {
408 0 : return WERR_NOT_ENOUGH_MEMORY;
409 : }
410 408 : tsig->rr_class = DNS_QCLASS_ANY;
411 408 : tsig->rr_type = DNS_QTYPE_TSIG;
412 408 : tsig->ttl = 0;
413 408 : tsig->length = UINT16_MAX;
414 408 : tsig->rdata.tsig_record.algorithm_name = talloc_strdup(tsig, "gss-tsig");
415 408 : if (tsig->rdata.tsig_record.algorithm_name == NULL) {
416 0 : return WERR_NOT_ENOUGH_MEMORY;
417 : }
418 408 : tsig->rdata.tsig_record.time_prefix = 0;
419 408 : tsig->rdata.tsig_record.time = current_time;
420 408 : tsig->rdata.tsig_record.fudge = 300;
421 408 : tsig->rdata.tsig_record.error = state->tsig_error;
422 408 : tsig->rdata.tsig_record.original_id = packet->id;
423 408 : tsig->rdata.tsig_record.other_size = 0;
424 408 : tsig->rdata.tsig_record.other_data = NULL;
425 408 : if (sig.length > 0) {
426 404 : tsig->rdata.tsig_record.mac_size = sig.length;
427 404 : tsig->rdata.tsig_record.mac = talloc_memdup(tsig, sig.data, sig.length);
428 404 : if (tsig->rdata.tsig_record.mac == NULL) {
429 0 : return WERR_NOT_ENOUGH_MEMORY;
430 : }
431 : }
432 :
433 408 : if (packet->arcount == 0) {
434 408 : packet->additional = talloc_zero(mem_ctx, struct dns_res_rec);
435 408 : if (packet->additional == NULL) {
436 0 : return WERR_NOT_ENOUGH_MEMORY;
437 : }
438 : }
439 408 : packet->additional = talloc_realloc(mem_ctx, packet->additional,
440 : struct dns_res_rec,
441 : packet->arcount + 1);
442 408 : if (packet->additional == NULL) {
443 0 : return WERR_NOT_ENOUGH_MEMORY;
444 : }
445 :
446 408 : werror = dns_copy_tsig(mem_ctx, tsig,
447 408 : &packet->additional[packet->arcount]);
448 408 : if (!W_ERROR_IS_OK(werror)) {
449 0 : return werror;
450 : }
451 408 : packet->arcount++;
452 :
453 408 : return WERR_OK;
454 : }
|