[getdns-users] Example using the "dnssec_return_validation_chain" extension
Willem Toorop
willem at nlnetlabs.nl
Fri Feb 26 11:16:38 UTC 2016
Op 11-02-16 om 13:15 schreef Linus Nordberg:
> Hi Willem,
>
> Thanks for bringing this up. I'm a bit undereducated on the matter of
> DNSSEC so please bear with me for being a bit daft here.
>
> The tl;dr reads "Sounds great! Can I haz a public wire-format-input
> entry-point as well?". For background and details, read on.
>
>
> My immediate use for a function validating a (single) DS RR is to be
> able to thwart spamming of an experimental DNSSEC Transparency log. In
> short, the log will accept submissions of a DS RR (from a limited number
> of zones) if accompanied by a valid chain consisting of RRSIG, DNSKEY
> and DS RR's. The log will store the DS and chain RRs together with a
> timestamp for later retrieval by any log client. (Current thinking is
> that the DS RR and the timestamp (but not the chain) will be covered by
> a signature issued by the log but this might change.)
>
> I've so far expected the time of submission of a DS RR to be close in
> time of observation in the DNS but I've lately come to think that this
> might not be such a good idea. I'd very much like old DS records to be
> submitted as well.
>
>
> The next use for a validation function would be log monitors. Their job
> is to poll logs for new entries and look at the data with the goal of
> finding proof of misissuance. Imagine f.ex. that you ran a monitor
> looking for DS records for getdnsapi.net which raised the alarm whenever
> it saw one, so that you could decide if that issuance was ordered by
> someone with control over the domain or if it was .net or . acting
> unauthorized.
>
> This is expected to happen not only close in time to the publication of
> the DS record in the DNS but also a long time after that. Since the log
> stores and on request hands out the full chain (up to and including one
> of potentially several configured trust anchors, but typically the
> DNSSEC root), this shouldn't be a problem using your suggested
> function. All a monitor would have to do is to use the timestamp from
> the log entry as the 'moment' argument.
>
> While perhaps not strictly necessary, I think this will be useful
> especially for spam mitigation -- a log could choose to refuse records
> that are deemed too old for being interesting and stop someone trying to
> fill up the log by submitting a gazillion of old DS records they've
> stored.
>
>
> Last, to my request for a validation function accepting RR's in wire
> format. Since all RR's to and from the log will be sent in DNS wire
> format, it'd be more conventient if I could use that with the
> getdns_validate_dnssec() function (or its variant with 'moment'). As it
> is now, I have to first turn the wire format into getdns_dict's and then
> put them in getdns_list's. What do you think?
Hi Linus,
Although currently the returned getdns_dicts are indeed data structures,
constructed from the wire format data *before* being returned; it is
(and has long be) my intention to postpone parsing that wire format
until you actually access it. So for example this usage of an access
function to read the AD bit:
getdns_dict_get_int(response, "/replies_tree/0/header/ad", &ad);
would then internally be equivalent with
*ret = response->replies[0][2] & 0x20 ? 1 : 0;
So in that sense you may as well consider those "conversion" functions
(at least for the big wire format blobs, like complete replies) more
like a "create a getdns handle" sort of function.
The response dicts that getdns returns b.t.w. offer direct access to the
wire format data via the "replies_full" key at the packet level, and the
"rdata_raw" keys [1] at the individual resource record level. Also the
owner "name" data is returned in wire format just like all of the rdata
fields. Getdns doesn't do too much conversion really; It just helps
with determining the position, type and boundaries of RRs and rdata
fields in the underlying wire format data.
That said; We do have a "wire format" validate_dnssec function under the
hood:
( https://github.com/getdnsapi/getdns/blob/develop/src/dnssec.c#L3281 )
static int wire_validate_dnssec(struct mem_funcs *mf,
time_t now, uint32_t skew, uint8_t *to_val, size_t to_val_len,
uint8_t *support, size_t support_len, uint8_t *tas, size_t tas_len)
The to_val, to_val_len should be a DNS packet to validate.
The support/support_len and tas/tas_len are also given in DNS packet
format; but handled as a list as RR's (doesn't matter in which section)
and the headers and the query count are completely ignored. This was
convenient implementation wise, because the support packet is then laid
out as answers along the branches of the internal chain structure.
( see https://github.com/getdnsapi/getdns/blob/develop/src/dnssec.c#L79
for a good description of the operation and the data structures involved )
I consider this function a bit too low level and too dangerous for
public use though. I suppose we could expose the symbol in some form if
you really want, but it doesn't seem too much of an effort to convert
from wire format to a getdns_list. I.e. for example with the well
documented wire2rr_dict_scan ;) :
/**
* Convert wireformat resource record in a getdns rr_dict representation.
*
* @param wire A pointer to the pointer of the wireformat buffer.
* On return this pointer is moved to after first read
* in resource record.
* @param wire_sz On input the size of the wire buffer
* On output the size is decreased with the length
* of the wireformat resource record.
* @return rr_dict The returned rr_dict
* @return GETDNS_RETURN_GOOD on success or an error code on failure.
*/
getdns_return_t
getdns_wire2rr_dict_scan(
const uint8_t **wire, size_t *wire_sz, getdns_dict **rr_dict);
Suppose (uint8_t *) buf is a buffer containing a list of RRs and
(size_t) buf_len is its size; then to convert it to a getdns_list:
getdns_list *support_rrs = getdns_list_create();
getdns_dict *rr_dict;
getdns_return_t r;
size_t n_rrs;
n_rrs = 0;
while (buf_len > 0) {
r = getdns_wire2rr_dict_scan(&buf, &buf_len, &rr_dict);
if (r)
break;
r = getdns_list_set_dict(support_rrs, n_rrs, rr_dict);
getdns_dict_destroy(rr_dict);
if (r)
break;
n_rrs++;
}
We could expose this as
getdns_return_t
getdns_wire_rrs2list(uint8_t *wire, size_t wire_len, getdns_list **list);
But I also like to keep the API as small as possible and don't want to
expose a lot of helper functions that you could have easily recreated
with the existing functions as well.
Cheers & regards,
-- Willem
[1] be carefull with those "rdata_raw" keys though because they will
contain compression pointers!
>
>
> On a side note, it'd be great to have you and other DNS and DNSSEC
> experts participating in the transparency discussions over at [1].
>
> [1] https://lists.sunet.se/listinfo/dnssec-transparency
>
>
> Willem Toorop <willem at nlnetlabs.nl> wrote
> Thu, 11 Feb 2016 10:25:24 +0100:
>
> | I know you're interested to validate resource records at specified
> | moments. I'll try to expose a "non-API" version of the
> | getdns_validate_dnssec() function with that extra parameter in the
> | upcoming release. I.e.
> |
> | getdns_return_t getdns_validate_dnssec2(
> | getdns_list *to_validate,
> | getdns_list *bundle_of_support_records,
> | getdns_list *trust_anchor_records,
> | time_t moment
> | );
> |
> | OK?
> |
> | -- Willem
> |
> | Op 11-02-16 om 10:15 schreef Willem Toorop:
> | > Hi Linus,
> | >
> | > Rereading that message you are referring, I realise that a lot has been
> | > improved since may 2015.
> | >
> | > The dnssec_return_validation_chain extension currently works perfectly
> | > inn all possible circumstances. The chain will also contain proofs for
> | > insecure zones.
> | >
> | > The record_to_validate parameter to getdns_validate_dnssec() may now
> | > also contain a list of reply dicts to validate actual DNS packets. This
> | > allows to also validate proof of denial of existence or insecure
> | > NXDOMAINs etc.
> | >
> | > The getdns_query program (did you compile the binary with
> | > --with-getdns_query ?) contains example usage of getdns_validate_dnssec
> | > and will revalidate the answer with getdns_validate_dnssec() when the
> | > dnssec_return_validation_chain was used. This happens in function
> | > validate_chain on line 537 of getdns_query.c.
> | >
> | > It basically boils down to:
> | >
> | > getdns_return_t validate_chain(getdns_dict *response)
> | > {
> | > getdns_status r = GETDNS_RETURN_GENERIC_ERROR;
> | > getdns_list *trust_anchor;
> | > getdns_list *validation_chain;
> | > getdns_list *replies_tree;
> | >
> | > /* Get the trust anchors ...
> | > */
> | > if (getdns_context_get_dnssec_trust_anchors(
> | > context, &trust_anchor))
> | > trust_anchor = getdns_root_trust_anchor(NULL);
> | >
> | > if (!trust_anchor)
> | > fprintf(stderr, "No trust anchor to validate with.\n");
> | >
> | > /* ... get the validation chain ...
> | > */
> | > else if ((r = getdns_dict_get_list(
> | > response, "validation_chain", &validation_chain)))
> | > fprintf(stderr, "Could not get validation chain\n");
> | >
> | >
> | > /* .. get the replies tree ..
> | > */
> | > else if (r = getdns_dict_get_list(
> | > response, "replies_tree", &replies_tree)))
> | > fprintf(stderr, "Could not get replies tree\n");
> | >
> | > /* .. and validate.
> | > */
> | > else switch(getdns_validate_dnssec(
> | > replies_tree, validation_chain, trust_anchors)) {
> | >
> | > case GETDNS_DNSSEC_SECURE : printf("Replies are secure\n");
> | > return GETDNS_RETURN_GOOD;
> | >
> | > case GETDNS_DNSSEC_INDETERMINATE:
> | > case GETDNS_DNSSEC_INSECURE: printf("Replies are insecure\n");
> | > return GETDNS_RETURN_GOOD;
> | >
> | > case GETDNS_DNSSEC_BOGUS : printf("Replies are bogus\n");
> | > return GETDNS_RETURN_GOOD;
> | >
> | > default : /* Not possible to get here */
> | > fprintf( stderr
> | > , "Unkown dnssec status\n");
> | > return GETDNS_RETURN_GENERIC_ERROR;
> | > }
> | > return r;
> | > }
> | >
> | > Op 10-02-16 om 20:44 schreef Linus Nordberg:
> | >> Hi list,
> | >>
> | >> I've been trying to use the "dnssec_return_validation_chain" extension,
> | >> so far without luck. I define luck as seeing a "validation_chain"
> | >> section in a reply. I have verified that my context has proper trust
> | >> anchor(s).
> | >>
> | >> It'd be great to be able to run some example code, C or Python, to rule
> | >> out local problems at my end.
> | >>
> | >> My ultimate goal with this exercise is to understand what to pass in the
> | >> support_records argument to getdns_validate_dnssec(). The rationale
> | >> behind this is
> | >> https://getdnsapi.net/pipermail/users/2015-May/000032.html which says
> | >>
> | >> --8<---------------cut here---------------start------------->8---
> | >> - bundle_of_support_records must be a list of DS's RR-dicts and DNSKEY
> | >> RR-dicts with companion RRSIG-RR-dicts that lead up from one of the
> | >> trust_anchors to the RR-dicts to validate.
> | >> ...
> | >> If you would do a query with the "dnssec_return_validation_chain"
> | >> extension, you can use the "validation_chain" key in the response dict
> | >> as the bundle_of_support_records parameter ro getdns_validate_dnssec.
> | >> --8<---------------cut here---------------end--------------->8---
> | >>
> | >> Thanks,
> | >> Linus
> | >> _______________________________________________
> | >> Users mailing list
> | >> Users at getdnsapi.net
> | >> http://getdnsapi.net/mailman/listinfo/users>>
> | >
> | > _______________________________________________
> | > Users mailing list
> | > Users at getdnsapi.net
> | > http://getdnsapi.net/mailman/listinfo/users>
> |
> | _______________________________________________
> | Users mailing list
> | Users at getdnsapi.net
> | http://getdnsapi.net/mailman/listinfo/users
>
More information about the Users
mailing list