Coverage Report

Created: 2025-11-11 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/private.c
Line
Count
Source
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
#include <stdbool.h>
15
16
#include <isc/base64.h>
17
#include <isc/result.h>
18
#include <isc/string.h>
19
#include <isc/types.h>
20
#include <isc/util.h>
21
22
#include <dns/nsec3.h>
23
#include <dns/private.h>
24
25
/*
26
 * We need to build the relevant chain if there exists a NSEC/NSEC3PARAM
27
 * at the apex; normally only one or the other of NSEC/NSEC3PARAM will exist.
28
 *
29
 * If a NSEC3PARAM RRset exists then we will need to build a NSEC chain
30
 * if all the NSEC3PARAM records (and associated chains) are slated for
31
 * destruction and we have not been told to NOT build the NSEC chain.
32
 *
33
 * If the NSEC set exist then check to see if there is a request to create
34
 * a NSEC3 chain.
35
 *
36
 * If neither NSEC/NSEC3PARAM RRsets exist at the origin and the private
37
 * type exists then we need to examine it to determine if NSEC3 chain has
38
 * been requested to be built otherwise a NSEC chain needs to be built.
39
 */
40
41
0
#define REMOVE(x)  (((x) & DNS_NSEC3FLAG_REMOVE) != 0)
42
0
#define CREATE(x)  (((x) & DNS_NSEC3FLAG_CREATE) != 0)
43
#define INITIAL(x) (((x) & DNS_NSEC3FLAG_INITIAL) != 0)
44
0
#define NONSEC(x)  (((x) & DNS_NSEC3FLAG_NONSEC) != 0)
45
46
#define CHECK(x)                             \
47
0
  do {                                 \
48
0
    result = (x);                \
49
0
    if (result != ISC_R_SUCCESS) \
50
0
      goto failure;        \
51
0
  } while (0)
52
53
/*
54
 * Work out if 'param' should be ignored or not (i.e. it is in the process
55
 * of being removed).
56
 *
57
 * Note: we 'belt-and-braces' here by also checking for a CREATE private
58
 * record and keep the param record in this case.
59
 */
60
61
static bool
62
0
ignore(dns_rdata_t *param, dns_rdataset_t *privateset) {
63
0
  DNS_RDATASET_FOREACH(privateset) {
64
0
    unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
65
0
    dns_rdata_t private = DNS_RDATA_INIT;
66
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
67
68
0
    dns_rdataset_current(privateset, &private);
69
0
    if (!dns_nsec3param_fromprivate(&private, &rdata, buf,
70
0
            sizeof(buf)))
71
0
    {
72
0
      continue;
73
0
    }
74
    /*
75
     * We are going to create a new NSEC3 chain so it
76
     * doesn't matter if we are removing this one.
77
     */
78
0
    if (CREATE(rdata.data[1])) {
79
0
      return false;
80
0
    }
81
0
    if (rdata.data[0] != param->data[0] ||
82
0
        rdata.data[2] != param->data[2] ||
83
0
        rdata.data[3] != param->data[3] ||
84
0
        rdata.data[4] != param->data[4] ||
85
0
        memcmp(&rdata.data[5], &param->data[5], param->data[4]))
86
0
    {
87
0
      continue;
88
0
    }
89
    /*
90
     * The removal of this NSEC3 chain does NOT cause a
91
     * NSEC chain to be created so we don't need to tell
92
     * the caller that it will be removed.
93
     */
94
0
    if (NONSEC(rdata.data[1])) {
95
0
      return false;
96
0
    }
97
0
    return true;
98
0
  }
99
0
  return false;
100
0
}
101
102
isc_result_t
103
dns_private_chains(dns_db_t *db, dns_dbversion_t *ver,
104
       dns_rdatatype_t privatetype, bool *build_nsec,
105
0
       bool *build_nsec3) {
106
0
  dns_dbnode_t *node;
107
0
  dns_rdataset_t nsecset, nsec3paramset, privateset;
108
0
  bool nsec3chain;
109
0
  bool signing;
110
0
  isc_result_t result;
111
0
  unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
112
0
  unsigned int count;
113
114
0
  node = NULL;
115
0
  dns_rdataset_init(&nsecset);
116
0
  dns_rdataset_init(&nsec3paramset);
117
0
  dns_rdataset_init(&privateset);
118
119
0
  CHECK(dns_db_getoriginnode(db, &node));
120
121
0
  result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, 0,
122
0
             (isc_stdtime_t)0, &nsecset, NULL);
123
124
0
  if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
125
0
    goto failure;
126
0
  }
127
128
0
  result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 0,
129
0
             (isc_stdtime_t)0, &nsec3paramset, NULL);
130
0
  if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
131
0
    goto failure;
132
0
  }
133
134
0
  if (dns_rdataset_isassociated(&nsecset) &&
135
0
      dns_rdataset_isassociated(&nsec3paramset))
136
0
  {
137
0
    SET_IF_NOT_NULL(build_nsec, true);
138
0
    SET_IF_NOT_NULL(build_nsec3, true);
139
0
    goto success;
140
0
  }
141
142
0
  if (privatetype != (dns_rdatatype_t)0) {
143
0
    result = dns_db_findrdataset(db, node, ver, privatetype, 0,
144
0
               (isc_stdtime_t)0, &privateset,
145
0
               NULL);
146
0
    if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
147
0
      goto failure;
148
0
    }
149
0
  }
150
151
  /*
152
   * Look to see if we also need to be creating a NSEC3 chain.
153
   */
154
0
  if (dns_rdataset_isassociated(&nsecset)) {
155
0
    SET_IF_NOT_NULL(build_nsec, true);
156
0
    SET_IF_NOT_NULL(build_nsec3, false);
157
0
    if (!dns_rdataset_isassociated(&privateset)) {
158
0
      goto success;
159
0
    }
160
0
    DNS_RDATASET_FOREACH(&privateset) {
161
0
      dns_rdata_t private = DNS_RDATA_INIT;
162
0
      dns_rdata_t rdata = DNS_RDATA_INIT;
163
164
0
      dns_rdataset_current(&privateset, &private);
165
0
      if (!dns_nsec3param_fromprivate(&private, &rdata, buf,
166
0
              sizeof(buf)))
167
0
      {
168
0
        continue;
169
0
      }
170
0
      if (REMOVE(rdata.data[1])) {
171
0
        continue;
172
0
      }
173
0
      SET_IF_NOT_NULL(build_nsec3, true);
174
0
      break;
175
0
    }
176
0
    goto success;
177
0
  }
178
179
0
  if (dns_rdataset_isassociated(&nsec3paramset)) {
180
0
    SET_IF_NOT_NULL(build_nsec3, true);
181
0
    SET_IF_NOT_NULL(build_nsec, false);
182
0
    if (!dns_rdataset_isassociated(&privateset)) {
183
0
      goto success;
184
0
    }
185
    /*
186
     * If we are in the process of building a new NSEC3 chain
187
     * then we don't need to build a NSEC chain.
188
     */
189
0
    DNS_RDATASET_FOREACH(&privateset) {
190
0
      dns_rdata_t private = DNS_RDATA_INIT;
191
0
      dns_rdata_t rdata = DNS_RDATA_INIT;
192
193
0
      dns_rdataset_current(&privateset, &private);
194
0
      if (!dns_nsec3param_fromprivate(&private, &rdata, buf,
195
0
              sizeof(buf)))
196
0
      {
197
0
        continue;
198
0
      }
199
0
      if (CREATE(rdata.data[1])) {
200
0
        goto success;
201
0
      }
202
0
    }
203
204
    /*
205
     * Check to see if there will be a active NSEC3CHAIN once
206
     * the changes queued complete.
207
     */
208
0
    count = 0;
209
0
    DNS_RDATASET_FOREACH(&nsec3paramset) {
210
0
      dns_rdata_t rdata = DNS_RDATA_INIT;
211
212
      /*
213
       * If there is more that one NSEC3 chain present then
214
       * we don't need to construct a NSEC chain.
215
       */
216
0
      if (++count > 1) {
217
0
        goto success;
218
0
      }
219
0
      dns_rdataset_current(&nsec3paramset, &rdata);
220
0
      if (ignore(&rdata, &privateset)) {
221
0
        continue;
222
0
      }
223
      /*
224
       * We still have a good NSEC3 chain or we are
225
       * not creating a NSEC chain as NONSEC is set.
226
       */
227
0
      goto success;
228
0
    }
229
230
    /*
231
     * The last NSEC3 chain is being removed and does not have
232
     * have NONSEC set.
233
     */
234
0
    SET_IF_NOT_NULL(build_nsec, true);
235
0
    goto success;
236
0
  }
237
238
0
  SET_IF_NOT_NULL(build_nsec, false);
239
0
  SET_IF_NOT_NULL(build_nsec3, false);
240
0
  if (!dns_rdataset_isassociated(&privateset)) {
241
0
    goto success;
242
0
  }
243
244
0
  signing = false;
245
0
  nsec3chain = false;
246
247
0
  DNS_RDATASET_FOREACH(&privateset) {
248
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
249
0
    dns_rdata_t private = DNS_RDATA_INIT;
250
251
0
    dns_rdataset_current(&privateset, &private);
252
0
    if (!dns_nsec3param_fromprivate(&private, &rdata, buf,
253
0
            sizeof(buf)))
254
0
    {
255
      /*
256
       * Look for record that says we are signing the
257
       * zone with a key.
258
       */
259
0
      if (private.length == 5 && private.data[0] != 0 &&
260
0
          private.data[3] == 0 && private.data[4] == 0)
261
0
      {
262
0
        signing = true;
263
0
      }
264
0
    } else {
265
0
      if (CREATE(rdata.data[1])) {
266
0
        nsec3chain = true;
267
0
      }
268
0
    }
269
0
  }
270
271
0
  if (signing) {
272
0
    if (nsec3chain) {
273
0
      SET_IF_NOT_NULL(build_nsec3, true);
274
0
    } else {
275
0
      SET_IF_NOT_NULL(build_nsec, true);
276
0
    }
277
0
  }
278
279
0
success:
280
0
  result = ISC_R_SUCCESS;
281
0
failure:
282
0
  if (dns_rdataset_isassociated(&nsecset)) {
283
0
    dns_rdataset_disassociate(&nsecset);
284
0
  }
285
0
  if (dns_rdataset_isassociated(&nsec3paramset)) {
286
0
    dns_rdataset_disassociate(&nsec3paramset);
287
0
  }
288
0
  if (dns_rdataset_isassociated(&privateset)) {
289
0
    dns_rdataset_disassociate(&privateset);
290
0
  }
291
0
  if (node != NULL) {
292
0
    dns_db_detachnode(&node);
293
0
  }
294
0
  return result;
295
0
}
296
297
isc_result_t
298
0
dns_private_totext(dns_rdata_t *private, isc_buffer_t *buf) {
299
0
  isc_result_t result;
300
301
0
  if (private->length < 5) {
302
0
    return ISC_R_NOTFOUND;
303
0
  }
304
305
0
  if (private->data[0] == 0) {
306
0
    unsigned char nsec3buf[DNS_NSEC3PARAM_BUFFERSIZE];
307
0
    unsigned char newbuf[DNS_NSEC3PARAM_BUFFERSIZE];
308
0
    dns_rdata_t rdata = DNS_RDATA_INIT;
309
0
    dns_rdata_nsec3param_t nsec3param;
310
0
    bool del, init, nonsec;
311
0
    isc_buffer_t b;
312
313
0
    if (!dns_nsec3param_fromprivate(private, &rdata, nsec3buf,
314
0
            sizeof(nsec3buf)))
315
0
    {
316
0
      CHECK(ISC_R_FAILURE);
317
0
    }
318
319
0
    CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
320
321
0
    del = ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0);
322
0
    init = ((nsec3param.flags & DNS_NSEC3FLAG_INITIAL) != 0);
323
0
    nonsec = ((nsec3param.flags & DNS_NSEC3FLAG_NONSEC) != 0);
324
325
0
    nsec3param.flags &=
326
0
      ~(DNS_NSEC3FLAG_CREATE | DNS_NSEC3FLAG_REMOVE |
327
0
        DNS_NSEC3FLAG_INITIAL | DNS_NSEC3FLAG_NONSEC);
328
329
0
    if (init) {
330
0
      isc_buffer_putstr(buf, "Pending NSEC3 chain ");
331
0
    } else if (del) {
332
0
      isc_buffer_putstr(buf, "Removing NSEC3 chain ");
333
0
    } else {
334
0
      isc_buffer_putstr(buf, "Creating NSEC3 chain ");
335
0
    }
336
337
0
    dns_rdata_reset(&rdata);
338
0
    isc_buffer_init(&b, newbuf, sizeof(newbuf));
339
0
    CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_in,
340
0
             dns_rdatatype_nsec3param,
341
0
             &nsec3param, &b));
342
343
0
    CHECK(dns_rdata_totext(&rdata, NULL, buf));
344
345
0
    if (del && !nonsec) {
346
0
      isc_buffer_putstr(buf, " / creating NSEC chain");
347
0
    }
348
0
  } else if (private->length == 5) {
349
    /* Old Form */
350
0
    unsigned char alg = private->data[0];
351
0
    dns_keytag_t keyid = (private->data[2] | private->data[1] << 8);
352
0
    char keybuf[DNS_SECALG_FORMATSIZE + BUFSIZ],
353
0
      algbuf[DNS_SECALG_FORMATSIZE];
354
0
    bool del = private->data[3];
355
0
    bool complete = private->data[4];
356
357
0
    if (del && complete) {
358
0
      isc_buffer_putstr(buf, "Done removing signatures for ");
359
0
    } else if (del) {
360
0
      isc_buffer_putstr(buf, "Removing signatures for ");
361
0
    } else if (complete) {
362
0
      isc_buffer_putstr(buf, "Done signing with ");
363
0
    } else {
364
0
      isc_buffer_putstr(buf, "Signing with ");
365
0
    }
366
367
0
    dns_secalg_format(alg, algbuf, sizeof(algbuf));
368
0
    snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf);
369
0
    isc_buffer_putstr(buf, keybuf);
370
0
  } else if (private->length == 7) {
371
    /* New Form - supports private types */
372
0
    dns_keytag_t keyid = private->data[2] | (private->data[1] << 8);
373
0
    char keybuf[DNS_SECALG_FORMATSIZE + BUFSIZ],
374
0
      algbuf[DNS_SECALG_FORMATSIZE];
375
0
    bool del = private->data[3];
376
0
    bool complete = private->data[4];
377
0
    dst_algorithm_t alg = private->data[6] |
378
0
              (private->data[5] << 8);
379
380
0
    if (dst_algorithm_tosecalg(alg) != private->data[0]) {
381
0
      return ISC_R_NOTFOUND;
382
0
    }
383
384
0
    if (del && complete) {
385
0
      isc_buffer_putstr(buf, "Done removing signatures for ");
386
0
    } else if (del) {
387
0
      isc_buffer_putstr(buf, "Removing signatures for ");
388
0
    } else if (complete) {
389
0
      isc_buffer_putstr(buf, "Done signing with ");
390
0
    } else {
391
0
      isc_buffer_putstr(buf, "Signing with ");
392
0
    }
393
394
0
    dns_secalg_format(alg, algbuf, sizeof(algbuf));
395
0
    snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf);
396
0
    isc_buffer_putstr(buf, keybuf);
397
0
  } else {
398
0
    return ISC_R_NOTFOUND;
399
0
  }
400
401
0
  isc_buffer_putuint8(buf, 0);
402
0
  result = ISC_R_SUCCESS;
403
0
failure:
404
0
  return result;
405
0
}