2 // dnssec_v2_trust_anchor.c
5 // Copyright (c) 2020 Apple Inc. All rights reserved.
8 #include "mDNSEmbeddedAPI.h"
9 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
10 #include "DNSCommon.h"
11 #include "dnssec_v2_trust_anchor.h"
12 #include "dnssec_v2_crypto.h"
13 #include "dnssec_v2_trust_anchor.h"
14 #include "dnssec_v2_helper.h"
15 #include "dnssec_v2_log.h"
17 static list_t trust_anchors
; // list_t<trust_anchor_t>
19 // trust anchors egtting from https://www.iana.org/dnssec/files
20 typedef struct trust_anchor_ds trust_anchor_ds_t
;
21 struct trust_anchor_ds
{
26 mDNSu16 digest_length
;
27 mDNSu8 digest
[MAX_HASH_OUTPUT_SIZE
];
30 // hard code root trust anchor
31 static const trust_anchor_ds_t trusted_trust_anchor
[] = {
43 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60, 0x7A, 0x1A,
44 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5
58 0xE0, 0x6D, 0x44, 0xB8, 0x0B, 0x8F, 0x1D, 0x39, 0xA9, 0x5C, 0x0B, 0x0D, 0x7C, 0x65, 0xD0, 0x84, 0x58, 0xE8,
59 0x80, 0x40, 0x9B, 0xBC, 0x68, 0x34, 0x57, 0x10, 0x42, 0x37, 0xC7, 0xF8, 0xEC, 0x8D
62 // This dnssec.test. trust anchor is set to run local DNSSEC test by using dnssdutil server
66 6, 'd', 'n', 's', 's', 'e', 'c', 4, 't', 'e', 's' ,'t', 0
74 0x23, 0x51, 0xA5, 0x30, 0x3C, 0xD1, 0x68, 0x26, 0x70, 0x64, 0xF1, 0xED, 0x82, 0x53, 0x59, 0x82, 0x05, 0xE7,
75 0xDF, 0xBE, 0xE1, 0x8E, 0xBA, 0xA9, 0x40, 0xDD, 0x1F, 0x3F, 0x49, 0x97, 0xE3, 0x20
80 //======================================================================================================================
81 // trust_anchors_t functions
82 //======================================================================================================================
84 //======================================================================================================================
85 // initialize_trust_anchors_t
86 //======================================================================================================================
89 initialize_trust_anchors_t(trust_anchors_t
* const _Nonnull anchor
, const mDNSu8
*const _Nonnull zone_name
) {
90 memcpy(anchor
->name
.c
, zone_name
, DOMAIN_NAME_LENGTH(zone_name
));
91 anchor
->name_hash
= DomainNameHashValue(&anchor
->name
);
92 list_init(&anchor
->dnskey_trust_anchors
, sizeof(dnssec_dnskey_t
));
93 list_init(&anchor
->ds_trust_anchors
, sizeof(dnssec_ds_t
));
96 //======================================================================================================================
97 // uninitialize_trust_anchors_t
98 //======================================================================================================================
101 uninitialize_trust_anchors_t(trust_anchors_t
* const _Nonnull anchor
) {
102 list_uninit(&anchor
->dnskey_trust_anchors
);
103 list_uninit(&anchor
->ds_trust_anchors
);
106 //======================================================================================================================
107 // print_trust_anchors_t
108 //======================================================================================================================
111 print_trust_anchors_t(const trust_anchors_t
* const _Nonnull anchor
, mDNSu8 num_of_tabs
) {
112 log_debug(TAB_STR
"Name: " PRI_DM_NAME
, TAB_PARAM(num_of_tabs
), DM_NAME_PARAM(&anchor
->name
));
113 log_debug(TAB_STR
"Name Hash: %u", TAB_PARAM(num_of_tabs
), anchor
->name_hash
);
115 const list_t
* const dnskey_records
= &anchor
->dnskey_trust_anchors
;
116 const list_t
* const ds_records
= &anchor
->ds_trust_anchors
;
118 log_debug(TAB_STR
"DNSKEY Trust Anchor:", TAB_PARAM(num_of_tabs
));
119 for (list_node_t
*node
= list_get_first(dnskey_records
); !list_has_ended(dnskey_records
, node
); node
= list_next(node
)) {
120 const dnssec_dnskey_t
* const dnssec_dnskey
= (dnssec_dnskey_t
*)node
->data
;
121 print_dnssec_dnskey_t(dnssec_dnskey
, num_of_tabs
+ 1);
124 log_debug(TAB_STR
"DS Trust Anchor:", TAB_PARAM(num_of_tabs
));
125 for (list_node_t
*node
= list_get_first(ds_records
); !list_has_ended(ds_records
, node
); node
= list_next(node
)) {
126 const dnssec_ds_t
* const dnssec_ds
= (dnssec_ds_t
*)node
->data
;
127 print_dnssec_ds_t(dnssec_ds
, num_of_tabs
+ 1);
131 //======================================================================================================================
132 // init_and_load_trust_anchors
133 // load trust anchor when mDNSResponder initializes
134 //======================================================================================================================
137 init_and_load_trust_anchors(void) {
139 list_t
* trust_anchor_list
= &trust_anchors
;
141 list_init(trust_anchor_list
, sizeof(trust_anchors_t
));
143 for (int i
= 0, limit
= sizeof(trusted_trust_anchor
) / sizeof(trust_anchor_ds_t
); i
< limit
; i
++) {
144 const trust_anchor_ds_t
* const hardcoded_trust_anchor_ds
= &trusted_trust_anchor
[i
];
145 trust_anchors_t
* trust_anchors_from_same_zone
= get_trust_anchor_with_name(hardcoded_trust_anchor_ds
->name
.c
);
146 trust_anchors_t
* new_trust_anchors_initialized
= mDNSNULL
;
147 dnssec_ds_t
* ds_to_insert
;
148 if (trust_anchors_from_same_zone
== mDNSNULL
) {
149 error
= list_append_uinitialized(trust_anchor_list
, sizeof(trust_anchors_t
), (void **)&trust_anchors_from_same_zone
);
150 require_quiet(error
== mStatus_NoError
, for_loop_exit
);
152 initialize_trust_anchors_t(trust_anchors_from_same_zone
, hardcoded_trust_anchor_ds
->name
.c
);
153 new_trust_anchors_initialized
= trust_anchors_from_same_zone
;
156 error
= list_append_uinitialized(&trust_anchors_from_same_zone
->ds_trust_anchors
, sizeof(dnssec_ds_t
), (void **)&ds_to_insert
);
157 require_quiet(error
== mStatus_NoError
, for_loop_exit
);
159 ds_to_insert
->key_tag
= hardcoded_trust_anchor_ds
->key_tag
;
160 ds_to_insert
->algorithm
= hardcoded_trust_anchor_ds
->algorithm
;
161 ds_to_insert
->digest_type
= hardcoded_trust_anchor_ds
->digest_type
;
162 ds_to_insert
->digest_length
= hardcoded_trust_anchor_ds
->digest_length
;
163 ds_to_insert
->digest
= hardcoded_trust_anchor_ds
->digest
;
165 ds_to_insert
->dnssec_rr
.rr_type
= kDNSType_DS
;
166 ds_to_insert
->dnssec_rr
.rr_class
= 1; // IN_CLASS
167 ds_to_insert
->dnssec_rr
.rdata_length
= 36; // 4 + 32
168 ds_to_insert
->dnssec_rr
.name_hash
= DomainNameHashValue(&trust_anchors_from_same_zone
->name
);
169 memcpy(ds_to_insert
->dnssec_rr
.name
.c
, trust_anchors_from_same_zone
->name
.c
, DomainNameLength(&trust_anchors_from_same_zone
->name
));
170 ds_to_insert
->dnssec_rr
.rdata_hash
= 0; // fake value
171 ds_to_insert
->dnssec_rr
.rdata
= mDNSNULL
; // fake value
174 if (error
!= mStatus_NoError
) {
175 if (new_trust_anchors_initialized
!= mDNSNULL
) {
176 list_delete_node_with_data_ptr(trust_anchor_list
, new_trust_anchors_initialized
);
185 //======================================================================================================================
186 // get_trust_anchor_with_name
187 // get the trust anchor with the corresponding zone name
188 //======================================================================================================================
190 mDNSexport trust_anchors_t
* _Nullable
191 get_trust_anchor_with_name(const mDNSu8
* _Nonnull
const name
) {
192 list_t
*trust_anchor_list
= &trust_anchors
;
193 mDNSu32 name_hash
= DomainNameHashValue((domainname
*)name
);
195 for (list_node_t
*trust_anchor_node
= list_get_first(trust_anchor_list
);
196 !list_has_ended(trust_anchor_list
, trust_anchor_node
);
197 trust_anchor_node
= list_next(trust_anchor_node
)) {
199 trust_anchors_t
* trust_anchor
= (trust_anchors_t
*)trust_anchor_node
->data
;
201 if (trust_anchor
->name_hash
== name_hash
&& DOMAIN_NAME_EQUALS(name
, trust_anchor
->name
.c
)) {
209 //======================================================================================================================
210 // unint_trust_anchors
211 // free the trust anchor list when mDNSResponder exits
212 //======================================================================================================================
215 uninit_trust_anchors(void) { // list_t <trust_anchors_t>
216 list_t
*trust_anchor_list
= &trust_anchors
;
217 for (list_node_t
*trust_anchor_node
= list_get_first(trust_anchor_list
);
218 !list_has_ended(trust_anchor_list
, trust_anchor_node
);
219 trust_anchor_node
= list_next(trust_anchor_node
)) {
221 trust_anchors_t
* trust_anchor
= (trust_anchors_t
*)trust_anchor_node
->data
;
222 uninitialize_trust_anchors_t(trust_anchor
);
225 list_uninit(trust_anchor_list
);
228 //======================================================================================================================
229 // trust_anchor_can_be_reached
230 // check if there is a path in validation tree from leaf(the expected answer/NSEC/NSEC3) to the trust anchor.
231 // If so, we can start the validation process.
232 //======================================================================================================================
235 trust_anchor_can_be_reached(dnssec_context_t
* const _Nonnull context
) {
236 list_t
* zones
= &context
->zone_chain
;
237 dnssec_zone_t
* zone
= mDNSNULL
;
238 originals_with_rrsig_t
* original
= &context
->original
.original_result_with_rrsig
;
239 const list_node_t
* last_node
;
240 mDNSu32 request_id
= context
->original
.original_parameters
.request_id
;
242 // If the original response has trust anchor, just match itto see if it is trusted
243 if (context
->original
.original_trust_anchor
!= mDNSNULL
) {
244 log_default("[R%u] trust_anchor_can_be_reached? Yes, trust anchor found for original response", request_id
);
248 // Suppressed response comes from mDNSResponder intenal policy, it shold always be trusted
249 if (original
->type
== original_response
&& original
->u
.original
.suppressed_response
) {
250 log_default("[R%u] trust_anchor_can_be_reached? Yes, suppressed answer is always trusted", request_id
);
254 if (original
->type
== unknown_response
) {
255 log_default("[R%u] trust_anchor_can_be_reached? No, did not get any original response", request_id
);
259 // needs at leaset one zone to reach the trust anchor
260 if (list_empty(zones
)) {
261 log_default("[R%u] trust_anchor_can_be_reached? No, zone list is empty", request_id
);
265 // The first zone is leaf, the last one is root, the root must have trust anchor to be validated.
266 last_node
= list_get_last(zones
);
267 zone
= (dnssec_zone_t
*)last_node
->data
;
268 if (zone
->trust_anchor
== mDNSNULL
) {
269 log_default("[R%u] trust_anchor_can_be_reached? No, no trust anchor found in the current root", request_id
);
273 // starting from the first zone node(which is leaf) to the last node(which is root), to see if they could be connected
274 // through chain of trust
275 for (list_node_t
*node
= list_get_first(zones
); !list_has_ended(zones
, node
); node
= list_next(node
)) {
276 zone
= (dnssec_zone_t
*)node
->data
;
278 if (zone
->trust_anchor
== mDNSNULL
) { // nodes below the root have no trust anchor
279 verify(node
!= last_node
);
281 // if the data structure that caches the necessary records is not initialized, then the chain is not completed
282 if (!zone
->dses_initialized
) {
283 log_default("[R%u] trust_anchor_can_be_reached? No, not receiving DS reocrds; qname=" PRI_DM_NAME
,
284 request_id
, DM_NAME_PARAM(&zone
->domain_name
));
288 // if the response is not completely returned to the DNSSEC handler, then the chain is not completed
289 if (!zone
->dnskeys_with_rrsig
.set_completed
) {
290 log_default("[R%u] trust_anchor_can_be_reached? No, DNSKEY Set is not completed; qname=" PRI_DM_NAME
,
291 request_id
, DM_NAME_PARAM(&zone
->domain_name
));
295 if (!contains_rrsig_in_dnskeys_with_rrsig_t(&zone
->dnskeys_with_rrsig
)) {
296 log_default("[R%u] trust_anchor_can_be_reached? No, DNSKEY Set does not have any RRSIG; qname=" PRI_DM_NAME
,
297 request_id
, DM_NAME_PARAM(&zone
->domain_name
));
301 // if the response is not completely returned to the DNSSEC handler, then the chain is not completed
302 if (!zone
->dses_with_rrsig
.set_completed
) {
303 log_default("[R%u] trust_anchor_can_be_reached? No, DS Set is not completed; qname=" PRI_DM_NAME
,
304 request_id
, DM_NAME_PARAM(&zone
->domain_name
));
308 if (!contains_rrsig_in_dses_with_rrsig_t(&zone
->dses_with_rrsig
)) {
309 log_default("[R%u] trust_anchor_can_be_reached? No, DS Set does not have any RRSIG; qname=" PRI_DM_NAME
,
310 request_id
, DM_NAME_PARAM(&zone
->domain_name
));
315 // zone->trust_anchor != mDNSNULL
316 verify(node
== last_node
);
318 // If the trust anchor is saved as DNSKEY, then we could use it to validate the records directly instead of
320 if (trust_anchor_contains_dnskey(zone
->trust_anchor
)) {
321 // has DNSKEY trust anchor, can be used to establish chain of trust
322 log_default("[R%u] trust_anchor_can_be_reached? Yes, it is DNSKEY trust anchor; qname=" PRI_DM_NAME
, request_id
, DM_NAME_PARAM(&zone
->domain_name
));
326 // If the trust anchor is saved as DS, we should wait for the DNSKEY response come back before doing validation
327 if (trust_anchor_contains_ds(zone
->trust_anchor
) && zone
->dnskeys_with_rrsig
.set_completed
) {
328 log_default("[R%u] trust_anchor_can_be_reached? Yes, it is DS trust anchor; qname=" PRI_DM_NAME
, request_id
, DM_NAME_PARAM(&zone
->domain_name
));
332 // does not get enough dnskey records to verify
333 log_default("[R%u] trust_anchor_can_be_reached? No, DNSKEY set is not complete; qname=" PRI_DM_NAME
, request_id
, DM_NAME_PARAM(&zone
->domain_name
));
338 log_error("should never reach here");
342 //======================================================================================================================
343 // trust_anchor_contains_deskey
344 //======================================================================================================================
347 trust_anchor_contains_dnskey(const trust_anchors_t
* const anchor
) {
348 return anchor
? (!list_empty(&anchor
->dnskey_trust_anchors
)) : mDNSfalse
;
351 //======================================================================================================================
352 // trust_anchor_contains_ds
353 //======================================================================================================================
356 trust_anchor_contains_ds(const trust_anchors_t
* const anchor
) {
357 return anchor
? (!list_empty(&anchor
->ds_trust_anchors
)) : mDNSfalse
;
360 #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)