1 /*  Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 
3     This program is free software: you can redistribute it and/or modify
4     it under the terms of the GNU General Public License as published by
5     the Free Software Foundation, either version 3 of the License, or
6     (at your option) any later version.
7 
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12 
13     You should have received a copy of the GNU General Public License
14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
15  */
16 
17 #include <assert.h>
18 
19 #include "knot/nameserver/tsig_ctx.h"
20 #include "contrib/string.h"
21 #include "libknot/libknot.h"
22 
23 /*!
24  * Maximal total size for unsigned messages.
25  */
26 static const size_t TSIG_BUFFER_MAX_SIZE = (UINT16_MAX * 100);
27 
tsig_init(tsig_ctx_t * ctx,const knot_tsig_key_t * key)28 void tsig_init(tsig_ctx_t *ctx, const knot_tsig_key_t *key)
29 {
30 	if (!ctx) {
31 		return;
32 	}
33 
34 	memzero(ctx, sizeof(*ctx));
35 	ctx->key = key;
36 }
37 
tsig_cleanup(tsig_ctx_t * ctx)38 void tsig_cleanup(tsig_ctx_t *ctx)
39 {
40 	if (!ctx) {
41 		return;
42 	}
43 
44 	free(ctx->buffer);
45 	memzero(ctx, sizeof(*ctx));
46 }
47 
tsig_reset(tsig_ctx_t * ctx)48 void tsig_reset(tsig_ctx_t *ctx)
49 {
50 	if (!ctx) {
51 		return;
52 	}
53 
54 	const knot_tsig_key_t *backup = ctx->key;
55 	tsig_cleanup(ctx);
56 	tsig_init(ctx, backup);
57 }
58 
tsig_sign_packet(tsig_ctx_t * ctx,knot_pkt_t * packet)59 int tsig_sign_packet(tsig_ctx_t *ctx, knot_pkt_t *packet)
60 {
61 	if (!ctx || !packet) {
62 		return KNOT_EINVAL;
63 	}
64 
65 	if (ctx->key == NULL) {
66 		return KNOT_EOK;
67 	}
68 
69 	int ret = KNOT_ERROR;
70 	if (ctx->digest_size == 0) {
71 		ctx->digest_size = dnssec_tsig_algorithm_size(ctx->key->algorithm);
72 		ret = knot_tsig_sign(packet->wire, &packet->size, packet->max_size,
73 		                     NULL, 0,
74 		                     ctx->digest, &ctx->digest_size,
75 		                     ctx->key, 0, 0);
76 	} else {
77 		uint8_t previous_digest[ctx->digest_size];
78 		memcpy(previous_digest, ctx->digest, ctx->digest_size);
79 
80 		ret = knot_tsig_sign_next(packet->wire, &packet->size, packet->max_size,
81 		                          previous_digest, ctx->digest_size,
82 		                          ctx->digest, &ctx->digest_size,
83 		                          ctx->key, packet->wire, packet->size);
84 	}
85 
86 	return ret;
87 }
88 
update_ctx_after_verify(tsig_ctx_t * ctx,knot_rrset_t * tsig_rr)89 static int update_ctx_after_verify(tsig_ctx_t *ctx, knot_rrset_t *tsig_rr)
90 {
91 	assert(ctx);
92 	assert(tsig_rr);
93 
94 	if (ctx->digest_size != knot_tsig_rdata_mac_length(tsig_rr)) {
95 		return KNOT_EMALF;
96 	}
97 
98 	memcpy(ctx->digest, knot_tsig_rdata_mac(tsig_rr), ctx->digest_size);
99 	ctx->prev_signed_time = knot_tsig_rdata_time_signed(tsig_rr);
100 	ctx->unsigned_count = 0;
101 	ctx->buffer_used = 0;
102 
103 	return KNOT_EOK;
104 }
105 
buffer_add_packet(tsig_ctx_t * ctx,knot_pkt_t * packet)106 static int buffer_add_packet(tsig_ctx_t *ctx, knot_pkt_t *packet)
107 {
108 	size_t need = ctx->buffer_used + packet->size;
109 
110 	// Inflate the buffer if necessary.
111 
112 	if (need > TSIG_BUFFER_MAX_SIZE) {
113 		return KNOT_ENOMEM;
114 	}
115 
116 	if (need > ctx->buffer_size) {
117 		uint8_t *buffer = realloc(ctx->buffer, need);
118 		if (!buffer) {
119 			return KNOT_ENOMEM;
120 		}
121 
122 		ctx->buffer = buffer;
123 		ctx->buffer_size = need;
124 	}
125 
126 	// Buffer the packet.
127 
128 	uint8_t *write = ctx->buffer + ctx->buffer_used;
129 	memcpy(write, packet->wire, packet->size);
130 	ctx->buffer_used = need;
131 
132 	return KNOT_EOK;
133 }
134 
tsig_verify_packet(tsig_ctx_t * ctx,knot_pkt_t * packet)135 int tsig_verify_packet(tsig_ctx_t *ctx, knot_pkt_t *packet)
136 {
137 	if (!ctx || !packet) {
138 		return KNOT_EINVAL;
139 	}
140 
141 	if (ctx->key == NULL) {
142 		return KNOT_EOK;
143 	}
144 
145 	int ret = buffer_add_packet(ctx, packet);
146 	if (ret != KNOT_EOK) {
147 		return ret;
148 	}
149 
150 	// Unsigned packet.
151 
152 	if (packet->tsig_rr == NULL) {
153 		ctx->unsigned_count += 1;
154 		return KNOT_EOK;
155 	}
156 
157 	// Signed packet.
158 
159 	if (ctx->prev_signed_time == 0) {
160 		ret = knot_tsig_client_check(packet->tsig_rr, ctx->buffer,
161 		                             ctx->buffer_used, ctx->digest,
162 		                             ctx->digest_size, ctx->key, 0);
163 	} else {
164 		ret = knot_tsig_client_check_next(packet->tsig_rr, ctx->buffer,
165 		                                  ctx->buffer_used, ctx->digest,
166 		                                  ctx->digest_size, ctx->key,
167 		                                  ctx->prev_signed_time);
168 	}
169 
170 	if (ret != KNOT_EOK) {
171 		return ret;
172 	}
173 
174 	ret = update_ctx_after_verify(ctx, packet->tsig_rr);
175 	if (ret != KNOT_EOK) {
176 		return ret;
177 	}
178 
179 	return KNOT_EOK;
180 }
181 
tsig_unsigned_count(tsig_ctx_t * ctx)182 unsigned tsig_unsigned_count(tsig_ctx_t *ctx)
183 {
184 	if (!ctx) {
185 		return -1;
186 	}
187 
188 	return ctx->unsigned_count;
189 }
190