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