unbound
0.1
|
This file checks to see that the current 5011 keys work to prime the current root anchor. More...
#include "config.h"
#include "libunbound/unbound.h"
#include <ldns/rr.h>
#include <expat.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
Data Structures | |
struct | ip_list |
list of IP addresses More... | |
struct | xml_data |
XML parse private data during the parse. More... | |
Defines | |
#define | URLNAME "data.iana.org" |
name of server in URL to fetch HTTPS from | |
#define | XMLNAME "root-anchors/root-anchors.xml" |
path on HTTPS server to xml file | |
#define | P7SNAME "root-anchors/root-anchors.p7s" |
path on HTTPS server to p7s file | |
#define | HTTPS_PORT 443 |
port number for https access | |
Functions | |
static void | usage () |
Give unbound-anchor usage, and exit (1). | |
static const char * | get_builtin_cert (void) |
return the built in root update certificate | |
static const char * | get_builtin_ds (void) |
return the built in root DS trust anchor | |
static void | print_data (char *msg, char *data, int len) |
print hex data | |
static void | ub_ctx_error_exit (struct ub_ctx *ctx, const char *str, const char *str2) |
print ub context creation error and exit | |
static struct ub_ctx * | create_unbound_context (char *res_conf, char *root_hints, char *debugconf, int ip4only, int ip6only) |
Create a new unbound context with the commandline settings applied. | |
static void | verb_cert (char *msg, X509 *x) |
printout certificate in detail | |
static void | verb_certs (char *msg, STACK_OF(X509)*sk) |
printout certificates in detail | |
static | STACK_OF (X509) |
read certificates from a PEM bio | |
static void | do_list_builtin (void) |
static void | verb_addr (char *msg, struct ip_list *ip) |
printout IP address with message | |
static void | ip_list_free (struct ip_list *p) |
free ip_list | |
static struct ip_list * | RR_to_ip (int tp, char *data, int len, int port) |
create ip_list entry for a RR record | |
static void | resolve_host_ip (struct ub_ctx *ctx, char *host, int port, int tp, int cl, struct ip_list **head) |
Resolve name, type, class and add addresses to iplist. | |
static struct ip_list * | parse_ip_addr (char *str, int port) |
parse a text IP address into a sockaddr | |
static struct ip_list * | resolve_name (char *host, int port, char *res_conf, char *root_hints, char *debugconf, int ip4only, int ip6only) |
Resolve a domain name (even though the resolver is down and there is no trust anchor). | |
static void | wipe_ip_usage (struct ip_list *p) |
clear used flags | |
static int | count_unused (struct ip_list *p) |
cound unused IPs | |
static struct ip_list * | pick_random_ip (struct ip_list *list) |
pick random unused element from IP list | |
static void | fd_close (int fd) |
close the fd | |
static void | print_sock_err (const char *msg) |
printout socket errno | |
static int | connect_to_ip (struct ip_list *ip) |
connect to IP address | |
static SSL_CTX * | setup_sslctx (void) |
create SSL context | |
static SSL * | TLS_initiate (SSL_CTX *sslctx, int fd) |
initiate TLS on a connection | |
static void | TLS_shutdown (int fd, SSL *ssl, SSL_CTX *sslctx) |
perform neat TLS shutdown | |
static int | write_ssl_line (SSL *ssl, char *str, char *sec) |
write a line over SSL | |
static int | process_one_header (char *buf, size_t *clen, int *chunked) |
process header line, check rcode and keeping track of size | |
static int | read_ssl_line (SSL *ssl, char *buf, size_t len) |
Read one line from SSL zero terminates. | |
static size_t | read_http_headers (SSL *ssl, size_t *clen) |
read http headers and process them | |
static char * | read_data_chunk (SSL *ssl, size_t len) |
read a data chunk | |
static int | parse_chunk_header (char *buf, size_t *result) |
parse chunk header | |
static BIO * | do_chunked_read (SSL *ssl) |
read chunked data from connection | |
static int | write_http_get (SSL *ssl, char *pathname, char *urlname) |
start HTTP1.1 transaction on SSL | |
static char * | read_chunked_zero_terminate (SSL *ssl, size_t *len) |
read chunked data and zero terminate; len is without zero | |
static BIO * | read_http_result (SSL *ssl) |
read HTTP result from SSL | |
static BIO * | https_to_ip (struct ip_list *ip, char *pathname, char *urlname) |
https to an IP addr, return BIO with pathname or NULL | |
static BIO * | https (struct ip_list *ip_list, char *pathname, char *urlname) |
Do a HTTPS, HTTP1.1 over TLS, to fetch a file. | |
static void | free_file_bio (BIO *bio) |
free up a downloaded file BIO | |
static BIO * | xml_selectbio (struct xml_data *data, const char *tag) |
The BIO for the tag. | |
void | xml_charhandle (void *userData, const XML_Char *s, int len) |
XML handle character data, the data inside an element. | |
static const XML_Char * | find_att (const XML_Char **atts, XML_Char *name) |
XML fetch value of particular attribute(by name) or NULL if not present. | |
static time_t | xml_convertdate (const char *str) |
XML convert DateTime element to time_t. | |
static void | handle_keydigest (struct xml_data *data, const XML_Char **atts) |
XML handle the KeyDigest start tag, check validity periods. | |
static int | xml_is_zone_name (BIO *zone, char *name) |
See if XML element equals the zone name. | |
static void | xml_startelem (void *userData, const XML_Char *name, const XML_Char **atts) |
XML start of element. | |
static void | xml_append_str (BIO *b, const char *s) |
Append str to bio. | |
static void | xml_append_bio (BIO *b, BIO *a) |
Append bio to bio. | |
static void | xml_append_ds (struct xml_data *data) |
write the parsed xml-DS to the DS list | |
static void | xml_endelem (void *userData, const XML_Char *name) |
XML end of element. | |
static void | xml_parse_setup (XML_Parser parser, struct xml_data *data, time_t now) |
XML parser setup of the callbacks for the tags. | |
static BIO * | xml_parse (BIO *xml, time_t now) |
Perform XML parsing of the root-anchors file Its format description can be read here https://data.iana.org/root-anchors/draft-icann-dnssec-trust-anchor.txt It uses libexpat. | |
static int | verify_p7sig (BIO *data, BIO *p7s, STACK_OF(X509)*trust) |
verify a PKCS7 signature, false on failure | |
static void | write_unsigned_root (char *root_anchor_file) |
write unsigned root anchor file, a 5011 revoked tp | |
static void | write_root_anchor (char *root_anchor_file, BIO *ds) |
write root anchor file | |
static void | verify_and_update_anchor (char *root_anchor_file, BIO *xml, BIO *p7s, STACK_OF(X509)*cert) |
Perform the verification and update of the trustanchor file. | |
static int | do_certupdate (char *root_anchor_file, char *root_cert_file, char *urlname, char *xmlname, char *p7sname, char *res_conf, char *root_hints, char *debugconf, int ip4only, int ip6only, int port, struct ub_result *dnskey) |
perform actual certupdate work | |
static int | try_read_anchor (char *file) |
Try to read the root RFC5011 autotrust anchor file,. | |
static void | write_builtin_anchor (char *file) |
Write the builtin root anchor to a file. | |
static int | provide_builtin (char *root_anchor_file, int *used_builtin) |
Check the root anchor file. | |
static void | add_5011_probe_root (struct ub_ctx *ctx, char *root_anchor_file) |
add an autotrust anchor for the root to the context | |
static struct ub_result * | prime_root_key (struct ub_ctx *ctx) |
Prime the root key and return the result. | |
static int | read_if_pending_keys (char *file) |
see if ADDPEND keys exist in autotrust file (if possible) | |
static int32_t | read_last_success_time (char *file) |
read last successful probe time from autotrust file (if possible) | |
static int | probe_date_allows_certupdate (char *root_anchor_file) |
Read autotrust 5011 probe file and see if the date compared to the current date allows a certupdate. | |
static int | do_root_update_work (char *root_anchor_file, char *root_cert_file, char *urlname, char *xmlname, char *p7sname, char *res_conf, char *root_hints, char *debugconf, int ip4only, int ip6only, int force, int port) |
perform the unbound-anchor work | |
int | main (int argc, char *argv[]) |
Main routine for unbound-anchor. | |
Variables | |
static int | verb = 0 |
verbosity for this application | |
int | optind |
getopt global, in case header files fail to declare it. | |
char * | optarg |
getopt global, in case header files fail to declare it. |
This file checks to see that the current 5011 keys work to prime the current root anchor.
If not a certificate is used to update the anchor.
This is a concept solution for distribution of the DNSSEC root trust anchor. It is a small tool, called "unbound-anchor", that runs before the main validator starts. I.e. in the init script: unbound-anchor; unbound. Thus it is meant to run at system boot time.
Management-Abstract: * first run: fill root.key file with hardcoded DS record. * mostly: use RFC5011 tracking, quick . DNSKEY UDP query. * failover: use builtin certificate, do https and update. Special considerations: * 30-days RFC5011 timer saves a lot of https traffic. * DNSKEY probe must be NOERROR, saves a lot of https traffic. * fail if clock before sign date of the root, if cert expired. * if the root goes back to unsigned, deals with it.
It has hardcoded the root DS anchors and the ICANN CA root certificate. It allows with options to override those. It also takes root-hints (it has to do a DNS resolve), and also has hardcoded defaults for those.
Once it starts, just before the validator starts, it quickly checks if the root anchor file needs to be updated. First it tries to use RFC5011-tracking of the root key. If that fails (and for 30-days since last successful probe), then it attempts to update using the certificate. So most of the time, the RFC5011 tracking will work fine, and within a couple milliseconds, the main daemon can start. It will have only probed the . DNSKEY, not done expensive https transfers on the root infrastructure.
If there is no root key in the root.key file, it bootstraps the RFC5011-tracking with its builtin DS anchors; if that fails it bootstraps the RFC5011-tracking using the certificate. (again to avoid https, and it is also faster).
It uses the XML file by converting it to DS records and writing that to the key file. Unbound can detect that the 'special comments' are gone, and the file contains a list of normal DNSKEY/DS records, and uses that to bootstrap 5011 (the KSK is made VALID).
The certificate update is done by fetching root-anchors.xml and root-anchors.p7s via SSL. The HTTPS certificate can be logged but is not validated (https for channel security; the security comes from the certificate). The 'data.iana.org' domain name A and AAAA are resolved without DNSSEC. It tries a random IP until the transfer succeeds. It then checks the p7s signature.
On any failure, it leaves the root key file untouched. The main validator has to cope with it, it cannot fix things (So a failure does not go 'without DNSSEC', no downgrade). If it used its builtin stuff or did the https, it exits with an exit code, so that this can trigger the init script to log the event and potentially alert the operator that can do a manual check.
The date is also checked. Before 2010-07-15 is a failure (root not signed yet; avoids attacks on system clock). The last-successful-RFC5011-probe (if available) has to be more than 30 days in the past (otherwise, RFC5011 should have worked). This keeps unneccesary https traffic down. If the main certificate is expired, it fails.
The dates on the keys in the xml are checked (uses the libexpat xml parser), only the valid ones are used to re-enstate RFC5011 tracking. If 0 keys are valid, the zone has gone to insecure (a special marker is written in the keyfile that tells the main validator daemon the zone is insecure).
Only the root ICANN CA is shipped, not the intermediate ones. The intermediate CAs are included in the p7s file that was downloaded. (the root cert is valid to 2028 and the intermediate to 2014, today).
Obviously, the tool also has options so the operator can provide a new keyfile, a new certificate and new URLs, and fresh root hints. By default it logs nothing on failure and success; it 'just works'.
static void usage | ( | void | ) | [static] |
static STACK_OF | ( | X509 | ) | [static] |
read certificates from a PEM bio
read update cert file or use builtin
read certificates from the builtin certificate
References verb.
Referenced by do_certupdate().
static struct ip_list* resolve_name | ( | char * | host, |
int | port, | ||
char * | res_conf, | ||
char * | root_hints, | ||
char * | debugconf, | ||
int | ip4only, | ||
int | ip6only | ||
) | [static, read] |
Resolve a domain name (even though the resolver is down and there is no trust anchor).
Without DNSSEC validation.
host,: | the name to resolve. If this name is an IP4 or IP6 address this address is returned. |
port,: | the port number used for the returned IP structs. |
res_conf,: | resolv.conf (if any). |
root_hints,: | root hints (if any). |
debugconf,: | unbound.conf for debugging options. |
ip4only,: | use only ip4 for resolve and only lookup A |
ip6only,: | use only ip6 for resolve and only lookup AAAA default is to lookup A and AAAA using ip4 and ip6. |
References create_unbound_context(), parse_ip_addr(), resolve_host_ip(), ub_ctx_delete(), and verb.
Referenced by do_certupdate().
static int read_ssl_line | ( | SSL * | ssl, |
char * | buf, | ||
size_t | len | ||
) | [static] |
Read one line from SSL zero terminates.
skips "\r\n" (but not copied to buf).
ssl,: | the SSL connection to read from (blocking). |
buf,: | buffer to return line in. |
len,: | size of the buffer. |
References verb.
Referenced by do_chunked_read(), and read_http_headers().
Do a HTTPS, HTTP1.1 over TLS, to fetch a file.
ip_list,: | list of IP addresses to use to fetch from. |
pathname,: | pathname of file on server to GET. |
urlname,: | name to pass as the virtual host for this request. |
References https_to_ip(), pick_random_ip(), ip_list::used, verb, and wipe_ip_usage().
Referenced by do_certupdate().
void xml_charhandle | ( | void * | userData, |
const XML_Char * | s, | ||
int | len | ||
) |
XML handle character data, the data inside an element.
userData,: | xml_data structure |
s,: | the character data. May not all be in one callback. NOT zero terminated. |
len,: | length of this part of the data. |
References xml_data::czone, xml_data::tag, xml_data::use_key, verb, and xml_selectbio().
Referenced by xml_parse_setup().
static const XML_Char* find_att | ( | const XML_Char ** | atts, |
XML_Char * | name | ||
) | [static] |
XML fetch value of particular attribute(by name) or NULL if not present.
atts,: | attribute array (from xml_startelem). |
name,: | name of attribute to look for. |
Referenced by handle_keydigest().
static time_t xml_convertdate | ( | const char * | str | ) | [static] |
XML convert DateTime element to time_t.
[-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm] (with optional .ssssss fractional seconds)
str,: | the string |
References verb.
Referenced by handle_keydigest(), and probe_date_allows_certupdate().
static void xml_startelem | ( | void * | userData, |
const XML_Char * | name, | ||
const XML_Char ** | atts | ||
) | [static] |
XML start of element.
This callback is called whenever an XML tag starts. XML_Char is UTF8.
userData,: | the xml_data structure. |
name,: | the tag that starts. |
atts,: | array of strings, pairs of attr = value, ends with NULL. i.e. att[0]="att[1]" att[2]="att[3]" att[4]isNull |
References xml_data::czone, handle_keydigest(), xml_data::tag, xml_data::use_key, verb, and xml_selectbio().
Referenced by xml_parse_setup().
static void xml_endelem | ( | void * | userData, |
const XML_Char * | name | ||
) | [static] |
XML end of element.
This callback is called whenever an XML tag ends. XML_Char is UTF8.
userData,: | the xml_data structure |
name,: | the tag that ends. |
References xml_data::czone, xml_data::tag, xml_data::use_key, verb, xml_append_ds(), and xml_is_zone_name().
Referenced by xml_parse_setup().
static BIO* xml_parse | ( | BIO * | xml, |
time_t | now | ||
) | [static] |
Perform XML parsing of the root-anchors file Its format description can be read here https://data.iana.org/root-anchors/draft-icann-dnssec-trust-anchor.txt It uses libexpat.
xml,: | BIO with xml data. |
now,: | the current time for checking DS validity periods. |
References xml_data::calgo, xml_data::cdigest, xml_data::cdigtype, xml_data::ctag, xml_data::czone, xml_data::ds, xml_data::num_keys, xml_data::parser, xml_data::tag, verb, and xml_parse_setup().
Referenced by verify_and_update_anchor().
static int try_read_anchor | ( | char * | file | ) | [static] |
Try to read the root RFC5011 autotrust anchor file,.
file,: | filename. |
References verb.
Referenced by provide_builtin().
static int provide_builtin | ( | char * | root_anchor_file, |
int * | used_builtin | ||
) | [static] |
Check the root anchor file.
If does not exist, provide builtin and write file. If empty, provide builtin and write file. If trust-point-revoked-5011 file: make the program exit.
root_anchor_file,: | filename of the root anchor. |
used_builtin,: | set to 1 if the builtin is written. |
References try_read_anchor(), and write_builtin_anchor().
Referenced by do_root_update_work().
static struct ub_result* prime_root_key | ( | struct ub_ctx * | ctx | ) | [static, read] |
Prime the root key and return the result.
Exit on error.
ctx,: | the unbound context to perform the priming with. |
References ub_ctx_delete(), ub_resolve(), ub_strerror(), and verb.
Referenced by do_root_update_work().
static int probe_date_allows_certupdate | ( | char * | root_anchor_file | ) | [static] |
Read autotrust 5011 probe file and see if the date compared to the current date allows a certupdate.
If the last successful probe was recent then 5011 cannot be behind, and the failure cannot be solved with a certupdate. The debugconf is to validation-override the date for testing.
root_anchor_file,: | filename of root key |
References read_if_pending_keys(), read_last_success_time(), verb, and xml_convertdate().
Referenced by do_root_update_work().
int optind |
getopt global, in case header files fail to declare it.
char* optarg |
getopt global, in case header files fail to declare it.