Coverage Report

Created: 2026-01-24 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/bind9/lib/dns/skr.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
/*! \file */
15
16
#include <isc/lex.h>
17
#include <isc/log.h>
18
19
#include <dns/callbacks.h>
20
#include <dns/fixedname.h>
21
#include <dns/rdata.h>
22
#include <dns/rdataclass.h>
23
#include <dns/rdatatype.h>
24
#include <dns/skr.h>
25
#include <dns/time.h>
26
#include <dns/ttl.h>
27
28
#define READLINE(lex, opt, token)
29
30
0
#define NEXTTOKEN(lex, opt, token) CHECK(isc_lex_gettoken(lex, opt, token))
31
32
0
#define BADTOKEN() CLEANUP(ISC_R_UNEXPECTEDTOKEN)
33
34
0
#define TOKENSIZ (8 * 1024)
35
0
#define STR(t)   ((t).value.as_textregion.base)
36
37
static isc_result_t
38
parse_rr(isc_lex_t *lex, isc_mem_t *mctx, char *owner, dns_name_t *origin,
39
   dns_rdataclass_t rdclass, isc_buffer_t *buf, dns_ttl_t *ttl,
40
0
   dns_rdatatype_t *rdtype, dns_rdata_t **rdata) {
41
0
  dns_rdatacallbacks_t callbacks;
42
0
  dns_fixedname_t dfname;
43
0
  dns_name_t *dname = NULL;
44
0
  dns_rdataclass_t clas;
45
0
  isc_buffer_t b;
46
0
  isc_token_t token;
47
0
  unsigned int opt = ISC_LEXOPT_EOL;
48
0
  isc_result_t result = ISC_R_SUCCESS;
49
50
0
  isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
51
52
  /* Read the domain name */
53
0
  if (!strcmp(owner, "@")) {
54
0
    BADTOKEN();
55
0
  }
56
0
  dname = dns_fixedname_initname(&dfname);
57
0
  isc_buffer_init(&b, owner, strlen(owner));
58
0
  isc_buffer_add(&b, strlen(owner));
59
0
  CHECK(dns_name_fromtext(dname, &b, dns_rootname, 0));
60
0
  if (dns_name_compare(dname, origin) != 0) {
61
0
    CLEANUP(DNS_R_BADOWNERNAME);
62
0
  }
63
0
  isc_buffer_clear(&b);
64
65
  /* Read the next word: either TTL, class, or type */
66
0
  NEXTTOKEN(lex, opt, &token);
67
0
  if (token.type != isc_tokentype_string) {
68
0
    BADTOKEN();
69
0
  }
70
71
  /* If it's a TTL, read the next one */
72
0
  result = dns_ttl_fromtext(&token.value.as_textregion, ttl);
73
0
  if (result == ISC_R_SUCCESS) {
74
0
    NEXTTOKEN(lex, opt, &token);
75
0
  }
76
0
  if (token.type != isc_tokentype_string) {
77
0
    BADTOKEN();
78
0
  }
79
80
  /* If it's a class, read the next one */
81
0
  result = dns_rdataclass_fromtext(&clas, &token.value.as_textregion);
82
0
  if (result == ISC_R_SUCCESS) {
83
0
    if (clas != rdclass) {
84
0
      BADTOKEN();
85
0
    }
86
0
    NEXTTOKEN(lex, opt, &token);
87
0
  }
88
0
  if (token.type != isc_tokentype_string) {
89
0
    BADTOKEN();
90
0
  }
91
92
  /* Must be the record type */
93
0
  result = dns_rdatatype_fromtext(rdtype, &token.value.as_textregion);
94
0
  if (result != ISC_R_SUCCESS) {
95
0
    BADTOKEN();
96
0
  }
97
0
  switch (*rdtype) {
98
0
  case dns_rdatatype_dnskey:
99
0
  case dns_rdatatype_cdnskey:
100
0
  case dns_rdatatype_cds:
101
0
  case dns_rdatatype_rrsig:
102
    /* Allowed record types */
103
0
    break;
104
0
  default:
105
0
    BADTOKEN();
106
0
  }
107
108
0
  dns_rdatacallbacks_init(&callbacks);
109
0
  result = dns_rdata_fromtext(*rdata, rdclass, *rdtype, lex, dname, 0,
110
0
            mctx, buf, &callbacks);
111
0
cleanup:
112
0
  isc_lex_setcomments(lex, 0);
113
0
  return result;
114
0
}
115
116
static void
117
skrbundle_create(isc_mem_t *mctx, isc_stdtime_t inception,
118
0
     dns_skrbundle_t **bp) {
119
0
  dns_skrbundle_t *b;
120
121
0
  REQUIRE(bp != NULL && *bp == NULL);
122
123
0
  b = isc_mem_get(mctx, sizeof(*b));
124
0
  b->magic = DNS_SKRBUNDLE_MAGIC;
125
0
  b->inception = inception;
126
0
  dns_diff_init(mctx, &b->diff);
127
128
0
  ISC_LINK_INIT(b, link);
129
130
0
  *bp = b;
131
0
}
132
133
static void
134
0
skrbundle_addtuple(dns_skrbundle_t *bundle, dns_difftuple_t **tuple) {
135
0
  REQUIRE(DNS_DIFFTUPLE_VALID(*tuple));
136
0
  REQUIRE(DNS_SKRBUNDLE_VALID(bundle));
137
0
  REQUIRE(DNS_DIFF_VALID(&bundle->diff));
138
139
0
  dns_diff_append(&bundle->diff, tuple);
140
0
}
141
142
isc_result_t
143
dns_skrbundle_getsig(dns_skrbundle_t *bundle, dst_key_t *key,
144
0
         dns_rdatatype_t covering_type, dns_rdata_t *sigrdata) {
145
0
  REQUIRE(DNS_SKRBUNDLE_VALID(bundle));
146
0
  REQUIRE(DNS_DIFF_VALID(&bundle->diff));
147
148
0
  ISC_LIST_FOREACH(bundle->diff.tuples, tuple, link) {
149
0
    dns_rdata_rrsig_t rrsig;
150
151
0
    if (tuple->op != DNS_DIFFOP_ADDRESIGN) {
152
0
      continue;
153
0
    }
154
0
    INSIST(tuple->rdata.type == dns_rdatatype_rrsig);
155
156
0
    RETERR(dns_rdata_tostruct(&tuple->rdata, &rrsig, NULL));
157
158
    /*
159
     * Check if covering type matches, and if the signature is
160
     * generated by 'key'.
161
     */
162
0
    if (rrsig.covered == covering_type &&
163
0
        rrsig.keyid == dst_key_id(key))
164
0
    {
165
0
      dns_rdata_clone(&tuple->rdata, sigrdata);
166
0
      return ISC_R_SUCCESS;
167
0
    }
168
0
  }
169
170
0
  return ISC_R_NOTFOUND;
171
0
}
172
173
void
174
dns_skr_create(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
175
0
         dns_rdataclass_t rdclass, dns_skr_t **skrp) {
176
0
  isc_time_t now;
177
0
  dns_skr_t *skr = NULL;
178
179
0
  REQUIRE(skrp != NULL && *skrp == NULL);
180
0
  REQUIRE(mctx != NULL);
181
182
0
  UNUSED(origin);
183
0
  UNUSED(rdclass);
184
185
0
  now = isc_time_now();
186
0
  skr = isc_mem_get(mctx, sizeof(*skr));
187
0
  *skr = (dns_skr_t){
188
0
    .magic = DNS_SKR_MAGIC,
189
0
    .filename = isc_mem_strdup(mctx, filename),
190
0
    .loadtime = now,
191
0
  };
192
  /*
193
   * A list is not the best structure to store bundles that
194
   * we need to look up, but we don't expect many bundles
195
   * per SKR so it is acceptable for now.
196
   */
197
0
  ISC_LIST_INIT(skr->bundles);
198
199
0
  isc_mem_attach(mctx, &skr->mctx);
200
0
  isc_refcount_init(&skr->references, 1);
201
0
  *skrp = skr;
202
0
}
203
204
static void
205
0
addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep) {
206
0
  REQUIRE(DNS_SKR_VALID(skr));
207
0
  REQUIRE(DNS_SKRBUNDLE_VALID(*bundlep));
208
209
0
  ISC_LIST_APPEND(skr->bundles, *bundlep, link);
210
0
  *bundlep = NULL;
211
0
}
212
213
isc_result_t
214
dns_skr_read(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
215
0
       dns_rdataclass_t rdclass, dns_ttl_t dnskeyttl, dns_skr_t **skrp) {
216
0
  isc_result_t result;
217
0
  dns_skrbundle_t *bundle = NULL;
218
0
  char bundlebuf[1024];
219
0
  uint32_t bundle_id;
220
0
  isc_lex_t *lex = NULL;
221
0
  isc_lexspecials_t specials;
222
0
  isc_token_t token;
223
0
  unsigned int opt = ISC_LEXOPT_EOL;
224
225
0
  REQUIRE(DNS_SKR_VALID(*skrp));
226
227
0
  isc_lex_create(mctx, TOKENSIZ, &lex);
228
0
  memset(specials, 0, sizeof(specials));
229
0
  specials['('] = 1;
230
0
  specials[')'] = 1;
231
0
  specials['"'] = 1;
232
0
  isc_lex_setspecials(lex, specials);
233
0
  result = isc_lex_openfile(lex, filename);
234
0
  if (result != ISC_R_SUCCESS) {
235
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
236
0
            ISC_LOG_ERROR, "unable to open ksr file %s: %s",
237
0
            filename, isc_result_totext(result));
238
0
    isc_lex_destroy(&lex);
239
0
    return result;
240
0
  }
241
242
0
  for (result = isc_lex_gettoken(lex, opt, &token);
243
0
       result == ISC_R_SUCCESS;
244
0
       result = isc_lex_gettoken(lex, opt, &token))
245
0
  {
246
0
    if (token.type == isc_tokentype_eol) {
247
0
      continue;
248
0
    }
249
250
0
    if (token.type != isc_tokentype_string) {
251
0
      CLEANUP(DNS_R_SYNTAX);
252
0
    }
253
254
0
    if (strcmp(STR(token), ";;") == 0) {
255
      /* New bundle */
256
0
      CHECK(isc_lex_gettoken(lex, opt, &token));
257
0
      if (token.type != isc_tokentype_string ||
258
0
          strcmp(STR(token), "SignedKeyResponse") != 0)
259
0
      {
260
0
        CLEANUP(DNS_R_SYNTAX);
261
0
      }
262
263
      /* Version */
264
0
      CHECK(isc_lex_gettoken(lex, opt, &token));
265
0
      if (token.type != isc_tokentype_string ||
266
0
          strcmp(STR(token), "1.0") != 0)
267
0
      {
268
0
        CLEANUP(DNS_R_SYNTAX);
269
0
      }
270
271
      /* Date and time of bundle */
272
0
      CHECK(isc_lex_gettoken(lex, opt, &token));
273
0
      if (token.type != isc_tokentype_string) {
274
0
        CLEANUP(DNS_R_SYNTAX);
275
0
      }
276
0
      if (strcmp(STR(token), "generated") == 0) {
277
        /* Final bundle */
278
0
        goto readline;
279
0
      }
280
0
      if (token.type != isc_tokentype_string) {
281
0
        CLEANUP(DNS_R_SYNTAX);
282
0
      }
283
284
      /* Add previous bundle */
285
0
      if (bundle != NULL) {
286
0
        addbundle(*skrp, &bundle);
287
0
      }
288
289
      /* Create new bundle */
290
0
      sscanf(STR(token), "%s", bundlebuf);
291
0
      CHECK(dns_time32_fromtext(bundlebuf, &bundle_id));
292
0
      bundle = NULL;
293
0
      skrbundle_create(mctx, (isc_stdtime_t)bundle_id,
294
0
           &bundle);
295
296
0
    readline:
297
      /* Read remainder of header line */
298
0
      do {
299
0
        CHECK(isc_lex_gettoken(lex, opt, &token));
300
0
      } while (token.type != isc_tokentype_eol);
301
0
    } else {
302
0
      isc_buffer_t buf;
303
0
      dns_rdata_t *rdata = NULL;
304
0
      u_char rdatabuf[DST_KEY_MAXSIZE];
305
0
      dns_rdatatype_t rdtype;
306
307
      /* Parse record */
308
0
      rdata = isc_mem_get(mctx, sizeof(*rdata));
309
0
      dns_rdata_init(rdata);
310
0
      isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf));
311
0
      result = parse_rr(lex, mctx, STR(token), origin,
312
0
            rdclass, &buf, &dnskeyttl, &rdtype,
313
0
            &rdata);
314
0
      if (result != ISC_R_SUCCESS) {
315
0
        isc_log_write(
316
0
          DNS_LOGCATEGORY_GENERAL,
317
0
          DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(1),
318
0
          "read skr file %s(%lu) parse rr "
319
0
          "failed: %s",
320
0
          filename, isc_lex_getsourceline(lex),
321
0
          isc_result_totext(result));
322
0
        isc_mem_put(mctx, rdata, sizeof(*rdata));
323
0
        goto cleanup;
324
0
      }
325
326
      /* Create new diff tuple */
327
0
      dns_diffop_t op = (rdtype == dns_rdatatype_rrsig)
328
0
              ? DNS_DIFFOP_ADDRESIGN
329
0
              : DNS_DIFFOP_ADD;
330
0
      dns_difftuple_t *tuple = NULL;
331
332
0
      dns_difftuple_create((*skrp)->mctx, op, origin,
333
0
               dnskeyttl, rdata, &tuple);
334
335
0
      skrbundle_addtuple(bundle, &tuple);
336
0
      INSIST(tuple == NULL);
337
338
0
      isc_mem_put(mctx, rdata, sizeof(*rdata));
339
0
    }
340
0
  }
341
342
0
  if (result != ISC_R_EOF) {
343
0
    CLEANUP(DNS_R_SYNTAX);
344
0
  }
345
0
  result = ISC_R_SUCCESS;
346
347
  /* Add final bundle */
348
0
  if (bundle != NULL) {
349
0
    addbundle(*skrp, &bundle);
350
0
  }
351
352
0
cleanup:
353
0
  if (result != ISC_R_SUCCESS) {
354
0
    isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
355
0
            ISC_LOG_DEBUG(1),
356
0
            "read skr file %s(%lu) failed: %s", filename,
357
0
            isc_lex_getsourceline(lex),
358
0
            isc_result_totext(result));
359
0
  }
360
361
  /* Clean up */
362
0
  isc_lex_destroy(&lex);
363
0
  return result;
364
0
}
365
366
dns_skrbundle_t *
367
0
dns_skr_lookup(dns_skr_t *skr, isc_stdtime_t time, uint32_t sigval) {
368
0
  REQUIRE(DNS_SKR_VALID(skr));
369
370
0
  ISC_LIST_FOREACH(skr->bundles, b, link) {
371
0
    dns_skrbundle_t *next = ISC_LIST_NEXT(b, link);
372
0
    isc_stdtime_t expired = (next != NULL)
373
0
            ? next->inception
374
0
            : (b->inception + sigval);
375
0
    if (b->inception <= time && time < expired) {
376
0
      return b;
377
0
    }
378
0
  }
379
380
0
  return NULL;
381
0
}
382
383
void
384
0
dns_skr_attach(dns_skr_t *source, dns_skr_t **targetp) {
385
0
  REQUIRE(DNS_SKR_VALID(source));
386
0
  REQUIRE(targetp != NULL && *targetp == NULL);
387
388
0
  isc_refcount_increment(&source->references);
389
0
  *targetp = source;
390
0
}
391
392
void
393
0
dns_skr_detach(dns_skr_t **skrp) {
394
0
  REQUIRE(skrp != NULL && DNS_SKR_VALID(*skrp));
395
396
0
  dns_skr_t *skr = *skrp;
397
0
  *skrp = NULL;
398
399
0
  if (isc_refcount_decrement(&skr->references) == 1) {
400
0
    dns_skr_destroy(skr);
401
0
  }
402
0
}
403
404
void
405
0
dns_skr_destroy(dns_skr_t *skr) {
406
0
  REQUIRE(DNS_SKR_VALID(skr));
407
408
0
  ISC_LIST_FOREACH(skr->bundles, b, link) {
409
0
    ISC_LIST_UNLINK(skr->bundles, b, link);
410
0
    dns_diff_clear(&b->diff);
411
0
    isc_mem_put(skr->mctx, b, sizeof(*b));
412
0
  }
413
0
  INSIST(ISC_LIST_EMPTY(skr->bundles));
414
415
0
  isc_mem_free(skr->mctx, skr->filename);
416
  isc_mem_putanddetach(&skr->mctx, skr, sizeof(*skr));
417
0
}