Documentation in comments as a review guideline

This commit is contained in:
Willem Toorop 2015-07-06 11:57:16 +02:00
parent 70edb60f09
commit 55444d07a2
1 changed files with 506 additions and 286 deletions

View File

@ -39,8 +39,152 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Elements used in this file.
/*
* From the API:
*
* The "dnssec_return_validation_chain" extension as explained in section 3.1:
*
* Applications that want to do their own validation will want to have the
* DNSSEC-related records for a particular response. Use the
* dnssec_return_validation_chain extension. The extension's value
* (an int) is set to GETDNS_EXTENSION_TRUE to cause a set of additional
* DNSSEC-related records needed for validation to be returned in the
* response object. This set comes as validation_chain (a list) at the top
* level of the response object. This list includes all resource record
* dicts for all the resource records (DS, DNSKEY and their RRSIGs) that
* are needed to perform the validation from the root up.
*
*
* The getdns_validate_dnssec() function as explained in section 7:
*
* If an application wants the API do perform DNSSEC validation without
* using the extensions, it can use the getdns_validate_dnssec() helper
* function.
*
* getdns_return_t
* getdns_validate_dnssec(
* getdns_list *record_to_validate,
* getdns_list *bundle_of_support_records,
* getdns_list *trust_anchor_records
* );
*
* The record_to_validate is the resource record being validated together
* with the associated signatures. The API will use the resource records
* in bundle_of_support_records to construct the validation chain and the
* DNSKEY or DS records in trust_anchor_records as trust anchors. The
* function returns one of GETDNS_DNSSEC_SECURE, GETDNS_DNSSEC_BOGUS,
* GETDNS_DNSSEC_INDETERMINATE, or GETDNS_DNSSEC_INSECURE.
*/
/* Outline of operations in this file
* ==================================
*
* Data structure to represent the delegation/referal hierarchy
* ------------------------------------------------------------
* Both the "dnssec_return_validation_chain" extension, and the
* getdns_validate_dnssec() function use the same structs to represent the
* involved pieces of the DNS in a hierarchical manner.
*
* However, the tree is not represented from the root, but from the RRsets that
* need to be validated. The RRset to validate is a member of the chain_head
* struct for this. The chain_head struct has a "next" member to form a linked
* list of RRsets to validate.
*
* The chain_head struct also has a "parent" member to a linked list of
* chain_node structs (linked with the "parent" member of those chain_nodes).
* For each label in the name of the rrset in a chain_head, is a chain_node,
* all the way to the root. The last chain_node is thus always the root, for
* every chain_head.
*
* The construction functions for this datastructure make sure there is always
* a single chain_node representing the same name. They also make sure space
* for chain_head + the number of extra chain_nodes needed is allocated in a
* single region, so that on destruction one only has to free the chain_heads.
*
* A chain_node contains two RRset members, "dnskey" and "ds" which represent
* the potential client side DNSKEYs and the parent side DS records of a
* potential zonecut at this point. Whether or not there is an actual zone
* cut is determined separately. With the "dnssec_return_validation_chain"
* extension by scheduling queries, and with the getdns_validation_dnssec()
* function by provisioning the support records at the chain nodes.
*
* In the construction functions a chain_head is created for every RRset in
* the answer and authority section of a given packet (except for synthesized
* CNAMEs). Furthermore, if the queries for name/class/type is not in the
* packet, a chain_head for the non-existent rrset is created too, to that
* it will be evaluated for non-existence later in the validation process.
*
* The chain_head and chain_node structs are defined in section:
* "Validation Chain Data Structs". The functions to construct the hierarchy
* are defined in section "Validation Chain Construction". When the
* construction functions are called for the purpose of the
* "dnssec_return_validation_chain" extension, queries to provision the
* chain_nodes are scheduled. Function theretofore are in section:
* "Schedule Queries to Provision Validation Chain"
*
*
* getdns_rrset
* ------------
* RRsets used in the structure described above are represented by the
* getdns_rrset struct. They consist of name/rr_class and rr_type members
* plus a reference to the wireformat packet that should contain the RRset.
*
* The actual RR's in the rrset and the signatures are only accessed via
* iterators; substantiated with the rrtype_iter struct to iterate over RRs
* in a getdns_rrset, and the rrsig_iter to iterate over the RRSIGs covering
* the RRs in the getdns_rrset.
*
* The getdns_rrsets are already equiped with name/rr_class and rr_type when
* constructing the linked list of chain_nodes up to the root for a chain_head.
* They are substantiated with the wireformat packets that are returned with
* the queries that were sheduled in the context of the
* "dnssec_return_validation_chain" extension.
*
* Note that the NSEC(3) RRsets proving the non-existance of and getdns_rrset
* can be found by processing that getdns_rrset, as it contains the pointer
* to the wireformat data that should either contain the RRset or the proof
* of non-existance.
*
* The getdns_validate_dnssec() function, after it constructed the chain_heads
* hierarchy, creates an artifical packet for the support records and equips
* all the ds and dnskey getdns_rrsets on the chain_nodes with this packet.
*
* The getdns_rrset + support function and data types are defined in section:
* "getdns_rrset + Support Iterators"
*
*
* Validation
* ----------
* Validation of a constructed chain is done by the
* chain_set_netreq_dnssec_status() function when validating in stub mode.
* And with the chain_validate_dnssec() function when using the
* getdns_validate_dnssec() function. They are the same, except that
* chain_set_netreq_dnssec_status() evaluates DNSSEC status per network
* request and chain_validate_dnssec() does it for the whole chain.
*
* They both evaluate the DNSSEC status for each head in turn. The worst
* DNSSEC status determines the status of all heads evaluated. Where
* INSECURE is worse then SECURE, and BOGUS is worse then INSECURE.
*
* For each head, each trust anchor is tried. Here the best DNSSEC status
* determines the status of the head.
*
* Security status for a head (with a specific trust anchor) is evaluated by
* first finding a authenticated keyset from the parent chain_nodes, and then
* evaluating the rrset of the head (existent or not) with that keyset.
*
* Functions that implement DNSSEC validation are in section:
* "DNSSEC Validation".
*
* Many functions are of key verification boolean return type; e.g.
* key_proves_non_existance(), ds_authenticates_keys(), a_key_signed_rrset()
* These will return the keytag identifying the key that was used to
* authenticate + 0x10000 to allow keytag 0.
*
* These returned keytag's are used later with function
* append_rrs2val_chain_list() to return a "dnssec_validation_chain" that
* enumerates a single RRSIG per RRset. This can be found in section:
* "dnssec_return_validation_chain Extension".
*/
#include <sys/types.h>
@ -98,6 +242,19 @@ static int _dname_is_parent(
return *parent == 0;
}
static uint8_t *_dname_label_copy(uint8_t *dst, uint8_t *src, size_t dst_len)
{
uint8_t *r = dst, i;
if (!src || *src + 1 > dst_len)
return NULL;
for (i = *src + 1; i > 0; i--)
*dst++ = tolower(*src++);
return r;
}
static uint8_t **reverse_labels(uint8_t *dname, uint8_t **labels)
{
if (*dname)
@ -228,11 +385,28 @@ inline static void debug_sec_print_pkt(
*****************************************************************************/
/* Utility functions to read rr_type and rr_class from a rr iterator */
static inline uint16_t rr_iter_type(priv_getdns_rr_iter *rr)
{ return rr->rr_type + 2 <= rr->nxt ? gldns_read_uint16(rr->rr_type) : 0; }
static inline uint16_t rr_iter_class(priv_getdns_rr_iter *rr)
{ return rr->rr_type + 4 <= rr->nxt ? gldns_read_uint16(rr->rr_type + 2) : 0; }
/* Utility function to compare owner name of rr with name */
static int rr_owner_equal(priv_getdns_rr_iter *rr, uint8_t *name)
{
uint8_t owner_spc[256], *owner;
size_t owner_len = sizeof(owner_spc);
return (owner = priv_getdns_owner_if_or_as_decompressed(rr, owner_spc
, &owner_len))
&& _dname_equal(owner, name);
}
/* First a few filter functions that filter a RR iterator to point only
* to RRs with certain constraints (and moves on otherwise).
*/
/* Filter that only iterates over the ANSWER and AUTHORITY section */
static priv_getdns_rr_iter *rr_iter_ansauth(priv_getdns_rr_iter *rr)
{
while (rr && rr->pos && !(
@ -244,16 +418,7 @@ static priv_getdns_rr_iter *rr_iter_ansauth(priv_getdns_rr_iter *rr)
return rr && rr->pos ? rr : NULL;
}
static int rr_owner_equal(priv_getdns_rr_iter *rr, uint8_t *name)
{
uint8_t owner_spc[256], *owner;
size_t owner_len = sizeof(owner_spc);
return (owner = priv_getdns_owner_if_or_as_decompressed(rr, owner_spc
, &owner_len))
&& _dname_equal(owner, name);
}
/* Filter that only iterates over RRs with a certain name/class/type */
static priv_getdns_rr_iter *rr_iter_name_class_type(priv_getdns_rr_iter *rr,
uint8_t *name, uint16_t rr_class, uint16_t rr_type)
{
@ -267,6 +432,7 @@ static priv_getdns_rr_iter *rr_iter_name_class_type(priv_getdns_rr_iter *rr,
return rr && rr->pos ? rr : NULL;
}
/* Filter that only iterates over RRs that do not have a name/class/type */
static priv_getdns_rr_iter *rr_iter_not_name_class_type(priv_getdns_rr_iter *rr,
uint8_t *name, uint16_t rr_class, uint16_t rr_type)
{
@ -281,6 +447,9 @@ static priv_getdns_rr_iter *rr_iter_not_name_class_type(priv_getdns_rr_iter *rr,
return rr && rr->pos ? rr : NULL;
}
/* Filter that only iterates over RRs that are of type RRSIG, that cover
* a RRset with a certain name/class/type
*/
static priv_getdns_rr_iter *rr_iter_rrsig_covering(priv_getdns_rr_iter *rr,
uint8_t *name, uint16_t rr_class, uint16_t rr_type)
{
@ -406,6 +575,9 @@ static void debug_sec_print_rrset(const char *msg, getdns_rrset *rrset)
#define debug_sec_print_rrset(...) DEBUG_OFF(__VA_ARGS__)
#endif
/* The rrset_iter manifests an iterator of a wireformat packet that will return
* all unique rrsets within that packet in turn.
*/
typedef struct rrset_iter rrset_iter;
struct rrset_iter {
getdns_rrset rrset;
@ -476,6 +648,24 @@ static getdns_rrset *rrset_iter_value(rrset_iter *i)
return &i->rrset;
}
static getdns_rrset *rrset_by_type(
rrset_iter *i_spc, getdns_network_req *netreq, uint16_t rr_type)
{
rrset_iter *i;
getdns_rrset *rrset;
for ( i = rrset_iter_init(i_spc,netreq->response,netreq->response_len)
; i
; i = rrset_iter_next(i)) {
rrset = rrset_iter_value(i);
if (rrset->rr_type == rr_type) /* Check class too? */
return rrset;
}
return NULL;
}
/********************* Validation Chain Data Structs ***********************
*****************************************************************************/
@ -841,6 +1031,189 @@ static void add_question2val_chain(struct mem_funcs *mf,
}
/************* Schedule Queries to Provision Validation Chain ***************
*****************************************************************************/
static void check_chain_complete(chain_head *chain);
static void val_chain_node_soa_cb(getdns_dns_req *dnsreq);
static void val_chain_sched_soa_node(chain_node *node)
{
getdns_context *context;
getdns_eventloop *loop;
getdns_dns_req *dnsreq;
char name[1024];
context = node->chains->netreq->owner->context;
loop = node->chains->netreq->owner->loop;
if (!gldns_wire2str_dname_buf(node->ds.name, 256, name, sizeof(name)))
return;
if (! node->soa_req &&
! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_SOA,
dnssec_ok_checking_disabled, node, &dnsreq, NULL,
val_chain_node_soa_cb))
node->soa_req = dnsreq->netreqs[0];
}
static void val_chain_sched_soa(chain_head *head, uint8_t *dname)
{
chain_node *node;
if (!head->netreq)
return;
if (!*dname)
return;
for ( node = head->parent
; node && !_dname_equal(dname, node->ds.name)
; node = node->parent);
if (node)
val_chain_sched_soa_node(node);
}
static void val_chain_node_cb(getdns_dns_req *dnsreq);
static void val_chain_sched_node(chain_node *node)
{
getdns_context *context;
getdns_eventloop *loop;
getdns_dns_req *dnsreq;
char name[1024];
context = node->chains->netreq->owner->context;
loop = node->chains->netreq->owner->loop;
if (!gldns_wire2str_dname_buf(node->ds.name, 256, name, sizeof(name)))
return;
DEBUG_SEC("schedule DS & DNSKEY lookup for %s\n", name);
if (! node->dnskey_req /* not scheduled */ &&
! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_DNSKEY,
dnssec_ok_checking_disabled, node, &dnsreq, NULL, val_chain_node_cb))
node->dnskey_req = dnsreq->netreqs[0];
if (! node->ds_req && node->parent /* not root */ &&
! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_DS,
dnssec_ok_checking_disabled, node, &dnsreq, NULL, val_chain_node_cb))
node->ds_req = dnsreq->netreqs[0];
}
static void val_chain_sched(chain_head *head, uint8_t *dname)
{
chain_node *node;
if (!head->netreq)
return;
for ( node = head->parent
; node && !_dname_equal(dname, node->ds.name)
; node = node->parent);
if (node)
val_chain_sched_node(node);
}
static void val_chain_sched_signer_node(chain_node *node, rrsig_iter *rrsig)
{
priv_getdns_rdf_iter rdf_spc, *rdf;
uint8_t signer_spc[256], *signer;
size_t signer_len;
if (!(rdf = priv_getdns_rdf_iter_init_at(&rdf_spc, &rrsig->rr_i, 7)))
return;
if (!(signer = priv_getdns_rdf_if_or_as_decompressed(
rdf, signer_spc, &signer_len)))
return;
while (node && !_dname_equal(signer, node->ds.name))
node = node->parent;
if (node)
val_chain_sched_node(node);
}
static void val_chain_sched_signer(chain_head *head, rrsig_iter *rrsig)
{
if (!head->netreq)
return;
val_chain_sched_signer_node(head->parent, rrsig);
}
static void val_chain_node_cb(getdns_dns_req *dnsreq)
{
chain_node *node = (chain_node *)dnsreq->user_pointer;
getdns_network_req *netreq = dnsreq->netreqs[0];
rrset_iter *i, i_spc;
getdns_rrset *rrset;
rrsig_iter *rrsig, rrsig_spc;
getdns_context_clear_outbound_request(dnsreq);
switch (netreq->request_type) {
case GETDNS_RRTYPE_DS : node->ds.pkt = netreq->response;
node->ds.pkt_len = netreq->response_len;
break;
case GETDNS_RRTYPE_DNSKEY: node->dnskey.pkt = netreq->response;
node->dnskey.pkt_len = netreq->response_len;
default : check_chain_complete(node->chains);
return;
}
for ( i = rrset_iter_init(&i_spc,netreq->response,netreq->response_len)
; i
; i = rrset_iter_next(i)) {
rrset = rrset_iter_value(i);
if (rrset->rr_type != GETDNS_RRTYPE_DS &&
rrset->rr_type != GETDNS_RRTYPE_NSEC &&
rrset->rr_type != GETDNS_RRTYPE_NSEC3)
continue;
for ( rrsig = rrsig_iter_init(&rrsig_spc, rrset)
; rrsig; rrsig = rrsig_iter_next(rrsig))
val_chain_sched_signer_node(node, rrsig);
}
check_chain_complete(node->chains);
}
static void val_chain_node_soa_cb(getdns_dns_req *dnsreq)
{
chain_node *node = (chain_node *)dnsreq->user_pointer;
getdns_network_req *netreq = dnsreq->netreqs[0];
rrset_iter i_spc;
getdns_rrset *rrset;
getdns_context_clear_outbound_request(dnsreq);
if ((rrset = rrset_by_type(&i_spc, netreq, GETDNS_RRTYPE_SOA))) {
while (node &&
! _dname_equal(node->ds.name, rrset->name))
node = node->parent;
if (node)
val_chain_sched_node(node);
} else
val_chain_sched_soa_node(node->parent);
check_chain_complete(node->chains);
}
/*************************** DNSSEC Validation *****************************
*****************************************************************************/
/* Returns whether a key in set dnskey is used to sign rrset.
* Only keytag and signer name is compared. The signature is not verified.
*/
static int key_matches_signer(getdns_rrset *dnskey, getdns_rrset *rrset)
{
rrtype_iter rr_spc, *rr;
@ -965,6 +1338,7 @@ static int _getdns_verify_rrsig(
return 1;
}
/* Calculates NSEC3 hash for name, and stores that into label */
static uint8_t *_getdns_nsec3_hash_label(uint8_t *label, size_t label_len,
uint8_t *name, uint8_t algorithm, uint16_t iterations, uint8_t *salt)
{
@ -986,6 +1360,53 @@ static uint8_t *_getdns_nsec3_hash_label(uint8_t *label, size_t label_len,
return label;
}
static uint8_t *name2nsec3_label(
getdns_rrset *nsec3, uint8_t *name, uint8_t *label, size_t label_len)
{
rrsig_iter rrsig_spc, *rrsig;
priv_getdns_rdf_iter rdf_spc, *rdf;
uint8_t signer_spc[256], *signer;
size_t signer_len = sizeof(signer_spc);
rrtype_iter rr_spc, *rr;
if (/* With the "first" signature */
(rrsig = rrsig_iter_init(&rrsig_spc, nsec3))
/* Access the signer name rdata field (7th) */
&& (rdf = priv_getdns_rdf_iter_init_at(
&rdf_spc, &rrsig->rr_i, 7))
/* Verify & decompress */
&& (signer = priv_getdns_rdf_if_or_as_decompressed(
rdf, signer_spc, &signer_len))
/* signer of the NSEC3 is direct parent for this NSEC3? */
&& _dname_equal(
signer, nsec3->name + *nsec3->name + 1)
/* signer of the NSEC3 is parent of name? */
&& _dname_is_parent(signer, name)
/* Initialize rr for getting NSEC3 rdata fields */
&& (rr = rrtype_iter_init(&rr_spc, nsec3))
/* Check for available space to get rdata fields */
&& rr->rr_i.rr_type + 15 <= rr->rr_i.nxt
&& rr->rr_i.rr_type + 14 + rr->rr_i.rr_type[14] <= rr->rr_i.nxt)
/* Get the hashed label */
return _getdns_nsec3_hash_label(label, label_len, name,
rr->rr_i.rr_type[10],
gldns_read_uint16(rr->rr_i.rr_type + 12),
rr->rr_i.rr_type + 14);
return NULL;
}
/* Returns whether dnskey signed rrset. If the rrset was a valid wildcard
* expansion, nc_name will point to the next closer part of the name in rrset.
*/
static int dnskey_signed_rrset(
rrtype_iter *dnskey, getdns_rrset *rrset, uint8_t **nc_name)
{
@ -1036,6 +1457,10 @@ static int dnskey_signed_rrset(
static int find_nsec_covering_name(
getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *name, int *opt_out);
/* Returns whether a dnskey for keyset signed rrset. If the rrset was a valid
* wildcard expansion, nc_name will point to the next closer part of the name
* in rrset.
*/
static int a_key_signed_rrset(getdns_rrset *keyset, getdns_rrset *rrset)
{
rrtype_iter dnskey_spc, *dnskey;
@ -1070,6 +1495,9 @@ static int a_key_signed_rrset(getdns_rrset *keyset, getdns_rrset *rrset)
return 0;
}
/* Returns whether a DS in ds_set matches a dnskey in dnskey_set which in turn
* signed the dnskey set.
*/
static int ds_authenticates_keys(getdns_rrset *ds_set, getdns_rrset *dnskey_set)
{
rrtype_iter dnskey_spc, *dnskey;
@ -1193,61 +1621,6 @@ static int nsec_covers_name(
}
}
static uint8_t *name2nsec3_label(
getdns_rrset *nsec3, uint8_t *name, uint8_t *label, size_t label_len)
{
rrsig_iter rrsig_spc, *rrsig;
priv_getdns_rdf_iter rdf_spc, *rdf;
uint8_t signer_spc[256], *signer;
size_t signer_len = sizeof(signer_spc);
rrtype_iter rr_spc, *rr;
if (/* With the "first" signature */
(rrsig = rrsig_iter_init(&rrsig_spc, nsec3))
/* Access the signer name rdata field (7th) */
&& (rdf = priv_getdns_rdf_iter_init_at(
&rdf_spc, &rrsig->rr_i, 7))
/* Verify & decompress */
&& (signer = priv_getdns_rdf_if_or_as_decompressed(
rdf, signer_spc, &signer_len))
/* signer of the NSEC3 is direct parent for this NSEC3? */
&& _dname_equal(
signer, nsec3->name + *nsec3->name + 1)
/* signer of the NSEC3 is parent of name? */
&& _dname_is_parent(signer, name)
/* Initialize rr for getting NSEC3 rdata fields */
&& (rr = rrtype_iter_init(&rr_spc, nsec3))
/* Check for available space to get rdata fields */
&& rr->rr_i.rr_type + 15 <= rr->rr_i.nxt
&& rr->rr_i.rr_type + 14 + rr->rr_i.rr_type[14] <= rr->rr_i.nxt)
/* Get the hashed label */
return _getdns_nsec3_hash_label(label, label_len, name,
rr->rr_i.rr_type[10],
gldns_read_uint16(rr->rr_i.rr_type + 12),
rr->rr_i.rr_type + 14);
return NULL;
}
static uint8_t *_dname_label_copy(uint8_t *dst, uint8_t *src, size_t dst_len)
{
uint8_t *r = dst, i;
if (!src || *src + 1 > dst_len)
return NULL;
for (i = *src + 1; i > 0; i--)
*dst++ = tolower(*src++);
return r;
}
static int nsec3_matches_name(getdns_rrset *nsec3, uint8_t *name)
{
uint8_t label[64], owner[64];
@ -1706,6 +2079,14 @@ static int key_proves_nonexistance(
return 0;
}
/* Descend down to the root along chain_nodes. Try to find a keyset
* authenticated by a key in ta rrset (trust anchor). When we found one,
* ascend back up, authenticating more specific keysets along the chain.
*
* The most specific keyset is returned in keys. Also a DNSSEC status is
* returned. BOGUS if no keyset could be found. INSECURE if the
* non-existence of a DS along the path is proofed, and SECURE otherwise.
*/
static int chain_node_get_trusted_keys(
chain_node *node, getdns_rrset *ta, getdns_rrset **keys)
{
@ -1774,6 +2155,10 @@ static int chain_node_get_trusted_keys(
return GETDNS_DNSSEC_SECURE;
}
/* The DNSSEC status of the rrset of head is evaluated with trust anchor ta.
* For this first a secure keyset is looked up, with which the keyset is
* evaluated.
*/
static int chain_head_validate_with_ta(chain_head *head, getdns_rrset *ta)
{
getdns_rrset *keys;
@ -1804,6 +2189,9 @@ static int chain_head_validate_with_ta(chain_head *head, getdns_rrset *ta)
return GETDNS_DNSSEC_BOGUS;
}
/* The DNSSEC status of the rrset in head is evaluated by trying the trust
* anchors in tas in turn. The best outcome counts.
*/
static int chain_head_validate(chain_head *head, rrset_iter *tas)
{
rrset_iter *i;
@ -1834,39 +2222,10 @@ static int chain_head_validate(chain_head *head, rrset_iter *tas)
return s;
}
static int chain_validate_dnssec(chain_head *chain, rrset_iter *tas)
{
int s = GETDNS_DNSSEC_INDETERMINATE, t;
chain_head *head;
/* The netreq status is the worst for any head */
for (head = chain; head; head = head->next) {
t = chain_head_validate(head, tas);
debug_sec_print_rrset("head ", &head->rrset);
DEBUG_SEC("1. validated to: %d -> %d\n", t, s);
switch (t) {
case GETDNS_DNSSEC_SECURE:
if (s == GETDNS_DNSSEC_INDETERMINATE)
s = GETDNS_DNSSEC_SECURE;
break;
case GETDNS_DNSSEC_INSECURE:
if (s != GETDNS_DNSSEC_BOGUS)
s = GETDNS_DNSSEC_INSECURE;
break;
case GETDNS_DNSSEC_BOGUS :
s = GETDNS_DNSSEC_BOGUS;
break;
default:
break;
}
DEBUG_SEC("2. validated to: %d -> %d\n", t, s);
}
return s;
}
/* The DNSSEC status of the network requests which constructed the chain is
* evaluated by processing each head in turn. The worst outcome per network request
* is the dnssec status for that network request.
*/
static void chain_set_netreq_dnssec_status(chain_head *chain, rrset_iter *tas)
{
chain_head *head;
@ -1901,6 +2260,44 @@ static void chain_set_netreq_dnssec_status(chain_head *chain, rrset_iter *tas)
}
}
/* The DNSSEC status of all heads for a chain structure is evaluated by
* processing each head in turn. The worst outcome is the dnssec status for
* the whole.
*/
static int chain_validate_dnssec(chain_head *chain, rrset_iter *tas)
{
int s = GETDNS_DNSSEC_INDETERMINATE, t;
chain_head *head;
/* The netreq status is the worst for any head */
for (head = chain; head; head = head->next) {
t = chain_head_validate(head, tas);
switch (t) {
case GETDNS_DNSSEC_SECURE:
if (s == GETDNS_DNSSEC_INDETERMINATE)
s = GETDNS_DNSSEC_SECURE;
break;
case GETDNS_DNSSEC_INSECURE:
if (s != GETDNS_DNSSEC_BOGUS)
s = GETDNS_DNSSEC_INSECURE;
break;
case GETDNS_DNSSEC_BOGUS :
s = GETDNS_DNSSEC_BOGUS;
break;
default:
break;
}
}
return s;
}
/**************** dnssec_return_validation_chain Extension ******************
*****************************************************************************/
static size_t count_outstanding_requests(chain_head *head)
{
size_t count;
@ -2063,193 +2460,6 @@ static void check_chain_complete(chain_head *chain)
priv_getdns_call_user_callback(dnsreq, response_dict);
}
static void val_chain_node_soa_cb(getdns_dns_req *dnsreq);
static void val_chain_sched_soa_node(chain_node *node)
{
getdns_context *context;
getdns_eventloop *loop;
getdns_dns_req *dnsreq;
char name[1024];
context = node->chains->netreq->owner->context;
loop = node->chains->netreq->owner->loop;
if (!gldns_wire2str_dname_buf(node->ds.name, 256, name, sizeof(name)))
return;
if (! node->soa_req &&
! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_SOA,
dnssec_ok_checking_disabled, node, &dnsreq, NULL,
val_chain_node_soa_cb))
node->soa_req = dnsreq->netreqs[0];
}
static void val_chain_sched_soa(chain_head *head, uint8_t *dname)
{
chain_node *node;
if (!head->netreq)
return;
if (!*dname)
return;
for ( node = head->parent
; node && !_dname_equal(dname, node->ds.name)
; node = node->parent);
if (node)
val_chain_sched_soa_node(node);
}
static void val_chain_node_cb(getdns_dns_req *dnsreq);
static void val_chain_sched_node(chain_node *node)
{
getdns_context *context;
getdns_eventloop *loop;
getdns_dns_req *dnsreq;
char name[1024];
context = node->chains->netreq->owner->context;
loop = node->chains->netreq->owner->loop;
if (!gldns_wire2str_dname_buf(node->ds.name, 256, name, sizeof(name)))
return;
DEBUG_SEC("schedule DS & DNSKEY lookup for %s\n", name);
if (! node->dnskey_req /* not scheduled */ &&
! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_DNSKEY,
dnssec_ok_checking_disabled, node, &dnsreq, NULL, val_chain_node_cb))
node->dnskey_req = dnsreq->netreqs[0];
if (! node->ds_req && node->parent /* not root */ &&
! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_DS,
dnssec_ok_checking_disabled, node, &dnsreq, NULL, val_chain_node_cb))
node->ds_req = dnsreq->netreqs[0];
}
static void val_chain_sched(chain_head *head, uint8_t *dname)
{
chain_node *node;
if (!head->netreq)
return;
for ( node = head->parent
; node && !_dname_equal(dname, node->ds.name)
; node = node->parent);
if (node)
val_chain_sched_node(node);
}
static void val_chain_sched_signer_node(chain_node *node, rrsig_iter *rrsig)
{
priv_getdns_rdf_iter rdf_spc, *rdf;
uint8_t signer_spc[256], *signer;
size_t signer_len;
if (!(rdf = priv_getdns_rdf_iter_init_at(&rdf_spc, &rrsig->rr_i, 7)))
return;
if (!(signer = priv_getdns_rdf_if_or_as_decompressed(
rdf, signer_spc, &signer_len)))
return;
while (node && !_dname_equal(signer, node->ds.name))
node = node->parent;
if (node)
val_chain_sched_node(node);
}
static void val_chain_sched_signer(chain_head *head, rrsig_iter *rrsig)
{
if (!head->netreq)
return;
val_chain_sched_signer_node(head->parent, rrsig);
}
static void val_chain_node_cb(getdns_dns_req *dnsreq)
{
chain_node *node = (chain_node *)dnsreq->user_pointer;
getdns_network_req *netreq = dnsreq->netreqs[0];
rrset_iter *i, i_spc;
getdns_rrset *rrset;
rrsig_iter *rrsig, rrsig_spc;
getdns_context_clear_outbound_request(dnsreq);
switch (netreq->request_type) {
case GETDNS_RRTYPE_DS : node->ds.pkt = netreq->response;
node->ds.pkt_len = netreq->response_len;
break;
case GETDNS_RRTYPE_DNSKEY: node->dnskey.pkt = netreq->response;
node->dnskey.pkt_len = netreq->response_len;
default : check_chain_complete(node->chains);
return;
}
for ( i = rrset_iter_init(&i_spc,netreq->response,netreq->response_len)
; i
; i = rrset_iter_next(i)) {
rrset = rrset_iter_value(i);
if (rrset->rr_type != GETDNS_RRTYPE_DS &&
rrset->rr_type != GETDNS_RRTYPE_NSEC &&
rrset->rr_type != GETDNS_RRTYPE_NSEC3)
continue;
for ( rrsig = rrsig_iter_init(&rrsig_spc, rrset)
; rrsig; rrsig = rrsig_iter_next(rrsig))
val_chain_sched_signer_node(node, rrsig);
}
check_chain_complete(node->chains);
}
static getdns_rrset *rrset_by_type(
rrset_iter *i_spc, getdns_network_req *netreq, uint16_t rr_type)
{
rrset_iter *i;
getdns_rrset *rrset;
for ( i = rrset_iter_init(i_spc,netreq->response,netreq->response_len)
; i
; i = rrset_iter_next(i)) {
rrset = rrset_iter_value(i);
if (rrset->rr_type == rr_type) /* Check class too? */
return rrset;
}
return NULL;
}
static void val_chain_node_soa_cb(getdns_dns_req *dnsreq)
{
chain_node *node = (chain_node *)dnsreq->user_pointer;
getdns_network_req *netreq = dnsreq->netreqs[0];
rrset_iter i_spc;
getdns_rrset *rrset;
getdns_context_clear_outbound_request(dnsreq);
if ((rrset = rrset_by_type(&i_spc, netreq, GETDNS_RRTYPE_SOA))) {
while (node &&
! _dname_equal(node->ds.name, rrset->name))
node = node->parent;
if (node)
val_chain_sched_node(node);
} else
val_chain_sched_soa_node(node->parent);
check_chain_complete(node->chains);
}
void priv_getdns_get_validation_chain(getdns_dns_req *dnsreq)
{
@ -2285,6 +2495,12 @@ void priv_getdns_get_validation_chain(getdns_dns_req *dnsreq)
priv_getdns_call_user_callback(dnsreq,
create_getdns_response(dnsreq));
}
/******************* getdns_validate_dnssec() Function *********************
*****************************************************************************/
static int wire_validate_dnssec(uint8_t *to_val, size_t to_val_len,
uint8_t *support, size_t support_len, uint8_t *tas, size_t tas_len,
struct mem_funcs *mf)
@ -2453,6 +2669,10 @@ exit_free_support:
return r;
}
/****************** getdns_root_trust_anchor() Function ********************
*****************************************************************************/
uint16_t
_getdns_parse_ta_file(time_t *ta_mtime, gldns_buffer *gbuf)
{