1b7579f77SDag-Erling Smørgrav /* 2b7579f77SDag-Erling Smørgrav * unbound-anchor.c - update the root anchor if necessary. 3b7579f77SDag-Erling Smørgrav * 4b7579f77SDag-Erling Smørgrav * Copyright (c) 2010, NLnet Labs. All rights reserved. 5b7579f77SDag-Erling Smørgrav * 6b7579f77SDag-Erling Smørgrav * This software is open source. 7b7579f77SDag-Erling Smørgrav * 8b7579f77SDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 9b7579f77SDag-Erling Smørgrav * modification, are permitted provided that the following conditions 10b7579f77SDag-Erling Smørgrav * are met: 11b7579f77SDag-Erling Smørgrav * 12b7579f77SDag-Erling Smørgrav * Redistributions of source code must retain the above copyright notice, 13b7579f77SDag-Erling Smørgrav * this list of conditions and the following disclaimer. 14b7579f77SDag-Erling Smørgrav * 15b7579f77SDag-Erling Smørgrav * Redistributions in binary form must reproduce the above copyright notice, 16b7579f77SDag-Erling Smørgrav * this list of conditions and the following disclaimer in the documentation 17b7579f77SDag-Erling Smørgrav * and/or other materials provided with the distribution. 18b7579f77SDag-Erling Smørgrav * 19b7579f77SDag-Erling Smørgrav * Neither the name of the NLNET LABS nor the names of its contributors may 20b7579f77SDag-Erling Smørgrav * be used to endorse or promote products derived from this software without 21b7579f77SDag-Erling Smørgrav * specific prior written permission. 22b7579f77SDag-Erling Smørgrav * 23b7579f77SDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2417d15b25SDag-Erling Smørgrav * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2517d15b25SDag-Erling Smørgrav * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2617d15b25SDag-Erling Smørgrav * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2717d15b25SDag-Erling Smørgrav * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2817d15b25SDag-Erling Smørgrav * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 2917d15b25SDag-Erling Smørgrav * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 3017d15b25SDag-Erling Smørgrav * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 3117d15b25SDag-Erling Smørgrav * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 3217d15b25SDag-Erling Smørgrav * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 3317d15b25SDag-Erling Smørgrav * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34b7579f77SDag-Erling Smørgrav */ 35b7579f77SDag-Erling Smørgrav 36b7579f77SDag-Erling Smørgrav /** 37b7579f77SDag-Erling Smørgrav * \file 38b7579f77SDag-Erling Smørgrav * 39b7579f77SDag-Erling Smørgrav * This file checks to see that the current 5011 keys work to prime the 40b5663de9SDag-Erling Smørgrav * current root anchor. If not a certificate is used to update the anchor, 41b5663de9SDag-Erling Smørgrav * with RFC7958 https xml fetch. 42b7579f77SDag-Erling Smørgrav * 43b7579f77SDag-Erling Smørgrav * This is a concept solution for distribution of the DNSSEC root 44b7579f77SDag-Erling Smørgrav * trust anchor. It is a small tool, called "unbound-anchor", that 45b7579f77SDag-Erling Smørgrav * runs before the main validator starts. I.e. in the init script: 46b7579f77SDag-Erling Smørgrav * unbound-anchor; unbound. Thus it is meant to run at system boot time. 47b7579f77SDag-Erling Smørgrav * 48b7579f77SDag-Erling Smørgrav * Management-Abstract: 49b7579f77SDag-Erling Smørgrav * * first run: fill root.key file with hardcoded DS record. 50b7579f77SDag-Erling Smørgrav * * mostly: use RFC5011 tracking, quick . DNSKEY UDP query. 51b5663de9SDag-Erling Smørgrav * * failover: use RFC7958 builtin certificate, do https and update. 52b7579f77SDag-Erling Smørgrav * Special considerations: 53b7579f77SDag-Erling Smørgrav * * 30-days RFC5011 timer saves a lot of https traffic. 54b7579f77SDag-Erling Smørgrav * * DNSKEY probe must be NOERROR, saves a lot of https traffic. 55b7579f77SDag-Erling Smørgrav * * fail if clock before sign date of the root, if cert expired. 56b7579f77SDag-Erling Smørgrav * * if the root goes back to unsigned, deals with it. 57b7579f77SDag-Erling Smørgrav * 58b7579f77SDag-Erling Smørgrav * It has hardcoded the root DS anchors and the ICANN CA root certificate. 59b7579f77SDag-Erling Smørgrav * It allows with options to override those. It also takes root-hints (it 60b7579f77SDag-Erling Smørgrav * has to do a DNS resolve), and also has hardcoded defaults for those. 61b7579f77SDag-Erling Smørgrav * 62b7579f77SDag-Erling Smørgrav * Once it starts, just before the validator starts, it quickly checks if 63b7579f77SDag-Erling Smørgrav * the root anchor file needs to be updated. First it tries to use 64b7579f77SDag-Erling Smørgrav * RFC5011-tracking of the root key. If that fails (and for 30-days since 65b7579f77SDag-Erling Smørgrav * last successful probe), then it attempts to update using the 66b7579f77SDag-Erling Smørgrav * certificate. So most of the time, the RFC5011 tracking will work fine, 67b7579f77SDag-Erling Smørgrav * and within a couple milliseconds, the main daemon can start. It will 68b7579f77SDag-Erling Smørgrav * have only probed the . DNSKEY, not done expensive https transfers on the 69b7579f77SDag-Erling Smørgrav * root infrastructure. 70b7579f77SDag-Erling Smørgrav * 71b7579f77SDag-Erling Smørgrav * If there is no root key in the root.key file, it bootstraps the 72b7579f77SDag-Erling Smørgrav * RFC5011-tracking with its builtin DS anchors; if that fails it 73b7579f77SDag-Erling Smørgrav * bootstraps the RFC5011-tracking using the certificate. (again to avoid 74b7579f77SDag-Erling Smørgrav * https, and it is also faster). 75b7579f77SDag-Erling Smørgrav * 76b7579f77SDag-Erling Smørgrav * It uses the XML file by converting it to DS records and writing that to the 77b7579f77SDag-Erling Smørgrav * key file. Unbound can detect that the 'special comments' are gone, and 78b7579f77SDag-Erling Smørgrav * the file contains a list of normal DNSKEY/DS records, and uses that to 79b7579f77SDag-Erling Smørgrav * bootstrap 5011 (the KSK is made VALID). 80b7579f77SDag-Erling Smørgrav * 81b5663de9SDag-Erling Smørgrav * The certificate RFC7958 update is done by fetching root-anchors.xml and 82b7579f77SDag-Erling Smørgrav * root-anchors.p7s via SSL. The HTTPS certificate can be logged but is 83b7579f77SDag-Erling Smørgrav * not validated (https for channel security; the security comes from the 84b7579f77SDag-Erling Smørgrav * certificate). The 'data.iana.org' domain name A and AAAA are resolved 85b7579f77SDag-Erling Smørgrav * without DNSSEC. It tries a random IP until the transfer succeeds. It 86b7579f77SDag-Erling Smørgrav * then checks the p7s signature. 87b7579f77SDag-Erling Smørgrav * 88b7579f77SDag-Erling Smørgrav * On any failure, it leaves the root key file untouched. The main 89b7579f77SDag-Erling Smørgrav * validator has to cope with it, it cannot fix things (So a failure does 90b7579f77SDag-Erling Smørgrav * not go 'without DNSSEC', no downgrade). If it used its builtin stuff or 91b7579f77SDag-Erling Smørgrav * did the https, it exits with an exit code, so that this can trigger the 92b7579f77SDag-Erling Smørgrav * init script to log the event and potentially alert the operator that can 93b7579f77SDag-Erling Smørgrav * do a manual check. 94b7579f77SDag-Erling Smørgrav * 95b7579f77SDag-Erling Smørgrav * The date is also checked. Before 2010-07-15 is a failure (root not 96b7579f77SDag-Erling Smørgrav * signed yet; avoids attacks on system clock). The 97b7579f77SDag-Erling Smørgrav * last-successful-RFC5011-probe (if available) has to be more than 30 days 98b7579f77SDag-Erling Smørgrav * in the past (otherwise, RFC5011 should have worked). This keeps 9905ab2901SDag-Erling Smørgrav * unnecessary https traffic down. If the main certificate is expired, it 100b7579f77SDag-Erling Smørgrav * fails. 101b7579f77SDag-Erling Smørgrav * 102b7579f77SDag-Erling Smørgrav * The dates on the keys in the xml are checked (uses the libexpat xml 103b7579f77SDag-Erling Smørgrav * parser), only the valid ones are used to re-enstate RFC5011 tracking. 104b7579f77SDag-Erling Smørgrav * If 0 keys are valid, the zone has gone to insecure (a special marker is 105b7579f77SDag-Erling Smørgrav * written in the keyfile that tells the main validator daemon the zone is 106b7579f77SDag-Erling Smørgrav * insecure). 107b7579f77SDag-Erling Smørgrav * 108b7579f77SDag-Erling Smørgrav * Only the root ICANN CA is shipped, not the intermediate ones. The 109b7579f77SDag-Erling Smørgrav * intermediate CAs are included in the p7s file that was downloaded. (the 110b7579f77SDag-Erling Smørgrav * root cert is valid to 2028 and the intermediate to 2014, today). 111b7579f77SDag-Erling Smørgrav * 112b7579f77SDag-Erling Smørgrav * Obviously, the tool also has options so the operator can provide a new 113b7579f77SDag-Erling Smørgrav * keyfile, a new certificate and new URLs, and fresh root hints. By 114b7579f77SDag-Erling Smørgrav * default it logs nothing on failure and success; it 'just works'. 115b7579f77SDag-Erling Smørgrav * 116b7579f77SDag-Erling Smørgrav */ 117b7579f77SDag-Erling Smørgrav 118b7579f77SDag-Erling Smørgrav #include "config.h" 119b7579f77SDag-Erling Smørgrav #include "libunbound/unbound.h" 12009a3aaf3SDag-Erling Smørgrav #include "sldns/rrdef.h" 121b75612f8SDag-Erling Smørgrav #include "sldns/parseutil.h" 122b7579f77SDag-Erling Smørgrav #include <expat.h> 123b7579f77SDag-Erling Smørgrav #ifndef HAVE_EXPAT_H 124b7579f77SDag-Erling Smørgrav #error "need libexpat to parse root-anchors.xml file." 125b7579f77SDag-Erling Smørgrav #endif 126b7579f77SDag-Erling Smørgrav #ifdef HAVE_GETOPT_H 127b7579f77SDag-Erling Smørgrav #include <getopt.h> 128b7579f77SDag-Erling Smørgrav #endif 129b7579f77SDag-Erling Smørgrav #ifdef HAVE_OPENSSL_SSL_H 130b7579f77SDag-Erling Smørgrav #include <openssl/ssl.h> 131b7579f77SDag-Erling Smørgrav #endif 132b7579f77SDag-Erling Smørgrav #ifdef HAVE_OPENSSL_ERR_H 133b7579f77SDag-Erling Smørgrav #include <openssl/err.h> 134b7579f77SDag-Erling Smørgrav #endif 135b7579f77SDag-Erling Smørgrav #ifdef HAVE_OPENSSL_RAND_H 136b7579f77SDag-Erling Smørgrav #include <openssl/rand.h> 137b7579f77SDag-Erling Smørgrav #endif 138b7579f77SDag-Erling Smørgrav #include <openssl/x509.h> 1398ed2b524SDag-Erling Smørgrav #include <openssl/x509v3.h> 140b7579f77SDag-Erling Smørgrav #include <openssl/pem.h> 141b7579f77SDag-Erling Smørgrav 142b7579f77SDag-Erling Smørgrav /** name of server in URL to fetch HTTPS from */ 143b7579f77SDag-Erling Smørgrav #define URLNAME "data.iana.org" 144b7579f77SDag-Erling Smørgrav /** path on HTTPS server to xml file */ 145b7579f77SDag-Erling Smørgrav #define XMLNAME "root-anchors/root-anchors.xml" 146b7579f77SDag-Erling Smørgrav /** path on HTTPS server to p7s file */ 147b7579f77SDag-Erling Smørgrav #define P7SNAME "root-anchors/root-anchors.p7s" 1488ed2b524SDag-Erling Smørgrav /** name of the signer of the certificate */ 1498ed2b524SDag-Erling Smørgrav #define P7SIGNER "dnssec@iana.org" 150b7579f77SDag-Erling Smørgrav /** port number for https access */ 151b7579f77SDag-Erling Smørgrav #define HTTPS_PORT 443 152b7579f77SDag-Erling Smørgrav 153b7579f77SDag-Erling Smørgrav #ifdef USE_WINSOCK 154b7579f77SDag-Erling Smørgrav /* sneakily reuse the the wsa_strerror function, on windows */ 155b7579f77SDag-Erling Smørgrav char* wsa_strerror(int err); 156b7579f77SDag-Erling Smørgrav #endif 157b7579f77SDag-Erling Smørgrav 158b7579f77SDag-Erling Smørgrav /** verbosity for this application */ 159b7579f77SDag-Erling Smørgrav static int verb = 0; 160b7579f77SDag-Erling Smørgrav 161b7579f77SDag-Erling Smørgrav /** list of IP addresses */ 162b7579f77SDag-Erling Smørgrav struct ip_list { 163b7579f77SDag-Erling Smørgrav /** next in list */ 164b7579f77SDag-Erling Smørgrav struct ip_list* next; 165b7579f77SDag-Erling Smørgrav /** length of addr */ 166b7579f77SDag-Erling Smørgrav socklen_t len; 167b7579f77SDag-Erling Smørgrav /** address ready to connect to */ 168b7579f77SDag-Erling Smørgrav struct sockaddr_storage addr; 169b7579f77SDag-Erling Smørgrav /** has the address been used */ 170b7579f77SDag-Erling Smørgrav int used; 171b7579f77SDag-Erling Smørgrav }; 172b7579f77SDag-Erling Smørgrav 173b7579f77SDag-Erling Smørgrav /** Give unbound-anchor usage, and exit (1). */ 174b7579f77SDag-Erling Smørgrav static void 175b5663de9SDag-Erling Smørgrav usage(void) 176b7579f77SDag-Erling Smørgrav { 177b70d78d6SDag-Erling Smørgrav printf("Usage: local-unbound-anchor [opts]\n"); 178b7579f77SDag-Erling Smørgrav printf(" Setup or update root anchor. " 179b7579f77SDag-Erling Smørgrav "Most options have defaults.\n"); 180b7579f77SDag-Erling Smørgrav printf(" Run this program before you start the validator.\n"); 181b7579f77SDag-Erling Smørgrav printf("\n"); 182b7579f77SDag-Erling Smørgrav printf(" The anchor and cert have default builtin content\n"); 183b7579f77SDag-Erling Smørgrav printf(" if the file does not exist or is empty.\n"); 184b7579f77SDag-Erling Smørgrav printf("\n"); 185b7579f77SDag-Erling Smørgrav printf("-a file root key file, default %s\n", ROOT_ANCHOR_FILE); 186b7579f77SDag-Erling Smørgrav printf(" The key is input and output for this tool.\n"); 187b7579f77SDag-Erling Smørgrav printf("-c file cert file, default %s\n", ROOT_CERT_FILE); 188b7579f77SDag-Erling Smørgrav printf("-l list builtin key and cert on stdout\n"); 189b7579f77SDag-Erling Smørgrav printf("-u name server in https url, default %s\n", URLNAME); 190*25039b37SCy Schubert printf("-S do not use SNI for the https connection\n"); 191b7579f77SDag-Erling Smørgrav printf("-x path pathname to xml in url, default %s\n", XMLNAME); 192b7579f77SDag-Erling Smørgrav printf("-s path pathname to p7s in url, default %s\n", P7SNAME); 1938ed2b524SDag-Erling Smørgrav printf("-n name signer's subject emailAddress, default %s\n", P7SIGNER); 1940eefd307SCy Schubert printf("-b address source address to bind to\n"); 195b7579f77SDag-Erling Smørgrav printf("-4 work using IPv4 only\n"); 196b7579f77SDag-Erling Smørgrav printf("-6 work using IPv6 only\n"); 1974c75e3aaSDag-Erling Smørgrav printf("-f resolv.conf use given resolv.conf\n"); 1984c75e3aaSDag-Erling Smørgrav printf("-r root.hints use given root.hints\n" 199b7579f77SDag-Erling Smørgrav " builtin root hints are used by default\n"); 2004c75e3aaSDag-Erling Smørgrav printf("-R fallback from -f to root query on error\n"); 201b7579f77SDag-Erling Smørgrav printf("-v more verbose\n"); 202b7579f77SDag-Erling Smørgrav printf("-C conf debug, read config\n"); 203b7579f77SDag-Erling Smørgrav printf("-P port use port for https connect, default 443\n"); 204b7579f77SDag-Erling Smørgrav printf("-F debug, force update with cert\n"); 205b7579f77SDag-Erling Smørgrav printf("-h show this usage help\n"); 206b7579f77SDag-Erling Smørgrav printf("Version %s\n", PACKAGE_VERSION); 207b7579f77SDag-Erling Smørgrav printf("BSD licensed, see LICENSE in source package for details.\n"); 208b7579f77SDag-Erling Smørgrav printf("Report bugs to %s\n", PACKAGE_BUGREPORT); 209b7579f77SDag-Erling Smørgrav exit(1); 210b7579f77SDag-Erling Smørgrav } 211b7579f77SDag-Erling Smørgrav 212b7579f77SDag-Erling Smørgrav /** return the built in root update certificate */ 213b7579f77SDag-Erling Smørgrav static const char* 214b7579f77SDag-Erling Smørgrav get_builtin_cert(void) 215b7579f77SDag-Erling Smørgrav { 216b7579f77SDag-Erling Smørgrav return 217b7579f77SDag-Erling Smørgrav /* The ICANN CA fetched at 24 Sep 2010. Valid to 2028 */ 218b7579f77SDag-Erling Smørgrav "-----BEGIN CERTIFICATE-----\n" 219b7579f77SDag-Erling Smørgrav "MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n" 220b7579f77SDag-Erling Smørgrav "TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n" 221b7579f77SDag-Erling Smørgrav "BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTA5MTIyMzA0MTkxMloX\n" 222b7579f77SDag-Erling Smørgrav "DTI5MTIxODA0MTkxMlowXTEOMAwGA1UEChMFSUNBTk4xJjAkBgNVBAsTHUlDQU5O\n" 223b7579f77SDag-Erling Smørgrav "IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1JQ0FOTiBSb290IENB\n" 224b7579f77SDag-Erling Smørgrav "MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDb\n" 225b7579f77SDag-Erling Smørgrav "cLhPNNqc1NB+u+oVvOnJESofYS9qub0/PXagmgr37pNublVThIzyLPGCJ8gPms9S\n" 226b7579f77SDag-Erling Smørgrav "G1TaKNIsMI7d+5IgMy3WyPEOECGIcfqEIktdR1YWfJufXcMReZwU4v/AdKzdOdfg\n" 227b7579f77SDag-Erling Smørgrav "ONiwc6r70duEr1IiqPbVm5T05l1e6D+HkAvHGnf1LtOPGs4CHQdpIUcy2kauAEy2\n" 228b7579f77SDag-Erling Smørgrav "paKcOcHASvbTHK7TbbvHGPB+7faAztABLoneErruEcumetcNfPMIjXKdv1V1E3C7\n" 229b7579f77SDag-Erling Smørgrav "MSJKy+jAqqQJqjZoQGB0necZgUMiUv7JK1IPQRM2CXJllcyJrm9WFxY0c1KjBO29\n" 230b7579f77SDag-Erling Smørgrav "iIKK69fcglKcBuFShUECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n" 231b7579f77SDag-Erling Smørgrav "Af8EBAMCAf4wHQYDVR0OBBYEFLpS6UmDJIZSL8eZzfyNa2kITcBQMA0GCSqGSIb3\n" 232b7579f77SDag-Erling Smørgrav "DQEBCwUAA4IBAQAP8emCogqHny2UYFqywEuhLys7R9UKmYY4suzGO4nkbgfPFMfH\n" 233b7579f77SDag-Erling Smørgrav "6M+Zj6owwxlwueZt1j/IaCayoKU3QsrYYoDRolpILh+FPwx7wseUEV8ZKpWsoDoD\n" 234b7579f77SDag-Erling Smørgrav "2JFbLg2cfB8u/OlE4RYmcxxFSmXBg0yQ8/IoQt/bxOcEEhhiQ168H2yE5rxJMt9h\n" 235b7579f77SDag-Erling Smørgrav "15nu5JBSewrCkYqYYmaxyOC3WrVGfHZxVI7MpIFcGdvSb2a1uyuua8l0BKgk3ujF\n" 236b7579f77SDag-Erling Smørgrav "0/wsHNeP22qNyVO+XVBzrM8fk8BSUFuiT/6tZTYXRtEt5aKQZgXbKU5dUF3jT9qg\n" 237b7579f77SDag-Erling Smørgrav "j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk\n" 238b7579f77SDag-Erling Smørgrav "-----END CERTIFICATE-----\n" 239b7579f77SDag-Erling Smørgrav ; 240b7579f77SDag-Erling Smørgrav } 241b7579f77SDag-Erling Smørgrav 242b7579f77SDag-Erling Smørgrav /** return the built in root DS trust anchor */ 243b7579f77SDag-Erling Smørgrav static const char* 244b7579f77SDag-Erling Smørgrav get_builtin_ds(void) 245b7579f77SDag-Erling Smørgrav { 246b7579f77SDag-Erling Smørgrav return 247c7f4d7adSDag-Erling Smørgrav /* The anchors must start on a new line with ". IN DS and end with \n"[;] 248c7f4d7adSDag-Erling Smørgrav * because the makedist script greps on the source here */ 2493005e0a3SDag-Erling Smørgrav /* anchor 20326 is from 2017 */ 2503005e0a3SDag-Erling Smørgrav ". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n"; 251b7579f77SDag-Erling Smørgrav } 252b7579f77SDag-Erling Smørgrav 253b7579f77SDag-Erling Smørgrav /** print hex data */ 254b7579f77SDag-Erling Smørgrav static void 255ebc5657fSDag-Erling Smørgrav print_data(const char* msg, const char* data, int len) 256b7579f77SDag-Erling Smørgrav { 257b7579f77SDag-Erling Smørgrav int i; 258b7579f77SDag-Erling Smørgrav printf("%s: ", msg); 259b7579f77SDag-Erling Smørgrav for(i=0; i<len; i++) { 260b7579f77SDag-Erling Smørgrav printf(" %2.2x", (unsigned char)data[i]); 261b7579f77SDag-Erling Smørgrav } 262b7579f77SDag-Erling Smørgrav printf("\n"); 263b7579f77SDag-Erling Smørgrav } 264b7579f77SDag-Erling Smørgrav 265b7579f77SDag-Erling Smørgrav /** print ub context creation error and exit */ 266b7579f77SDag-Erling Smørgrav static void 267b7579f77SDag-Erling Smørgrav ub_ctx_error_exit(struct ub_ctx* ctx, const char* str, const char* str2) 268b7579f77SDag-Erling Smørgrav { 269b7579f77SDag-Erling Smørgrav ub_ctx_delete(ctx); 270b7579f77SDag-Erling Smørgrav if(str && str2 && verb) printf("%s: %s\n", str, str2); 271b7579f77SDag-Erling Smørgrav if(verb) printf("error: could not create unbound resolver context\n"); 272b7579f77SDag-Erling Smørgrav exit(0); 273b7579f77SDag-Erling Smørgrav } 274b7579f77SDag-Erling Smørgrav 275b7579f77SDag-Erling Smørgrav /** 276b7579f77SDag-Erling Smørgrav * Create a new unbound context with the commandline settings applied 277b7579f77SDag-Erling Smørgrav */ 278b7579f77SDag-Erling Smørgrav static struct ub_ctx* 279ebc5657fSDag-Erling Smørgrav create_unbound_context(const char* res_conf, const char* root_hints, 2800eefd307SCy Schubert const char* debugconf, const char* srcaddr, int ip4only, int ip6only) 281b7579f77SDag-Erling Smørgrav { 282b7579f77SDag-Erling Smørgrav int r; 283b7579f77SDag-Erling Smørgrav struct ub_ctx* ctx = ub_ctx_create(); 284b7579f77SDag-Erling Smørgrav if(!ctx) { 285b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 286b7579f77SDag-Erling Smørgrav exit(0); 287b7579f77SDag-Erling Smørgrav } 288b7579f77SDag-Erling Smørgrav /* do not waste time and network traffic to fetch extra nameservers */ 289b7579f77SDag-Erling Smørgrav r = ub_ctx_set_option(ctx, "target-fetch-policy:", "0 0 0 0 0"); 290b7579f77SDag-Erling Smørgrav if(r && verb) printf("ctx targetfetchpolicy: %s\n", ub_strerror(r)); 291b7579f77SDag-Erling Smørgrav /* read config file first, so its settings can be overridden */ 292b7579f77SDag-Erling Smørgrav if(debugconf) { 293b7579f77SDag-Erling Smørgrav r = ub_ctx_config(ctx, debugconf); 294b7579f77SDag-Erling Smørgrav if(r) ub_ctx_error_exit(ctx, debugconf, ub_strerror(r)); 295b7579f77SDag-Erling Smørgrav } 296b7579f77SDag-Erling Smørgrav if(res_conf) { 297b7579f77SDag-Erling Smørgrav r = ub_ctx_resolvconf(ctx, res_conf); 298b7579f77SDag-Erling Smørgrav if(r) ub_ctx_error_exit(ctx, res_conf, ub_strerror(r)); 299b7579f77SDag-Erling Smørgrav } 300b7579f77SDag-Erling Smørgrav if(root_hints) { 301b7579f77SDag-Erling Smørgrav r = ub_ctx_set_option(ctx, "root-hints:", root_hints); 302b7579f77SDag-Erling Smørgrav if(r) ub_ctx_error_exit(ctx, root_hints, ub_strerror(r)); 303b7579f77SDag-Erling Smørgrav } 3040eefd307SCy Schubert if(srcaddr) { 3050eefd307SCy Schubert r = ub_ctx_set_option(ctx, "outgoing-interface:", srcaddr); 3060eefd307SCy Schubert if(r) ub_ctx_error_exit(ctx, srcaddr, ub_strerror(r)); 3070eefd307SCy Schubert } 308b7579f77SDag-Erling Smørgrav if(ip4only) { 309b7579f77SDag-Erling Smørgrav r = ub_ctx_set_option(ctx, "do-ip6:", "no"); 310b7579f77SDag-Erling Smørgrav if(r) ub_ctx_error_exit(ctx, "ip4only", ub_strerror(r)); 311b7579f77SDag-Erling Smørgrav } 312b7579f77SDag-Erling Smørgrav if(ip6only) { 313b7579f77SDag-Erling Smørgrav r = ub_ctx_set_option(ctx, "do-ip4:", "no"); 314b7579f77SDag-Erling Smørgrav if(r) ub_ctx_error_exit(ctx, "ip6only", ub_strerror(r)); 315b7579f77SDag-Erling Smørgrav } 316b7579f77SDag-Erling Smørgrav return ctx; 317b7579f77SDag-Erling Smørgrav } 318b7579f77SDag-Erling Smørgrav 319b7579f77SDag-Erling Smørgrav /** printout certificate in detail */ 320b7579f77SDag-Erling Smørgrav static void 321ebc5657fSDag-Erling Smørgrav verb_cert(const char* msg, X509* x) 322b7579f77SDag-Erling Smørgrav { 323b7579f77SDag-Erling Smørgrav if(verb == 0 || verb == 1) return; 324b7579f77SDag-Erling Smørgrav if(verb == 2) { 325b7579f77SDag-Erling Smørgrav if(msg) printf("%s\n", msg); 326b7579f77SDag-Erling Smørgrav X509_print_ex_fp(stdout, x, 0, (unsigned long)-1 327b7579f77SDag-Erling Smørgrav ^(X509_FLAG_NO_SUBJECT 328b7579f77SDag-Erling Smørgrav |X509_FLAG_NO_ISSUER|X509_FLAG_NO_VALIDITY)); 329b7579f77SDag-Erling Smørgrav return; 330b7579f77SDag-Erling Smørgrav } 331b7579f77SDag-Erling Smørgrav if(msg) printf("%s\n", msg); 332b7579f77SDag-Erling Smørgrav X509_print_fp(stdout, x); 333b7579f77SDag-Erling Smørgrav } 334b7579f77SDag-Erling Smørgrav 335b7579f77SDag-Erling Smørgrav /** printout certificates in detail */ 336b7579f77SDag-Erling Smørgrav static void 337ebc5657fSDag-Erling Smørgrav verb_certs(const char* msg, STACK_OF(X509)* sk) 338b7579f77SDag-Erling Smørgrav { 339b7579f77SDag-Erling Smørgrav int i, num = sk_X509_num(sk); 340b7579f77SDag-Erling Smørgrav if(verb == 0 || verb == 1) return; 341b7579f77SDag-Erling Smørgrav for(i=0; i<num; i++) { 342b7579f77SDag-Erling Smørgrav printf("%s (%d/%d)\n", msg, i, num); 343b7579f77SDag-Erling Smørgrav verb_cert(NULL, sk_X509_value(sk, i)); 344b7579f77SDag-Erling Smørgrav } 345b7579f77SDag-Erling Smørgrav } 346b7579f77SDag-Erling Smørgrav 347b7579f77SDag-Erling Smørgrav /** read certificates from a PEM bio */ 348b7579f77SDag-Erling Smørgrav static STACK_OF(X509)* 349b7579f77SDag-Erling Smørgrav read_cert_bio(BIO* bio) 350b7579f77SDag-Erling Smørgrav { 351b7579f77SDag-Erling Smørgrav STACK_OF(X509) *sk = sk_X509_new_null(); 352b7579f77SDag-Erling Smørgrav if(!sk) { 353b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 354b7579f77SDag-Erling Smørgrav exit(0); 355b7579f77SDag-Erling Smørgrav } 356b7579f77SDag-Erling Smørgrav while(!BIO_eof(bio)) { 3570eefd307SCy Schubert X509* x = PEM_read_bio_X509(bio, NULL, NULL, NULL); 358b7579f77SDag-Erling Smørgrav if(x == NULL) { 359b7579f77SDag-Erling Smørgrav if(verb) { 360b7579f77SDag-Erling Smørgrav printf("failed to read X509\n"); 361b7579f77SDag-Erling Smørgrav ERR_print_errors_fp(stdout); 362b7579f77SDag-Erling Smørgrav } 363b7579f77SDag-Erling Smørgrav continue; 364b7579f77SDag-Erling Smørgrav } 365b7579f77SDag-Erling Smørgrav if(!sk_X509_push(sk, x)) { 366b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 367b7579f77SDag-Erling Smørgrav exit(0); 368b7579f77SDag-Erling Smørgrav } 369b7579f77SDag-Erling Smørgrav } 370b7579f77SDag-Erling Smørgrav return sk; 371b7579f77SDag-Erling Smørgrav } 372b7579f77SDag-Erling Smørgrav 373b7579f77SDag-Erling Smørgrav /* read the certificate file */ 374b7579f77SDag-Erling Smørgrav static STACK_OF(X509)* 375ebc5657fSDag-Erling Smørgrav read_cert_file(const char* file) 376b7579f77SDag-Erling Smørgrav { 377b7579f77SDag-Erling Smørgrav STACK_OF(X509)* sk; 378b7579f77SDag-Erling Smørgrav FILE* in; 379b7579f77SDag-Erling Smørgrav int content = 0; 380b7579f77SDag-Erling Smørgrav char buf[128]; 381b7579f77SDag-Erling Smørgrav if(file == NULL || strcmp(file, "") == 0) { 382b7579f77SDag-Erling Smørgrav return NULL; 383b7579f77SDag-Erling Smørgrav } 384b7579f77SDag-Erling Smørgrav sk = sk_X509_new_null(); 385b7579f77SDag-Erling Smørgrav if(!sk) { 386b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 387b7579f77SDag-Erling Smørgrav exit(0); 388b7579f77SDag-Erling Smørgrav } 389b7579f77SDag-Erling Smørgrav in = fopen(file, "r"); 390b7579f77SDag-Erling Smørgrav if(!in) { 391b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", file, strerror(errno)); 392b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S 393b7579f77SDag-Erling Smørgrav sk_X509_pop_free(sk, X509_free); 394b7579f77SDag-Erling Smørgrav #endif 395b7579f77SDag-Erling Smørgrav return NULL; 396b7579f77SDag-Erling Smørgrav } 397b7579f77SDag-Erling Smørgrav while(!feof(in)) { 3980eefd307SCy Schubert X509* x = PEM_read_X509(in, NULL, NULL, NULL); 399b7579f77SDag-Erling Smørgrav if(x == NULL) { 400b7579f77SDag-Erling Smørgrav if(verb) { 401b7579f77SDag-Erling Smørgrav printf("failed to read X509 file\n"); 402b7579f77SDag-Erling Smørgrav ERR_print_errors_fp(stdout); 403b7579f77SDag-Erling Smørgrav } 404b7579f77SDag-Erling Smørgrav continue; 405b7579f77SDag-Erling Smørgrav } 406b7579f77SDag-Erling Smørgrav if(!sk_X509_push(sk, x)) { 407b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 408b7579f77SDag-Erling Smørgrav fclose(in); 409b7579f77SDag-Erling Smørgrav exit(0); 410b7579f77SDag-Erling Smørgrav } 411b7579f77SDag-Erling Smørgrav content = 1; 412b7579f77SDag-Erling Smørgrav /* read away newline after --END CERT-- */ 413b7579f77SDag-Erling Smørgrav if(!fgets(buf, (int)sizeof(buf), in)) 414b7579f77SDag-Erling Smørgrav break; 415b7579f77SDag-Erling Smørgrav } 416b7579f77SDag-Erling Smørgrav fclose(in); 417b7579f77SDag-Erling Smørgrav if(!content) { 418b7579f77SDag-Erling Smørgrav if(verb) printf("%s is empty\n", file); 419b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S 420b7579f77SDag-Erling Smørgrav sk_X509_pop_free(sk, X509_free); 421b7579f77SDag-Erling Smørgrav #endif 422b7579f77SDag-Erling Smørgrav return NULL; 423b7579f77SDag-Erling Smørgrav } 424b7579f77SDag-Erling Smørgrav return sk; 425b7579f77SDag-Erling Smørgrav } 426b7579f77SDag-Erling Smørgrav 427b7579f77SDag-Erling Smørgrav /** read certificates from the builtin certificate */ 428b7579f77SDag-Erling Smørgrav static STACK_OF(X509)* 429b7579f77SDag-Erling Smørgrav read_builtin_cert(void) 430b7579f77SDag-Erling Smørgrav { 431b7579f77SDag-Erling Smørgrav const char* builtin_cert = get_builtin_cert(); 432b7579f77SDag-Erling Smørgrav STACK_OF(X509)* sk; 433b5663de9SDag-Erling Smørgrav BIO *bio = BIO_new_mem_buf(builtin_cert, 434b7579f77SDag-Erling Smørgrav (int)strlen(builtin_cert)); 435b7579f77SDag-Erling Smørgrav if(!bio) { 436b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 437b7579f77SDag-Erling Smørgrav exit(0); 438b7579f77SDag-Erling Smørgrav } 439b7579f77SDag-Erling Smørgrav sk = read_cert_bio(bio); 440b7579f77SDag-Erling Smørgrav if(!sk) { 441b7579f77SDag-Erling Smørgrav if(verb) printf("internal error, out of memory\n"); 442b7579f77SDag-Erling Smørgrav exit(0); 443b7579f77SDag-Erling Smørgrav } 444b7579f77SDag-Erling Smørgrav BIO_free(bio); 445b7579f77SDag-Erling Smørgrav return sk; 446b7579f77SDag-Erling Smørgrav } 447b7579f77SDag-Erling Smørgrav 448b7579f77SDag-Erling Smørgrav /** read update cert file or use builtin */ 449b7579f77SDag-Erling Smørgrav static STACK_OF(X509)* 450ebc5657fSDag-Erling Smørgrav read_cert_or_builtin(const char* file) 451b7579f77SDag-Erling Smørgrav { 452b7579f77SDag-Erling Smørgrav STACK_OF(X509) *sk = read_cert_file(file); 453b7579f77SDag-Erling Smørgrav if(!sk) { 454b7579f77SDag-Erling Smørgrav if(verb) printf("using builtin certificate\n"); 455b7579f77SDag-Erling Smørgrav sk = read_builtin_cert(); 456b7579f77SDag-Erling Smørgrav } 457b7579f77SDag-Erling Smørgrav if(verb) printf("have %d trusted certificates\n", sk_X509_num(sk)); 458b7579f77SDag-Erling Smørgrav verb_certs("trusted certificates", sk); 459b7579f77SDag-Erling Smørgrav return sk; 460b7579f77SDag-Erling Smørgrav } 461b7579f77SDag-Erling Smørgrav 462b7579f77SDag-Erling Smørgrav static void 463b7579f77SDag-Erling Smørgrav do_list_builtin(void) 464b7579f77SDag-Erling Smørgrav { 465b7579f77SDag-Erling Smørgrav const char* builtin_cert = get_builtin_cert(); 466b7579f77SDag-Erling Smørgrav const char* builtin_ds = get_builtin_ds(); 467b7579f77SDag-Erling Smørgrav printf("%s\n", builtin_ds); 468b7579f77SDag-Erling Smørgrav printf("%s\n", builtin_cert); 469b7579f77SDag-Erling Smørgrav exit(0); 470b7579f77SDag-Erling Smørgrav } 471b7579f77SDag-Erling Smørgrav 472b7579f77SDag-Erling Smørgrav /** printout IP address with message */ 473b7579f77SDag-Erling Smørgrav static void 474ebc5657fSDag-Erling Smørgrav verb_addr(const char* msg, struct ip_list* ip) 475b7579f77SDag-Erling Smørgrav { 476b7579f77SDag-Erling Smørgrav if(verb) { 477b7579f77SDag-Erling Smørgrav char out[100]; 478b7579f77SDag-Erling Smørgrav void* a = &((struct sockaddr_in*)&ip->addr)->sin_addr; 479b7579f77SDag-Erling Smørgrav if(ip->len != (socklen_t)sizeof(struct sockaddr_in)) 480b7579f77SDag-Erling Smørgrav a = &((struct sockaddr_in6*)&ip->addr)->sin6_addr; 481b7579f77SDag-Erling Smørgrav 482b7579f77SDag-Erling Smørgrav if(inet_ntop((int)((struct sockaddr_in*)&ip->addr)->sin_family, 483b7579f77SDag-Erling Smørgrav a, out, (socklen_t)sizeof(out))==0) 484b7579f77SDag-Erling Smørgrav printf("%s (inet_ntop error)\n", msg); 485b7579f77SDag-Erling Smørgrav else printf("%s %s\n", msg, out); 486b7579f77SDag-Erling Smørgrav } 487b7579f77SDag-Erling Smørgrav } 488b7579f77SDag-Erling Smørgrav 489b7579f77SDag-Erling Smørgrav /** free ip_list */ 490b7579f77SDag-Erling Smørgrav static void 491b7579f77SDag-Erling Smørgrav ip_list_free(struct ip_list* p) 492b7579f77SDag-Erling Smørgrav { 493b7579f77SDag-Erling Smørgrav struct ip_list* np; 494b7579f77SDag-Erling Smørgrav while(p) { 495b7579f77SDag-Erling Smørgrav np = p->next; 496b7579f77SDag-Erling Smørgrav free(p); 497b7579f77SDag-Erling Smørgrav p = np; 498b7579f77SDag-Erling Smørgrav } 499b7579f77SDag-Erling Smørgrav } 500b7579f77SDag-Erling Smørgrav 501b7579f77SDag-Erling Smørgrav /** create ip_list entry for a RR record */ 502b7579f77SDag-Erling Smørgrav static struct ip_list* 503b7579f77SDag-Erling Smørgrav RR_to_ip(int tp, char* data, int len, int port) 504b7579f77SDag-Erling Smørgrav { 505b7579f77SDag-Erling Smørgrav struct ip_list* ip = (struct ip_list*)calloc(1, sizeof(*ip)); 506b7579f77SDag-Erling Smørgrav uint16_t p = (uint16_t)port; 507b7579f77SDag-Erling Smørgrav if(tp == LDNS_RR_TYPE_A) { 508b7579f77SDag-Erling Smørgrav struct sockaddr_in* sa = (struct sockaddr_in*)&ip->addr; 509b7579f77SDag-Erling Smørgrav ip->len = (socklen_t)sizeof(*sa); 510b7579f77SDag-Erling Smørgrav sa->sin_family = AF_INET; 511b7579f77SDag-Erling Smørgrav sa->sin_port = (in_port_t)htons(p); 512b7579f77SDag-Erling Smørgrav if(len != (int)sizeof(sa->sin_addr)) { 513b7579f77SDag-Erling Smørgrav if(verb) printf("skipped badly formatted A\n"); 514b7579f77SDag-Erling Smørgrav free(ip); 515b7579f77SDag-Erling Smørgrav return NULL; 516b7579f77SDag-Erling Smørgrav } 517b7579f77SDag-Erling Smørgrav memmove(&sa->sin_addr, data, sizeof(sa->sin_addr)); 518b7579f77SDag-Erling Smørgrav 519b7579f77SDag-Erling Smørgrav } else if(tp == LDNS_RR_TYPE_AAAA) { 520b7579f77SDag-Erling Smørgrav struct sockaddr_in6* sa = (struct sockaddr_in6*)&ip->addr; 521b7579f77SDag-Erling Smørgrav ip->len = (socklen_t)sizeof(*sa); 522b7579f77SDag-Erling Smørgrav sa->sin6_family = AF_INET6; 523b7579f77SDag-Erling Smørgrav sa->sin6_port = (in_port_t)htons(p); 524b7579f77SDag-Erling Smørgrav if(len != (int)sizeof(sa->sin6_addr)) { 525b7579f77SDag-Erling Smørgrav if(verb) printf("skipped badly formatted AAAA\n"); 526b7579f77SDag-Erling Smørgrav free(ip); 527b7579f77SDag-Erling Smørgrav return NULL; 528b7579f77SDag-Erling Smørgrav } 529b7579f77SDag-Erling Smørgrav memmove(&sa->sin6_addr, data, sizeof(sa->sin6_addr)); 530b7579f77SDag-Erling Smørgrav } else { 531b7579f77SDag-Erling Smørgrav if(verb) printf("internal error: bad type in RRtoip\n"); 532b7579f77SDag-Erling Smørgrav free(ip); 533b7579f77SDag-Erling Smørgrav return NULL; 534b7579f77SDag-Erling Smørgrav } 535b7579f77SDag-Erling Smørgrav verb_addr("resolved server address", ip); 536b7579f77SDag-Erling Smørgrav return ip; 537b7579f77SDag-Erling Smørgrav } 538b7579f77SDag-Erling Smørgrav 539b7579f77SDag-Erling Smørgrav /** Resolve name, type, class and add addresses to iplist */ 540b7579f77SDag-Erling Smørgrav static void 541ebc5657fSDag-Erling Smørgrav resolve_host_ip(struct ub_ctx* ctx, const char* host, int port, int tp, int cl, 542b7579f77SDag-Erling Smørgrav struct ip_list** head) 543b7579f77SDag-Erling Smørgrav { 544b7579f77SDag-Erling Smørgrav struct ub_result* res = NULL; 545b7579f77SDag-Erling Smørgrav int r; 546b7579f77SDag-Erling Smørgrav int i; 547b7579f77SDag-Erling Smørgrav 548b7579f77SDag-Erling Smørgrav r = ub_resolve(ctx, host, tp, cl, &res); 549b7579f77SDag-Erling Smørgrav if(r) { 550b7579f77SDag-Erling Smørgrav if(verb) printf("error: resolve %s %s: %s\n", host, 551b7579f77SDag-Erling Smørgrav (tp==LDNS_RR_TYPE_A)?"A":"AAAA", ub_strerror(r)); 552b7579f77SDag-Erling Smørgrav return; 553b7579f77SDag-Erling Smørgrav } 554b7579f77SDag-Erling Smørgrav if(!res) { 555b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 556b7579f77SDag-Erling Smørgrav ub_ctx_delete(ctx); 557b7579f77SDag-Erling Smørgrav exit(0); 558b7579f77SDag-Erling Smørgrav } 5598ed2b524SDag-Erling Smørgrav if(!res->havedata || res->rcode || !res->data) { 5608ed2b524SDag-Erling Smørgrav if(verb) printf("resolve %s %s: no result\n", host, 5618ed2b524SDag-Erling Smørgrav (tp==LDNS_RR_TYPE_A)?"A":"AAAA"); 5628ed2b524SDag-Erling Smørgrav return; 5638ed2b524SDag-Erling Smørgrav } 564b7579f77SDag-Erling Smørgrav for(i = 0; res->data[i]; i++) { 565b7579f77SDag-Erling Smørgrav struct ip_list* ip = RR_to_ip(tp, res->data[i], res->len[i], 566b7579f77SDag-Erling Smørgrav port); 567b7579f77SDag-Erling Smørgrav if(!ip) continue; 568b7579f77SDag-Erling Smørgrav ip->next = *head; 569b7579f77SDag-Erling Smørgrav *head = ip; 570b7579f77SDag-Erling Smørgrav } 571b7579f77SDag-Erling Smørgrav ub_resolve_free(res); 572b7579f77SDag-Erling Smørgrav } 573b7579f77SDag-Erling Smørgrav 574b7579f77SDag-Erling Smørgrav /** parse a text IP address into a sockaddr */ 575b7579f77SDag-Erling Smørgrav static struct ip_list* 576ebc5657fSDag-Erling Smørgrav parse_ip_addr(const char* str, int port) 577b7579f77SDag-Erling Smørgrav { 578b7579f77SDag-Erling Smørgrav socklen_t len = 0; 579ebc5657fSDag-Erling Smørgrav union { 580b7579f77SDag-Erling Smørgrav struct sockaddr_in6 a6; 581b7579f77SDag-Erling Smørgrav struct sockaddr_in a; 582ebc5657fSDag-Erling Smørgrav } addr; 583b7579f77SDag-Erling Smørgrav struct ip_list* ip; 584b7579f77SDag-Erling Smørgrav uint16_t p = (uint16_t)port; 585ebc5657fSDag-Erling Smørgrav memset(&addr, 0, sizeof(addr)); 586b7579f77SDag-Erling Smørgrav 587ebc5657fSDag-Erling Smørgrav if(inet_pton(AF_INET6, str, &addr.a6.sin6_addr) > 0) { 588b7579f77SDag-Erling Smørgrav /* it is an IPv6 */ 589ebc5657fSDag-Erling Smørgrav addr.a6.sin6_family = AF_INET6; 590ebc5657fSDag-Erling Smørgrav addr.a6.sin6_port = (in_port_t)htons(p); 591ebc5657fSDag-Erling Smørgrav len = (socklen_t)sizeof(addr.a6); 592b7579f77SDag-Erling Smørgrav } 593ebc5657fSDag-Erling Smørgrav if(inet_pton(AF_INET, str, &addr.a.sin_addr) > 0) { 594b7579f77SDag-Erling Smørgrav /* it is an IPv4 */ 595ebc5657fSDag-Erling Smørgrav addr.a.sin_family = AF_INET; 596ebc5657fSDag-Erling Smørgrav addr.a.sin_port = (in_port_t)htons(p); 597b7579f77SDag-Erling Smørgrav len = (socklen_t)sizeof(struct sockaddr_in); 598b7579f77SDag-Erling Smørgrav } 599b7579f77SDag-Erling Smørgrav if(!len) return NULL; 600b7579f77SDag-Erling Smørgrav ip = (struct ip_list*)calloc(1, sizeof(*ip)); 601b7579f77SDag-Erling Smørgrav if(!ip) { 602b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 603b7579f77SDag-Erling Smørgrav exit(0); 604b7579f77SDag-Erling Smørgrav } 605b7579f77SDag-Erling Smørgrav ip->len = len; 606ebc5657fSDag-Erling Smørgrav memmove(&ip->addr, &addr, len); 607b7579f77SDag-Erling Smørgrav if(verb) printf("server address is %s\n", str); 608b7579f77SDag-Erling Smørgrav return ip; 609b7579f77SDag-Erling Smørgrav } 610b7579f77SDag-Erling Smørgrav 611b7579f77SDag-Erling Smørgrav /** 612b7579f77SDag-Erling Smørgrav * Resolve a domain name (even though the resolver is down and there is 613b7579f77SDag-Erling Smørgrav * no trust anchor). Without DNSSEC validation. 614b7579f77SDag-Erling Smørgrav * @param host: the name to resolve. 615b7579f77SDag-Erling Smørgrav * If this name is an IP4 or IP6 address this address is returned. 616b7579f77SDag-Erling Smørgrav * @param port: the port number used for the returned IP structs. 617b7579f77SDag-Erling Smørgrav * @param res_conf: resolv.conf (if any). 618b7579f77SDag-Erling Smørgrav * @param root_hints: root hints (if any). 619b7579f77SDag-Erling Smørgrav * @param debugconf: unbound.conf for debugging options. 6200eefd307SCy Schubert * @param srcaddr: source address option (if any). 621b7579f77SDag-Erling Smørgrav * @param ip4only: use only ip4 for resolve and only lookup A 622b7579f77SDag-Erling Smørgrav * @param ip6only: use only ip6 for resolve and only lookup AAAA 623b7579f77SDag-Erling Smørgrav * default is to lookup A and AAAA using ip4 and ip6. 624b7579f77SDag-Erling Smørgrav * @return list of IP addresses. 625b7579f77SDag-Erling Smørgrav */ 626b7579f77SDag-Erling Smørgrav static struct ip_list* 627ebc5657fSDag-Erling Smørgrav resolve_name(const char* host, int port, const char* res_conf, 6280eefd307SCy Schubert const char* root_hints, const char* debugconf, 6290eefd307SCy Schubert const char* srcaddr, int ip4only, int ip6only) 630b7579f77SDag-Erling Smørgrav { 631b7579f77SDag-Erling Smørgrav struct ub_ctx* ctx; 632b7579f77SDag-Erling Smørgrav struct ip_list* list = NULL; 633b7579f77SDag-Erling Smørgrav /* first see if name is an IP address itself */ 634b7579f77SDag-Erling Smørgrav if( (list=parse_ip_addr(host, port)) ) { 635b7579f77SDag-Erling Smørgrav return list; 636b7579f77SDag-Erling Smørgrav } 637b7579f77SDag-Erling Smørgrav 638b7579f77SDag-Erling Smørgrav /* create resolver context */ 639b7579f77SDag-Erling Smørgrav ctx = create_unbound_context(res_conf, root_hints, debugconf, 6400eefd307SCy Schubert srcaddr, ip4only, ip6only); 641b7579f77SDag-Erling Smørgrav 642b7579f77SDag-Erling Smørgrav /* try resolution of A */ 643b7579f77SDag-Erling Smørgrav if(!ip6only) { 644b7579f77SDag-Erling Smørgrav resolve_host_ip(ctx, host, port, LDNS_RR_TYPE_A, 645b7579f77SDag-Erling Smørgrav LDNS_RR_CLASS_IN, &list); 646b7579f77SDag-Erling Smørgrav } 647b7579f77SDag-Erling Smørgrav 648b7579f77SDag-Erling Smørgrav /* try resolution of AAAA */ 649b7579f77SDag-Erling Smørgrav if(!ip4only) { 650b7579f77SDag-Erling Smørgrav resolve_host_ip(ctx, host, port, LDNS_RR_TYPE_AAAA, 651b7579f77SDag-Erling Smørgrav LDNS_RR_CLASS_IN, &list); 652b7579f77SDag-Erling Smørgrav } 653b7579f77SDag-Erling Smørgrav 654b7579f77SDag-Erling Smørgrav ub_ctx_delete(ctx); 655b7579f77SDag-Erling Smørgrav if(!list) { 656b7579f77SDag-Erling Smørgrav if(verb) printf("%s has no IP addresses I can use\n", host); 657b7579f77SDag-Erling Smørgrav exit(0); 658b7579f77SDag-Erling Smørgrav } 659b7579f77SDag-Erling Smørgrav return list; 660b7579f77SDag-Erling Smørgrav } 661b7579f77SDag-Erling Smørgrav 662b7579f77SDag-Erling Smørgrav /** clear used flags */ 663b7579f77SDag-Erling Smørgrav static void 664b7579f77SDag-Erling Smørgrav wipe_ip_usage(struct ip_list* p) 665b7579f77SDag-Erling Smørgrav { 666b7579f77SDag-Erling Smørgrav while(p) { 667b7579f77SDag-Erling Smørgrav p->used = 0; 668b7579f77SDag-Erling Smørgrav p = p->next; 669b7579f77SDag-Erling Smørgrav } 670b7579f77SDag-Erling Smørgrav } 671b7579f77SDag-Erling Smørgrav 6728a384985SDag-Erling Smørgrav /** count unused IPs */ 673b7579f77SDag-Erling Smørgrav static int 674b7579f77SDag-Erling Smørgrav count_unused(struct ip_list* p) 675b7579f77SDag-Erling Smørgrav { 676b7579f77SDag-Erling Smørgrav int num = 0; 677b7579f77SDag-Erling Smørgrav while(p) { 678b7579f77SDag-Erling Smørgrav if(!p->used) num++; 679b7579f77SDag-Erling Smørgrav p = p->next; 680b7579f77SDag-Erling Smørgrav } 681b7579f77SDag-Erling Smørgrav return num; 682b7579f77SDag-Erling Smørgrav } 683b7579f77SDag-Erling Smørgrav 684b7579f77SDag-Erling Smørgrav /** pick random unused element from IP list */ 685b7579f77SDag-Erling Smørgrav static struct ip_list* 686b7579f77SDag-Erling Smørgrav pick_random_ip(struct ip_list* list) 687b7579f77SDag-Erling Smørgrav { 688b7579f77SDag-Erling Smørgrav struct ip_list* p = list; 689b7579f77SDag-Erling Smørgrav int num = count_unused(list); 690b7579f77SDag-Erling Smørgrav int sel; 691b7579f77SDag-Erling Smørgrav if(num == 0) return NULL; 692b7579f77SDag-Erling Smørgrav /* not perfect, but random enough */ 693ff825849SDag-Erling Smørgrav sel = (int)arc4random_uniform((uint32_t)num); 694b7579f77SDag-Erling Smørgrav /* skip over unused elements that we did not select */ 695b7579f77SDag-Erling Smørgrav while(sel > 0 && p) { 696b7579f77SDag-Erling Smørgrav if(!p->used) sel--; 697b7579f77SDag-Erling Smørgrav p = p->next; 698b7579f77SDag-Erling Smørgrav } 699b7579f77SDag-Erling Smørgrav /* find the next unused element */ 700b7579f77SDag-Erling Smørgrav while(p && p->used) 701b7579f77SDag-Erling Smørgrav p = p->next; 702b7579f77SDag-Erling Smørgrav if(!p) return NULL; /* robustness */ 703b7579f77SDag-Erling Smørgrav return p; 704b7579f77SDag-Erling Smørgrav } 705b7579f77SDag-Erling Smørgrav 706b7579f77SDag-Erling Smørgrav /** close the fd */ 707b7579f77SDag-Erling Smørgrav static void 708b7579f77SDag-Erling Smørgrav fd_close(int fd) 709b7579f77SDag-Erling Smørgrav { 710b7579f77SDag-Erling Smørgrav #ifndef USE_WINSOCK 711b7579f77SDag-Erling Smørgrav close(fd); 712b7579f77SDag-Erling Smørgrav #else 713b7579f77SDag-Erling Smørgrav closesocket(fd); 714b7579f77SDag-Erling Smørgrav #endif 715b7579f77SDag-Erling Smørgrav } 716b7579f77SDag-Erling Smørgrav 717b7579f77SDag-Erling Smørgrav /** printout socket errno */ 718b7579f77SDag-Erling Smørgrav static void 719b7579f77SDag-Erling Smørgrav print_sock_err(const char* msg) 720b7579f77SDag-Erling Smørgrav { 721b7579f77SDag-Erling Smørgrav #ifndef USE_WINSOCK 722b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", msg, strerror(errno)); 723b7579f77SDag-Erling Smørgrav #else 724b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", msg, wsa_strerror(WSAGetLastError())); 725b7579f77SDag-Erling Smørgrav #endif 726b7579f77SDag-Erling Smørgrav } 727b7579f77SDag-Erling Smørgrav 728b7579f77SDag-Erling Smørgrav /** connect to IP address */ 729b7579f77SDag-Erling Smørgrav static int 7300eefd307SCy Schubert connect_to_ip(struct ip_list* ip, struct ip_list* src) 731b7579f77SDag-Erling Smørgrav { 732b7579f77SDag-Erling Smørgrav int fd; 733b7579f77SDag-Erling Smørgrav verb_addr("connect to", ip); 734b7579f77SDag-Erling Smørgrav fd = socket(ip->len==(socklen_t)sizeof(struct sockaddr_in)? 735b7579f77SDag-Erling Smørgrav AF_INET:AF_INET6, SOCK_STREAM, 0); 736b7579f77SDag-Erling Smørgrav if(fd == -1) { 737b7579f77SDag-Erling Smørgrav print_sock_err("socket"); 738b7579f77SDag-Erling Smørgrav return -1; 739b7579f77SDag-Erling Smørgrav } 7400eefd307SCy Schubert if(src && bind(fd, (struct sockaddr*)&src->addr, src->len) < 0) { 7410eefd307SCy Schubert print_sock_err("bind"); 7420eefd307SCy Schubert fd_close(fd); 7430eefd307SCy Schubert return -1; 7440eefd307SCy Schubert } 745b7579f77SDag-Erling Smørgrav if(connect(fd, (struct sockaddr*)&ip->addr, ip->len) < 0) { 746b7579f77SDag-Erling Smørgrav print_sock_err("connect"); 747b7579f77SDag-Erling Smørgrav fd_close(fd); 748b7579f77SDag-Erling Smørgrav return -1; 749b7579f77SDag-Erling Smørgrav } 750b7579f77SDag-Erling Smørgrav return fd; 751b7579f77SDag-Erling Smørgrav } 752b7579f77SDag-Erling Smørgrav 753b7579f77SDag-Erling Smørgrav /** create SSL context */ 754b7579f77SDag-Erling Smørgrav static SSL_CTX* 755b7579f77SDag-Erling Smørgrav setup_sslctx(void) 756b7579f77SDag-Erling Smørgrav { 757b7579f77SDag-Erling Smørgrav SSL_CTX* sslctx = SSL_CTX_new(SSLv23_client_method()); 758b7579f77SDag-Erling Smørgrav if(!sslctx) { 759b7579f77SDag-Erling Smørgrav if(verb) printf("SSL_CTX_new error\n"); 760b7579f77SDag-Erling Smørgrav return NULL; 761b7579f77SDag-Erling Smørgrav } 762b7579f77SDag-Erling Smørgrav return sslctx; 763b7579f77SDag-Erling Smørgrav } 764b7579f77SDag-Erling Smørgrav 765b7579f77SDag-Erling Smørgrav /** initiate TLS on a connection */ 766b7579f77SDag-Erling Smørgrav static SSL* 767*25039b37SCy Schubert TLS_initiate(SSL_CTX* sslctx, int fd, const char* urlname, int use_sni) 768b7579f77SDag-Erling Smørgrav { 769b7579f77SDag-Erling Smørgrav X509* x; 770b7579f77SDag-Erling Smørgrav int r; 771b7579f77SDag-Erling Smørgrav SSL* ssl = SSL_new(sslctx); 772b7579f77SDag-Erling Smørgrav if(!ssl) { 773b7579f77SDag-Erling Smørgrav if(verb) printf("SSL_new error\n"); 774b7579f77SDag-Erling Smørgrav return NULL; 775b7579f77SDag-Erling Smørgrav } 776b7579f77SDag-Erling Smørgrav SSL_set_connect_state(ssl); 7770eefd307SCy Schubert (void)SSL_set_mode(ssl, (long)SSL_MODE_AUTO_RETRY); 778b7579f77SDag-Erling Smørgrav if(!SSL_set_fd(ssl, fd)) { 779b7579f77SDag-Erling Smørgrav if(verb) printf("SSL_set_fd error\n"); 780b7579f77SDag-Erling Smørgrav SSL_free(ssl); 781b7579f77SDag-Erling Smørgrav return NULL; 782b7579f77SDag-Erling Smørgrav } 783*25039b37SCy Schubert if(use_sni) { 784*25039b37SCy Schubert (void)SSL_set_tlsext_host_name(ssl, urlname); 785*25039b37SCy Schubert } 786b7579f77SDag-Erling Smørgrav while(1) { 787b7579f77SDag-Erling Smørgrav ERR_clear_error(); 788b7579f77SDag-Erling Smørgrav if( (r=SSL_do_handshake(ssl)) == 1) 789b7579f77SDag-Erling Smørgrav break; 790b7579f77SDag-Erling Smørgrav r = SSL_get_error(ssl, r); 791b7579f77SDag-Erling Smørgrav if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) { 792b7579f77SDag-Erling Smørgrav if(verb) printf("SSL handshake failed\n"); 793b7579f77SDag-Erling Smørgrav SSL_free(ssl); 794b7579f77SDag-Erling Smørgrav return NULL; 795b7579f77SDag-Erling Smørgrav } 796b7579f77SDag-Erling Smørgrav /* wants to be called again */ 797b7579f77SDag-Erling Smørgrav } 798b7579f77SDag-Erling Smørgrav x = SSL_get_peer_certificate(ssl); 799b7579f77SDag-Erling Smørgrav if(!x) { 800b7579f77SDag-Erling Smørgrav if(verb) printf("Server presented no peer certificate\n"); 801b7579f77SDag-Erling Smørgrav SSL_free(ssl); 802b7579f77SDag-Erling Smørgrav return NULL; 803b7579f77SDag-Erling Smørgrav } 804b7579f77SDag-Erling Smørgrav verb_cert("server SSL certificate", x); 805b7579f77SDag-Erling Smørgrav X509_free(x); 806b7579f77SDag-Erling Smørgrav return ssl; 807b7579f77SDag-Erling Smørgrav } 808b7579f77SDag-Erling Smørgrav 809b7579f77SDag-Erling Smørgrav /** perform neat TLS shutdown */ 810b7579f77SDag-Erling Smørgrav static void 811b7579f77SDag-Erling Smørgrav TLS_shutdown(int fd, SSL* ssl, SSL_CTX* sslctx) 812b7579f77SDag-Erling Smørgrav { 813b7579f77SDag-Erling Smørgrav /* shutdown the SSL connection nicely */ 814b7579f77SDag-Erling Smørgrav if(SSL_shutdown(ssl) == 0) { 815b7579f77SDag-Erling Smørgrav SSL_shutdown(ssl); 816b7579f77SDag-Erling Smørgrav } 817b7579f77SDag-Erling Smørgrav SSL_free(ssl); 818b7579f77SDag-Erling Smørgrav SSL_CTX_free(sslctx); 819b7579f77SDag-Erling Smørgrav fd_close(fd); 820b7579f77SDag-Erling Smørgrav } 821b7579f77SDag-Erling Smørgrav 822b7579f77SDag-Erling Smørgrav /** write a line over SSL */ 823b7579f77SDag-Erling Smørgrav static int 824ebc5657fSDag-Erling Smørgrav write_ssl_line(SSL* ssl, const char* str, const char* sec) 825b7579f77SDag-Erling Smørgrav { 826b7579f77SDag-Erling Smørgrav char buf[1024]; 827b7579f77SDag-Erling Smørgrav size_t l; 828b7579f77SDag-Erling Smørgrav if(sec) { 829b7579f77SDag-Erling Smørgrav snprintf(buf, sizeof(buf), str, sec); 830b7579f77SDag-Erling Smørgrav } else { 831b7579f77SDag-Erling Smørgrav snprintf(buf, sizeof(buf), "%s", str); 832b7579f77SDag-Erling Smørgrav } 833b7579f77SDag-Erling Smørgrav l = strlen(buf); 834b7579f77SDag-Erling Smørgrav if(l+2 >= sizeof(buf)) { 835b7579f77SDag-Erling Smørgrav if(verb) printf("line too long\n"); 836b7579f77SDag-Erling Smørgrav return 0; 837b7579f77SDag-Erling Smørgrav } 838b7579f77SDag-Erling Smørgrav if(verb >= 2) printf("SSL_write: %s\n", buf); 839b7579f77SDag-Erling Smørgrav buf[l] = '\r'; 840b7579f77SDag-Erling Smørgrav buf[l+1] = '\n'; 841b7579f77SDag-Erling Smørgrav buf[l+2] = 0; 842b7579f77SDag-Erling Smørgrav /* add \r\n */ 843b7579f77SDag-Erling Smørgrav if(SSL_write(ssl, buf, (int)strlen(buf)) <= 0) { 844b7579f77SDag-Erling Smørgrav if(verb) printf("could not SSL_write %s", str); 845b7579f77SDag-Erling Smørgrav return 0; 846b7579f77SDag-Erling Smørgrav } 847b7579f77SDag-Erling Smørgrav return 1; 848b7579f77SDag-Erling Smørgrav } 849b7579f77SDag-Erling Smørgrav 850b7579f77SDag-Erling Smørgrav /** process header line, check rcode and keeping track of size */ 851b7579f77SDag-Erling Smørgrav static int 852b7579f77SDag-Erling Smørgrav process_one_header(char* buf, size_t* clen, int* chunked) 853b7579f77SDag-Erling Smørgrav { 854b7579f77SDag-Erling Smørgrav if(verb>=2) printf("header: '%s'\n", buf); 855b7579f77SDag-Erling Smørgrav if(strncasecmp(buf, "HTTP/1.1 ", 9) == 0) { 856b7579f77SDag-Erling Smørgrav /* check returncode */ 857b7579f77SDag-Erling Smørgrav if(buf[9] != '2') { 858b7579f77SDag-Erling Smørgrav if(verb) printf("bad status %s\n", buf+9); 859b7579f77SDag-Erling Smørgrav return 0; 860b7579f77SDag-Erling Smørgrav } 861b7579f77SDag-Erling Smørgrav } else if(strncasecmp(buf, "Content-Length: ", 16) == 0) { 862b7579f77SDag-Erling Smørgrav if(!*chunked) 863b7579f77SDag-Erling Smørgrav *clen = (size_t)atoi(buf+16); 864b7579f77SDag-Erling Smørgrav } else if(strncasecmp(buf, "Transfer-Encoding: chunked", 19+7) == 0) { 865b7579f77SDag-Erling Smørgrav *clen = 0; 866b7579f77SDag-Erling Smørgrav *chunked = 1; 867b7579f77SDag-Erling Smørgrav } 868b7579f77SDag-Erling Smørgrav return 1; 869b7579f77SDag-Erling Smørgrav } 870b7579f77SDag-Erling Smørgrav 871b7579f77SDag-Erling Smørgrav /** 872b7579f77SDag-Erling Smørgrav * Read one line from SSL 873b7579f77SDag-Erling Smørgrav * zero terminates. 874b7579f77SDag-Erling Smørgrav * skips "\r\n" (but not copied to buf). 875b7579f77SDag-Erling Smørgrav * @param ssl: the SSL connection to read from (blocking). 876b7579f77SDag-Erling Smørgrav * @param buf: buffer to return line in. 877b7579f77SDag-Erling Smørgrav * @param len: size of the buffer. 878b7579f77SDag-Erling Smørgrav * @return 0 on error, 1 on success. 879b7579f77SDag-Erling Smørgrav */ 880b7579f77SDag-Erling Smørgrav static int 881b7579f77SDag-Erling Smørgrav read_ssl_line(SSL* ssl, char* buf, size_t len) 882b7579f77SDag-Erling Smørgrav { 883b7579f77SDag-Erling Smørgrav size_t n = 0; 884b7579f77SDag-Erling Smørgrav int r; 885b7579f77SDag-Erling Smørgrav int endnl = 0; 886b7579f77SDag-Erling Smørgrav while(1) { 887b7579f77SDag-Erling Smørgrav if(n >= len) { 888b7579f77SDag-Erling Smørgrav if(verb) printf("line too long\n"); 889b7579f77SDag-Erling Smørgrav return 0; 890b7579f77SDag-Erling Smørgrav } 891b7579f77SDag-Erling Smørgrav if((r = SSL_read(ssl, buf+n, 1)) <= 0) { 892b7579f77SDag-Erling Smørgrav if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { 893b7579f77SDag-Erling Smørgrav /* EOF */ 894b7579f77SDag-Erling Smørgrav break; 895b7579f77SDag-Erling Smørgrav } 896b7579f77SDag-Erling Smørgrav if(verb) printf("could not SSL_read\n"); 897b7579f77SDag-Erling Smørgrav return 0; 898b7579f77SDag-Erling Smørgrav } 899b7579f77SDag-Erling Smørgrav if(endnl && buf[n] == '\n') { 900b7579f77SDag-Erling Smørgrav break; 901b7579f77SDag-Erling Smørgrav } else if(endnl) { 902b7579f77SDag-Erling Smørgrav /* bad data */ 903b7579f77SDag-Erling Smørgrav if(verb) printf("error: stray linefeeds\n"); 904b7579f77SDag-Erling Smørgrav return 0; 905b7579f77SDag-Erling Smørgrav } else if(buf[n] == '\r') { 906b7579f77SDag-Erling Smørgrav /* skip \r, and also \n on the wire */ 907b7579f77SDag-Erling Smørgrav endnl = 1; 908b7579f77SDag-Erling Smørgrav continue; 909b7579f77SDag-Erling Smørgrav } else if(buf[n] == '\n') { 910b7579f77SDag-Erling Smørgrav /* skip the \n, we are done */ 911b7579f77SDag-Erling Smørgrav break; 912b7579f77SDag-Erling Smørgrav } else n++; 913b7579f77SDag-Erling Smørgrav } 914b7579f77SDag-Erling Smørgrav buf[n] = 0; 915b7579f77SDag-Erling Smørgrav return 1; 916b7579f77SDag-Erling Smørgrav } 917b7579f77SDag-Erling Smørgrav 918b7579f77SDag-Erling Smørgrav /** read http headers and process them */ 919b7579f77SDag-Erling Smørgrav static size_t 920b7579f77SDag-Erling Smørgrav read_http_headers(SSL* ssl, size_t* clen) 921b7579f77SDag-Erling Smørgrav { 922b7579f77SDag-Erling Smørgrav char buf[1024]; 923b7579f77SDag-Erling Smørgrav int chunked = 0; 924b7579f77SDag-Erling Smørgrav *clen = 0; 925b7579f77SDag-Erling Smørgrav while(read_ssl_line(ssl, buf, sizeof(buf))) { 926b7579f77SDag-Erling Smørgrav if(buf[0] == 0) 927b7579f77SDag-Erling Smørgrav return 1; 928b7579f77SDag-Erling Smørgrav if(!process_one_header(buf, clen, &chunked)) 929b7579f77SDag-Erling Smørgrav return 0; 930b7579f77SDag-Erling Smørgrav } 931b7579f77SDag-Erling Smørgrav return 0; 932b7579f77SDag-Erling Smørgrav } 933b7579f77SDag-Erling Smørgrav 934b7579f77SDag-Erling Smørgrav /** read a data chunk */ 935b7579f77SDag-Erling Smørgrav static char* 936b7579f77SDag-Erling Smørgrav read_data_chunk(SSL* ssl, size_t len) 937b7579f77SDag-Erling Smørgrav { 938b7579f77SDag-Erling Smørgrav size_t got = 0; 939b7579f77SDag-Erling Smørgrav int r; 94009a3aaf3SDag-Erling Smørgrav char* data; 9410eefd307SCy Schubert if((unsigned)len >= (unsigned)0xfffffff0) 94209a3aaf3SDag-Erling Smørgrav return NULL; /* to protect against integer overflow in malloc*/ 94309a3aaf3SDag-Erling Smørgrav data = malloc(len+1); 944b7579f77SDag-Erling Smørgrav if(!data) { 945b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 946b7579f77SDag-Erling Smørgrav return NULL; 947b7579f77SDag-Erling Smørgrav } 948b7579f77SDag-Erling Smørgrav while(got < len) { 949b7579f77SDag-Erling Smørgrav if((r = SSL_read(ssl, data+got, (int)(len-got))) <= 0) { 950b7579f77SDag-Erling Smørgrav if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { 951b7579f77SDag-Erling Smørgrav /* EOF */ 952b7579f77SDag-Erling Smørgrav if(verb) printf("could not SSL_read: unexpected EOF\n"); 953b7579f77SDag-Erling Smørgrav free(data); 954b7579f77SDag-Erling Smørgrav return NULL; 955b7579f77SDag-Erling Smørgrav } 956b7579f77SDag-Erling Smørgrav if(verb) printf("could not SSL_read\n"); 957b7579f77SDag-Erling Smørgrav free(data); 958b7579f77SDag-Erling Smørgrav return NULL; 959b7579f77SDag-Erling Smørgrav } 960b7579f77SDag-Erling Smørgrav if(verb >= 2) printf("at %d/%d\n", (int)got, (int)len); 961b7579f77SDag-Erling Smørgrav got += r; 962b7579f77SDag-Erling Smørgrav } 963b7579f77SDag-Erling Smørgrav if(verb>=2) printf("read %d data\n", (int)len); 964b7579f77SDag-Erling Smørgrav data[len] = 0; 965b7579f77SDag-Erling Smørgrav return data; 966b7579f77SDag-Erling Smørgrav } 967b7579f77SDag-Erling Smørgrav 968b7579f77SDag-Erling Smørgrav /** parse chunk header */ 969b7579f77SDag-Erling Smørgrav static int 970b7579f77SDag-Erling Smørgrav parse_chunk_header(char* buf, size_t* result) 971b7579f77SDag-Erling Smørgrav { 972b7579f77SDag-Erling Smørgrav char* e = NULL; 973b7579f77SDag-Erling Smørgrav size_t v = (size_t)strtol(buf, &e, 16); 974b7579f77SDag-Erling Smørgrav if(e == buf) 975b7579f77SDag-Erling Smørgrav return 0; 976b7579f77SDag-Erling Smørgrav *result = v; 977b7579f77SDag-Erling Smørgrav return 1; 978b7579f77SDag-Erling Smørgrav } 979b7579f77SDag-Erling Smørgrav 980b7579f77SDag-Erling Smørgrav /** read chunked data from connection */ 981b7579f77SDag-Erling Smørgrav static BIO* 982b7579f77SDag-Erling Smørgrav do_chunked_read(SSL* ssl) 983b7579f77SDag-Erling Smørgrav { 984b7579f77SDag-Erling Smørgrav char buf[1024]; 985b7579f77SDag-Erling Smørgrav size_t len; 986b7579f77SDag-Erling Smørgrav char* body; 987b7579f77SDag-Erling Smørgrav BIO* mem = BIO_new(BIO_s_mem()); 988b7579f77SDag-Erling Smørgrav if(verb>=3) printf("do_chunked_read\n"); 989b7579f77SDag-Erling Smørgrav if(!mem) { 990b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 991b7579f77SDag-Erling Smørgrav return NULL; 992b7579f77SDag-Erling Smørgrav } 993b7579f77SDag-Erling Smørgrav while(read_ssl_line(ssl, buf, sizeof(buf))) { 994b7579f77SDag-Erling Smørgrav /* read the chunked start line */ 995b7579f77SDag-Erling Smørgrav if(verb>=2) printf("chunk header: %s\n", buf); 996b7579f77SDag-Erling Smørgrav if(!parse_chunk_header(buf, &len)) { 997b7579f77SDag-Erling Smørgrav BIO_free(mem); 998b7579f77SDag-Erling Smørgrav if(verb>=3) printf("could not parse chunk header\n"); 999b7579f77SDag-Erling Smørgrav return NULL; 1000b7579f77SDag-Erling Smørgrav } 1001b7579f77SDag-Erling Smørgrav if(verb>=2) printf("chunk len: %d\n", (int)len); 1002b7579f77SDag-Erling Smørgrav /* are we done? */ 1003b7579f77SDag-Erling Smørgrav if(len == 0) { 1004b7579f77SDag-Erling Smørgrav char z = 0; 1005b7579f77SDag-Erling Smørgrav /* skip end-of-chunk-trailer lines, 1006b7579f77SDag-Erling Smørgrav * until the empty line after that */ 1007b7579f77SDag-Erling Smørgrav do { 1008b7579f77SDag-Erling Smørgrav if(!read_ssl_line(ssl, buf, sizeof(buf))) { 1009b7579f77SDag-Erling Smørgrav BIO_free(mem); 1010b7579f77SDag-Erling Smørgrav return NULL; 1011b7579f77SDag-Erling Smørgrav } 1012b7579f77SDag-Erling Smørgrav } while (strlen(buf) > 0); 1013b7579f77SDag-Erling Smørgrav /* end of chunks, zero terminate it */ 1014b7579f77SDag-Erling Smørgrav if(BIO_write(mem, &z, 1) <= 0) { 1015b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1016b7579f77SDag-Erling Smørgrav BIO_free(mem); 1017b7579f77SDag-Erling Smørgrav return NULL; 1018b7579f77SDag-Erling Smørgrav } 1019b7579f77SDag-Erling Smørgrav return mem; 1020b7579f77SDag-Erling Smørgrav } 1021b7579f77SDag-Erling Smørgrav /* read the chunked body */ 1022b7579f77SDag-Erling Smørgrav body = read_data_chunk(ssl, len); 1023b7579f77SDag-Erling Smørgrav if(!body) { 1024b7579f77SDag-Erling Smørgrav BIO_free(mem); 1025b7579f77SDag-Erling Smørgrav return NULL; 1026b7579f77SDag-Erling Smørgrav } 1027b7579f77SDag-Erling Smørgrav if(BIO_write(mem, body, (int)len) <= 0) { 1028b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1029b7579f77SDag-Erling Smørgrav free(body); 1030b7579f77SDag-Erling Smørgrav BIO_free(mem); 1031b7579f77SDag-Erling Smørgrav return NULL; 1032b7579f77SDag-Erling Smørgrav } 1033b7579f77SDag-Erling Smørgrav free(body); 1034b7579f77SDag-Erling Smørgrav /* skip empty line after data chunk */ 1035b7579f77SDag-Erling Smørgrav if(!read_ssl_line(ssl, buf, sizeof(buf))) { 1036b7579f77SDag-Erling Smørgrav BIO_free(mem); 1037b7579f77SDag-Erling Smørgrav return NULL; 1038b7579f77SDag-Erling Smørgrav } 1039b7579f77SDag-Erling Smørgrav } 1040b7579f77SDag-Erling Smørgrav BIO_free(mem); 1041b7579f77SDag-Erling Smørgrav return NULL; 1042b7579f77SDag-Erling Smørgrav } 1043b7579f77SDag-Erling Smørgrav 1044b7579f77SDag-Erling Smørgrav /** start HTTP1.1 transaction on SSL */ 1045b7579f77SDag-Erling Smørgrav static int 1046ebc5657fSDag-Erling Smørgrav write_http_get(SSL* ssl, const char* pathname, const char* urlname) 1047b7579f77SDag-Erling Smørgrav { 1048b7579f77SDag-Erling Smørgrav if(write_ssl_line(ssl, "GET /%s HTTP/1.1", pathname) && 1049b7579f77SDag-Erling Smørgrav write_ssl_line(ssl, "Host: %s", urlname) && 1050b7579f77SDag-Erling Smørgrav write_ssl_line(ssl, "User-Agent: unbound-anchor/%s", 1051b7579f77SDag-Erling Smørgrav PACKAGE_VERSION) && 1052b7579f77SDag-Erling Smørgrav /* We do not really do multiple queries per connection, 1053b7579f77SDag-Erling Smørgrav * but this header setting is also not needed. 1054b7579f77SDag-Erling Smørgrav * write_ssl_line(ssl, "Connection: close", NULL) &&*/ 1055b7579f77SDag-Erling Smørgrav write_ssl_line(ssl, "", NULL)) { 1056b7579f77SDag-Erling Smørgrav return 1; 1057b7579f77SDag-Erling Smørgrav } 1058b7579f77SDag-Erling Smørgrav return 0; 1059b7579f77SDag-Erling Smørgrav } 1060b7579f77SDag-Erling Smørgrav 1061b7579f77SDag-Erling Smørgrav /** read chunked data and zero terminate; len is without zero */ 1062b7579f77SDag-Erling Smørgrav static char* 1063b7579f77SDag-Erling Smørgrav read_chunked_zero_terminate(SSL* ssl, size_t* len) 1064b7579f77SDag-Erling Smørgrav { 1065b7579f77SDag-Erling Smørgrav /* do the chunked version */ 1066b7579f77SDag-Erling Smørgrav BIO* tmp = do_chunked_read(ssl); 1067b7579f77SDag-Erling Smørgrav char* data, *d = NULL; 1068b7579f77SDag-Erling Smørgrav size_t l; 1069b7579f77SDag-Erling Smørgrav if(!tmp) { 1070b7579f77SDag-Erling Smørgrav if(verb) printf("could not read from https\n"); 1071b7579f77SDag-Erling Smørgrav return NULL; 1072b7579f77SDag-Erling Smørgrav } 1073b7579f77SDag-Erling Smørgrav l = (size_t)BIO_get_mem_data(tmp, &d); 1074b7579f77SDag-Erling Smørgrav if(verb>=2) printf("chunked data is %d\n", (int)l); 1075b7579f77SDag-Erling Smørgrav if(l == 0 || d == NULL) { 1076b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1077b7579f77SDag-Erling Smørgrav return NULL; 1078b7579f77SDag-Erling Smørgrav } 1079b7579f77SDag-Erling Smørgrav *len = l-1; 1080b7579f77SDag-Erling Smørgrav data = (char*)malloc(l); 1081b7579f77SDag-Erling Smørgrav if(data == NULL) { 1082b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1083b7579f77SDag-Erling Smørgrav return NULL; 1084b7579f77SDag-Erling Smørgrav } 1085b7579f77SDag-Erling Smørgrav memcpy(data, d, l); 1086b7579f77SDag-Erling Smørgrav BIO_free(tmp); 1087b7579f77SDag-Erling Smørgrav return data; 1088b7579f77SDag-Erling Smørgrav } 1089b7579f77SDag-Erling Smørgrav 1090b7579f77SDag-Erling Smørgrav /** read HTTP result from SSL */ 1091b7579f77SDag-Erling Smørgrav static BIO* 1092b7579f77SDag-Erling Smørgrav read_http_result(SSL* ssl) 1093b7579f77SDag-Erling Smørgrav { 1094b7579f77SDag-Erling Smørgrav size_t len = 0; 1095b7579f77SDag-Erling Smørgrav char* data; 1096b7579f77SDag-Erling Smørgrav BIO* m; 1097b7579f77SDag-Erling Smørgrav if(!read_http_headers(ssl, &len)) { 1098b7579f77SDag-Erling Smørgrav return NULL; 1099b7579f77SDag-Erling Smørgrav } 1100b7579f77SDag-Erling Smørgrav if(len == 0) { 1101b7579f77SDag-Erling Smørgrav data = read_chunked_zero_terminate(ssl, &len); 1102b7579f77SDag-Erling Smørgrav } else { 1103b7579f77SDag-Erling Smørgrav data = read_data_chunk(ssl, len); 1104b7579f77SDag-Erling Smørgrav } 1105b7579f77SDag-Erling Smørgrav if(!data) return NULL; 1106b7579f77SDag-Erling Smørgrav if(verb >= 4) print_data("read data", data, (int)len); 1107a755b6f6SDag-Erling Smørgrav m = BIO_new(BIO_s_mem()); 1108b7579f77SDag-Erling Smørgrav if(!m) { 1109b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1110a755b6f6SDag-Erling Smørgrav free(data); 1111b7579f77SDag-Erling Smørgrav exit(0); 1112b7579f77SDag-Erling Smørgrav } 1113a755b6f6SDag-Erling Smørgrav BIO_write(m, data, (int)len); 1114a755b6f6SDag-Erling Smørgrav free(data); 1115b7579f77SDag-Erling Smørgrav return m; 1116b7579f77SDag-Erling Smørgrav } 1117b7579f77SDag-Erling Smørgrav 1118b7579f77SDag-Erling Smørgrav /** https to an IP addr, return BIO with pathname or NULL */ 1119b7579f77SDag-Erling Smørgrav static BIO* 11200eefd307SCy Schubert https_to_ip(struct ip_list* ip, const char* pathname, const char* urlname, 1121*25039b37SCy Schubert struct ip_list* src, int use_sni) 1122b7579f77SDag-Erling Smørgrav { 1123b7579f77SDag-Erling Smørgrav int fd; 1124b7579f77SDag-Erling Smørgrav SSL* ssl; 1125b7579f77SDag-Erling Smørgrav BIO* bio; 1126b7579f77SDag-Erling Smørgrav SSL_CTX* sslctx = setup_sslctx(); 1127b7579f77SDag-Erling Smørgrav if(!sslctx) { 1128b7579f77SDag-Erling Smørgrav return NULL; 1129b7579f77SDag-Erling Smørgrav } 11300eefd307SCy Schubert fd = connect_to_ip(ip, src); 1131b7579f77SDag-Erling Smørgrav if(fd == -1) { 1132b7579f77SDag-Erling Smørgrav SSL_CTX_free(sslctx); 1133b7579f77SDag-Erling Smørgrav return NULL; 1134b7579f77SDag-Erling Smørgrav } 1135*25039b37SCy Schubert ssl = TLS_initiate(sslctx, fd, urlname, use_sni); 1136b7579f77SDag-Erling Smørgrav if(!ssl) { 1137b7579f77SDag-Erling Smørgrav SSL_CTX_free(sslctx); 1138b7579f77SDag-Erling Smørgrav fd_close(fd); 1139b7579f77SDag-Erling Smørgrav return NULL; 1140b7579f77SDag-Erling Smørgrav } 1141b7579f77SDag-Erling Smørgrav if(!write_http_get(ssl, pathname, urlname)) { 1142b7579f77SDag-Erling Smørgrav if(verb) printf("could not write to server\n"); 1143b7579f77SDag-Erling Smørgrav SSL_free(ssl); 1144b7579f77SDag-Erling Smørgrav SSL_CTX_free(sslctx); 1145b7579f77SDag-Erling Smørgrav fd_close(fd); 1146b7579f77SDag-Erling Smørgrav return NULL; 1147b7579f77SDag-Erling Smørgrav } 1148b7579f77SDag-Erling Smørgrav bio = read_http_result(ssl); 1149b7579f77SDag-Erling Smørgrav TLS_shutdown(fd, ssl, sslctx); 1150b7579f77SDag-Erling Smørgrav return bio; 1151b7579f77SDag-Erling Smørgrav } 1152b7579f77SDag-Erling Smørgrav 1153b7579f77SDag-Erling Smørgrav /** 1154b7579f77SDag-Erling Smørgrav * Do a HTTPS, HTTP1.1 over TLS, to fetch a file 1155b7579f77SDag-Erling Smørgrav * @param ip_list: list of IP addresses to use to fetch from. 1156b7579f77SDag-Erling Smørgrav * @param pathname: pathname of file on server to GET. 1157b7579f77SDag-Erling Smørgrav * @param urlname: name to pass as the virtual host for this request. 11580eefd307SCy Schubert * @param src: if nonNULL, source address to bind to. 1159*25039b37SCy Schubert * @param use_sni: if SNI will be used. 1160b7579f77SDag-Erling Smørgrav * @return a memory BIO with the file in it. 1161b7579f77SDag-Erling Smørgrav */ 1162b7579f77SDag-Erling Smørgrav static BIO* 11630eefd307SCy Schubert https(struct ip_list* ip_list, const char* pathname, const char* urlname, 1164*25039b37SCy Schubert struct ip_list* src, int use_sni) 1165b7579f77SDag-Erling Smørgrav { 1166b7579f77SDag-Erling Smørgrav struct ip_list* ip; 1167b7579f77SDag-Erling Smørgrav BIO* bio = NULL; 1168b7579f77SDag-Erling Smørgrav /* try random address first, and work through the list */ 1169b7579f77SDag-Erling Smørgrav wipe_ip_usage(ip_list); 1170b7579f77SDag-Erling Smørgrav while( (ip = pick_random_ip(ip_list)) ) { 1171b7579f77SDag-Erling Smørgrav ip->used = 1; 1172*25039b37SCy Schubert bio = https_to_ip(ip, pathname, urlname, src, use_sni); 1173b7579f77SDag-Erling Smørgrav if(bio) break; 1174b7579f77SDag-Erling Smørgrav } 1175b7579f77SDag-Erling Smørgrav if(!bio) { 1176b7579f77SDag-Erling Smørgrav if(verb) printf("could not fetch %s\n", pathname); 1177b7579f77SDag-Erling Smørgrav exit(0); 1178b7579f77SDag-Erling Smørgrav } else { 1179b7579f77SDag-Erling Smørgrav if(verb) printf("fetched %s (%d bytes)\n", 1180b7579f77SDag-Erling Smørgrav pathname, (int)BIO_ctrl_pending(bio)); 1181b7579f77SDag-Erling Smørgrav } 1182b7579f77SDag-Erling Smørgrav return bio; 1183b7579f77SDag-Erling Smørgrav } 1184b7579f77SDag-Erling Smørgrav 1185b7579f77SDag-Erling Smørgrav /** XML parse private data during the parse */ 1186b7579f77SDag-Erling Smørgrav struct xml_data { 1187b7579f77SDag-Erling Smørgrav /** the parser, reference */ 1188b7579f77SDag-Erling Smørgrav XML_Parser parser; 1189b7579f77SDag-Erling Smørgrav /** the current tag; malloced; or NULL outside of tags */ 1190b7579f77SDag-Erling Smørgrav char* tag; 1191b7579f77SDag-Erling Smørgrav /** current date to use during the parse */ 1192b7579f77SDag-Erling Smørgrav time_t date; 1193b7579f77SDag-Erling Smørgrav /** number of keys usefully read in */ 1194b7579f77SDag-Erling Smørgrav int num_keys; 1195b7579f77SDag-Erling Smørgrav /** the compiled anchors as DS records */ 1196b7579f77SDag-Erling Smørgrav BIO* ds; 1197b7579f77SDag-Erling Smørgrav 1198b7579f77SDag-Erling Smørgrav /** do we want to use this anchor? */ 1199b7579f77SDag-Erling Smørgrav int use_key; 1200b7579f77SDag-Erling Smørgrav /** the current anchor: Zone */ 1201b7579f77SDag-Erling Smørgrav BIO* czone; 1202b7579f77SDag-Erling Smørgrav /** the current anchor: KeyTag */ 1203b7579f77SDag-Erling Smørgrav BIO* ctag; 1204b7579f77SDag-Erling Smørgrav /** the current anchor: Algorithm */ 1205b7579f77SDag-Erling Smørgrav BIO* calgo; 1206b7579f77SDag-Erling Smørgrav /** the current anchor: DigestType */ 1207b7579f77SDag-Erling Smørgrav BIO* cdigtype; 1208b7579f77SDag-Erling Smørgrav /** the current anchor: Digest*/ 1209b7579f77SDag-Erling Smørgrav BIO* cdigest; 1210b7579f77SDag-Erling Smørgrav }; 1211b7579f77SDag-Erling Smørgrav 1212b7579f77SDag-Erling Smørgrav /** The BIO for the tag */ 1213b7579f77SDag-Erling Smørgrav static BIO* 1214b7579f77SDag-Erling Smørgrav xml_selectbio(struct xml_data* data, const char* tag) 1215b7579f77SDag-Erling Smørgrav { 1216b7579f77SDag-Erling Smørgrav BIO* b = NULL; 1217b7579f77SDag-Erling Smørgrav if(strcasecmp(tag, "KeyTag") == 0) 1218b7579f77SDag-Erling Smørgrav b = data->ctag; 1219b7579f77SDag-Erling Smørgrav else if(strcasecmp(tag, "Algorithm") == 0) 1220b7579f77SDag-Erling Smørgrav b = data->calgo; 1221b7579f77SDag-Erling Smørgrav else if(strcasecmp(tag, "DigestType") == 0) 1222b7579f77SDag-Erling Smørgrav b = data->cdigtype; 1223b7579f77SDag-Erling Smørgrav else if(strcasecmp(tag, "Digest") == 0) 1224b7579f77SDag-Erling Smørgrav b = data->cdigest; 1225b7579f77SDag-Erling Smørgrav return b; 1226b7579f77SDag-Erling Smørgrav } 1227b7579f77SDag-Erling Smørgrav 1228b7579f77SDag-Erling Smørgrav /** 1229b7579f77SDag-Erling Smørgrav * XML handle character data, the data inside an element. 1230b7579f77SDag-Erling Smørgrav * @param userData: xml_data structure 1231b7579f77SDag-Erling Smørgrav * @param s: the character data. May not all be in one callback. 1232b7579f77SDag-Erling Smørgrav * NOT zero terminated. 1233b7579f77SDag-Erling Smørgrav * @param len: length of this part of the data. 1234b7579f77SDag-Erling Smørgrav */ 1235ebc5657fSDag-Erling Smørgrav static void 1236b7579f77SDag-Erling Smørgrav xml_charhandle(void *userData, const XML_Char *s, int len) 1237b7579f77SDag-Erling Smørgrav { 1238b7579f77SDag-Erling Smørgrav struct xml_data* data = (struct xml_data*)userData; 1239b7579f77SDag-Erling Smørgrav BIO* b = NULL; 1240b7579f77SDag-Erling Smørgrav /* skip characters outside of elements */ 1241b7579f77SDag-Erling Smørgrav if(!data->tag) 1242b7579f77SDag-Erling Smørgrav return; 1243b7579f77SDag-Erling Smørgrav if(verb>=4) { 1244b7579f77SDag-Erling Smørgrav int i; 1245b7579f77SDag-Erling Smørgrav printf("%s%s charhandle: '", 1246b7579f77SDag-Erling Smørgrav data->use_key?"use ":"", 1247b7579f77SDag-Erling Smørgrav data->tag?data->tag:"none"); 1248b7579f77SDag-Erling Smørgrav for(i=0; i<len; i++) 1249b7579f77SDag-Erling Smørgrav printf("%c", s[i]); 1250b7579f77SDag-Erling Smørgrav printf("'\n"); 1251b7579f77SDag-Erling Smørgrav } 1252b7579f77SDag-Erling Smørgrav if(strcasecmp(data->tag, "Zone") == 0) { 125317d15b25SDag-Erling Smørgrav if(BIO_write(data->czone, s, len) < 0) { 1254b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory in BIO_write\n"); 1255b7579f77SDag-Erling Smørgrav exit(0); 1256b7579f77SDag-Erling Smørgrav } 1257b7579f77SDag-Erling Smørgrav return; 1258b7579f77SDag-Erling Smørgrav } 1259b7579f77SDag-Erling Smørgrav /* only store if key is used */ 1260b7579f77SDag-Erling Smørgrav if(!data->use_key) 1261b7579f77SDag-Erling Smørgrav return; 1262b7579f77SDag-Erling Smørgrav b = xml_selectbio(data, data->tag); 1263b7579f77SDag-Erling Smørgrav if(b) { 126417d15b25SDag-Erling Smørgrav if(BIO_write(b, s, len) < 0) { 1265b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory in BIO_write\n"); 1266b7579f77SDag-Erling Smørgrav exit(0); 1267b7579f77SDag-Erling Smørgrav } 1268b7579f77SDag-Erling Smørgrav } 1269b7579f77SDag-Erling Smørgrav } 1270b7579f77SDag-Erling Smørgrav 1271b7579f77SDag-Erling Smørgrav /** 1272b7579f77SDag-Erling Smørgrav * XML fetch value of particular attribute(by name) or NULL if not present. 1273b7579f77SDag-Erling Smørgrav * @param atts: attribute array (from xml_startelem). 1274b7579f77SDag-Erling Smørgrav * @param name: name of attribute to look for. 1275b7579f77SDag-Erling Smørgrav * @return the value or NULL. (ptr into atts). 1276b7579f77SDag-Erling Smørgrav */ 1277b7579f77SDag-Erling Smørgrav static const XML_Char* 1278ebc5657fSDag-Erling Smørgrav find_att(const XML_Char **atts, const XML_Char* name) 1279b7579f77SDag-Erling Smørgrav { 1280b7579f77SDag-Erling Smørgrav int i; 1281b7579f77SDag-Erling Smørgrav for(i=0; atts[i]; i+=2) { 1282b7579f77SDag-Erling Smørgrav if(strcasecmp(atts[i], name) == 0) 1283b7579f77SDag-Erling Smørgrav return atts[i+1]; 1284b7579f77SDag-Erling Smørgrav } 1285b7579f77SDag-Erling Smørgrav return NULL; 1286b7579f77SDag-Erling Smørgrav } 1287b7579f77SDag-Erling Smørgrav 1288b7579f77SDag-Erling Smørgrav /** 1289b7579f77SDag-Erling Smørgrav * XML convert DateTime element to time_t. 1290b7579f77SDag-Erling Smørgrav * [-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm] 1291b7579f77SDag-Erling Smørgrav * (with optional .ssssss fractional seconds) 1292b7579f77SDag-Erling Smørgrav * @param str: the string 1293b7579f77SDag-Erling Smørgrav * @return a time_t representation or 0 on failure. 1294b7579f77SDag-Erling Smørgrav */ 1295b7579f77SDag-Erling Smørgrav static time_t 1296b7579f77SDag-Erling Smørgrav xml_convertdate(const char* str) 1297b7579f77SDag-Erling Smørgrav { 1298b7579f77SDag-Erling Smørgrav time_t t = 0; 1299b7579f77SDag-Erling Smørgrav struct tm tm; 1300b7579f77SDag-Erling Smørgrav const char* s; 1301b7579f77SDag-Erling Smørgrav /* for this application, ignore minus in front; 1302b7579f77SDag-Erling Smørgrav * only positive dates are expected */ 1303b7579f77SDag-Erling Smørgrav s = str; 1304b7579f77SDag-Erling Smørgrav if(s[0] == '-') s++; 1305b7579f77SDag-Erling Smørgrav memset(&tm, 0, sizeof(tm)); 1306b7579f77SDag-Erling Smørgrav /* parse initial content of the string (lots of whitespace allowed) */ 1307b7579f77SDag-Erling Smørgrav s = strptime(s, "%t%Y%t-%t%m%t-%t%d%tT%t%H%t:%t%M%t:%t%S%t", &tm); 1308b7579f77SDag-Erling Smørgrav if(!s) { 1309b7579f77SDag-Erling Smørgrav if(verb) printf("xml_convertdate parse failure %s\n", str); 1310b7579f77SDag-Erling Smørgrav return 0; 1311b7579f77SDag-Erling Smørgrav } 1312b7579f77SDag-Erling Smørgrav /* parse remainder of date string */ 1313b7579f77SDag-Erling Smørgrav if(*s == '.') { 1314b7579f77SDag-Erling Smørgrav /* optional '.' and fractional seconds */ 1315b7579f77SDag-Erling Smørgrav int frac = 0, n = 0; 1316b7579f77SDag-Erling Smørgrav if(sscanf(s+1, "%d%n", &frac, &n) < 1) { 1317b7579f77SDag-Erling Smørgrav if(verb) printf("xml_convertdate f failure %s\n", str); 1318b7579f77SDag-Erling Smørgrav return 0; 1319b7579f77SDag-Erling Smørgrav } 1320b7579f77SDag-Erling Smørgrav /* fraction is not used, time_t has second accuracy */ 1321b7579f77SDag-Erling Smørgrav s++; 1322b7579f77SDag-Erling Smørgrav s+=n; 1323b7579f77SDag-Erling Smørgrav } 1324b7579f77SDag-Erling Smørgrav if(*s == 'Z' || *s == 'z') { 1325b7579f77SDag-Erling Smørgrav /* nothing to do for this */ 1326b7579f77SDag-Erling Smørgrav s++; 1327b7579f77SDag-Erling Smørgrav } else if(*s == '+' || *s == '-') { 1328b7579f77SDag-Erling Smørgrav /* optional timezone spec: Z or +hh:mm or -hh:mm */ 1329b7579f77SDag-Erling Smørgrav int hr = 0, mn = 0, n = 0; 1330b7579f77SDag-Erling Smørgrav if(sscanf(s+1, "%d:%d%n", &hr, &mn, &n) < 2) { 1331b7579f77SDag-Erling Smørgrav if(verb) printf("xml_convertdate tz failure %s\n", str); 1332b7579f77SDag-Erling Smørgrav return 0; 1333b7579f77SDag-Erling Smørgrav } 1334b7579f77SDag-Erling Smørgrav if(*s == '+') { 1335b7579f77SDag-Erling Smørgrav tm.tm_hour += hr; 1336b7579f77SDag-Erling Smørgrav tm.tm_min += mn; 1337b7579f77SDag-Erling Smørgrav } else { 1338b7579f77SDag-Erling Smørgrav tm.tm_hour -= hr; 1339b7579f77SDag-Erling Smørgrav tm.tm_min -= mn; 1340b7579f77SDag-Erling Smørgrav } 1341b7579f77SDag-Erling Smørgrav s++; 1342b7579f77SDag-Erling Smørgrav s += n; 1343b7579f77SDag-Erling Smørgrav } 1344b7579f77SDag-Erling Smørgrav if(*s != 0) { 1345b7579f77SDag-Erling Smørgrav /* not ended properly */ 1346b7579f77SDag-Erling Smørgrav /* but ignore, (lenient) */ 1347b7579f77SDag-Erling Smørgrav } 1348b7579f77SDag-Erling Smørgrav 1349b75612f8SDag-Erling Smørgrav t = sldns_mktime_from_utc(&tm); 1350b7579f77SDag-Erling Smørgrav if(t == (time_t)-1) { 1351b7579f77SDag-Erling Smørgrav if(verb) printf("xml_convertdate mktime failure\n"); 1352b7579f77SDag-Erling Smørgrav return 0; 1353b7579f77SDag-Erling Smørgrav } 1354b7579f77SDag-Erling Smørgrav return t; 1355b7579f77SDag-Erling Smørgrav } 1356b7579f77SDag-Erling Smørgrav 1357b7579f77SDag-Erling Smørgrav /** 1358b7579f77SDag-Erling Smørgrav * XML handle the KeyDigest start tag, check validity periods. 1359b7579f77SDag-Erling Smørgrav */ 1360b7579f77SDag-Erling Smørgrav static void 1361b7579f77SDag-Erling Smørgrav handle_keydigest(struct xml_data* data, const XML_Char **atts) 1362b7579f77SDag-Erling Smørgrav { 1363b7579f77SDag-Erling Smørgrav data->use_key = 0; 1364b7579f77SDag-Erling Smørgrav if(find_att(atts, "validFrom")) { 1365b7579f77SDag-Erling Smørgrav time_t from = xml_convertdate(find_att(atts, "validFrom")); 1366b7579f77SDag-Erling Smørgrav if(from == 0) { 1367b7579f77SDag-Erling Smørgrav if(verb) printf("error: xml cannot be parsed\n"); 1368b7579f77SDag-Erling Smørgrav exit(0); 1369b7579f77SDag-Erling Smørgrav } 1370b7579f77SDag-Erling Smørgrav if(data->date < from) 1371b7579f77SDag-Erling Smørgrav return; 1372b7579f77SDag-Erling Smørgrav } 1373b7579f77SDag-Erling Smørgrav if(find_att(atts, "validUntil")) { 1374b7579f77SDag-Erling Smørgrav time_t until = xml_convertdate(find_att(atts, "validUntil")); 1375b7579f77SDag-Erling Smørgrav if(until == 0) { 1376b7579f77SDag-Erling Smørgrav if(verb) printf("error: xml cannot be parsed\n"); 1377b7579f77SDag-Erling Smørgrav exit(0); 1378b7579f77SDag-Erling Smørgrav } 1379b7579f77SDag-Erling Smørgrav if(data->date > until) 1380b7579f77SDag-Erling Smørgrav return; 1381b7579f77SDag-Erling Smørgrav } 1382b7579f77SDag-Erling Smørgrav /* yes we want to use this key */ 1383b7579f77SDag-Erling Smørgrav data->use_key = 1; 1384b7579f77SDag-Erling Smørgrav (void)BIO_reset(data->ctag); 1385b7579f77SDag-Erling Smørgrav (void)BIO_reset(data->calgo); 1386b7579f77SDag-Erling Smørgrav (void)BIO_reset(data->cdigtype); 1387b7579f77SDag-Erling Smørgrav (void)BIO_reset(data->cdigest); 1388b7579f77SDag-Erling Smørgrav } 1389b7579f77SDag-Erling Smørgrav 1390b7579f77SDag-Erling Smørgrav /** See if XML element equals the zone name */ 1391b7579f77SDag-Erling Smørgrav static int 1392ebc5657fSDag-Erling Smørgrav xml_is_zone_name(BIO* zone, const char* name) 1393b7579f77SDag-Erling Smørgrav { 1394b7579f77SDag-Erling Smørgrav char buf[1024]; 1395b7579f77SDag-Erling Smørgrav char* z = NULL; 1396b7579f77SDag-Erling Smørgrav long zlen; 1397b7579f77SDag-Erling Smørgrav (void)BIO_seek(zone, 0); 1398b7579f77SDag-Erling Smørgrav zlen = BIO_get_mem_data(zone, &z); 1399b7579f77SDag-Erling Smørgrav if(!zlen || !z) return 0; 1400b7579f77SDag-Erling Smørgrav /* zero terminate */ 1401b7579f77SDag-Erling Smørgrav if(zlen >= (long)sizeof(buf)) return 0; 1402b7579f77SDag-Erling Smørgrav memmove(buf, z, (size_t)zlen); 1403b7579f77SDag-Erling Smørgrav buf[zlen] = 0; 1404b7579f77SDag-Erling Smørgrav /* compare */ 1405b7579f77SDag-Erling Smørgrav return (strncasecmp(buf, name, strlen(name)) == 0); 1406b7579f77SDag-Erling Smørgrav } 1407b7579f77SDag-Erling Smørgrav 1408b7579f77SDag-Erling Smørgrav /** 1409b7579f77SDag-Erling Smørgrav * XML start of element. This callback is called whenever an XML tag starts. 1410b7579f77SDag-Erling Smørgrav * XML_Char is UTF8. 1411b7579f77SDag-Erling Smørgrav * @param userData: the xml_data structure. 1412b7579f77SDag-Erling Smørgrav * @param name: the tag that starts. 1413b7579f77SDag-Erling Smørgrav * @param atts: array of strings, pairs of attr = value, ends with NULL. 1414b7579f77SDag-Erling Smørgrav * i.e. att[0]="att[1]" att[2]="att[3]" att[4]isNull 1415b7579f77SDag-Erling Smørgrav */ 1416b7579f77SDag-Erling Smørgrav static void 1417b7579f77SDag-Erling Smørgrav xml_startelem(void *userData, const XML_Char *name, const XML_Char **atts) 1418b7579f77SDag-Erling Smørgrav { 1419b7579f77SDag-Erling Smørgrav struct xml_data* data = (struct xml_data*)userData; 1420b7579f77SDag-Erling Smørgrav BIO* b; 1421b7579f77SDag-Erling Smørgrav if(verb>=4) printf("xml tag start '%s'\n", name); 1422b7579f77SDag-Erling Smørgrav free(data->tag); 1423b7579f77SDag-Erling Smørgrav data->tag = strdup(name); 1424b7579f77SDag-Erling Smørgrav if(!data->tag) { 1425b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1426b7579f77SDag-Erling Smørgrav exit(0); 1427b7579f77SDag-Erling Smørgrav } 1428b7579f77SDag-Erling Smørgrav if(verb>=4) { 1429b7579f77SDag-Erling Smørgrav int i; 1430b7579f77SDag-Erling Smørgrav for(i=0; atts[i]; i+=2) { 1431b7579f77SDag-Erling Smørgrav printf(" %s='%s'\n", atts[i], atts[i+1]); 1432b7579f77SDag-Erling Smørgrav } 1433b7579f77SDag-Erling Smørgrav } 1434b7579f77SDag-Erling Smørgrav /* handle attributes to particular types */ 1435b7579f77SDag-Erling Smørgrav if(strcasecmp(name, "KeyDigest") == 0) { 1436b7579f77SDag-Erling Smørgrav handle_keydigest(data, atts); 1437b7579f77SDag-Erling Smørgrav return; 1438b7579f77SDag-Erling Smørgrav } else if(strcasecmp(name, "Zone") == 0) { 1439b7579f77SDag-Erling Smørgrav (void)BIO_reset(data->czone); 1440b7579f77SDag-Erling Smørgrav return; 1441b7579f77SDag-Erling Smørgrav } 1442b7579f77SDag-Erling Smørgrav 1443b7579f77SDag-Erling Smørgrav /* for other types we prepare to pick up the data */ 1444b7579f77SDag-Erling Smørgrav if(!data->use_key) 1445b7579f77SDag-Erling Smørgrav return; 1446b7579f77SDag-Erling Smørgrav b = xml_selectbio(data, data->tag); 1447b7579f77SDag-Erling Smørgrav if(b) { 1448b7579f77SDag-Erling Smørgrav /* empty it */ 1449b7579f77SDag-Erling Smørgrav (void)BIO_reset(b); 1450b7579f77SDag-Erling Smørgrav } 1451b7579f77SDag-Erling Smørgrav } 1452b7579f77SDag-Erling Smørgrav 1453b7579f77SDag-Erling Smørgrav /** Append str to bio */ 1454b7579f77SDag-Erling Smørgrav static void 1455b7579f77SDag-Erling Smørgrav xml_append_str(BIO* b, const char* s) 1456b7579f77SDag-Erling Smørgrav { 145717d15b25SDag-Erling Smørgrav if(BIO_write(b, s, (int)strlen(s)) < 0) { 1458b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory in BIO_write\n"); 1459b7579f77SDag-Erling Smørgrav exit(0); 1460b7579f77SDag-Erling Smørgrav } 1461b7579f77SDag-Erling Smørgrav } 1462b7579f77SDag-Erling Smørgrav 1463b7579f77SDag-Erling Smørgrav /** Append bio to bio */ 1464b7579f77SDag-Erling Smørgrav static void 1465b7579f77SDag-Erling Smørgrav xml_append_bio(BIO* b, BIO* a) 1466b7579f77SDag-Erling Smørgrav { 1467b7579f77SDag-Erling Smørgrav char* z = NULL; 1468b7579f77SDag-Erling Smørgrav long i, len; 1469b7579f77SDag-Erling Smørgrav (void)BIO_seek(a, 0); 1470b7579f77SDag-Erling Smørgrav len = BIO_get_mem_data(a, &z); 1471b7579f77SDag-Erling Smørgrav if(!len || !z) { 1472b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory in BIO_write\n"); 1473b7579f77SDag-Erling Smørgrav exit(0); 1474b7579f77SDag-Erling Smørgrav } 1475b7579f77SDag-Erling Smørgrav /* remove newlines in the data here */ 1476b7579f77SDag-Erling Smørgrav for(i=0; i<len; i++) { 1477b7579f77SDag-Erling Smørgrav if(z[i] == '\r' || z[i] == '\n') 1478b7579f77SDag-Erling Smørgrav z[i] = ' '; 1479b7579f77SDag-Erling Smørgrav } 1480b7579f77SDag-Erling Smørgrav /* write to BIO */ 148117d15b25SDag-Erling Smørgrav if(BIO_write(b, z, len) < 0) { 1482b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory in BIO_write\n"); 1483b7579f77SDag-Erling Smørgrav exit(0); 1484b7579f77SDag-Erling Smørgrav } 1485b7579f77SDag-Erling Smørgrav } 1486b7579f77SDag-Erling Smørgrav 1487b7579f77SDag-Erling Smørgrav /** write the parsed xml-DS to the DS list */ 1488b7579f77SDag-Erling Smørgrav static void 1489b7579f77SDag-Erling Smørgrav xml_append_ds(struct xml_data* data) 1490b7579f77SDag-Erling Smørgrav { 1491b7579f77SDag-Erling Smørgrav /* write DS to accumulated DS */ 1492b7579f77SDag-Erling Smørgrav xml_append_str(data->ds, ". IN DS "); 1493b7579f77SDag-Erling Smørgrav xml_append_bio(data->ds, data->ctag); 1494b7579f77SDag-Erling Smørgrav xml_append_str(data->ds, " "); 1495b7579f77SDag-Erling Smørgrav xml_append_bio(data->ds, data->calgo); 1496b7579f77SDag-Erling Smørgrav xml_append_str(data->ds, " "); 1497b7579f77SDag-Erling Smørgrav xml_append_bio(data->ds, data->cdigtype); 1498b7579f77SDag-Erling Smørgrav xml_append_str(data->ds, " "); 1499b7579f77SDag-Erling Smørgrav xml_append_bio(data->ds, data->cdigest); 1500b7579f77SDag-Erling Smørgrav xml_append_str(data->ds, "\n"); 1501b7579f77SDag-Erling Smørgrav data->num_keys++; 1502b7579f77SDag-Erling Smørgrav } 1503b7579f77SDag-Erling Smørgrav 1504b7579f77SDag-Erling Smørgrav /** 1505b7579f77SDag-Erling Smørgrav * XML end of element. This callback is called whenever an XML tag ends. 1506b7579f77SDag-Erling Smørgrav * XML_Char is UTF8. 1507b7579f77SDag-Erling Smørgrav * @param userData: the xml_data structure 1508b7579f77SDag-Erling Smørgrav * @param name: the tag that ends. 1509b7579f77SDag-Erling Smørgrav */ 1510b7579f77SDag-Erling Smørgrav static void 1511b7579f77SDag-Erling Smørgrav xml_endelem(void *userData, const XML_Char *name) 1512b7579f77SDag-Erling Smørgrav { 1513b7579f77SDag-Erling Smørgrav struct xml_data* data = (struct xml_data*)userData; 1514b7579f77SDag-Erling Smørgrav if(verb>=4) printf("xml tag end '%s'\n", name); 1515b7579f77SDag-Erling Smørgrav free(data->tag); 1516b7579f77SDag-Erling Smørgrav data->tag = NULL; 1517b7579f77SDag-Erling Smørgrav if(strcasecmp(name, "KeyDigest") == 0) { 1518b7579f77SDag-Erling Smørgrav if(data->use_key) 1519b7579f77SDag-Erling Smørgrav xml_append_ds(data); 1520b7579f77SDag-Erling Smørgrav data->use_key = 0; 1521b7579f77SDag-Erling Smørgrav } else if(strcasecmp(name, "Zone") == 0) { 1522b7579f77SDag-Erling Smørgrav if(!xml_is_zone_name(data->czone, ".")) { 1523b7579f77SDag-Erling Smørgrav if(verb) printf("xml not for the right zone\n"); 1524b7579f77SDag-Erling Smørgrav exit(0); 1525b7579f77SDag-Erling Smørgrav } 1526b7579f77SDag-Erling Smørgrav } 1527b7579f77SDag-Erling Smørgrav } 1528b7579f77SDag-Erling Smørgrav 15298ed2b524SDag-Erling Smørgrav /* Stop the parser when an entity declaration is encountered. For safety. */ 15308ed2b524SDag-Erling Smørgrav static void 15318ed2b524SDag-Erling Smørgrav xml_entitydeclhandler(void *userData, 15328ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(entityName), 15338ed2b524SDag-Erling Smørgrav int ATTR_UNUSED(is_parameter_entity), 15348ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(value), int ATTR_UNUSED(value_length), 15358ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(base), 15368ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(systemId), 15378ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(publicId), 15388ed2b524SDag-Erling Smørgrav const XML_Char *ATTR_UNUSED(notationName)) 15398ed2b524SDag-Erling Smørgrav { 154005ab2901SDag-Erling Smørgrav #if HAVE_DECL_XML_STOPPARSER 15418ed2b524SDag-Erling Smørgrav (void)XML_StopParser((XML_Parser)userData, XML_FALSE); 154205ab2901SDag-Erling Smørgrav #else 154305ab2901SDag-Erling Smørgrav (void)userData; 154405ab2901SDag-Erling Smørgrav #endif 15458ed2b524SDag-Erling Smørgrav } 15468ed2b524SDag-Erling Smørgrav 1547b7579f77SDag-Erling Smørgrav /** 1548b7579f77SDag-Erling Smørgrav * XML parser setup of the callbacks for the tags 1549b7579f77SDag-Erling Smørgrav */ 1550b7579f77SDag-Erling Smørgrav static void 1551b7579f77SDag-Erling Smørgrav xml_parse_setup(XML_Parser parser, struct xml_data* data, time_t now) 1552b7579f77SDag-Erling Smørgrav { 1553b7579f77SDag-Erling Smørgrav char buf[1024]; 1554b7579f77SDag-Erling Smørgrav memset(data, 0, sizeof(*data)); 1555b7579f77SDag-Erling Smørgrav XML_SetUserData(parser, data); 1556b7579f77SDag-Erling Smørgrav data->parser = parser; 1557b7579f77SDag-Erling Smørgrav data->date = now; 1558b7579f77SDag-Erling Smørgrav data->ds = BIO_new(BIO_s_mem()); 1559b7579f77SDag-Erling Smørgrav data->ctag = BIO_new(BIO_s_mem()); 1560b7579f77SDag-Erling Smørgrav data->czone = BIO_new(BIO_s_mem()); 1561b7579f77SDag-Erling Smørgrav data->calgo = BIO_new(BIO_s_mem()); 1562b7579f77SDag-Erling Smørgrav data->cdigtype = BIO_new(BIO_s_mem()); 1563b7579f77SDag-Erling Smørgrav data->cdigest = BIO_new(BIO_s_mem()); 1564b7579f77SDag-Erling Smørgrav if(!data->ds || !data->ctag || !data->calgo || !data->czone || 1565b7579f77SDag-Erling Smørgrav !data->cdigtype || !data->cdigest) { 1566b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1567b7579f77SDag-Erling Smørgrav exit(0); 1568b7579f77SDag-Erling Smørgrav } 1569b7579f77SDag-Erling Smørgrav snprintf(buf, sizeof(buf), "; created by unbound-anchor on %s", 1570b7579f77SDag-Erling Smørgrav ctime(&now)); 157117d15b25SDag-Erling Smørgrav if(BIO_write(data->ds, buf, (int)strlen(buf)) < 0) { 1572b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1573b7579f77SDag-Erling Smørgrav exit(0); 1574b7579f77SDag-Erling Smørgrav } 15758ed2b524SDag-Erling Smørgrav XML_SetEntityDeclHandler(parser, xml_entitydeclhandler); 1576b7579f77SDag-Erling Smørgrav XML_SetElementHandler(parser, xml_startelem, xml_endelem); 1577b7579f77SDag-Erling Smørgrav XML_SetCharacterDataHandler(parser, xml_charhandle); 1578b7579f77SDag-Erling Smørgrav } 1579b7579f77SDag-Erling Smørgrav 1580b7579f77SDag-Erling Smørgrav /** 1581b7579f77SDag-Erling Smørgrav * Perform XML parsing of the root-anchors file 1582b7579f77SDag-Erling Smørgrav * Its format description can be read here 1583b7579f77SDag-Erling Smørgrav * https://data.iana.org/root-anchors/draft-icann-dnssec-trust-anchor.txt 1584b7579f77SDag-Erling Smørgrav * It uses libexpat. 1585b7579f77SDag-Erling Smørgrav * @param xml: BIO with xml data. 1586b7579f77SDag-Erling Smørgrav * @param now: the current time for checking DS validity periods. 1587b7579f77SDag-Erling Smørgrav * @return memoryBIO with the DS data in zone format. 1588b7579f77SDag-Erling Smørgrav * or NULL if the zone is insecure. 1589b7579f77SDag-Erling Smørgrav * (It exit()s on error) 1590b7579f77SDag-Erling Smørgrav */ 1591b7579f77SDag-Erling Smørgrav static BIO* 1592b7579f77SDag-Erling Smørgrav xml_parse(BIO* xml, time_t now) 1593b7579f77SDag-Erling Smørgrav { 1594b7579f77SDag-Erling Smørgrav char* pp; 1595b7579f77SDag-Erling Smørgrav int len; 1596b7579f77SDag-Erling Smørgrav XML_Parser parser; 1597b7579f77SDag-Erling Smørgrav struct xml_data data; 1598b7579f77SDag-Erling Smørgrav 1599b7579f77SDag-Erling Smørgrav parser = XML_ParserCreate(NULL); 1600b7579f77SDag-Erling Smørgrav if(!parser) { 1601b7579f77SDag-Erling Smørgrav if(verb) printf("could not XML_ParserCreate\n"); 1602b7579f77SDag-Erling Smørgrav exit(0); 1603b7579f77SDag-Erling Smørgrav } 1604b7579f77SDag-Erling Smørgrav 1605b7579f77SDag-Erling Smørgrav /* setup callbacks */ 1606b7579f77SDag-Erling Smørgrav xml_parse_setup(parser, &data, now); 1607b7579f77SDag-Erling Smørgrav 1608b7579f77SDag-Erling Smørgrav /* parse it */ 1609a755b6f6SDag-Erling Smørgrav (void)BIO_seek(xml, 0); 1610b7579f77SDag-Erling Smørgrav len = (int)BIO_get_mem_data(xml, &pp); 1611b7579f77SDag-Erling Smørgrav if(!len || !pp) { 1612b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1613b7579f77SDag-Erling Smørgrav exit(0); 1614b7579f77SDag-Erling Smørgrav } 1615b7579f77SDag-Erling Smørgrav if(!XML_Parse(parser, pp, len, 1 /*isfinal*/ )) { 1616b7579f77SDag-Erling Smørgrav const char *e = XML_ErrorString(XML_GetErrorCode(parser)); 1617b7579f77SDag-Erling Smørgrav if(verb) printf("XML_Parse failure %s\n", e?e:""); 1618b7579f77SDag-Erling Smørgrav exit(0); 1619b7579f77SDag-Erling Smørgrav } 1620b7579f77SDag-Erling Smørgrav 1621b7579f77SDag-Erling Smørgrav /* parsed */ 1622b7579f77SDag-Erling Smørgrav if(verb) printf("XML was parsed successfully, %d keys\n", 1623b7579f77SDag-Erling Smørgrav data.num_keys); 1624b7579f77SDag-Erling Smørgrav free(data.tag); 1625b7579f77SDag-Erling Smørgrav XML_ParserFree(parser); 1626b7579f77SDag-Erling Smørgrav 1627b7579f77SDag-Erling Smørgrav if(verb >= 4) { 1628b7579f77SDag-Erling Smørgrav (void)BIO_seek(data.ds, 0); 1629b7579f77SDag-Erling Smørgrav len = BIO_get_mem_data(data.ds, &pp); 1630b7579f77SDag-Erling Smørgrav printf("got DS bio %d: '", len); 1631b7579f77SDag-Erling Smørgrav if(!fwrite(pp, (size_t)len, 1, stdout)) 1632b7579f77SDag-Erling Smørgrav /* compilers do not allow us to ignore fwrite .. */ 1633b7579f77SDag-Erling Smørgrav fprintf(stderr, "error writing to stdout\n"); 1634b7579f77SDag-Erling Smørgrav printf("'\n"); 1635b7579f77SDag-Erling Smørgrav } 1636b7579f77SDag-Erling Smørgrav BIO_free(data.czone); 1637b7579f77SDag-Erling Smørgrav BIO_free(data.ctag); 1638b7579f77SDag-Erling Smørgrav BIO_free(data.calgo); 1639b7579f77SDag-Erling Smørgrav BIO_free(data.cdigtype); 1640b7579f77SDag-Erling Smørgrav BIO_free(data.cdigest); 1641b7579f77SDag-Erling Smørgrav 1642b7579f77SDag-Erling Smørgrav if(data.num_keys == 0) { 1643b7579f77SDag-Erling Smørgrav /* the root zone seems to have gone insecure */ 1644b7579f77SDag-Erling Smørgrav BIO_free(data.ds); 1645b7579f77SDag-Erling Smørgrav return NULL; 1646b7579f77SDag-Erling Smørgrav } else { 1647b7579f77SDag-Erling Smørgrav return data.ds; 1648b7579f77SDag-Erling Smørgrav } 1649b7579f77SDag-Erling Smørgrav } 1650b7579f77SDag-Erling Smørgrav 16518ed2b524SDag-Erling Smørgrav /* get key usage out of its extension, returns 0 if no key_usage extension */ 16528ed2b524SDag-Erling Smørgrav static unsigned long 16538ed2b524SDag-Erling Smørgrav get_usage_of_ex(X509* cert) 16548ed2b524SDag-Erling Smørgrav { 16558ed2b524SDag-Erling Smørgrav unsigned long val = 0; 16568ed2b524SDag-Erling Smørgrav ASN1_BIT_STRING* s; 16578ed2b524SDag-Erling Smørgrav if((s=X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL))) { 16588ed2b524SDag-Erling Smørgrav if(s->length > 0) { 16598ed2b524SDag-Erling Smørgrav val = s->data[0]; 16608ed2b524SDag-Erling Smørgrav if(s->length > 1) 16618ed2b524SDag-Erling Smørgrav val |= s->data[1] << 8; 16628ed2b524SDag-Erling Smørgrav } 16638ed2b524SDag-Erling Smørgrav ASN1_BIT_STRING_free(s); 16648ed2b524SDag-Erling Smørgrav } 16658ed2b524SDag-Erling Smørgrav return val; 16668ed2b524SDag-Erling Smørgrav } 16678ed2b524SDag-Erling Smørgrav 16688ed2b524SDag-Erling Smørgrav /** get valid signers from the list of signers in the signature */ 16698ed2b524SDag-Erling Smørgrav static STACK_OF(X509)* 1670ebc5657fSDag-Erling Smørgrav get_valid_signers(PKCS7* p7, const char* p7signer) 16718ed2b524SDag-Erling Smørgrav { 16728ed2b524SDag-Erling Smørgrav int i; 16738ed2b524SDag-Erling Smørgrav STACK_OF(X509)* validsigners = sk_X509_new_null(); 16748ed2b524SDag-Erling Smørgrav STACK_OF(X509)* signers = PKCS7_get0_signers(p7, NULL, 0); 16758ed2b524SDag-Erling Smørgrav unsigned long usage = 0; 16768ed2b524SDag-Erling Smørgrav if(!validsigners) { 16778ed2b524SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 16788ed2b524SDag-Erling Smørgrav sk_X509_free(signers); 16798ed2b524SDag-Erling Smørgrav return NULL; 16808ed2b524SDag-Erling Smørgrav } 16818ed2b524SDag-Erling Smørgrav if(!signers) { 16828ed2b524SDag-Erling Smørgrav if(verb) printf("no signers in pkcs7 signature\n"); 16838ed2b524SDag-Erling Smørgrav sk_X509_free(validsigners); 16848ed2b524SDag-Erling Smørgrav return NULL; 16858ed2b524SDag-Erling Smørgrav } 16868ed2b524SDag-Erling Smørgrav for(i=0; i<sk_X509_num(signers); i++) { 16878ed2b524SDag-Erling Smørgrav X509_NAME* nm = X509_get_subject_name( 16888ed2b524SDag-Erling Smørgrav sk_X509_value(signers, i)); 16898ed2b524SDag-Erling Smørgrav char buf[1024]; 16908ed2b524SDag-Erling Smørgrav if(!nm) { 16918ed2b524SDag-Erling Smørgrav if(verb) printf("signer %d: cert has no subject name\n", i); 16928ed2b524SDag-Erling Smørgrav continue; 16938ed2b524SDag-Erling Smørgrav } 16948ed2b524SDag-Erling Smørgrav if(verb && nm) { 16958ed2b524SDag-Erling Smørgrav char* nmline = X509_NAME_oneline(nm, buf, 16968ed2b524SDag-Erling Smørgrav (int)sizeof(buf)); 16978ed2b524SDag-Erling Smørgrav printf("signer %d: Subject: %s\n", i, 16988ed2b524SDag-Erling Smørgrav nmline?nmline:"no subject"); 16998ed2b524SDag-Erling Smørgrav if(verb >= 3 && X509_NAME_get_text_by_NID(nm, 17008ed2b524SDag-Erling Smørgrav NID_commonName, buf, (int)sizeof(buf))) 17018ed2b524SDag-Erling Smørgrav printf("commonName: %s\n", buf); 17028ed2b524SDag-Erling Smørgrav if(verb >= 3 && X509_NAME_get_text_by_NID(nm, 17038ed2b524SDag-Erling Smørgrav NID_pkcs9_emailAddress, buf, (int)sizeof(buf))) 17048ed2b524SDag-Erling Smørgrav printf("emailAddress: %s\n", buf); 17058ed2b524SDag-Erling Smørgrav } 17068ed2b524SDag-Erling Smørgrav if(verb) { 17078ed2b524SDag-Erling Smørgrav int ku_loc = X509_get_ext_by_NID( 17088ed2b524SDag-Erling Smørgrav sk_X509_value(signers, i), NID_key_usage, -1); 17098ed2b524SDag-Erling Smørgrav if(verb >= 3 && ku_loc >= 0) { 17108ed2b524SDag-Erling Smørgrav X509_EXTENSION *ex = X509_get_ext( 17118ed2b524SDag-Erling Smørgrav sk_X509_value(signers, i), ku_loc); 17128ed2b524SDag-Erling Smørgrav if(ex) { 17138ed2b524SDag-Erling Smørgrav printf("keyUsage: "); 17148ed2b524SDag-Erling Smørgrav X509V3_EXT_print_fp(stdout, ex, 0, 0); 17158ed2b524SDag-Erling Smørgrav printf("\n"); 17168ed2b524SDag-Erling Smørgrav } 17178ed2b524SDag-Erling Smørgrav } 17188ed2b524SDag-Erling Smørgrav } 17198ed2b524SDag-Erling Smørgrav if(!p7signer || strcmp(p7signer, "")==0) { 17208ed2b524SDag-Erling Smørgrav /* there is no name to check, return all records */ 17218ed2b524SDag-Erling Smørgrav if(verb) printf("did not check commonName of signer\n"); 17228ed2b524SDag-Erling Smørgrav } else { 17238ed2b524SDag-Erling Smørgrav if(!X509_NAME_get_text_by_NID(nm, 17248ed2b524SDag-Erling Smørgrav NID_pkcs9_emailAddress, 17258ed2b524SDag-Erling Smørgrav buf, (int)sizeof(buf))) { 17268ed2b524SDag-Erling Smørgrav if(verb) printf("removed cert with no name\n"); 17278ed2b524SDag-Erling Smørgrav continue; /* no name, no use */ 17288ed2b524SDag-Erling Smørgrav } 17298ed2b524SDag-Erling Smørgrav if(strcmp(buf, p7signer) != 0) { 17308ed2b524SDag-Erling Smørgrav if(verb) printf("removed cert with wrong name\n"); 17318ed2b524SDag-Erling Smørgrav continue; /* wrong name, skip it */ 17328ed2b524SDag-Erling Smørgrav } 17338ed2b524SDag-Erling Smørgrav } 17348ed2b524SDag-Erling Smørgrav 17358ed2b524SDag-Erling Smørgrav /* check that the key usage allows digital signatures 17368ed2b524SDag-Erling Smørgrav * (the p7s) */ 17378ed2b524SDag-Erling Smørgrav usage = get_usage_of_ex(sk_X509_value(signers, i)); 17388ed2b524SDag-Erling Smørgrav if(!(usage & KU_DIGITAL_SIGNATURE)) { 17398ed2b524SDag-Erling Smørgrav if(verb) printf("removed cert with no key usage Digital Signature allowed\n"); 17408ed2b524SDag-Erling Smørgrav continue; 17418ed2b524SDag-Erling Smørgrav } 17428ed2b524SDag-Erling Smørgrav 17438ed2b524SDag-Erling Smørgrav /* we like this cert, add it to our list of valid 17448ed2b524SDag-Erling Smørgrav * signers certificates */ 17458ed2b524SDag-Erling Smørgrav sk_X509_push(validsigners, sk_X509_value(signers, i)); 17468ed2b524SDag-Erling Smørgrav } 17478ed2b524SDag-Erling Smørgrav sk_X509_free(signers); 17488ed2b524SDag-Erling Smørgrav return validsigners; 17498ed2b524SDag-Erling Smørgrav } 17508ed2b524SDag-Erling Smørgrav 1751b7579f77SDag-Erling Smørgrav /** verify a PKCS7 signature, false on failure */ 1752b7579f77SDag-Erling Smørgrav static int 1753ebc5657fSDag-Erling Smørgrav verify_p7sig(BIO* data, BIO* p7s, STACK_OF(X509)* trust, const char* p7signer) 1754b7579f77SDag-Erling Smørgrav { 1755b7579f77SDag-Erling Smørgrav PKCS7* p7; 1756b7579f77SDag-Erling Smørgrav X509_STORE *store = X509_STORE_new(); 17578ed2b524SDag-Erling Smørgrav STACK_OF(X509)* validsigners; 1758b7579f77SDag-Erling Smørgrav int secure = 0; 1759b7579f77SDag-Erling Smørgrav int i; 1760b7579f77SDag-Erling Smørgrav #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE 1761b7579f77SDag-Erling Smørgrav X509_VERIFY_PARAM* param = X509_VERIFY_PARAM_new(); 1762b7579f77SDag-Erling Smørgrav if(!param) { 1763b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1764b7579f77SDag-Erling Smørgrav X509_STORE_free(store); 1765b7579f77SDag-Erling Smørgrav return 0; 1766b7579f77SDag-Erling Smørgrav } 1767b7579f77SDag-Erling Smørgrav /* do the selfcheck on the root certificate; it checks that the 1768b7579f77SDag-Erling Smørgrav * input is valid */ 1769b7579f77SDag-Erling Smørgrav X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CHECK_SS_SIGNATURE); 1770b7579f77SDag-Erling Smørgrav if(store) X509_STORE_set1_param(store, param); 1771b7579f77SDag-Erling Smørgrav #endif 1772b7579f77SDag-Erling Smørgrav if(!store) { 1773b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1774b7579f77SDag-Erling Smørgrav #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE 1775b7579f77SDag-Erling Smørgrav X509_VERIFY_PARAM_free(param); 1776b7579f77SDag-Erling Smørgrav #endif 1777b7579f77SDag-Erling Smørgrav return 0; 1778b7579f77SDag-Erling Smørgrav } 17798ed2b524SDag-Erling Smørgrav #ifdef X509_V_FLAG_CHECK_SS_SIGNATURE 17808ed2b524SDag-Erling Smørgrav X509_VERIFY_PARAM_free(param); 17818ed2b524SDag-Erling Smørgrav #endif 1782b7579f77SDag-Erling Smørgrav 1783a755b6f6SDag-Erling Smørgrav (void)BIO_seek(p7s, 0); 1784a755b6f6SDag-Erling Smørgrav (void)BIO_seek(data, 0); 1785b7579f77SDag-Erling Smørgrav 1786b7579f77SDag-Erling Smørgrav /* convert p7s to p7 (the signature) */ 1787b7579f77SDag-Erling Smørgrav p7 = d2i_PKCS7_bio(p7s, NULL); 1788b7579f77SDag-Erling Smørgrav if(!p7) { 1789b7579f77SDag-Erling Smørgrav if(verb) printf("could not parse p7s signature file\n"); 1790b7579f77SDag-Erling Smørgrav X509_STORE_free(store); 1791b7579f77SDag-Erling Smørgrav return 0; 1792b7579f77SDag-Erling Smørgrav } 1793b7579f77SDag-Erling Smørgrav if(verb >= 2) printf("parsed the PKCS7 signature\n"); 1794b7579f77SDag-Erling Smørgrav 1795b7579f77SDag-Erling Smørgrav /* convert trust to trusted certificate store */ 1796b7579f77SDag-Erling Smørgrav for(i=0; i<sk_X509_num(trust); i++) { 1797b7579f77SDag-Erling Smørgrav if(!X509_STORE_add_cert(store, sk_X509_value(trust, i))) { 1798b7579f77SDag-Erling Smørgrav if(verb) printf("failed X509_STORE_add_cert\n"); 1799b7579f77SDag-Erling Smørgrav X509_STORE_free(store); 1800b7579f77SDag-Erling Smørgrav PKCS7_free(p7); 1801b7579f77SDag-Erling Smørgrav return 0; 1802b7579f77SDag-Erling Smørgrav } 1803b7579f77SDag-Erling Smørgrav } 1804b7579f77SDag-Erling Smørgrav if(verb >= 2) printf("setup the X509_STORE\n"); 1805b7579f77SDag-Erling Smørgrav 18068ed2b524SDag-Erling Smørgrav /* check what is in the Subject name of the certificates, 18078ed2b524SDag-Erling Smørgrav * and build a stack that contains only the right certificates */ 18088ed2b524SDag-Erling Smørgrav validsigners = get_valid_signers(p7, p7signer); 18098ed2b524SDag-Erling Smørgrav if(!validsigners) { 18108ed2b524SDag-Erling Smørgrav X509_STORE_free(store); 18118ed2b524SDag-Erling Smørgrav PKCS7_free(p7); 18128ed2b524SDag-Erling Smørgrav return 0; 18138ed2b524SDag-Erling Smørgrav } 18148ed2b524SDag-Erling Smørgrav if(PKCS7_verify(p7, validsigners, store, data, NULL, PKCS7_NOINTERN) == 1) { 1815b7579f77SDag-Erling Smørgrav secure = 1; 1816b7579f77SDag-Erling Smørgrav if(verb) printf("the PKCS7 signature verified\n"); 1817b7579f77SDag-Erling Smørgrav } else { 1818b7579f77SDag-Erling Smørgrav if(verb) { 1819b7579f77SDag-Erling Smørgrav ERR_print_errors_fp(stdout); 1820b7579f77SDag-Erling Smørgrav } 1821b7579f77SDag-Erling Smørgrav } 1822b7579f77SDag-Erling Smørgrav 18238ed2b524SDag-Erling Smørgrav sk_X509_free(validsigners); 1824b7579f77SDag-Erling Smørgrav X509_STORE_free(store); 1825b7579f77SDag-Erling Smørgrav PKCS7_free(p7); 1826b7579f77SDag-Erling Smørgrav return secure; 1827b7579f77SDag-Erling Smørgrav } 1828b7579f77SDag-Erling Smørgrav 1829b7579f77SDag-Erling Smørgrav /** write unsigned root anchor file, a 5011 revoked tp */ 1830b7579f77SDag-Erling Smørgrav static void 1831ebc5657fSDag-Erling Smørgrav write_unsigned_root(const char* root_anchor_file) 1832b7579f77SDag-Erling Smørgrav { 1833b7579f77SDag-Erling Smørgrav FILE* out; 1834b7579f77SDag-Erling Smørgrav time_t now = time(NULL); 1835b7579f77SDag-Erling Smørgrav out = fopen(root_anchor_file, "w"); 1836b7579f77SDag-Erling Smørgrav if(!out) { 1837b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", root_anchor_file, strerror(errno)); 1838b7579f77SDag-Erling Smørgrav return; 1839b7579f77SDag-Erling Smørgrav } 1840b7579f77SDag-Erling Smørgrav if(fprintf(out, "; autotrust trust anchor file\n" 1841b7579f77SDag-Erling Smørgrav ";;REVOKED\n" 1842b7579f77SDag-Erling Smørgrav ";;id: . 1\n" 1843b7579f77SDag-Erling Smørgrav "; This file was written by unbound-anchor on %s" 1844b7579f77SDag-Erling Smørgrav "; It indicates that the root does not use DNSSEC\n" 1845b7579f77SDag-Erling Smørgrav "; to restart DNSSEC overwrite this file with a\n" 1846b7579f77SDag-Erling Smørgrav "; valid trustanchor or (empty-it and run unbound-anchor)\n" 1847b7579f77SDag-Erling Smørgrav , ctime(&now)) < 0) { 1848b7579f77SDag-Erling Smørgrav if(verb) printf("failed to write 'unsigned' to %s\n", 1849b7579f77SDag-Erling Smørgrav root_anchor_file); 1850b7579f77SDag-Erling Smørgrav if(verb && errno != 0) printf("%s\n", strerror(errno)); 1851b7579f77SDag-Erling Smørgrav } 185205ab2901SDag-Erling Smørgrav fflush(out); 185305ab2901SDag-Erling Smørgrav #ifdef HAVE_FSYNC 185405ab2901SDag-Erling Smørgrav fsync(fileno(out)); 185505ab2901SDag-Erling Smørgrav #else 1856b5663de9SDag-Erling Smørgrav FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(out))); 185705ab2901SDag-Erling Smørgrav #endif 1858b7579f77SDag-Erling Smørgrav fclose(out); 1859b7579f77SDag-Erling Smørgrav } 1860b7579f77SDag-Erling Smørgrav 1861b7579f77SDag-Erling Smørgrav /** write root anchor file */ 1862b7579f77SDag-Erling Smørgrav static void 1863ebc5657fSDag-Erling Smørgrav write_root_anchor(const char* root_anchor_file, BIO* ds) 1864b7579f77SDag-Erling Smørgrav { 1865b7579f77SDag-Erling Smørgrav char* pp = NULL; 1866b7579f77SDag-Erling Smørgrav int len; 1867b7579f77SDag-Erling Smørgrav FILE* out; 1868b7579f77SDag-Erling Smørgrav (void)BIO_seek(ds, 0); 1869b7579f77SDag-Erling Smørgrav len = BIO_get_mem_data(ds, &pp); 1870b7579f77SDag-Erling Smørgrav if(!len || !pp) { 1871b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 1872b7579f77SDag-Erling Smørgrav return; 1873b7579f77SDag-Erling Smørgrav } 1874b7579f77SDag-Erling Smørgrav out = fopen(root_anchor_file, "w"); 1875b7579f77SDag-Erling Smørgrav if(!out) { 1876b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", root_anchor_file, strerror(errno)); 1877b7579f77SDag-Erling Smørgrav return; 1878b7579f77SDag-Erling Smørgrav } 1879b7579f77SDag-Erling Smørgrav if(fwrite(pp, (size_t)len, 1, out) != 1) { 1880b7579f77SDag-Erling Smørgrav if(verb) printf("failed to write all data to %s\n", 1881b7579f77SDag-Erling Smørgrav root_anchor_file); 1882b7579f77SDag-Erling Smørgrav if(verb && errno != 0) printf("%s\n", strerror(errno)); 1883b7579f77SDag-Erling Smørgrav } 188405ab2901SDag-Erling Smørgrav fflush(out); 188505ab2901SDag-Erling Smørgrav #ifdef HAVE_FSYNC 188605ab2901SDag-Erling Smørgrav fsync(fileno(out)); 188705ab2901SDag-Erling Smørgrav #else 1888b5663de9SDag-Erling Smørgrav FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(out))); 188905ab2901SDag-Erling Smørgrav #endif 1890b7579f77SDag-Erling Smørgrav fclose(out); 1891b7579f77SDag-Erling Smørgrav } 1892b7579f77SDag-Erling Smørgrav 1893b7579f77SDag-Erling Smørgrav /** Perform the verification and update of the trustanchor file */ 1894b7579f77SDag-Erling Smørgrav static void 1895ebc5657fSDag-Erling Smørgrav verify_and_update_anchor(const char* root_anchor_file, BIO* xml, BIO* p7s, 1896ebc5657fSDag-Erling Smørgrav STACK_OF(X509)* cert, const char* p7signer) 1897b7579f77SDag-Erling Smørgrav { 1898b7579f77SDag-Erling Smørgrav BIO* ds; 1899b7579f77SDag-Erling Smørgrav 1900b7579f77SDag-Erling Smørgrav /* verify xml file */ 19018ed2b524SDag-Erling Smørgrav if(!verify_p7sig(xml, p7s, cert, p7signer)) { 1902b7579f77SDag-Erling Smørgrav printf("the PKCS7 signature failed\n"); 1903b7579f77SDag-Erling Smørgrav exit(0); 1904b7579f77SDag-Erling Smørgrav } 1905b7579f77SDag-Erling Smørgrav 1906b7579f77SDag-Erling Smørgrav /* parse the xml file into DS records */ 1907b7579f77SDag-Erling Smørgrav ds = xml_parse(xml, time(NULL)); 1908b7579f77SDag-Erling Smørgrav if(!ds) { 1909b7579f77SDag-Erling Smørgrav /* the root zone is unsigned now */ 1910b7579f77SDag-Erling Smørgrav write_unsigned_root(root_anchor_file); 1911b7579f77SDag-Erling Smørgrav } else { 1912b7579f77SDag-Erling Smørgrav /* reinstate 5011 tracking */ 1913b7579f77SDag-Erling Smørgrav write_root_anchor(root_anchor_file, ds); 1914b7579f77SDag-Erling Smørgrav } 1915b7579f77SDag-Erling Smørgrav BIO_free(ds); 1916b7579f77SDag-Erling Smørgrav } 1917b7579f77SDag-Erling Smørgrav 1918b7579f77SDag-Erling Smørgrav #ifdef USE_WINSOCK 1919b7579f77SDag-Erling Smørgrav static void do_wsa_cleanup(void) { WSACleanup(); } 1920b7579f77SDag-Erling Smørgrav #endif 1921b7579f77SDag-Erling Smørgrav 1922b7579f77SDag-Erling Smørgrav /** perform actual certupdate work */ 1923b7579f77SDag-Erling Smørgrav static int 1924ebc5657fSDag-Erling Smørgrav do_certupdate(const char* root_anchor_file, const char* root_cert_file, 1925ebc5657fSDag-Erling Smørgrav const char* urlname, const char* xmlname, const char* p7sname, 1926ebc5657fSDag-Erling Smørgrav const char* p7signer, const char* res_conf, const char* root_hints, 19270eefd307SCy Schubert const char* debugconf, const char* srcaddr, int ip4only, int ip6only, 1928*25039b37SCy Schubert int port, int use_sni) 19290eefd307SCy Schubert 1930b7579f77SDag-Erling Smørgrav { 1931b7579f77SDag-Erling Smørgrav STACK_OF(X509)* cert; 1932b7579f77SDag-Erling Smørgrav BIO *xml, *p7s; 1933b7579f77SDag-Erling Smørgrav struct ip_list* ip_list = NULL; 19340eefd307SCy Schubert struct ip_list* src = NULL; 1935b7579f77SDag-Erling Smørgrav 1936b7579f77SDag-Erling Smørgrav /* read pem file or provide builtin */ 1937b7579f77SDag-Erling Smørgrav cert = read_cert_or_builtin(root_cert_file); 1938b7579f77SDag-Erling Smørgrav 1939b7579f77SDag-Erling Smørgrav /* lookup A, AAAA for the urlname (or parse urlname if IP address) */ 1940b7579f77SDag-Erling Smørgrav ip_list = resolve_name(urlname, port, res_conf, root_hints, debugconf, 19410eefd307SCy Schubert srcaddr, ip4only, ip6only); 19420eefd307SCy Schubert 19430eefd307SCy Schubert if(srcaddr && !(src = parse_ip_addr(srcaddr, 0))) { 19440eefd307SCy Schubert if(verb) printf("cannot parse source address: %s\n", srcaddr); 19450eefd307SCy Schubert exit(0); 19460eefd307SCy Schubert } 1947b7579f77SDag-Erling Smørgrav 1948b7579f77SDag-Erling Smørgrav #ifdef USE_WINSOCK 1949b7579f77SDag-Erling Smørgrav if(1) { /* libunbound finished, startup WSA for the https connection */ 1950b7579f77SDag-Erling Smørgrav WSADATA wsa_data; 1951b7579f77SDag-Erling Smørgrav int r; 1952b7579f77SDag-Erling Smørgrav if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) { 1953b7579f77SDag-Erling Smørgrav if(verb) printf("WSAStartup failed: %s\n", 1954b7579f77SDag-Erling Smørgrav wsa_strerror(r)); 1955b7579f77SDag-Erling Smørgrav exit(0); 1956b7579f77SDag-Erling Smørgrav } 1957b7579f77SDag-Erling Smørgrav atexit(&do_wsa_cleanup); 1958b7579f77SDag-Erling Smørgrav } 1959b7579f77SDag-Erling Smørgrav #endif 1960b7579f77SDag-Erling Smørgrav 1961b7579f77SDag-Erling Smørgrav /* fetch the necessary files over HTTPS */ 1962*25039b37SCy Schubert xml = https(ip_list, xmlname, urlname, src, use_sni); 1963*25039b37SCy Schubert p7s = https(ip_list, p7sname, urlname, src, use_sni); 1964b7579f77SDag-Erling Smørgrav 1965b7579f77SDag-Erling Smørgrav /* verify and update the root anchor */ 19668ed2b524SDag-Erling Smørgrav verify_and_update_anchor(root_anchor_file, xml, p7s, cert, p7signer); 1967b7579f77SDag-Erling Smørgrav if(verb) printf("success: the anchor has been updated " 1968b7579f77SDag-Erling Smørgrav "using the cert\n"); 1969b7579f77SDag-Erling Smørgrav 1970a755b6f6SDag-Erling Smørgrav BIO_free(xml); 1971a755b6f6SDag-Erling Smørgrav BIO_free(p7s); 1972b7579f77SDag-Erling Smørgrav #ifndef S_SPLINT_S 1973b7579f77SDag-Erling Smørgrav sk_X509_pop_free(cert, X509_free); 1974b7579f77SDag-Erling Smørgrav #endif 1975b7579f77SDag-Erling Smørgrav ip_list_free(ip_list); 1976b7579f77SDag-Erling Smørgrav return 1; 1977b7579f77SDag-Erling Smørgrav } 1978b7579f77SDag-Erling Smørgrav 1979b7579f77SDag-Erling Smørgrav /** 1980b7579f77SDag-Erling Smørgrav * Try to read the root RFC5011 autotrust anchor file, 1981b7579f77SDag-Erling Smørgrav * @param file: filename. 1982b7579f77SDag-Erling Smørgrav * @return: 1983b7579f77SDag-Erling Smørgrav * 0 if does not exist or empty 1984b7579f77SDag-Erling Smørgrav * 1 if trust-point-revoked-5011 1985b7579f77SDag-Erling Smørgrav * 2 if it is OK. 1986b7579f77SDag-Erling Smørgrav */ 1987b7579f77SDag-Erling Smørgrav static int 1988ebc5657fSDag-Erling Smørgrav try_read_anchor(const char* file) 1989b7579f77SDag-Erling Smørgrav { 1990b7579f77SDag-Erling Smørgrav int empty = 1; 1991b7579f77SDag-Erling Smørgrav char line[10240]; 1992b7579f77SDag-Erling Smørgrav char* p; 1993b7579f77SDag-Erling Smørgrav FILE* in = fopen(file, "r"); 1994b7579f77SDag-Erling Smørgrav if(!in) { 1995b7579f77SDag-Erling Smørgrav /* only if the file does not exist, can we fix it */ 1996b7579f77SDag-Erling Smørgrav if(errno != ENOENT) { 1997b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", file, strerror(errno)); 1998b7579f77SDag-Erling Smørgrav if(verb) printf("error: cannot access the file\n"); 1999b7579f77SDag-Erling Smørgrav exit(0); 2000b7579f77SDag-Erling Smørgrav } 2001b7579f77SDag-Erling Smørgrav if(verb) printf("%s does not exist\n", file); 2002b7579f77SDag-Erling Smørgrav return 0; 2003b7579f77SDag-Erling Smørgrav } 2004b7579f77SDag-Erling Smørgrav while(fgets(line, (int)sizeof(line), in)) { 2005b7579f77SDag-Erling Smørgrav line[sizeof(line)-1] = 0; 2006b7579f77SDag-Erling Smørgrav if(strncmp(line, ";;REVOKED", 9) == 0) { 2007b7579f77SDag-Erling Smørgrav fclose(in); 2008b7579f77SDag-Erling Smørgrav if(verb) printf("%s : the trust point is revoked\n" 2009b7579f77SDag-Erling Smørgrav "and the zone is considered unsigned.\n" 2010b7579f77SDag-Erling Smørgrav "if you wish to re-enable, delete the file\n", 2011b7579f77SDag-Erling Smørgrav file); 2012b7579f77SDag-Erling Smørgrav return 1; 2013b7579f77SDag-Erling Smørgrav } 2014b7579f77SDag-Erling Smørgrav p=line; 2015b7579f77SDag-Erling Smørgrav while(*p == ' ' || *p == '\t') 2016b7579f77SDag-Erling Smørgrav p++; 2017b7579f77SDag-Erling Smørgrav if(p[0]==0 || p[0]=='\n' || p[0]==';') continue; 2018b7579f77SDag-Erling Smørgrav /* this line is a line of content */ 2019b7579f77SDag-Erling Smørgrav empty = 0; 2020b7579f77SDag-Erling Smørgrav } 2021b7579f77SDag-Erling Smørgrav fclose(in); 2022b7579f77SDag-Erling Smørgrav if(empty) { 2023b7579f77SDag-Erling Smørgrav if(verb) printf("%s is empty\n", file); 2024b7579f77SDag-Erling Smørgrav return 0; 2025b7579f77SDag-Erling Smørgrav } 2026b7579f77SDag-Erling Smørgrav if(verb) printf("%s has content\n", file); 2027b7579f77SDag-Erling Smørgrav return 2; 2028b7579f77SDag-Erling Smørgrav } 2029b7579f77SDag-Erling Smørgrav 2030b7579f77SDag-Erling Smørgrav /** Write the builtin root anchor to a file */ 2031b7579f77SDag-Erling Smørgrav static void 2032ebc5657fSDag-Erling Smørgrav write_builtin_anchor(const char* file) 2033b7579f77SDag-Erling Smørgrav { 2034b7579f77SDag-Erling Smørgrav const char* builtin_root_anchor = get_builtin_ds(); 2035b7579f77SDag-Erling Smørgrav FILE* out = fopen(file, "w"); 2036b7579f77SDag-Erling Smørgrav if(!out) { 2037b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", file, strerror(errno)); 2038b7579f77SDag-Erling Smørgrav if(verb) printf(" could not write builtin anchor\n"); 2039b7579f77SDag-Erling Smørgrav return; 2040b7579f77SDag-Erling Smørgrav } 2041b7579f77SDag-Erling Smørgrav if(!fwrite(builtin_root_anchor, strlen(builtin_root_anchor), 1, out)) { 2042b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", file, strerror(errno)); 2043b7579f77SDag-Erling Smørgrav if(verb) printf(" could not complete write builtin anchor\n"); 2044b7579f77SDag-Erling Smørgrav } 2045b7579f77SDag-Erling Smørgrav fclose(out); 2046b7579f77SDag-Erling Smørgrav } 2047b7579f77SDag-Erling Smørgrav 2048b7579f77SDag-Erling Smørgrav /** 2049b7579f77SDag-Erling Smørgrav * Check the root anchor file. 2050b7579f77SDag-Erling Smørgrav * If does not exist, provide builtin and write file. 2051b7579f77SDag-Erling Smørgrav * If empty, provide builtin and write file. 2052b7579f77SDag-Erling Smørgrav * If trust-point-revoked-5011 file: make the program exit. 2053b7579f77SDag-Erling Smørgrav * @param root_anchor_file: filename of the root anchor. 2054b7579f77SDag-Erling Smørgrav * @param used_builtin: set to 1 if the builtin is written. 2055b7579f77SDag-Erling Smørgrav * @return 0 if trustpoint is insecure, 1 on success. Exit on failure. 2056b7579f77SDag-Erling Smørgrav */ 2057b7579f77SDag-Erling Smørgrav static int 2058ebc5657fSDag-Erling Smørgrav provide_builtin(const char* root_anchor_file, int* used_builtin) 2059b7579f77SDag-Erling Smørgrav { 2060b7579f77SDag-Erling Smørgrav /* try to read it */ 2061b7579f77SDag-Erling Smørgrav switch(try_read_anchor(root_anchor_file)) 2062b7579f77SDag-Erling Smørgrav { 2063b7579f77SDag-Erling Smørgrav case 0: /* no exist or empty */ 2064b7579f77SDag-Erling Smørgrav write_builtin_anchor(root_anchor_file); 2065b7579f77SDag-Erling Smørgrav *used_builtin = 1; 2066b7579f77SDag-Erling Smørgrav break; 2067b7579f77SDag-Erling Smørgrav case 1: /* revoked tp */ 2068b7579f77SDag-Erling Smørgrav return 0; 2069b7579f77SDag-Erling Smørgrav case 2: /* it is fine */ 2070b7579f77SDag-Erling Smørgrav default: 2071b7579f77SDag-Erling Smørgrav break; 2072b7579f77SDag-Erling Smørgrav } 2073b7579f77SDag-Erling Smørgrav return 1; 2074b7579f77SDag-Erling Smørgrav } 2075b7579f77SDag-Erling Smørgrav 2076b7579f77SDag-Erling Smørgrav /** 2077b7579f77SDag-Erling Smørgrav * add an autotrust anchor for the root to the context 2078b7579f77SDag-Erling Smørgrav */ 2079b7579f77SDag-Erling Smørgrav static void 2080ebc5657fSDag-Erling Smørgrav add_5011_probe_root(struct ub_ctx* ctx, const char* root_anchor_file) 2081b7579f77SDag-Erling Smørgrav { 2082b7579f77SDag-Erling Smørgrav int r; 2083b7579f77SDag-Erling Smørgrav r = ub_ctx_set_option(ctx, "auto-trust-anchor-file:", root_anchor_file); 2084b7579f77SDag-Erling Smørgrav if(r) { 2085b7579f77SDag-Erling Smørgrav if(verb) printf("add 5011 probe to ctx: %s\n", ub_strerror(r)); 2086b7579f77SDag-Erling Smørgrav ub_ctx_delete(ctx); 2087b7579f77SDag-Erling Smørgrav exit(0); 2088b7579f77SDag-Erling Smørgrav } 2089b7579f77SDag-Erling Smørgrav } 2090b7579f77SDag-Erling Smørgrav 2091b7579f77SDag-Erling Smørgrav /** 2092b7579f77SDag-Erling Smørgrav * Prime the root key and return the result. Exit on error. 2093b7579f77SDag-Erling Smørgrav * @param ctx: the unbound context to perform the priming with. 2094b7579f77SDag-Erling Smørgrav * @return: the result of the prime, on error it exit()s. 2095b7579f77SDag-Erling Smørgrav */ 2096b7579f77SDag-Erling Smørgrav static struct ub_result* 2097b7579f77SDag-Erling Smørgrav prime_root_key(struct ub_ctx* ctx) 2098b7579f77SDag-Erling Smørgrav { 2099b7579f77SDag-Erling Smørgrav struct ub_result* res = NULL; 2100b7579f77SDag-Erling Smørgrav int r; 2101b7579f77SDag-Erling Smørgrav r = ub_resolve(ctx, ".", LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN, &res); 2102b7579f77SDag-Erling Smørgrav if(r) { 2103b7579f77SDag-Erling Smørgrav if(verb) printf("resolve DNSKEY: %s\n", ub_strerror(r)); 2104b7579f77SDag-Erling Smørgrav ub_ctx_delete(ctx); 2105b7579f77SDag-Erling Smørgrav exit(0); 2106b7579f77SDag-Erling Smørgrav } 2107b7579f77SDag-Erling Smørgrav if(!res) { 2108b7579f77SDag-Erling Smørgrav if(verb) printf("out of memory\n"); 2109b7579f77SDag-Erling Smørgrav ub_ctx_delete(ctx); 2110b7579f77SDag-Erling Smørgrav exit(0); 2111b7579f77SDag-Erling Smørgrav } 2112b7579f77SDag-Erling Smørgrav return res; 2113b7579f77SDag-Erling Smørgrav } 2114b7579f77SDag-Erling Smørgrav 2115b7579f77SDag-Erling Smørgrav /** see if ADDPEND keys exist in autotrust file (if possible) */ 2116b7579f77SDag-Erling Smørgrav static int 2117ebc5657fSDag-Erling Smørgrav read_if_pending_keys(const char* file) 2118b7579f77SDag-Erling Smørgrav { 2119b7579f77SDag-Erling Smørgrav FILE* in = fopen(file, "r"); 2120b7579f77SDag-Erling Smørgrav char line[8192]; 2121b7579f77SDag-Erling Smørgrav if(!in) { 2122b7579f77SDag-Erling Smørgrav if(verb>=2) printf("%s: %s\n", file, strerror(errno)); 2123b7579f77SDag-Erling Smørgrav return 0; 2124b7579f77SDag-Erling Smørgrav } 2125b7579f77SDag-Erling Smørgrav while(fgets(line, (int)sizeof(line), in)) { 2126b7579f77SDag-Erling Smørgrav if(line[0]==';') continue; 2127b7579f77SDag-Erling Smørgrav if(strstr(line, "[ ADDPEND ]")) { 2128b7579f77SDag-Erling Smørgrav fclose(in); 2129b7579f77SDag-Erling Smørgrav if(verb) printf("RFC5011-state has ADDPEND keys\n"); 2130b7579f77SDag-Erling Smørgrav return 1; 2131b7579f77SDag-Erling Smørgrav } 2132b7579f77SDag-Erling Smørgrav } 2133b7579f77SDag-Erling Smørgrav fclose(in); 2134b7579f77SDag-Erling Smørgrav return 0; 2135b7579f77SDag-Erling Smørgrav } 2136b7579f77SDag-Erling Smørgrav 2137b7579f77SDag-Erling Smørgrav /** read last successful probe time from autotrust file (if possible) */ 2138b7579f77SDag-Erling Smørgrav static int32_t 2139ebc5657fSDag-Erling Smørgrav read_last_success_time(const char* file) 2140b7579f77SDag-Erling Smørgrav { 2141b7579f77SDag-Erling Smørgrav FILE* in = fopen(file, "r"); 2142b7579f77SDag-Erling Smørgrav char line[1024]; 2143b7579f77SDag-Erling Smørgrav if(!in) { 2144b7579f77SDag-Erling Smørgrav if(verb) printf("%s: %s\n", file, strerror(errno)); 2145b7579f77SDag-Erling Smørgrav return 0; 2146b7579f77SDag-Erling Smørgrav } 2147b7579f77SDag-Erling Smørgrav while(fgets(line, (int)sizeof(line), in)) { 2148b7579f77SDag-Erling Smørgrav if(strncmp(line, ";;last_success: ", 16) == 0) { 2149b7579f77SDag-Erling Smørgrav char* e; 2150b7579f77SDag-Erling Smørgrav time_t x = (unsigned int)strtol(line+16, &e, 10); 2151b7579f77SDag-Erling Smørgrav fclose(in); 2152b7579f77SDag-Erling Smørgrav if(line+16 == e) { 2153b7579f77SDag-Erling Smørgrav if(verb) printf("failed to parse " 2154b7579f77SDag-Erling Smørgrav "last_success probe time\n"); 2155b7579f77SDag-Erling Smørgrav return 0; 2156b7579f77SDag-Erling Smørgrav } 2157b7579f77SDag-Erling Smørgrav if(verb) printf("last successful probe: %s", ctime(&x)); 2158b7579f77SDag-Erling Smørgrav return (int32_t)x; 2159b7579f77SDag-Erling Smørgrav } 2160b7579f77SDag-Erling Smørgrav } 2161b7579f77SDag-Erling Smørgrav fclose(in); 2162b7579f77SDag-Erling Smørgrav if(verb) printf("no last_success probe time in anchor file\n"); 2163b7579f77SDag-Erling Smørgrav return 0; 2164b7579f77SDag-Erling Smørgrav } 2165b7579f77SDag-Erling Smørgrav 2166b7579f77SDag-Erling Smørgrav /** 2167b7579f77SDag-Erling Smørgrav * Read autotrust 5011 probe file and see if the date 2168b7579f77SDag-Erling Smørgrav * compared to the current date allows a certupdate. 2169b7579f77SDag-Erling Smørgrav * If the last successful probe was recent then 5011 cannot be behind, 2170b7579f77SDag-Erling Smørgrav * and the failure cannot be solved with a certupdate. 2171b7579f77SDag-Erling Smørgrav * The debugconf is to validation-override the date for testing. 2172b7579f77SDag-Erling Smørgrav * @param root_anchor_file: filename of root key 2173b7579f77SDag-Erling Smørgrav * @return true if certupdate is ok. 2174b7579f77SDag-Erling Smørgrav */ 2175b7579f77SDag-Erling Smørgrav static int 2176ebc5657fSDag-Erling Smørgrav probe_date_allows_certupdate(const char* root_anchor_file) 2177b7579f77SDag-Erling Smørgrav { 2178b7579f77SDag-Erling Smørgrav int has_pending_keys = read_if_pending_keys(root_anchor_file); 2179b7579f77SDag-Erling Smørgrav int32_t last_success = read_last_success_time(root_anchor_file); 2180b7579f77SDag-Erling Smørgrav int32_t now = (int32_t)time(NULL); 2181b7579f77SDag-Erling Smørgrav int32_t leeway = 30 * 24 * 3600; /* 30 days leeway */ 2182b7579f77SDag-Erling Smørgrav /* if the date is before 2010-07-15:00.00.00 then the root has not 2183b7579f77SDag-Erling Smørgrav * been signed yet, and thus we refuse to take action. */ 2184b7579f77SDag-Erling Smørgrav if(time(NULL) < xml_convertdate("2010-07-15T00:00:00")) { 2185b7579f77SDag-Erling Smørgrav if(verb) printf("the date is before the root was first signed," 2186b7579f77SDag-Erling Smørgrav " please correct the clock\n"); 2187b7579f77SDag-Erling Smørgrav return 0; 2188b7579f77SDag-Erling Smørgrav } 2189b7579f77SDag-Erling Smørgrav if(last_success == 0) 2190b7579f77SDag-Erling Smørgrav return 1; /* no probe time */ 2191b7579f77SDag-Erling Smørgrav if(has_pending_keys) 2192b7579f77SDag-Erling Smørgrav return 1; /* key in ADDPEND state, a previous probe has 2193b7579f77SDag-Erling Smørgrav inserted that, and it was present in all recent probes, 2194b7579f77SDag-Erling Smørgrav but it has not become active. The 30 day timer may not have 2195b7579f77SDag-Erling Smørgrav expired, but we know(for sure) there is a rollover going on. 2196b7579f77SDag-Erling Smørgrav If we only managed to pickup the new key on its last day 2197b7579f77SDag-Erling Smørgrav of announcement (for example) this can happen. */ 2198b7579f77SDag-Erling Smørgrav if(now - last_success < 0) { 2199b7579f77SDag-Erling Smørgrav if(verb) printf("the last successful probe is in the future," 2200b7579f77SDag-Erling Smørgrav " clock was modified\n"); 2201b7579f77SDag-Erling Smørgrav return 0; 2202b7579f77SDag-Erling Smørgrav } 2203b7579f77SDag-Erling Smørgrav if(now - last_success >= leeway) { 2204b7579f77SDag-Erling Smørgrav if(verb) printf("the last successful probe was more than 30 " 2205b7579f77SDag-Erling Smørgrav "days ago\n"); 2206b7579f77SDag-Erling Smørgrav return 1; 2207b7579f77SDag-Erling Smørgrav } 2208b7579f77SDag-Erling Smørgrav if(verb) printf("the last successful probe is recent\n"); 2209b7579f77SDag-Erling Smørgrav return 0; 2210b7579f77SDag-Erling Smørgrav } 2211b7579f77SDag-Erling Smørgrav 22124c75e3aaSDag-Erling Smørgrav static struct ub_result * 22134c75e3aaSDag-Erling Smørgrav fetch_root_key(const char* root_anchor_file, const char* res_conf, 22140eefd307SCy Schubert const char* root_hints, const char* debugconf, const char* srcaddr, 22154c75e3aaSDag-Erling Smørgrav int ip4only, int ip6only) 22164c75e3aaSDag-Erling Smørgrav { 22174c75e3aaSDag-Erling Smørgrav struct ub_ctx* ctx; 22184c75e3aaSDag-Erling Smørgrav struct ub_result* dnskey; 22194c75e3aaSDag-Erling Smørgrav 22204c75e3aaSDag-Erling Smørgrav ctx = create_unbound_context(res_conf, root_hints, debugconf, 22210eefd307SCy Schubert srcaddr, ip4only, ip6only); 22224c75e3aaSDag-Erling Smørgrav add_5011_probe_root(ctx, root_anchor_file); 22234c75e3aaSDag-Erling Smørgrav dnskey = prime_root_key(ctx); 22244c75e3aaSDag-Erling Smørgrav ub_ctx_delete(ctx); 22254c75e3aaSDag-Erling Smørgrav return dnskey; 22264c75e3aaSDag-Erling Smørgrav } 22274c75e3aaSDag-Erling Smørgrav 2228b7579f77SDag-Erling Smørgrav /** perform the unbound-anchor work */ 2229b7579f77SDag-Erling Smørgrav static int 2230ebc5657fSDag-Erling Smørgrav do_root_update_work(const char* root_anchor_file, const char* root_cert_file, 2231ebc5657fSDag-Erling Smørgrav const char* urlname, const char* xmlname, const char* p7sname, 2232ebc5657fSDag-Erling Smørgrav const char* p7signer, const char* res_conf, const char* root_hints, 22330eefd307SCy Schubert const char* debugconf, const char* srcaddr, int ip4only, int ip6only, 2234*25039b37SCy Schubert int force, int res_conf_fallback, int port, int use_sni) 2235b7579f77SDag-Erling Smørgrav { 2236b7579f77SDag-Erling Smørgrav struct ub_result* dnskey; 2237b7579f77SDag-Erling Smørgrav int used_builtin = 0; 22384c75e3aaSDag-Erling Smørgrav int rcode; 2239b7579f77SDag-Erling Smørgrav 2240b7579f77SDag-Erling Smørgrav /* see if builtin rootanchor needs to be provided, or if 2241b7579f77SDag-Erling Smørgrav * rootanchor is 'revoked-trust-point' */ 2242b7579f77SDag-Erling Smørgrav if(!provide_builtin(root_anchor_file, &used_builtin)) 2243b7579f77SDag-Erling Smørgrav return 0; 2244b7579f77SDag-Erling Smørgrav 2245b7579f77SDag-Erling Smørgrav /* make unbound context with 5011-probe for root anchor, 2246b7579f77SDag-Erling Smørgrav * and probe . DNSKEY */ 22474c75e3aaSDag-Erling Smørgrav dnskey = fetch_root_key(root_anchor_file, res_conf, 22480eefd307SCy Schubert root_hints, debugconf, srcaddr, ip4only, ip6only); 22494c75e3aaSDag-Erling Smørgrav rcode = dnskey->rcode; 22504c75e3aaSDag-Erling Smørgrav 22514c75e3aaSDag-Erling Smørgrav if (res_conf_fallback && res_conf && !dnskey->secure) { 22524c75e3aaSDag-Erling Smørgrav if (verb) printf("%s failed, retrying direct\n", res_conf); 22534c75e3aaSDag-Erling Smørgrav ub_resolve_free(dnskey); 22544c75e3aaSDag-Erling Smørgrav /* try direct query without res_conf */ 22554c75e3aaSDag-Erling Smørgrav dnskey = fetch_root_key(root_anchor_file, NULL, 22560eefd307SCy Schubert root_hints, debugconf, srcaddr, ip4only, ip6only); 22574c75e3aaSDag-Erling Smørgrav if (rcode != 0 && dnskey->rcode == 0) { 22584c75e3aaSDag-Erling Smørgrav res_conf = NULL; 22594c75e3aaSDag-Erling Smørgrav rcode = 0; 22604c75e3aaSDag-Erling Smørgrav } 22614c75e3aaSDag-Erling Smørgrav } 2262b7579f77SDag-Erling Smørgrav 2263b7579f77SDag-Erling Smørgrav /* if secure: exit */ 2264b7579f77SDag-Erling Smørgrav if(dnskey->secure && !force) { 2265b7579f77SDag-Erling Smørgrav if(verb) printf("success: the anchor is ok\n"); 2266b7579f77SDag-Erling Smørgrav ub_resolve_free(dnskey); 2267b7579f77SDag-Erling Smørgrav return used_builtin; 2268b7579f77SDag-Erling Smørgrav } 2269b7579f77SDag-Erling Smørgrav if(force && verb) printf("debug cert update forced\n"); 22704c75e3aaSDag-Erling Smørgrav ub_resolve_free(dnskey); 2271b7579f77SDag-Erling Smørgrav 2272b7579f77SDag-Erling Smørgrav /* if not (and NOERROR): check date and do certupdate */ 22734c75e3aaSDag-Erling Smørgrav if((rcode == 0 && 2274b7579f77SDag-Erling Smørgrav probe_date_allows_certupdate(root_anchor_file)) || force) { 2275b7579f77SDag-Erling Smørgrav if(do_certupdate(root_anchor_file, root_cert_file, urlname, 22768ed2b524SDag-Erling Smørgrav xmlname, p7sname, p7signer, res_conf, root_hints, 2277*25039b37SCy Schubert debugconf, srcaddr, ip4only, ip6only, port, use_sni)) 2278b7579f77SDag-Erling Smørgrav return 1; 2279b7579f77SDag-Erling Smørgrav return used_builtin; 2280b7579f77SDag-Erling Smørgrav } 2281b7579f77SDag-Erling Smørgrav if(verb) printf("fail: the anchor is NOT ok and could not be fixed\n"); 2282b7579f77SDag-Erling Smørgrav return used_builtin; 2283b7579f77SDag-Erling Smørgrav } 2284b7579f77SDag-Erling Smørgrav 2285b7579f77SDag-Erling Smørgrav /** getopt global, in case header files fail to declare it. */ 2286b7579f77SDag-Erling Smørgrav extern int optind; 2287b7579f77SDag-Erling Smørgrav /** getopt global, in case header files fail to declare it. */ 2288b7579f77SDag-Erling Smørgrav extern char* optarg; 2289b7579f77SDag-Erling Smørgrav 2290b7579f77SDag-Erling Smørgrav /** Main routine for unbound-anchor */ 2291b7579f77SDag-Erling Smørgrav int main(int argc, char* argv[]) 2292b7579f77SDag-Erling Smørgrav { 2293b7579f77SDag-Erling Smørgrav int c; 2294ebc5657fSDag-Erling Smørgrav const char* root_anchor_file = ROOT_ANCHOR_FILE; 2295ebc5657fSDag-Erling Smørgrav const char* root_cert_file = ROOT_CERT_FILE; 2296ebc5657fSDag-Erling Smørgrav const char* urlname = URLNAME; 2297ebc5657fSDag-Erling Smørgrav const char* xmlname = XMLNAME; 2298ebc5657fSDag-Erling Smørgrav const char* p7sname = P7SNAME; 2299ebc5657fSDag-Erling Smørgrav const char* p7signer = P7SIGNER; 2300ebc5657fSDag-Erling Smørgrav const char* res_conf = NULL; 2301ebc5657fSDag-Erling Smørgrav const char* root_hints = NULL; 2302ebc5657fSDag-Erling Smørgrav const char* debugconf = NULL; 23030eefd307SCy Schubert const char* srcaddr = NULL; 2304b7579f77SDag-Erling Smørgrav int dolist=0, ip4only=0, ip6only=0, force=0, port = HTTPS_PORT; 23054c75e3aaSDag-Erling Smørgrav int res_conf_fallback = 0; 2306*25039b37SCy Schubert int use_sni = 1; 2307b7579f77SDag-Erling Smørgrav /* parse the options */ 2308*25039b37SCy Schubert while( (c=getopt(argc, argv, "46C:FRSP:a:b:c:f:hln:r:s:u:vx:")) != -1) { 2309b7579f77SDag-Erling Smørgrav switch(c) { 2310b7579f77SDag-Erling Smørgrav case 'l': 2311b7579f77SDag-Erling Smørgrav dolist = 1; 2312b7579f77SDag-Erling Smørgrav break; 2313b7579f77SDag-Erling Smørgrav case '4': 2314b7579f77SDag-Erling Smørgrav ip4only = 1; 2315b7579f77SDag-Erling Smørgrav break; 2316b7579f77SDag-Erling Smørgrav case '6': 2317b7579f77SDag-Erling Smørgrav ip6only = 1; 2318b7579f77SDag-Erling Smørgrav break; 2319b7579f77SDag-Erling Smørgrav case 'a': 2320b7579f77SDag-Erling Smørgrav root_anchor_file = optarg; 2321b7579f77SDag-Erling Smørgrav break; 23220eefd307SCy Schubert case 'b': 23230eefd307SCy Schubert srcaddr = optarg; 23240eefd307SCy Schubert break; 2325b7579f77SDag-Erling Smørgrav case 'c': 2326b7579f77SDag-Erling Smørgrav root_cert_file = optarg; 2327b7579f77SDag-Erling Smørgrav break; 2328b7579f77SDag-Erling Smørgrav case 'u': 2329b7579f77SDag-Erling Smørgrav urlname = optarg; 2330b7579f77SDag-Erling Smørgrav break; 2331*25039b37SCy Schubert case 'S': 2332*25039b37SCy Schubert use_sni = 0; 2333*25039b37SCy Schubert break; 2334b7579f77SDag-Erling Smørgrav case 'x': 2335b7579f77SDag-Erling Smørgrav xmlname = optarg; 2336b7579f77SDag-Erling Smørgrav break; 2337b7579f77SDag-Erling Smørgrav case 's': 2338b7579f77SDag-Erling Smørgrav p7sname = optarg; 2339b7579f77SDag-Erling Smørgrav break; 23408ed2b524SDag-Erling Smørgrav case 'n': 23418ed2b524SDag-Erling Smørgrav p7signer = optarg; 23428ed2b524SDag-Erling Smørgrav break; 2343b7579f77SDag-Erling Smørgrav case 'f': 2344b7579f77SDag-Erling Smørgrav res_conf = optarg; 2345b7579f77SDag-Erling Smørgrav break; 2346b7579f77SDag-Erling Smørgrav case 'r': 2347b7579f77SDag-Erling Smørgrav root_hints = optarg; 2348b7579f77SDag-Erling Smørgrav break; 23494c75e3aaSDag-Erling Smørgrav case 'R': 23504c75e3aaSDag-Erling Smørgrav res_conf_fallback = 1; 23514c75e3aaSDag-Erling Smørgrav break; 2352b7579f77SDag-Erling Smørgrav case 'C': 2353b7579f77SDag-Erling Smørgrav debugconf = optarg; 2354b7579f77SDag-Erling Smørgrav break; 2355b7579f77SDag-Erling Smørgrav case 'F': 2356b7579f77SDag-Erling Smørgrav force = 1; 2357b7579f77SDag-Erling Smørgrav break; 2358b7579f77SDag-Erling Smørgrav case 'P': 2359b7579f77SDag-Erling Smørgrav port = atoi(optarg); 2360b7579f77SDag-Erling Smørgrav break; 2361b7579f77SDag-Erling Smørgrav case 'v': 2362b7579f77SDag-Erling Smørgrav verb++; 2363b7579f77SDag-Erling Smørgrav break; 2364b7579f77SDag-Erling Smørgrav case '?': 2365b7579f77SDag-Erling Smørgrav case 'h': 2366b7579f77SDag-Erling Smørgrav default: 2367b7579f77SDag-Erling Smørgrav usage(); 2368b7579f77SDag-Erling Smørgrav } 2369b7579f77SDag-Erling Smørgrav } 2370b7579f77SDag-Erling Smørgrav argc -= optind; 2371a755b6f6SDag-Erling Smørgrav /* argv += optind; not using further arguments */ 2372b7579f77SDag-Erling Smørgrav if(argc != 0) 2373b7579f77SDag-Erling Smørgrav usage(); 2374b7579f77SDag-Erling Smørgrav 2375b5663de9SDag-Erling Smørgrav #ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS 2376b7579f77SDag-Erling Smørgrav ERR_load_crypto_strings(); 2377b5663de9SDag-Erling Smørgrav #endif 2378971980c3SDag-Erling Smørgrav #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 2379b7579f77SDag-Erling Smørgrav ERR_load_SSL_strings(); 2380971980c3SDag-Erling Smørgrav #endif 2381b5663de9SDag-Erling Smørgrav #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) 23820eefd307SCy Schubert # ifndef S_SPLINT_S 2383b7579f77SDag-Erling Smørgrav OpenSSL_add_all_algorithms(); 23840eefd307SCy Schubert # endif 2385b5663de9SDag-Erling Smørgrav #else 2386b5663de9SDag-Erling Smørgrav OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS 2387b5663de9SDag-Erling Smørgrav | OPENSSL_INIT_ADD_ALL_DIGESTS 2388b5663de9SDag-Erling Smørgrav | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); 2389b5663de9SDag-Erling Smørgrav #endif 2390b5663de9SDag-Erling Smørgrav #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 2391b7579f77SDag-Erling Smørgrav (void)SSL_library_init(); 2392b5663de9SDag-Erling Smørgrav #else 2393971980c3SDag-Erling Smørgrav (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); 2394b5663de9SDag-Erling Smørgrav #endif 2395b7579f77SDag-Erling Smørgrav 2396b7579f77SDag-Erling Smørgrav if(dolist) do_list_builtin(); 2397b7579f77SDag-Erling Smørgrav 2398b7579f77SDag-Erling Smørgrav return do_root_update_work(root_anchor_file, root_cert_file, urlname, 23998ed2b524SDag-Erling Smørgrav xmlname, p7sname, p7signer, res_conf, root_hints, debugconf, 2400*25039b37SCy Schubert srcaddr, ip4only, ip6only, force, res_conf_fallback, port, use_sni); 2401b7579f77SDag-Erling Smørgrav } 2402