]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/dnssec_v2/dnssec_v2_trust_anchor.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / dnssec_v2 / dnssec_v2_trust_anchor.c
1 //
2 // dnssec_v2_trust_anchor.c
3 // mDNSResponder
4 //
5 // Copyright (c) 2020 Apple Inc. All rights reserved.
6 //
7
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"
16
17 static list_t trust_anchors; // list_t<trust_anchor_t>
18
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 {
22 domainname name;
23 mDNSu16 key_tag;
24 mDNSu8 algorithm;
25 mDNSu8 digest_type;
26 mDNSu16 digest_length;
27 mDNSu8 digest[MAX_HASH_OUTPUT_SIZE];
28 };
29
30 // hard code root trust anchor
31 static const trust_anchor_ds_t trusted_trust_anchor[] = {
32 {
33 .name = {
34 .c = {
35 0
36 }
37 },
38 .key_tag = 19036,
39 .algorithm = 8,
40 .digest_type = 2,
41 .digest_length = 32,
42 .digest = {
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
45 }
46 },
47 {
48 .name = {
49 .c = {
50 0
51 }
52 },
53 .key_tag = 20326,
54 .algorithm = 8,
55 .digest_type = 2,
56 .digest_length = 32,
57 .digest = {
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
60 }
61 },
62 // This dnssec.test. trust anchor is set to run local DNSSEC test by using dnssdutil server
63 {
64 .name = {
65 .c = {
66 6, 'd', 'n', 's', 's', 'e', 'c', 4, 't', 'e', 's' ,'t', 0
67 }
68 },
69 .key_tag = 36815,
70 .algorithm = 14,
71 .digest_type = 2,
72 .digest_length = 32,
73 .digest = {
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
76 }
77 }
78 };
79
80 //======================================================================================================================
81 // trust_anchors_t functions
82 //======================================================================================================================
83
84 //======================================================================================================================
85 // initialize_trust_anchors_t
86 //======================================================================================================================
87
88 mDNSexport void
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));
94 }
95
96 //======================================================================================================================
97 // uninitialize_trust_anchors_t
98 //======================================================================================================================
99
100 mDNSexport void
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);
104 }
105
106 //======================================================================================================================
107 // print_trust_anchors_t
108 //======================================================================================================================
109
110 mDNSexport void
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);
114
115 const list_t * const dnskey_records = &anchor->dnskey_trust_anchors;
116 const list_t * const ds_records = &anchor->ds_trust_anchors;
117
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);
122 }
123
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);
128 }
129 }
130
131 //======================================================================================================================
132 // init_and_load_trust_anchors
133 // load trust anchor when mDNSResponder initializes
134 //======================================================================================================================
135
136 mDNSexport mStatus
137 init_and_load_trust_anchors(void) {
138 mStatus error;
139 list_t * trust_anchor_list = &trust_anchors;
140
141 list_init(trust_anchor_list, sizeof(trust_anchors_t));
142
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);
151
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;
154 }
155
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);
158
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;
164
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
172
173 for_loop_exit:
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);
177 }
178 break;
179 }
180 }
181
182 return error;
183 }
184
185 //======================================================================================================================
186 // get_trust_anchor_with_name
187 // get the trust anchor with the corresponding zone name
188 //======================================================================================================================
189
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);
194
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)) {
198
199 trust_anchors_t * trust_anchor = (trust_anchors_t *)trust_anchor_node->data;
200
201 if (trust_anchor->name_hash == name_hash && DOMAIN_NAME_EQUALS(name, trust_anchor->name.c)) {
202 return trust_anchor;
203 }
204 }
205
206 return mDNSNULL;
207 }
208
209 //======================================================================================================================
210 // unint_trust_anchors
211 // free the trust anchor list when mDNSResponder exits
212 //======================================================================================================================
213
214 mDNSexport void
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)) {
220
221 trust_anchors_t * trust_anchor = (trust_anchors_t *)trust_anchor_node->data;
222 uninitialize_trust_anchors_t(trust_anchor);
223 }
224
225 list_uninit(trust_anchor_list);
226 }
227
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 //======================================================================================================================
233
234 mDNSexport mDNSBool
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;
241
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);
245 return mDNStrue;
246 }
247
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);
251 return mDNStrue;
252 }
253
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);
256 return mDNSfalse;
257 }
258
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);
262 return mDNSfalse;
263 }
264
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);
270 return mDNSfalse;
271 }
272
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;
277
278 if (zone->trust_anchor == mDNSNULL) { // nodes below the root have no trust anchor
279 verify(node != last_node);
280
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));
285 return mDNSfalse;
286 }
287
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));
292 return mDNSfalse;
293 }
294
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));
298 return mDNSfalse;
299 }
300
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));
305 return mDNSfalse;
306 }
307
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));
311 return mDNSfalse;
312 }
313
314 } else {
315 // zone->trust_anchor != mDNSNULL
316 verify(node == last_node);
317
318 // If the trust anchor is saved as DNSKEY, then we could use it to validate the records directly instead of
319 // querying for it
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));
323 return mDNStrue;
324 }
325
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));
329 return mDNStrue;
330 }
331
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));
334 return mDNSfalse;
335 }
336 }
337
338 log_error("should never reach here");
339 return mDNSfalse;
340 }
341
342 //======================================================================================================================
343 // trust_anchor_contains_deskey
344 //======================================================================================================================
345
346 mDNSexport mDNSBool
347 trust_anchor_contains_dnskey(const trust_anchors_t * const anchor) {
348 return anchor ? (!list_empty(&anchor->dnskey_trust_anchors)) : mDNSfalse;
349 }
350
351 //======================================================================================================================
352 // trust_anchor_contains_ds
353 //======================================================================================================================
354
355 mDNSexport mDNSBool
356 trust_anchor_contains_ds(const trust_anchors_t * const anchor) {
357 return anchor ? (!list_empty(&anchor->ds_trust_anchors)) : mDNSfalse;
358 }
359
360 #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)