unbound-anchor.c File Reference

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 "sldns/rrdef.h"
#include "sldns/parseutil.h"
#include <expat.h>
#include <openssl/x509.h>
#include <openssl/x509v3.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...
 

Macros

#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 P7SIGNER   "dnssec@iana.org"
 name of the signer of the certificate
 
#define HTTPS_PORT   443
 port number for https access
 

Functions

static void usage (void)
 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 (const char *msg, const char *data, size_t 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_ctxcreate_unbound_context (const char *res_conf, const char *root_hints, const char *debugconf, const char *srcaddr, int ip4only, int ip6only)
 Create a new unbound context with the commandline settings applied.
 
static void verb_cert (const char *msg, X509 *x)
 printout certificate in detail
 
static void verb_certs (const char *msg, STACK_OF(X509) *sk)
 printout certificates in detail
 
static STACK_OF (X509)
 read certificates from a PEM bio More...
 
static void do_list_builtin (void)
 
static void verb_addr (const 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_listRR_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, const char *host, int port, int tp, int cl, struct ip_list **head)
 Resolve name, type, class and add addresses to iplist.
 
static struct ip_listparse_ip_addr (const char *str, int port)
 parse a text IP address into a sockaddr
 
static struct ip_listresolve_name (const char *host, int port, const char *res_conf, const char *root_hints, const char *debugconf, const char *srcaddr, int ip4only, int ip6only)
 Resolve a domain name (even though the resolver is down and there is no trust anchor). More...
 
static void wipe_ip_usage (struct ip_list *p)
 clear used flags
 
static int count_unused (struct ip_list *p)
 count unused IPs
 
static struct ip_listpick_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, struct ip_list *src)
 connect to IP address
 
static SSL_CTX * setup_sslctx (void)
 create SSL context
 
static SSL * TLS_initiate (SSL_CTX *sslctx, int fd, const char *urlname, int use_sni)
 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, const char *str, const 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. More...
 
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, const char *pathname, const 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, const char *pathname, const char *urlname, struct ip_list *src, int use_sni)
 https to an IP addr, return BIO with pathname or NULL
 
static BIO * https (struct ip_list *ip_list, const char *pathname, const char *urlname, struct ip_list *src, int use_sni)
 Do a HTTPS, HTTP1.1 over TLS, to fetch a file. More...
 
static BIO * xml_selectbio (struct xml_data *data, const char *tag)
 The BIO for the tag.
 
static void xml_charhandle (void *userData, const XML_Char *s, int len)
 XML handle character data, the data inside an element. More...
 
static const XML_Char * find_att (const XML_Char **atts, const XML_Char *name)
 XML fetch value of particular attribute(by name) or NULL if not present. More...
 
static time_t xml_convertdate (const char *str)
 XML convert DateTime element to time_t. More...
 
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, const 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. More...
 
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. More...
 
static void xml_entitydeclhandler (void *userData, const XML_Char *ATTR_UNUSED(entityName), int ATTR_UNUSED(is_parameter_entity), const XML_Char *ATTR_UNUSED(value), int ATTR_UNUSED(value_length), const XML_Char *ATTR_UNUSED(base), const XML_Char *ATTR_UNUSED(systemId), const XML_Char *ATTR_UNUSED(publicId), const XML_Char *ATTR_UNUSED(notationName))
 
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 found in RFC 7958. More...
 
static unsigned long get_usage_of_ex (X509 *cert)
 
static int verify_p7sig (BIO *data, BIO *p7s, STACK_OF(X509) *trust, const char *p7signer)
 verify a PKCS7 signature, false on failure
 
static FILE * tempfile_open (char *tempf, size_t tempflen, const char *fname, const char *mode)
 open a temp file
 
static void tempfile_close (FILE *fd, const char *tempf, const char *fname)
 close an open temp file and replace the original with it
 
static void write_unsigned_root (const char *root_anchor_file)
 write unsigned root anchor file, a 5011 revoked tp
 
static void write_root_anchor (const char *root_anchor_file, BIO *ds)
 write root anchor file
 
static void verify_and_update_anchor (const char *root_anchor_file, BIO *xml, BIO *p7s, STACK_OF(X509) *cert, const char *p7signer)
 Perform the verification and update of the trustanchor file.
 
static int do_certupdate (const char *root_anchor_file, const char *root_cert_file, const char *urlname, const char *xmlname, const char *p7sname, const char *p7signer, const char *res_conf, const char *root_hints, const char *debugconf, const char *srcaddr, int ip4only, int ip6only, int port, int use_sni)
 perform actual certupdate work
 
static int try_read_anchor (const char *file)
 Try to read the root RFC5011 autotrust anchor file,. More...
 
static void write_builtin_anchor (const char *file)
 Write the builtin root anchor to a file.
 
static int provide_builtin (const char *root_anchor_file, int *used_builtin)
 Check the root anchor file. More...
 
static void add_5011_probe_root (struct ub_ctx *ctx, const char *root_anchor_file)
 add an autotrust anchor for the root to the context
 
static struct ub_resultprime_root_key (struct ub_ctx *ctx)
 Prime the root key and return the result. More...
 
static int read_if_pending_keys (const char *file)
 see if ADDPEND keys exist in autotrust file (if possible)
 
static int32_t read_last_success_time (const char *file)
 read last successful probe time from autotrust file (if possible)
 
static int probe_date_allows_certupdate (const char *root_anchor_file)
 Read autotrust 5011 probe file and see if the date compared to the current date allows a certupdate. More...
 
static struct ub_resultfetch_root_key (const char *root_anchor_file, const char *res_conf, const char *root_hints, const char *debugconf, const char *srcaddr, int ip4only, int ip6only)
 
static int do_root_update_work (const char *root_anchor_file, const char *root_cert_file, const char *urlname, const char *xmlname, const char *p7sname, const char *p7signer, const char *res_conf, const char *root_hints, const char *debugconf, const char *srcaddr, int ip4only, int ip6only, int force, int res_conf_fallback, int port, int use_sni)
 perform the unbound-anchor work
 
int main (int argc, char *argv[])
 Main routine for unbound-anchor.
 

Variables

static const char ICANN_UPDATE_CA []
 
static const char DS_TRUST_ANCHOR []
 
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.
 

Detailed Description

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, with RFC7958 https xml fetch.

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 RFC7958 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 RFC7958 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 unnecessary 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'.

Function Documentation

◆ STACK_OF()

static STACK_OF ( X509  )
static

read certificates from a PEM bio

get valid signers from the list of signers in the signature

read update cert file or use builtin

read certificates from the builtin certificate

References verb.

Referenced by do_certupdate(), and verify_p7sig().

◆ resolve_name()

static struct ip_list* resolve_name ( const char *  host,
int  port,
const char *  res_conf,
const char *  root_hints,
const char *  debugconf,
const char *  srcaddr,
int  ip4only,
int  ip6only 
)
static

Resolve a domain name (even though the resolver is down and there is no trust anchor).

Without DNSSEC validation.

Parameters
hostthe name to resolve. If this name is an IP4 or IP6 address this address is returned.
portthe port number used for the returned IP structs.
res_confresolv.conf (if any).
root_hintsroot hints (if any).
debugconfunbound.conf for debugging options.
srcaddrsource address option (if any).
ip4onlyuse only ip4 for resolve and only lookup A
ip6onlyuse only ip6 for resolve and only lookup AAAA default is to lookup A and AAAA using ip4 and ip6.
Returns
list of IP addresses.

References create_unbound_context(), LDNS_RR_CLASS_IN, LDNS_RR_TYPE_A, LDNS_RR_TYPE_AAAA, parse_ip_addr(), resolve_host_ip(), ub_ctx_delete(), and verb.

Referenced by do_certupdate().

◆ read_ssl_line()

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).

Parameters
sslthe SSL connection to read from (blocking).
bufbuffer to return line in.
lensize of the buffer.
Returns
0 on error, 1 on success.

References ip_list::len, and verb.

Referenced by do_chunked_read(), and read_http_headers().

◆ https()

static BIO* https ( struct ip_list ip_list,
const char *  pathname,
const char *  urlname,
struct ip_list src,
int  use_sni 
)
static

Do a HTTPS, HTTP1.1 over TLS, to fetch a file.

Parameters
ip_listlist of IP addresses to use to fetch from.
pathnamepathname of file on server to GET.
urlnamename to pass as the virtual host for this request.
srcif nonNULL, source address to bind to.
use_sniif SNI will be used.
Returns
a memory BIO with the file in it.

References https_to_ip(), pick_random_ip(), ip_list::used, verb, and wipe_ip_usage().

Referenced by do_certupdate().

◆ xml_charhandle()

static void xml_charhandle ( void *  userData,
const XML_Char *  s,
int  len 
)
static

XML handle character data, the data inside an element.

Parameters
userDataxml_data structure
sthe character data. May not all be in one callback. NOT zero terminated.
lenlength of this part of the data.

References xml_data::czone, xml_data::tag, xml_data::use_key, verb, and xml_selectbio().

◆ find_att()

static const XML_Char* find_att ( const XML_Char **  atts,
const XML_Char *  name 
)
static

XML fetch value of particular attribute(by name) or NULL if not present.

Parameters
attsattribute array (from xml_startelem).
namename of attribute to look for.
Returns
the value or NULL. (ptr into atts).

Referenced by handle_keydigest().

◆ xml_convertdate()

static time_t xml_convertdate ( const char *  str)
static

XML convert DateTime element to time_t.

[-]CCYY-MM-DDThh:mm:ssZ|(+|-)hh:mm

Parameters
strthe string
Returns
a time_t representation or 0 on failure.

Referenced by handle_keydigest(), and probe_date_allows_certupdate().

◆ xml_startelem()

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.

Parameters
userDatathe xml_data structure.
namethe tag that starts.
attsarray 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().

◆ xml_endelem()

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.

Parameters
userDatathe xml_data structure
namethe tag that ends.

References xml_data::czone, xml_data::tag, xml_data::use_key, verb, xml_append_ds(), and xml_is_zone_name().

◆ xml_parse()

static BIO* xml_parse ( BIO *  xml,
time_t  now 
)
static

Perform XML parsing of the root-anchors file Its format description can be found in RFC 7958.

It uses libexpat.

Parameters
xmlBIO with xml data.
nowthe current time for checking DS validity periods.
Returns
memoryBIO with the DS data in zone format. or NULL if the zone is insecure. (It exit()s on error)

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().

◆ try_read_anchor()

static int try_read_anchor ( const char *  file)
static

Try to read the root RFC5011 autotrust anchor file,.

Parameters
filefilename.
Returns
: 0 if does not exist or empty 1 if trust-point-revoked-5011 2 if it is OK.

References verb.

Referenced by provide_builtin().

◆ provide_builtin()

static int provide_builtin ( const 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.

Parameters
root_anchor_filefilename of the root anchor.
used_builtinset to 1 if the builtin is written.
Returns
0 if trustpoint is insecure, 1 on success. Exit on failure.

References try_read_anchor(), and write_builtin_anchor().

Referenced by do_root_update_work().

◆ prime_root_key()

static struct ub_result* prime_root_key ( struct ub_ctx ctx)
static

Prime the root key and return the result.

Exit on error.

Parameters
ctxthe unbound context to perform the priming with.
Returns
: the result of the prime, on error it exit()s.

References ub_resolve().

◆ probe_date_allows_certupdate()

static int probe_date_allows_certupdate ( const 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.

Parameters
root_anchor_filefilename of root key
Returns
true if certupdate is ok.

References read_if_pending_keys(), read_last_success_time(), verb, and xml_convertdate().

Variable Documentation

◆ ICANN_UPDATE_CA

const char ICANN_UPDATE_CA[]
static
Initial value:
=
"-----BEGIN CERTIFICATE-----\n"
"MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n"
"TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n"
"BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTA5MTIyMzA0MTkxMloX\n"
"DTI5MTIxODA0MTkxMlowXTEOMAwGA1UEChMFSUNBTk4xJjAkBgNVBAsTHUlDQU5O\n"
"IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1JQ0FOTiBSb290IENB\n"
"MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDb\n"
"cLhPNNqc1NB+u+oVvOnJESofYS9qub0/PXagmgr37pNublVThIzyLPGCJ8gPms9S\n"
"G1TaKNIsMI7d+5IgMy3WyPEOECGIcfqEIktdR1YWfJufXcMReZwU4v/AdKzdOdfg\n"
"ONiwc6r70duEr1IiqPbVm5T05l1e6D+HkAvHGnf1LtOPGs4CHQdpIUcy2kauAEy2\n"
"paKcOcHASvbTHK7TbbvHGPB+7faAztABLoneErruEcumetcNfPMIjXKdv1V1E3C7\n"
"MSJKy+jAqqQJqjZoQGB0necZgUMiUv7JK1IPQRM2CXJllcyJrm9WFxY0c1KjBO29\n"
"iIKK69fcglKcBuFShUECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
"Af8EBAMCAf4wHQYDVR0OBBYEFLpS6UmDJIZSL8eZzfyNa2kITcBQMA0GCSqGSIb3\n"
"DQEBCwUAA4IBAQAP8emCogqHny2UYFqywEuhLys7R9UKmYY4suzGO4nkbgfPFMfH\n"
"6M+Zj6owwxlwueZt1j/IaCayoKU3QsrYYoDRolpILh+FPwx7wseUEV8ZKpWsoDoD\n"
"2JFbLg2cfB8u/OlE4RYmcxxFSmXBg0yQ8/IoQt/bxOcEEhhiQ168H2yE5rxJMt9h\n"
"15nu5JBSewrCkYqYYmaxyOC3WrVGfHZxVI7MpIFcGdvSb2a1uyuua8l0BKgk3ujF\n"
"0/wsHNeP22qNyVO+XVBzrM8fk8BSUFuiT/6tZTYXRtEt5aKQZgXbKU5dUF3jT9qg\n"
"j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk\n"
"-----END CERTIFICATE-----\n"

◆ DS_TRUST_ANCHOR

const char DS_TRUST_ANCHOR[]
static
Initial value:
=
". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n"
". IN DS 38696 8 2 683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16\n"