tsig-op.c 18.7 KB
Newer Older
1
/*  Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

17
#include <assert.h>
18
#include <inttypes.h>
Jan Kadlec's avatar
Jan Kadlec committed
19
#include <time.h>
Daniel Salzman's avatar
Daniel Salzman committed
20
#include <stdint.h>
21

22 23
#include "dnssec/error.h"
#include "dnssec/tsig.h"
24
#include "libknot/attribute.h"
25
#include "libknot/tsig-op.h"
26
#include "libknot/errcode.h"
27
#include "libknot/descriptor.h"
28
#include "libknot/rrtype/tsig.h"
29
#include "libknot/packet/wire.h"
30
#include "libknot/consts.h"
31
#include "libknot/packet/rrset-wire.h"
32
#include "contrib/string.h"
33
#include "contrib/wire.h"
34

Lubos Slovak's avatar
Lubos Slovak committed
35
const int KNOT_TSIG_MAX_DIGEST_SIZE = 64;    // size of HMAC-SHA512 digest
36
const uint16_t KNOT_TSIG_FUDGE_DEFAULT = 300;  // default Fudge value
Lubos Slovak's avatar
Lubos Slovak committed
37

38
static int check_algorithm(const knot_rrset_t *tsig_rr)
Jan Kadlec's avatar
Jan Kadlec committed
39
{
40
	if (tsig_rr == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
41
		return KNOT_EINVAL;
42
	}
Jan Včelák's avatar
Jan Včelák committed
43

44
	const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig_rr);
Jan Kadlec's avatar
Jan Kadlec committed
45 46 47 48
	if (!alg_name) {
		return KNOT_EMALF;
	}

49 50
	dnssec_tsig_algorithm_t alg = dnssec_tsig_algorithm_from_dname(alg_name);
	if (alg == DNSSEC_TSIG_UNKNOWN) {
51
		return KNOT_TSIG_EBADKEY;
Jan Kadlec's avatar
Jan Kadlec committed
52 53 54 55 56
	}

	return KNOT_EOK;
}

57
static int check_key(const knot_rrset_t *tsig_rr, const knot_tsig_key_t *tsig_key)
Jan Kadlec's avatar
Jan Kadlec committed
58
{
Jan Kadlec's avatar
Jan Kadlec committed
59
	if (tsig_rr == NULL || tsig_key == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
60
		return KNOT_EINVAL;
61
	}
Jan Včelák's avatar
Jan Včelák committed
62

63
	const knot_dname_t *tsig_name = tsig_rr->owner;
Jan Kadlec's avatar
Jan Kadlec committed
64 65 66 67
	if (!tsig_name) {
		return KNOT_EMALF;
	}

68
	if (knot_dname_cmp(tsig_name, tsig_key->name) != 0) {
Jan Kadlec's avatar
Jan Kadlec committed
69 70 71 72 73 74
		return KNOT_TSIG_EBADKEY;
	}

	return KNOT_EOK;
}

75 76 77
static int compute_digest(const uint8_t *wire, size_t wire_len,
                          uint8_t *digest, size_t *digest_len,
                          const knot_tsig_key_t *key)
Jan Kadlec's avatar
Jan Kadlec committed
78
{
Lubos Slovak's avatar
Lubos Slovak committed
79
	if (!wire || !digest || !digest_len || !key) {
Marek Vavrusa's avatar
Marek Vavrusa committed
80
		return KNOT_EINVAL;
Jan Kadlec's avatar
Jan Kadlec committed
81 82
	}

Lubos Slovak's avatar
Lubos Slovak committed
83
	if (!key->name) {
Jan Kadlec's avatar
Jan Kadlec committed
84 85 86
		return KNOT_EMALF;
	}

87 88 89 90 91 92
	dnssec_tsig_ctx_t *ctx = NULL;
	int result = dnssec_tsig_new(&ctx, key->algorithm, &key->secret);
	if (result != DNSSEC_EOK) {
		return KNOT_TSIG_EBADSIG;
	}

93
	dnssec_binary_t cover = { .data = (uint8_t *)wire, .size = wire_len };
94 95
	dnssec_tsig_add(ctx, &cover);

96
	*digest_len = dnssec_tsig_size(ctx);
97 98 99
	dnssec_tsig_write(ctx, digest);
	dnssec_tsig_free(ctx);

Jan Kadlec's avatar
Jan Kadlec committed
100 101 102
	return KNOT_EOK;
}

103
static int check_time_signed(const knot_rrset_t *tsig_rr, uint64_t prev_time_signed)
Jan Kadlec's avatar
Jan Kadlec committed
104 105
{
	if (!tsig_rr) {
Marek Vavrusa's avatar
Marek Vavrusa committed
106
		return KNOT_EINVAL;
Jan Kadlec's avatar
Jan Kadlec committed
107 108 109
	}

	/* Get the time signed and fudge values. */
110
	uint64_t time_signed = knot_tsig_rdata_time_signed(tsig_rr);
Jan Kadlec's avatar
Jan Kadlec committed
111 112 113
	if (time_signed == 0) {
		return KNOT_TSIG_EBADTIME;
	}
114
	uint16_t fudge = knot_tsig_rdata_fudge(tsig_rr);
Jan Kadlec's avatar
Jan Kadlec committed
115 116 117 118 119 120 121 122
	if (fudge == 0) {
		return KNOT_TSIG_EBADTIME;
	}

	/* Get the current time. */
	time_t curr_time = time(NULL);

	/*!< \todo bleeding eyes. */
123 124 125 126 127 128 129 130 131
	double diff = difftime(curr_time, (time_t)time_signed);

	if (diff > fudge || diff < -fudge) {
		return KNOT_TSIG_EBADTIME;
	}

	diff = difftime((time_t)time_signed, prev_time_signed);

	if (diff < 0) {
Jan Kadlec's avatar
Jan Kadlec committed
132 133 134 135 136 137
		return KNOT_TSIG_EBADTIME;
	}

	return KNOT_EOK;
}

138
static int write_tsig_variables(uint8_t *wire, const knot_rrset_t *tsig_rr)
139
{
140
	if (wire == NULL || tsig_rr == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
141
		return KNOT_EINVAL;
142
	}
Jan Včelák's avatar
Jan Včelák committed
143

144
	/* Copy TSIG variables - starting with key name. */
145
	const knot_dname_t *tsig_owner = tsig_rr->owner;
146
	if (!tsig_owner) {
Marek Vavrusa's avatar
Marek Vavrusa committed
147
		return KNOT_EINVAL;
148 149
	}

Jan Kadlec's avatar
Jan Kadlec committed
150 151
	int offset = 0;

152
	offset += knot_dname_to_wire(wire + offset, tsig_owner, KNOT_DNAME_MAXLEN);
153

Jan Kadlec's avatar
Jan Kadlec committed
154 155
	/*!< \todo which order? */

156
	/* Copy class. */
157
	wire_write_u16(wire + offset, tsig_rr->rclass);
Jan Kadlec's avatar
Jan Kadlec committed
158
	offset += sizeof(uint16_t);
159 160

	/* Copy TTL - always 0. */
161
	wire_write_u32(wire + offset, knot_rdata_ttl(knot_rdataset_at(&tsig_rr->rrs, 0)));
162 163 164
	offset += sizeof(uint32_t);

	/* Copy alg name. */
165
	const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig_rr);
166
	if (!alg_name) {
Marek Vavrusa's avatar
Marek Vavrusa committed
167
		return KNOT_EINVAL;
168 169
	}

170 171 172 173
	/* Te algorithm name must be in canonical form, i.e. in lowercase. */
	uint8_t *alg_name_wire = wire + offset;
	offset += knot_dname_to_wire(alg_name_wire, alg_name, KNOT_DNAME_MAXLEN);
	if (knot_dname_to_lower(alg_name_wire) != KNOT_EOK) {
174 175
		return KNOT_EINVAL;
	}
176

177 178
	/* Following data are written in network order. */
	/* Time signed. */
179
	wire_write_u48(wire + offset, knot_tsig_rdata_time_signed(tsig_rr));
180 181
	offset += 6;
	/* Fudge. */
182
	wire_write_u16(wire + offset, knot_tsig_rdata_fudge(tsig_rr));
183 184
	offset += sizeof(uint16_t);
	/* TSIG error. */
185
	wire_write_u16(wire + offset, knot_tsig_rdata_error(tsig_rr));
186
	offset += sizeof(uint16_t);
187
	/* Get other data length. */
188
	uint16_t other_data_length = knot_tsig_rdata_other_data_length(tsig_rr);
189
	/* Get other data. */
190
	const uint8_t *other_data = knot_tsig_rdata_other_data(tsig_rr);
191
	if (!other_data) {
Marek Vavrusa's avatar
Marek Vavrusa committed
192
		return KNOT_EINVAL;
193 194
	}

Jan Kadlec's avatar
Jan Kadlec committed
195 196 197 198
	/*
	 * We cannot write the whole other_data, as it contains its length in
	 * machine order.
	 */
199
	wire_write_u16(wire + offset, other_data_length);
Jan Kadlec's avatar
Jan Kadlec committed
200
	offset += sizeof(uint16_t);
201 202 203 204 205 206 207

	/* Skip the length. */
	memcpy(wire + offset, other_data, other_data_length);

	return KNOT_EOK;
}

208
static int wire_write_timers(uint8_t *wire, const knot_rrset_t *tsig_rr)
209
{
210
	if (wire == NULL || tsig_rr == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
211
		return KNOT_EINVAL;
212
	}
Jan Včelák's avatar
Jan Včelák committed
213

214
	//write time signed
215
	wire_write_u48(wire, knot_tsig_rdata_time_signed(tsig_rr));
216
	//write fudge
217
	wire_write_u16(wire + 6, knot_tsig_rdata_fudge(tsig_rr));
218 219

	return KNOT_EOK;
220 221
}

222 223 224 225 226
static int create_sign_wire(const uint8_t *msg, size_t msg_len,
                            const uint8_t *request_mac, size_t request_mac_len,
                            uint8_t *digest, size_t *digest_len,
                            const knot_rrset_t *tmp_tsig,
                            const knot_tsig_key_t *key)
227
{
Jan Kadlec's avatar
Jan Kadlec committed
228
	if (!msg || !key || digest_len == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
229
		return KNOT_EINVAL;
230 231
	}

232
	/* Create tmp TSIG. */
233
	int ret = KNOT_EOK;
234 235 236 237 238

	/*
	 * Create tmp wire, it should contain message
	 * plus request mac plus tsig varibles.
	 */
239 240
	size_t wire_len = msg_len + request_mac_len + (request_mac_len > 0 ? 2 : 0) +
	                  knot_tsig_rdata_tsig_variables_length(tmp_tsig);
241 242 243 244 245
	uint8_t *wire = malloc(wire_len);
	if (!wire) {
		return KNOT_ENOMEM;
	}

Jan Kadlec's avatar
Jan Kadlec committed
246
	memset(wire, 0, wire_len);
247

Jan Kadlec's avatar
Jan Kadlec committed
248 249
	uint8_t *pos = wire;

250
	/* Copy the request MAC - should work even if NULL. */
Jan Kadlec's avatar
Jan Kadlec committed
251
	if (request_mac_len > 0) {
252
		wire_write_u16(pos, request_mac_len);
Jan Kadlec's avatar
Jan Kadlec committed
253
		pos += 2;
254
		memcpy(pos, request_mac, request_mac_len);
Jan Kadlec's avatar
Jan Kadlec committed
255 256
	}
	pos += request_mac_len;
257
	/* Copy the original message. */
Jan Kadlec's avatar
Jan Kadlec committed
258 259
	memcpy(pos, msg, msg_len);
	pos += msg_len;
260
	/* Copy TSIG variables. */
261
	ret = write_tsig_variables(pos, tmp_tsig);
Jan Kadlec's avatar
Jan Kadlec committed
262
	if (ret != KNOT_EOK) {
263
		free(wire);
Jan Kadlec's avatar
Jan Kadlec committed
264
		return ret;
Jan Kadlec's avatar
Jan Kadlec committed
265
	}
266

Jan Kadlec's avatar
Jan Kadlec committed
267
	/* Compute digest. */
268
	ret = compute_digest(wire, wire_len, digest, digest_len, key);
269
	if (ret != KNOT_EOK) {
Lubos Slovak's avatar
Lubos Slovak committed
270
		*digest_len = 0;
271
		free(wire);
272 273
		return ret;
	}
Jan Kadlec's avatar
Jan Kadlec committed
274

275
	free(wire);
Jan Kadlec's avatar
Jan Kadlec committed
276 277 278 279

	return KNOT_EOK;
}

280 281 282 283 284
static int create_sign_wire_next(const uint8_t *msg, size_t msg_len,
                                 const uint8_t *prev_mac, size_t prev_mac_len,
                                 uint8_t *digest, size_t *digest_len,
                                 const knot_rrset_t *tmp_tsig,
                                 const knot_tsig_key_t *key)
285 286
{
	if (!msg || !key || digest_len == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
287
		return KNOT_EINVAL;
288 289 290 291 292 293 294 295 296
	}

	/* Create tmp TSIG. */
	int ret = KNOT_EOK;

	/*
	 * Create tmp wire, it should contain message
	 * plus request mac plus tsig varibles.
	 */
297
	size_t wire_len = msg_len + prev_mac_len + knot_tsig_rdata_tsig_timers_length() + 2;
298 299 300 301 302 303 304 305
	uint8_t *wire = malloc(wire_len);
	if (!wire) {
		return KNOT_ENOMEM;
	}

	memset(wire, 0, wire_len);

	/* Copy the request MAC - should work even if NULL. */
306
	wire_write_u16(wire, prev_mac_len);
307
	memcpy(wire + 2, prev_mac, prev_mac_len);
308
	/* Copy the original message. */
309
	memcpy(wire + prev_mac_len + 2, msg, msg_len);
310
	/* Copy TSIG variables. */
Jan Včelák's avatar
Jan Včelák committed
311

312
	ret = wire_write_timers(wire + prev_mac_len + msg_len + 2, tmp_tsig);
313
	if (ret != KNOT_EOK) {
Jan Kadlec's avatar
Jan Kadlec committed
314
		free(wire);
315 316 317 318
		return ret;
	}

	/* Compute digest. */
319
	ret = compute_digest(wire, wire_len, digest, digest_len, key);
320 321
	if (ret != KNOT_EOK) {
		*digest_len = 0;
Jan Kadlec's avatar
Jan Kadlec committed
322
		free(wire);
323 324 325 326 327 328 329 330
		return ret;
	}

	free(wire);

	return KNOT_EOK;
}

331
_public_
332 333
int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
                   const uint8_t *request_mac, size_t request_mac_len,
Jan Kadlec's avatar
Jan Kadlec committed
334
                   uint8_t *digest, size_t *digest_len,
335
                   const knot_tsig_key_t *key, uint16_t tsig_rcode,
336
                   uint64_t request_time_signed)
Jan Kadlec's avatar
Jan Kadlec committed
337 338
{
	if (!msg || !msg_len || !key || digest == NULL || digest_len == NULL) {
Marek Vavrusa's avatar
Marek Vavrusa committed
339
		return KNOT_EINVAL;
Jan Kadlec's avatar
Jan Kadlec committed
340 341
	}

342 343
	knot_rrset_t *tmp_tsig = knot_rrset_new(key->name, KNOT_RRTYPE_TSIG,
	                                        KNOT_CLASS_ANY, NULL);
Jan Kadlec's avatar
Jan Kadlec committed
344 345 346 347
	if (!tmp_tsig) {
		return KNOT_ENOMEM;
	}

348
	/* Create rdata for TSIG RR. */
349 350
	uint16_t rdata_rcode = KNOT_RCODE_NOERROR;
	if (tsig_rcode == KNOT_RCODE_BADTIME) {
351
		rdata_rcode = tsig_rcode;
352
	}
353 354 355

	const uint8_t *alg_name = dnssec_tsig_algorithm_to_dname(key->algorithm);
	size_t alg_size = dnssec_tsig_algorithm_size(key->algorithm);
356
	knot_tsig_create_rdata(tmp_tsig, alg_name, alg_size, rdata_rcode);
357

358
	/* Distinguish BADTIME response. */
359
	if (tsig_rcode == KNOT_RCODE_BADTIME) {
360
		/* Set client's time signed into the time signed field. */
361
		knot_tsig_rdata_set_time_signed(tmp_tsig, request_time_signed);
362

363
		/* Store current time into Other data. */
Lubos Slovak's avatar
Lubos Slovak committed
364
		uint8_t time_signed[6];
365 366
		time_t now = time(NULL);
		wire_write_u48(time_signed, now);
367

368
		knot_tsig_rdata_set_other_data(tmp_tsig, 6, time_signed);
369
	} else {
370
		knot_tsig_rdata_set_time_signed(tmp_tsig, time(NULL));
371 372

		/* Set other len. */
373
		knot_tsig_rdata_set_other_data(tmp_tsig, 0, 0);
374 375
	}

376
	knot_tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT);
377 378

	/* Set original ID */
379
	knot_tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
Jan Kadlec's avatar
Jan Kadlec committed
380

381 382 383
	uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
	size_t digest_tmp_len = 0;

384 385 386
	int ret = create_sign_wire(msg, *msg_len, /*msg_max_len,*/
	                           request_mac, request_mac_len,
	                           digest_tmp, &digest_tmp_len, tmp_tsig, key);
Jan Kadlec's avatar
Jan Kadlec committed
387
	if (ret != KNOT_EOK) {
388
		knot_rrset_free(&tmp_tsig, NULL);
Jan Kadlec's avatar
Jan Kadlec committed
389
		return ret;
Lubos Slovak's avatar
Lubos Slovak committed
390
	}
391 392

	/* Set the digest. */
393
	knot_tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp);
394 395

	/* Write RRSet to wire */
396
	ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
397
	                         msg_max_len - *msg_len, NULL);
398
	if (ret < 0) {
Lubos Slovak's avatar
Lubos Slovak committed
399
		*digest_len = 0;
400
		knot_rrset_free(&tmp_tsig, NULL);
Jan Kadlec's avatar
Jan Kadlec committed
401 402
		return ret;
	}
403

404 405
	size_t tsig_wire_len = ret;

406
	knot_rrset_free(&tmp_tsig, NULL);
407 408 409 410 411

	*msg_len += tsig_wire_len;

	uint16_t arcount = knot_wire_get_arcount(msg);
	knot_wire_set_arcount(msg, ++arcount);
Jan Včelák's avatar
Jan Včelák committed
412

413
	/* everything went ok, save the digest to the output parameter */
Lubos Slovak's avatar
Lubos Slovak committed
414 415
	memcpy(digest, digest_tmp, digest_tmp_len);
	*digest_len = digest_tmp_len;
416

417 418 419
	return KNOT_EOK;
}

420
_public_
421
int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
422
                        const uint8_t *prev_digest, size_t prev_digest_len,
Lubos Slovak's avatar
Lubos Slovak committed
423
                        uint8_t *digest, size_t *digest_len,
424
                        const knot_tsig_key_t *key, uint8_t *to_sign,
425
                        size_t to_sign_len)
426
{
427
	if (!msg || !msg_len || !key || !digest || !digest_len) {
Marek Vavrusa's avatar
Marek Vavrusa committed
428
		return KNOT_EINVAL;
429
	}
Jan Včelák's avatar
Jan Včelák committed
430

Lubos Slovak's avatar
Lubos Slovak committed
431 432
	uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
	size_t digest_tmp_len = 0;
433 434
	knot_rrset_t *tmp_tsig = knot_rrset_new(key->name, KNOT_RRTYPE_TSIG,
	                                        KNOT_CLASS_ANY, NULL);
Jan Kadlec's avatar
Jan Kadlec committed
435 436
	if (!tmp_tsig) {
		return KNOT_ENOMEM;
437
	}
Jan Včelák's avatar
Jan Včelák committed
438

Jan Kadlec's avatar
Jan Kadlec committed
439
	/* Create rdata for TSIG RR. */
440 441
	const uint8_t *alg_name = dnssec_tsig_algorithm_to_dname(key->algorithm);
	size_t alg_size = dnssec_tsig_algorithm_size(key->algorithm);
442 443
	knot_tsig_create_rdata(tmp_tsig, alg_name, alg_size, 0);
	knot_tsig_rdata_set_time_signed(tmp_tsig, time(NULL));
444
	knot_tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT);
Jan Včelák's avatar
Jan Včelák committed
445

446
	/* Create wire to be signed. */
447
	size_t wire_len = prev_digest_len + to_sign_len + KNOT_TSIG_TIMERS_LENGTH + 2;
448 449
	uint8_t *wire = malloc(wire_len);
	if (!wire) {
450
		knot_rrset_free(&tmp_tsig, NULL);
451 452 453
		return KNOT_ENOMEM;
	}
	memset(wire, 0, wire_len);
454

Jan Kadlec's avatar
Jan Kadlec committed
455
	/* Write previous digest length. */
456
	wire_write_u16(wire, prev_digest_len);
Jan Kadlec's avatar
Jan Kadlec committed
457
	/* Write previous digest. */
458
	memcpy(wire + 2, prev_digest, prev_digest_len);
Jan Kadlec's avatar
Jan Kadlec committed
459
	/* Write original message. */
460
	memcpy(wire + prev_digest_len + 2, to_sign, to_sign_len);
Jan Kadlec's avatar
Jan Kadlec committed
461
	/* Write timers. */
462
	wire_write_timers(wire + prev_digest_len + to_sign_len + 2, tmp_tsig);
Jan Kadlec's avatar
Jan Kadlec committed
463

464
	int ret = compute_digest(wire, wire_len, digest_tmp, &digest_tmp_len, key);
465
	free(wire);
Jan Kadlec's avatar
Jan Kadlec committed
466
	if (ret != KNOT_EOK) {
467
		knot_rrset_free(&tmp_tsig, NULL);
Lubos Slovak's avatar
Lubos Slovak committed
468
		*digest_len = 0;
Jan Kadlec's avatar
Jan Kadlec committed
469 470
		return ret;
	}
Jan Včelák's avatar
Jan Včelák committed
471

Lubos Slovak's avatar
Lubos Slovak committed
472
	if (digest_tmp_len > *digest_len) {
473
		knot_rrset_free(&tmp_tsig, NULL);
Lubos Slovak's avatar
Lubos Slovak committed
474 475 476
		*digest_len = 0;
		return KNOT_ESPACE;
	}
Jan Kadlec's avatar
Jan Kadlec committed
477

478
	/* Set the MAC. */
479
	knot_tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp);
Jan Včelák's avatar
Jan Včelák committed
480

Jan Kadlec's avatar
Jan Kadlec committed
481
	/* Set original id. */
482
	knot_tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
Jan Včelák's avatar
Jan Včelák committed
483

Jan Kadlec's avatar
Jan Kadlec committed
484
	/* Set other data. */
485
	knot_tsig_rdata_set_other_data(tmp_tsig, 0, NULL);
486 487

	ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
488
	                         msg_max_len - *msg_len, NULL);
489
	if (ret < 0) {
490
		knot_rrset_free(&tmp_tsig, NULL);
Lubos Slovak's avatar
Lubos Slovak committed
491
		*digest_len = 0;
492 493 494
		return ret;
	}

495
	size_t tsig_wire_size = ret;
Jan Kadlec's avatar
Jan Kadlec committed
496

497
	knot_rrset_free(&tmp_tsig, NULL);
498 499 500 501

	*msg_len += tsig_wire_size;
	uint16_t arcount = knot_wire_get_arcount(msg);
	knot_wire_set_arcount(msg, ++arcount);
Jan Včelák's avatar
Jan Včelák committed
502

Lubos Slovak's avatar
Lubos Slovak committed
503 504
	memcpy(digest, digest_tmp, digest_tmp_len);
	*digest_len = digest_tmp_len;
505

506 507
	return KNOT_EOK;
}
508

509 510 511 512 513
static int check_digest(const knot_rrset_t *tsig_rr,
                        const uint8_t *wire, size_t size,
                        const uint8_t *request_mac, size_t request_mac_len,
                        const knot_tsig_key_t *tsig_key,
                        uint64_t prev_time_signed, int use_times)
514
{
515
	if (!wire || !tsig_key) {
Marek Vavrusa's avatar
Marek Vavrusa committed
516
		return KNOT_EINVAL;
517 518
	}

519 520 521 522 523
	/* No TSIG record means verification failure. */
	if (tsig_rr == NULL) {
		return KNOT_TSIG_EBADKEY;
	}

Jan Kadlec's avatar
Jan Kadlec committed
524
	/* Check that libknot knows the algorithm. */
525
	int ret = check_algorithm(tsig_rr);
Jan Kadlec's avatar
Jan Kadlec committed
526 527
	if (ret != KNOT_EOK) {
		return ret;
528 529
	}

Jan Kadlec's avatar
Jan Kadlec committed
530
	/* Check that key is valid, ie. the same as given in args. */
531
	ret = check_key(tsig_rr, tsig_key);
Jan Kadlec's avatar
Jan Kadlec committed
532 533
	if (ret != KNOT_EOK) {
		return ret;
534 535
	}

536
	uint8_t *wire_to_sign = malloc(size);
Jan Kadlec's avatar
Jan Kadlec committed
537 538 539 540 541
	if (!wire_to_sign) {
		return KNOT_ENOMEM;
	}

	memcpy(wire_to_sign, wire, size);
Jan Včelák's avatar
Jan Včelák committed
542

543 544 545
	// restore message ID to which the signature had been created with
	knot_wire_set_id(wire_to_sign, knot_tsig_rdata_orig_id(tsig_rr));

Lubos Slovak's avatar
Lubos Slovak committed
546 547
	uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
	size_t digest_tmp_len = 0;
548
	assert(tsig_rr->rrs.rr_count > 0);
Jan Včelák's avatar
Jan Včelák committed
549

550
	if (use_times) {
551
		/* Wire is not a single packet, TSIG RRs must be stripped already. */
552 553 554 555
		ret = create_sign_wire_next(wire_to_sign, size,
		                            request_mac, request_mac_len,
		                            digest_tmp, &digest_tmp_len,
		                            tsig_rr, tsig_key);
556 557 558 559 560
	} else {
		ret = create_sign_wire(wire_to_sign, size,
		                       request_mac, request_mac_len,
		                       digest_tmp, &digest_tmp_len,
		                       tsig_rr, tsig_key);
561
	}
Jan Kadlec's avatar
Jan Kadlec committed
562

563
	assert(tsig_rr->rrs.rr_count > 0);
Jan Kadlec's avatar
Jan Kadlec committed
564 565
	free(wire_to_sign);

Jan Kadlec's avatar
Jan Kadlec committed
566 567
	if (ret != KNOT_EOK) {
		return ret;
568 569 570 571
	}

	/* Compare MAC from TSIG RR RDATA with just computed digest. */

Jan Kadlec's avatar
Jan Kadlec committed
572
	/*!< \todo move to function. */
573
	const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig_rr);
574
	dnssec_tsig_algorithm_t alg = dnssec_tsig_algorithm_from_dname(alg_name);
Jan Kadlec's avatar
Jan Kadlec committed
575

Lubos Slovak's avatar
Lubos Slovak committed
576
	/*! \todo [TSIG] TRUNCATION */
577 578
	uint16_t mac_length = knot_tsig_rdata_mac_length(tsig_rr);
	const uint8_t *tsig_mac = knot_tsig_rdata_mac(tsig_rr);
Jan Kadlec's avatar
Jan Kadlec committed
579

580
	if (mac_length != dnssec_tsig_algorithm_size(alg)) {
Jan Kadlec's avatar
Jan Kadlec committed
581 582 583
		return KNOT_TSIG_EBADSIG;
	}

584
	if (const_time_memcmp(tsig_mac, digest_tmp, mac_length) != 0) {
Jan Kadlec's avatar
Jan Kadlec committed
585 586
		return KNOT_TSIG_EBADSIG;
	}
587

588 589 590 591 592 593
	/* Check TSIG validity period, must be after the signature check! */
	ret = check_time_signed(tsig_rr, prev_time_signed);
	if (ret != KNOT_EOK) {
		return ret;
	}

594 595
	return KNOT_EOK;
}
596

597
_public_
Jan Kadlec's avatar
Jan Kadlec committed
598 599
int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
                           const uint8_t *wire, size_t size,
600
                           const knot_tsig_key_t *tsig_key)
Jan Kadlec's avatar
Jan Kadlec committed
601
{
602
	return check_digest(tsig_rr, wire, size, NULL, 0, tsig_key, 0, 0);
Jan Kadlec's avatar
Jan Kadlec committed
603 604
}

605
_public_
606 607
int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
                           const uint8_t *wire, size_t size,
608
                           const uint8_t *request_mac, size_t request_mac_len,
609
                           const knot_tsig_key_t *tsig_key,
610
                           uint64_t prev_time_signed)
611
{
612 613
	return check_digest(tsig_rr, wire, size, request_mac, request_mac_len,
	                    tsig_key, prev_time_signed, 0);
614 615
}

616
_public_
617 618
int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
                                const uint8_t *wire, size_t size,
619 620
                                const uint8_t *prev_digest,
                                size_t prev_digest_len,
621
                                const knot_tsig_key_t *tsig_key,
622
                                uint64_t prev_time_signed)
623
{
624
	return check_digest(tsig_rr, wire, size, prev_digest,
625
	                    prev_digest_len, tsig_key, prev_time_signed, 1);
626
}
627

628
_public_
629
int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
630
                  uint16_t tsig_rcode, const knot_rrset_t *tsig_rr)
631
{
632 633
	/*! \todo Revise!! */

634
	if (!msg || !msg_len || !tsig_rr) {
Marek Vavrusa's avatar
Marek Vavrusa committed
635
		return KNOT_EINVAL;
636 637
	}

638
	/*! \todo What key to use, when we do not sign? Does this even work? */
639 640
	knot_rrset_t *tmp_tsig = knot_rrset_new(tsig_rr->owner, KNOT_RRTYPE_TSIG,
		                                KNOT_CLASS_ANY, NULL);
641 642 643
	if (!tmp_tsig) {
		return KNOT_ENOMEM;
	}
Jan Včelák's avatar
Jan Včelák committed
644

645
	assert(tsig_rcode != KNOT_RCODE_BADTIME);
646 647
	knot_tsig_create_rdata(tmp_tsig, knot_tsig_rdata_alg_name(tsig_rr), 0, tsig_rcode);
	knot_tsig_rdata_set_time_signed(tmp_tsig, knot_tsig_rdata_time_signed(tsig_rr));
648 649 650 651

	/* Comparing to BIND it was found out that the Fudge should always be
	 * set to the server's value.
	 */
652
	knot_tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT);
653 654

	/* Set original ID */
655
	knot_tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
656

657
	/* Set other len. */
658
	knot_tsig_rdata_set_other_data(tmp_tsig, 0, 0);
659

660
	/* Append TSIG RR. */
661
	int ret = knot_tsig_append(msg, msg_len, msg_max_len, tmp_tsig);
662 663

	/* key_name already referenced in RRSet, no need to free separately. */
664
	knot_rrset_free(&tmp_tsig, NULL);
665 666 667 668

	return ret;
}

669
_public_
670 671 672
int knot_tsig_append(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
                     const knot_rrset_t *tsig_rr)
{
673
	/* Write RRSet to wire */
674
	int ret = knot_rrset_to_wire(tsig_rr, msg + *msg_len,
675
	                             msg_max_len - *msg_len, NULL);
676
	if (ret < 0) {
677 678 679
		return ret;
	}

680
	*msg_len += ret;
681

682
	knot_wire_set_arcount(msg, knot_wire_get_arcount(msg) + 1);
683 684

	return KNOT_EOK;
685
}